This is the full developer documentation for Mantle JS # Mantle JS > Build real-time APIs in TypeScript with clean layers — services as contracts, swappable data adapters, and one hook pipeline for REST, sockets, and AI agents. ```bash npm create mantlejs@latest my-app ``` Services are contracts `Service` defines the API; `Repository` adapters do the data access. Swap Postgres for MongoDB or DynamoDB without touching a service. One hook pipeline Authentication, validation, logging, and auditing are plain function hooks that run identically for REST, Socket.IO, and MCP callers. Agent-native Expose services to AI agents over MCP — per-method tools or a sandboxed code mode — with scoped, revocable agent tokens and an audit trail. Batteries, unbundled Eight SQL, document, graph, and vector adapters; local and seven OAuth sign-in strategies; uploads to disk, S3, or GCS; OpenAPI; realtime sync — one package each. ## Explore [Section titled “Explore”](#explore) [Getting started](/getting-started/)Scaffold a project or write the smallest Mantle app by hand. [Adapters & queries](/guides/adapters/)Every Repository adapter and the query operators each one supports. [Agents (MCP)](/guides/agents/)Tool mode, code mode, agent tokens, and the audit trail. [UI blocks](/blocks/)React Aria blocks for shadcn/ui, wired to Mantle — install with shadcn add. [Packages](/packages/mantle/)The README of every @mantlejs package. [API reference](/api/)Generated from each package's TypeScript exports. Building with an AI assistant? Point it at [`/llms.txt`](/llms.txt) — or [`/llms-full.txt`](/llms-full.txt) for every guide and package README in one file. # Architecture > Mantle's layers, the inward-only dependency rule, and why services are contracts rather than data-access classes. Mantle is a layered framework in the spirit of Clean and Onion Architecture. The name is the metaphor: the application layer is the geological *mantle* — between the database at the core and the UI at the crust. ```text CRUST (UI) React, Vue, Angular, iOS, Android Transport layer Express / Koa / HTTP / Socket.IO / MCP — route calls to services Application layer Service contracts, the hook pipeline ← the mantle Domain layer (core) Entities, Repository interfaces Infrastructure KnexRepository, MongoRepository, … — implement the Domain interfaces ``` ## The dependency rule [Section titled “The dependency rule”](#the-dependency-rule) Dependencies always point **inward**: * Infrastructure implements interfaces the Domain defines — never the reverse. A service depends on `Repository`, not on `KnexRepository`. * Nothing in the Domain or Application layers knows about HTTP or databases. A hook that needs to tell callers apart reads `context.provider` (`"rest"` for Express, `"koa"`, `"http"`, `"socket.io"`, `"mcp"`, or `undefined` for an internal call), never a request object. * Transports are thin: they translate a request into a service call and run it through the same hook pipeline. REST, Socket.IO, and MCP calls to one service get identical authentication, validation, and auditing — that equivalence is covered by specs, not left to convention. ## Services are contracts [Section titled “Services are contracts”](#services-are-contracts) The closest relative of Mantle is FeathersJS, and the central difference is this: in Feathers a service *is* its data access — one class is use case, repository, and controller at once. In Mantle, `Service` is only a contract: ```typescript interface Service> { find(params?: ServiceParams): Promise>; get(id: Id, params?: ServiceParams): Promise; create(data: D, params?: ServiceParams): Promise; update(id: Id, data: D, params?: ServiceParams): Promise; patch(id: Id, data: D, params?: ServiceParams): Promise; remove(id: Id, params?: ServiceParams): Promise; } ``` Data access lives behind `Repository` (`findAll`, `findById`, `save`, `updateById`, `patchById`, `deleteById`, `count`, …), implemented by an adapter in the Infrastructure layer. `RepositoryService` bridges the two for plain CRUD; anything richer — a service that writes to two stores, or one with custom methods — is a hand-written class that implements `Service` and composes repositories it receives through its constructor. | Concern | FeathersJS | Mantle JS | | ------------------ | ------------------------------- | ------------------------------------------- | | Service definition | Class implementing data + logic | Interface (contract) only | | Data access | Inside the service via adapter | Repository in the Infrastructure layer | | Business logic | Mixed into service methods | Isolated in the Application / Domain layers | | Hooks | Transport-aware | Transport-agnostic | | Swapping databases | Requires service changes | Swap the adapter only | | Testability | Requires mocking the transport | Domain testable with no mocks | ## Hooks [Section titled “Hooks”](#hooks) Hooks are plain functions — `(context: HookContext) => HookContext | Promise`, never classes — registered per service and per method in three phases: ```typescript app.service("users").hooks({ before: { all: [authenticate("jwt")], create: [hashPassword()] }, after: { all: [sanitizeUser()] }, error: { all: [logError()] }, }); ``` A `before` hook that sets `context.result` skips the service call; an `error` hook sees `context.error`. Every cross-cutting package — `@mantlejs/auth`, `@mantlejs/schema`, `@mantlejs/logger`, `@mantlejs/audit`, `@mantlejs/storage`, `@mantlejs/embeddings` — plugs in as hooks rather than middleware, which is what keeps them transport-agnostic. ## Typed errors [Section titled “Typed errors”](#typed-errors) Code never throws a plain `Error`: every failure is a `MantleError` subclass — `BadRequest` (400), `NotAuthenticated` (401), `Forbidden` (403), `NotFound` (404), `Conflict` (409), `Unprocessable` (422), `GeneralError` (500), and a few more. Each serializes to the same JSON over every transport and can carry a `hint`: actionable guidance on how to make the request succeed, written for whoever reads it — a developer, a client SDK, or an AI agent. ## Packages and their boundaries [Section titled “Packages and their boundaries”](#packages-and-their-boundaries) The monorepo holds one package per concern. Which package may import which is part of the architecture, not a convention: every project is tagged in Nx, the root ESLint config turns each row below into an `@nx/enforce-module-boundaries` constraint, and a CI check fails if this table and the configuration drift apart. | Package | May depend on | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | @mantlejs/mantle | nothing | | @mantlejs/express | @mantlejs/mantle | | @mantlejs/koa | @mantlejs/mantle | | @mantlejs/http | @mantlejs/mantle | | @mantlejs/knex | @mantlejs/mantle | | @mantlejs/dynamodb | @mantlejs/mantle | | @mantlejs/supabase | @mantlejs/mantle | | @mantlejs/pinecone | @mantlejs/mantle | | @mantlejs/qdrant | @mantlejs/mantle | | @mantlejs/neo4j | @mantlejs/mantle | | @mantlejs/mongodb | @mantlejs/mantle | | @mantlejs/auth | @mantlejs/mantle | | @mantlejs/auth-local | @mantlejs/mantle, @mantlejs/auth | | @mantlejs/auth-oauth | @mantlejs/mantle, @mantlejs/auth | | @mantlejs/auth-google | @mantlejs/mantle, @mantlejs/auth-oauth | | @mantlejs/auth-github | @mantlejs/mantle, @mantlejs/auth-oauth | | @mantlejs/auth-facebook | @mantlejs/mantle, @mantlejs/auth-oauth | | @mantlejs/auth-apple | @mantlejs/mantle, @mantlejs/auth-oauth | | @mantlejs/auth-microsoft | @mantlejs/mantle, @mantlejs/auth-oauth | | @mantlejs/auth-linkedin | @mantlejs/mantle, @mantlejs/auth-oauth | | @mantlejs/auth-twitter | @mantlejs/mantle, @mantlejs/auth-oauth | | @mantlejs/auth-redis | @mantlejs/mantle, @mantlejs/auth, @mantlejs/auth-oauth | | @mantlejs/storage | @mantlejs/mantle | | @mantlejs/storage-s3 | @mantlejs/mantle, @mantlejs/storage | | @mantlejs/storage-gcs | @mantlejs/mantle, @mantlejs/storage | | @mantlejs/logger | @mantlejs/mantle | | @mantlejs/audit | @mantlejs/mantle (test-only: @mantlejs/memory, @mantlejs/knex) | | @mantlejs/embeddings | @mantlejs/mantle (test-only: @mantlejs/memory) | | @mantlejs/schema | @mantlejs/mantle | | @mantlejs/memory | @mantlejs/mantle | | @mantlejs/config | @mantlejs/mantle | | @mantlejs/socketio | @mantlejs/mantle | | @mantlejs/openapi | @mantlejs/mantle | | @mantlejs/mcp | @mantlejs/mantle (+ @modelcontextprotocol/sdk; test-only: @mantlejs/http, @mantlejs/memory, @mantlejs/auth) | | @mantlejs/mcp-code | @mantlejs/mantle, @mantlejs/mcp (+ quickjs-emscripten-core, @jitl/quickjs-wasmfile-release-sync; test-only: @mantlejs/http, @mantlejs/memory, @mantlejs/auth, @mantlejs/audit) | | @mantlejs/sync | @mantlejs/mantle | | @mantlejs/client | nothing (optional peer: socket.io-client; test-only: @mantlejs/mantle for conformance specs) | | @mantlejs/react | @mantlejs/client (peers: react, @tanstack/react-query) | | @mantlejs/cli | nothing (standalone code generator) | | create-mantlejs | @mantlejs/cli | | ui-registry | @mantlejs/client, @mantlejs/react (`registry/`, unpublished; peers: react, react-aria-components, @tanstack/react-query) | | examples/\* | anything (apps, not libraries — exempt from the boundary rules above, never depended on) | | website | anything (the mantlejs.com docs site — an app like examples/\*, never depended on) | Rendered at build time from [`CLAUDE.md`](https://github.com/MantleJs/mantle/blob/main/CLAUDE.md), so it can't drift from the repository. Each package has a page under [Packages](/packages/mantle/) (its README) and a generated [API reference](/api/). # Mantle UI registry > Copy-in React blocks wired to Mantle — auth forms, OAuth buttons, an upload dropzone, a realtime list, a server-sorted data table, pagination, and a type-ahead search — distributed as a shadcn registry and built on React Aria through shadcn's React Aria base. You shadcn add a block and own its source; nothing here is published to npm. Copy-in React blocks wired to Mantle — auth forms, OAuth buttons, an upload dropzone, a realtime list, a server-sorted data table, pagination, and a type-ahead search — distributed as a [shadcn registry](https://ui.shadcn.com/docs/registry) and built on [React Aria](https://react-spectrum.adobe.com/react-aria/) through shadcn’s React Aria base. You `shadcn add` a block and own its source; nothing here is published to npm. ## Requirements [Section titled “Requirements”](#requirements) * A shadcn project on a **React Aria style** (`aria-nova`, `aria-vega`, …): `npx shadcn init --base aria --preset nova`. Blocks compose React Aria primitives (`TextField` + the aria `Input`, `LinkButton`, `Table` render props) and don’t work on the Radix or Base UI styles — see [Why aria-only](#why-aria-only). * `@mantlejs/client` + `@mantlejs/react` with a `` around your app (`@mantlejs/react`’s README). * Tailwind v4 with the CSS-variable theme `shadcn init` writes. Blocks use only semantic tokens (`bg-primary`, `text-muted-foreground`, `border-border`, …), so they follow your theme and preset. ## Install [Section titled “Install”](#install) Register the namespace once in `components.json`. Keep `{style}` in the URL — it’s how a non-aria project gets refused at install time: ```json { "registries": { "@mantle": "https://mantlejs.com/r/{style}/{name}.json" } } ``` Then add blocks (each pulls in the shadcn primitives and Mantle helpers it needs): ```bash npx shadcn add @mantle/login-form @mantle/signup-form @mantle/oauth-buttons npx shadcn add @mantle/upload-dropzone @mantle/realtime-list @mantle/data-table @mantle/search-combobox ``` Files land under `components/mantle/` (and `lib/mantle-errors.ts`), so they never collide with shadcn’s own blocks. The registry is published with the Mantle website at [mantlejs.com/r/](https://mantlejs.com/r/aria-nova/registry.json), and every block has a live demo under [mantlejs.com/blocks/](https://mantlejs.com/blocks/). To try unreleased changes, serve a local build instead: `npx nx run ui-registry:build-registry`, then `npx serve registry/public` and use `http://localhost:3000/r/{style}/{name}.json`. ## Blocks [Section titled “Blocks”](#blocks) | Item | What it does | Built on | | ------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------- | | `auth-provider` | Session state + `useAuth()`: restores a persisted session, consumes OAuth `#accessToken` redirects | `@mantlejs/react` client | | `login-form` | Email/password against `@mantlejs/auth-local`; field + form-level server errors | RAC `Form`, `TextField`, `FieldError`; aria `Input` | | `signup-form` | Creates the user through your users service, then logs in | RAC `Form`, `TextField`; aria `Input`, `Button` | | `oauth-buttons` | “Continue with …” links to the seven `@mantlejs/auth-*` strategies (text-only, no logos bundled) | aria `LinkButton` | | `upload-dropzone` | Drop or pick files → multipart `POST` to a `handleUpload()` service, with progress | RAC `DropZone`, `FileTrigger`; aria `Progress` | | `realtime-list` | A `find()` result that applies `created`/`patched`/`removed` events in place — no refetch | RAC `GridList` | | `data-table` | Server-side sort (`$sort`) and paging (`$limit`/`$skip`), `$select` of shown fields | aria `Table` (RAC `Table`/`Column`) | | `mantle-pagination` | Page links for a `Paginated` envelope, driving `$skip` | aria `pagination` | | `search-combobox` | Debounced type-ahead over `find()` with `$ilike`, falling back to `$like` | aria `Combobox` (RAC `ComboBox`) | | `mantle-errors` | `toFormErrors()` — `MantleClientError` → React Aria `validationErrors` + a form-level message | — | ## Why aria-only [Section titled “Why aria-only”](#why-aria-only) The `{style}` URL placeholder is filled from the consumer’s `components.json`, and the build ([`scripts/fan-out-styles.mjs`](https://github.com/MantleJs/mantle/blob/main/registry/scripts/fan-out-styles.mjs)) only publishes items under `r/aria-/`. Observed on a `radix-nova` project with a plain `{name}` URL: `shadcn add` succeeds, then `tsc` fails with TS2322 in `data-table`, `login-form`, and `mantle-pagination` — and a block that happens to compile (an `Input` inside a React Aria `TextField`) is silently unwired at runtime. With `{style}`, the same project gets `The item at …/r/radix-nova/login-form.json was not found` before any file is written. The flat `r/.json` files are still published for tooling that can’t template `{style}`. ## Developing [Section titled “Developing”](#developing) This project is itself an `aria-nova` shadcn project (`components.json`, `@/` alias), so blocks are written with the exact imports a consumer gets and type-check/test in place. ```bash npx nx test ui-registry # Vitest + Testing Library + axe (jsdom) npx nx run ui-registry:build-registry # shadcn registry validate → shadcn build → per-style fan-out npx nx run ui-registry:e2e-install # fresh aria-nova + aria-vega apps: shadcn add every block, tsc + vite build; # a radix-nova app must be refused ``` * Block sources: `src/components/mantle/`, `src/lib/mantle-errors.ts`. Register new items in `registry.json` and add them to `e2e/fixture-app.tsx`. * `src/components/ui/` is **vendored verbatim** from shadcn’s aria-nova style (`npx shadcn add …` in a scratch aria-nova app, then copied) and excluded from lint/prettier so re-vendoring stays a clean diff. One local deviation: `label.tsx` drops upstream’s unused `import * as React` (TS6133 under this repo’s `noUnusedLocals`; the smoke test applies the same workaround to consumer apps and logs when upstream no longer needs it). * Every block spec covers the happy path, typed `MantleClientError` rendering, keyboard-only operation, and zero axe violations (`region` and `color-contrast` off — blocks render without page landmarks, and jsdom can’t lay out colors). * May import only `@mantlejs/client` and `@mantlejs/react` among workspace packages (`ui-registry` row of `CLAUDE.md`’s dependency matrix, enforced by lint). # Auth provider > Session state for the auth blocks — restores a persisted session, consumes OAuth redirects, and exposes useAuth(). A live demo against a mocked server in your browser — the block is the registry's own source. ## Install [Section titled “Install”](#install) ```bash npx shadcn add @mantle/auth-provider ``` Needs a React Aria shadcn project and the `@mantle` registry in `components.json` — see [Install](/blocks/#install). The block lands in `components/mantle/auth-provider.tsx`, yours to edit. ## Usage [Section titled “Usage”](#usage) Wrap your app once, inside ``. Every auth block reads the session from it. ```tsx import { mantle } from "@mantlejs/client"; import { MantleProvider } from "@mantlejs/react"; import { AuthProvider, useAuth } from "@/components/mantle/auth-provider"; const client = mantle({ url: "https://api.example.com" }); export function App() { return (
); } function Header() { const { status, user, logout } = useAuth(); if (status === "loading") return null; return status === "authenticated" ? ( ) : ( Log in ); } ``` * `status` is `"loading"` until the persisted-session check on mount resolves; `user` is set by `login()` and is `undefined` after a reload (the client persists tokens, not the user record). * With `handleOAuthRedirect` (default on), tokens arriving in the URL fragment from an `@mantlejs/auth-oauth` redirect are handed to the client and stripped from the address bar. * `signup()` creates the user through the `users` service (configurable with `usersService`), then logs in. [View the source](https://github.com/MantleJs/mantle/blob/main/registry/src/components/mantle/auth-provider.tsx). # Data table > Server-side sorting and paging over find(), selecting only the fields it shows. A live demo against a mocked server in your browser — the block is the registry's own source. ## Install [Section titled “Install”](#install) ```bash npx shadcn add @mantle/data-table ``` Needs a React Aria shadcn project and the `@mantle` registry in `components.json` — see [Install](/blocks/#install). The block lands in `components/mantle/data-table.tsx`, yours to edit. ## Usage [Section titled “Usage”](#usage) Works with any service that returns `Paginated` (every `RepositoryService` does). ```tsx import { DataTable } from "@/components/mantle/data-table"; service="articles" aria-label="Articles" pageSize={20} columns={[ { id: "title", header: "Title", sortable: true }, { id: "views", header: "Views", sortable: true }, ]} />; ``` * Column sort descriptors become `$sort`, pages become `$skip`/`$limit`, and visible columns become `$select`. * Built on shadcn’s aria `Table` (React Aria `Table`/`Column`), with sortable headers announced to screen readers. * Pagination controls appear automatically when the result is a `Paginated` envelope. [View the source](https://github.com/MantleJs/mantle/blob/main/registry/src/components/mantle/data-table.tsx). # Login form > Email and password sign-in against @mantlejs/auth-local, with server errors mapped onto the form. A live demo against a mocked server in your browser — the block is the registry's own source. ## Install [Section titled “Install”](#install) ```bash npx shadcn add @mantle/login-form ``` Needs a React Aria shadcn project and the `@mantle` registry in `components.json` — see [Install](/blocks/#install). The block lands in `components/mantle/login-form.tsx`, yours to edit. ## Usage [Section titled “Usage”](#usage) Render inside ``. ```tsx import { LoginForm } from "@/components/mantle/login-form"; navigate("/")} />; ``` * Built on React Aria `Form` + `TextField` with shadcn’s aria `Input`, so labels, required-field validation, and error messages are announced to assistive technology. * A `401 NotAuthenticated` becomes a form-level alert; a `422 Unprocessable` carrying field errors lands on the matching field. * An error from an OAuth redirect (`#error=…`) is shown here too. [View the source](https://github.com/MantleJs/mantle/blob/main/registry/src/components/mantle/login-form.tsx). # Pagination > Page links driven by a Paginated envelope. A live demo against a mocked server in your browser — the block is the registry's own source. ## Install [Section titled “Install”](#install) ```bash npx shadcn add @mantle/mantle-pagination ``` Needs a React Aria shadcn project and the `@mantle` registry in `components.json` — see [Install](/blocks/#install). The block lands in `components/mantle/mantle-pagination.tsx`, yours to edit. ## Usage [Section titled “Usage”](#usage) Feed it any `{ total, limit, skip }` and handle `onSkipChange`; `data-table` uses it internally. ```tsx import { MantlePagination } from "@/components/mantle/mantle-pagination"; setQuery({ ...query, $skip: skip })} />; ``` * Named `mantle-pagination` so it never collides with shadcn’s own `pagination` primitive, which it wraps. * Pass `getHref` for real, crawlable page URLs; omit it for in-place paging. * Renders nothing when everything fits on one page. [View the source](https://github.com/MantleJs/mantle/blob/main/registry/src/components/mantle/mantle-pagination.tsx). # OAuth buttons > "Continue with …" buttons for the seven @mantlejs OAuth strategies. A live demo against a mocked server in your browser — the block is the registry's own source. ## Install [Section titled “Install”](#install) ```bash npx shadcn add @mantle/oauth-buttons ``` Needs a React Aria shadcn project and the `@mantle` registry in `components.json` — see [Install](/blocks/#install). The block lands in `components/mantle/oauth-buttons.tsx`, yours to edit. ## Usage [Section titled “Usage”](#usage) List only the strategies configured on your server. Set each strategy’s `redirectUrl` to your frontend so the user comes back signed in. ```tsx import { OAuthButtons } from "@/components/mantle/oauth-buttons"; ; ``` * Each button is a link (a full-page navigation, as OAuth requires) to `${apiUrl}/auth/`; override routes with `paths`. * Text-only by design: provider logos are each licensed under their own brand guidelines, so add official assets yourself if you want them. * Pair with `auth-provider`, which consumes the tokens the provider redirect brings back. [View the source](https://github.com/MantleJs/mantle/blob/main/registry/src/components/mantle/oauth-buttons.tsx). # Realtime list > A find() result that applies created, patched, and removed events in place — no refetch. A live demo against a mocked server in your browser — the block is the registry's own source. ## Install [Section titled “Install”](#install) ```bash npx shadcn add @mantle/realtime-list ``` Needs a React Aria shadcn project and the `@mantle` registry in `components.json` — see [Install](/blocks/#install). The block lands in `components/mantle/realtime-list.tsx`, yours to edit. ## Usage [Section titled “Usage”](#usage) The client needs a socket (`mantle({ url, socket: {} })`) and the server a Socket.IO publisher for the service. ```tsx import { RealtimeList } from "@/components/mantle/realtime-list"; service="messages" aria-label="Messages" query={{ $sort: { createdAt: "desc" }, $limit: 50 }} insert="start" renderItem={(message) => message.text} />; ``` * Built on React Aria `GridList` — arrow-key navigation, typeahead, and an `onAction` for opening an item. * Uses `@mantlejs/react`’s `useFind` with `realtime: { mode: "patch" }`, which writes each event into the cached page instead of invalidating and refetching. * `matches` mirrors your query’s filter client-side, so a patched record that no longer matches leaves the list. [View the source](https://github.com/MantleJs/mantle/blob/main/registry/src/components/mantle/realtime-list.tsx). # Search combobox > Debounced type-ahead over find(), using $ilike where the adapter supports it. A live demo against a mocked server in your browser — the block is the registry's own source. ## Install [Section titled “Install”](#install) ```bash npx shadcn add @mantle/search-combobox ``` Needs a React Aria shadcn project and the `@mantle` registry in `components.json` — see [Install](/blocks/#install). The block lands in `components/mantle/search-combobox.tsx`, yours to edit. ## Usage [Section titled “Usage”](#usage) Point it at a service and the field to match. ```tsx import { SearchCombobox } from "@/components/mantle/search-combobox"; service="articles" field="title" label="Search articles" onSelect={(article) => open(article.id)} />; ``` * Built on shadcn’s aria `Combobox` (React Aria `ComboBox`): keyboard navigation and live-region announcements included. * `operator="auto"` (default) tries case-insensitive `$ilike` and falls back to `$like` for the rest of the session if the adapter rejects it — see the [capability matrix](/guides/adapters/#what-each-adapter-supports). * `minChars`, `debounceMs`, and `limit` tune how eagerly it queries. [View the source](https://github.com/MantleJs/mantle/blob/main/registry/src/components/mantle/search-combobox.tsx). # Signup form > Account creation through your users service, followed by an automatic login. A live demo against a mocked server in your browser — the block is the registry's own source. ## Install [Section titled “Install”](#install) ```bash npx shadcn add @mantle/signup-form ``` Needs a React Aria shadcn project and the `@mantle` registry in `components.json` — see [Install](/blocks/#install). The block lands in `components/mantle/signup-form.tsx`, yours to edit. ## Usage [Section titled “Usage”](#usage) Render inside ``; the users service must accept `create` from unauthenticated callers. ```tsx import { SignupForm } from "@/components/mantle/signup-form"; navigate("/welcome")} />; ``` * `withName` (default `true`) adds a name field sent as `name`. * `minPasswordLength` is a client-side check — keep it in line with your server-side schema. * Validation errors from `@mantlejs/schema`’s `validate()` hook (`data.errors[].field`) are mapped onto the matching fields. [View the source](https://github.com/MantleJs/mantle/blob/main/registry/src/components/mantle/signup-form.tsx). # Upload dropzone > Drag-and-drop or picked files, uploaded to a @mantlejs/storage service with progress and typed errors. A live demo against a mocked server in your browser — the block is the registry's own source. ## Install [Section titled “Install”](#install) ```bash npx shadcn add @mantle/upload-dropzone ``` Needs a React Aria shadcn project and the `@mantle` registry in `components.json` — see [Install](/blocks/#install). The block lands in `components/mantle/upload-dropzone.tsx`, yours to edit. ## Usage [Section titled “Usage”](#usage) Point `service` at a service whose `create` runs `@mantlejs/storage`’s `handleUpload()` hook — or pass `id` to upload into an existing record through its `patch`. ```tsx import { UploadDropzone } from "@/components/mantle/upload-dropzone"; console.log("stored", attachment)} />; ``` * React Aria `DropZone` + `FileTrigger`: the zone is keyboard-focusable, pasting works, and the picker is a real button. * Type and size are checked before upload, with the reason shown per file; the server’s own limits still apply. * Each file goes through `client.service(service).upload()`, so it gets the client’s bearer token, its 401 refresh-and-retry, and typed `MantleClientError`s, with progress events. [View the source](https://github.com/MantleJs/mantle/blob/main/registry/src/components/mantle/upload-dropzone.tsx). # Getting started > Scaffold a Mantle app with npm create, or write the smallest possible one by hand — one service, one file, no external infrastructure. Mantle needs **Node.js 22 or newer**. Every package is published under the `@mantlejs` scope. ## Scaffold a project [Section titled “Scaffold a project”](#scaffold-a-project) `create-mantlejs` generates a complete app — transport, database adapter, auth strategy, and a first service — and installs it: ```bash npm create mantlejs@latest my-app ``` It prompts for a database (`pg`, `sqlite`, `mongodb`, or none), an auth strategy (`local` or one of the OAuth providers), and a package manager. Pass flags after `--` to skip the prompts: ```bash npm create mantlejs@latest my-app -- --database pg --auth local --package-manager npm cd my-app npm run dev ``` See [`create-mantlejs`](/packages/create-mantlejs/) and [`@mantlejs/cli`](/packages/cli/) for every option and for generating services, repositories, and hooks inside an existing project. ## Or write one by hand [Section titled “Or write one by hand”](#or-write-one-by-hand) The smallest runnable Mantle app is one service over an in-memory repository, served by the zero-dependency HTTP transport. This is the whole of `examples/todo-minimal`: ```bash npm install @mantlejs/mantle @mantlejs/http @mantlejs/memory ``` src/index.ts ```typescript import { mantle, RepositoryService } from "@mantlejs/mantle"; import { http } from "@mantlejs/http"; import { MemoryRepository } from "@mantlejs/memory"; interface Todo extends Record { id: string; title: string; done: boolean; } const app = mantle().configure(http({ cors: true })); app.use("todos", new RepositoryService(new MemoryRepository()), { methods: ["find", "get", "create", "update", "patch", "remove"], }); app.listen(3000, () => console.log("listening on http://localhost:3000")); ``` Three pieces, one per layer: * **`MemoryRepository`** is the data access — a `Repository` implementation. Swap it for `KnexRepository`, `MongoRepository`, `DynamoDbRepository`, … without touching anything else. * **`RepositoryService`** is the service — the `Service` contract (`find`, `get`, `create`, `update`, `patch`, `remove`) over one repository, with query-string parsing, pagination, and typed errors built in. * **`http()`** is the transport — it maps `GET /todos`, `POST /todos`, `PATCH /todos/:id`, … onto the service. Try it: ```bash curl -X POST http://localhost:3000/todos -H 'content-type: application/json' \ -d '{"title": "write the README", "done": false}' curl 'http://localhost:3000/todos?$sort[title]=asc&$limit=10' ``` `find` always answers with a `Paginated` envelope — `{ "total": 1, "limit": 10, "skip": 0, "data": [ … ] }` — and the query string maps onto the repository’s `where`/`sort`/`limit`/`skip`/`select` ([query conventions](/packages/mantle/#query-conventions)). Query values arrive as strings; attach a schema to the service (`new RepositoryService(repo, { schema })`) to have them coerced to numbers and booleans. ## Add behavior with hooks [Section titled “Add behavior with hooks”](#add-behavior-with-hooks) Cross-cutting behavior — authentication, validation, logging, auditing — goes in **hooks**: plain functions that run before, after, or on error of a service method, for every transport alike. ```typescript import { BadRequest, type HookContext } from "@mantlejs/mantle"; const requireTitle = (context: HookContext) => { const title = (context.data as { title?: unknown } | undefined)?.title; if (typeof title !== "string" || title.trim() === "") { throw new BadRequest("A todo needs a title", undefined, undefined, "Send a non-empty string in `title`."); } return context; }; app.service("todos").hooks({ before: { create: [requireTitle] }, }); ``` Errors are always typed (`BadRequest`, `NotFound`, `Forbidden`, …) and serialize to JSON with an optional `hint` telling the caller — a person or an agent — how to fix the request. ## Call it from a frontend [Section titled “Call it from a frontend”](#call-it-from-a-frontend) ```typescript import { mantle } from "@mantlejs/client"; const api = mantle({ url: "http://localhost:3000" }); const todos = api.service("todos"); const todo = await todos.create({ title: "ship it", done: false }); await todos.patch(todo.id, { done: true }); const page = await todos.find({ query: { $sort: { title: "asc" }, $limit: 10 } }); ``` In React, [`@mantlejs/react`](/packages/react/) wraps the client in TanStack Query hooks, and the [UI blocks](/blocks/) give you ready-made auth forms, tables, lists, and uploads on top of them. ## Next steps [Section titled “Next steps”](#next-steps) * [Architecture](/architecture/) — the layers, the dependency rule, and how Mantle differs from FeathersJS. * [Adapters](/guides/adapters/) — databases, vector stores, and which query operators each supports. * [Authentication](/guides/auth/) — JWTs, local and OAuth sign-in, refresh-token rotation. * [Agents](/guides/agents/) — exposing services to AI agents over MCP, scoped agent tokens, and an audit trail. * [`examples/knowledge-base`](https://github.com/MantleJs/mantle/tree/main/examples/knowledge-base) — the canonical example, with nearly every package wired into one app. # Adapters & queries > The Repository adapters for SQL, document, key-value, graph, and vector stores — and exactly which query operators each one supports. An adapter is a `Repository` implementation in the Infrastructure layer. Services never import one directly — they receive a `Repository` — so moving a service from SQLite to Postgres to MongoDB is a change to the repository you construct, not to the service. | Package | Repository classes | Backends | | ------------------------------------------- | ------------------------------------------ | --------------------------------------------------------- | | [`@mantlejs/knex`](/packages/knex/) | `KnexRepository`, `KnexVectorRepository` | PostgreSQL (incl. pgvector), MySQL/MariaDB, SQLite, MSSQL | | [`@mantlejs/mongodb`](/packages/mongodb/) | `MongoRepository`, `MongoVectorRepository` | MongoDB, Atlas Vector Search | | [`@mantlejs/supabase`](/packages/supabase/) | `SupabaseRepository` | Supabase (Postgres via PostgREST) | | [`@mantlejs/dynamodb`](/packages/dynamodb/) | `DynamoDbRepository` | Amazon DynamoDB | | [`@mantlejs/neo4j`](/packages/neo4j/) | `Neo4jRepository` | Neo4j (with graph traversal) | | [`@mantlejs/pinecone`](/packages/pinecone/) | `PineconeRepository` | Pinecone (vector search) | | [`@mantlejs/qdrant`](/packages/qdrant/) | `QdrantRepository` | Qdrant (vector search) | | [`@mantlejs/memory`](/packages/memory/) | `MemoryRepository` | In-process `Map` — tests and prototypes | ## Defining a repository [Section titled “Defining a repository”](#defining-a-repository) Adapters are configured once as a plugin, then subclassed per table or collection: ```typescript import { mantle, RepositoryService } from "@mantlejs/mantle"; import { knex, KnexRepository } from "@mantlejs/knex"; const app = mantle().configure(knex({ client: "pg", connection: process.env.DATABASE_URL })); class UserRepository extends KnexRepository { readonly tableName = "users"; } app.use("users", new RepositoryService(new UserRepository(app))); ``` Every adapter keeps an escape hatch to its native client (the Knex query builder, the MongoDB collection, Neo4j’s Cypher `raw()`) for queries the portable API can’t express — put them in a named repository method, not in the service. ## Portable queries [Section titled “Portable queries”](#portable-queries) `find()` takes a `QueryParams` object — `where`, `sort`, `limit`, `skip`, `select` — that every adapter translates to its own query language. Over HTTP the same object travels as a bracket-notation query string (`?age[$gt]=21&$sort[name]=asc&$limit=10`). The `where` operators: * Equality `{ field: value }`, null `{ field: null }`, not-equal `$ne` * Comparison `$lt`, `$lte`, `$gt`, `$gte`; inclusion `$in`, `$nin` * Logical `$or`, `$and` (arrays of where clauses) * Pattern `$like`, `$notlike`, `$ilike` * Containment `$contains` — the field is a superset of the value (an element, every element, or a recursive key/value match) * Nested paths — dot-path keys such as `"metadata.owner.name"` ## What each adapter supports [Section titled “What each adapter supports”](#what-each-adapter-supports) Support is uneven by design: some backends can’t represent nested data at all. Rather than silently misbehaving, every adapter **rejects an operator it doesn’t support with a `BadRequest` naming it**, and reports exactly what it accepts through `describe().capabilities` — the same metadata `@mantlejs/openapi` and `@mantlejs/mcp` use, so an AI agent is never offered an operator the adapter would reject. | Adapter | `$contains` | Nested paths (`"a.b.c"`) | | ------------------- | ----------------------------------------------------------------------- | ------------------------------------------------ | | `memory` | ✅ full (reference implementation) | ✅ full | | `supabase` | ✅ jsonb `@>` (Postgres) | ✅ PostgREST `->`/`->>` | | `mongodb` | ✅ native | ✅ native | | `knex` — PostgreSQL | ✅ jsonb `@>` | ✅ `jsonb_path_query_first` (via `whereJsonPath`) | | `knex` — MySQL | ✅ `JSON_CONTAINS` | ✅ `JSON_EXTRACT` (via `whereJsonPath`) | | `knex` — SQLite | ❌ no native JSON-superset function | ✅ `json_extract` (via `whereJsonPath`) | | `knex` — MSSQL | ❌ no native JSON-superset function | ✅ `JSON_VALUE` (via `whereJsonPath`) | | `dynamodb` | ✅ `contains()`, ANDed per element/leaf for array/object operands | ✅ multi-segment `#name0.#name1` attribute paths | | `qdrant` | ✅ `match.value`, ANDed per element/leaf (arrays auto-match-any-element) | ✅ native (dot-path payload keys) | | `neo4j` | ❌ node properties can't hold nested objects at all | ❌ same — architectural, not a gap | | `pinecone` | ❌ metadata values are flat scalars/scalar-arrays only | ❌ same — architectural, not a gap | Rendered at build time from [`CLAUDE.md`](https://github.com/MantleJs/mantle/blob/main/CLAUDE.md), so it can't drift from the repository. On `knex`, capabilities depend on the **connected client** — check `describe().capabilities` at runtime rather than assuming every SQL database behaves the same. The semantics in this table are pinned by a shared conformance fixture exported from `@mantlejs/mantle` (`NESTED_QUERY_CASES`), which `@mantlejs/memory` executes as the reference implementation and the other adapters run as translator specs. ## Services with more than one repository [Section titled “Services with more than one repository”](#services-with-more-than-one-repository) `RepositoryService` covers one repository. When a service has to write to two stores — a record and its embedding, a record and an activity log — write a `Service` that receives both repositories. Writes across adapters aren’t atomic, so the rule is: the secondary write is an **idempotent upsert keyed on the source record’s id, safe to retry, and non-fatal on failure** — logged, never rethrown into the primary caller. [`@mantlejs/embeddings`](/packages/embeddings/)’s `embed()` hook is the reference implementation of that pattern. # Agents (MCP) > Expose services to AI agents over the Model Context Protocol — per-method tools or code mode — with scoped agent tokens and an audit trail, all through the same hook pipeline as every other caller. Mantle treats an AI agent as one more transport. An agent connects over the [Model Context Protocol](https://modelcontextprotocol.io), and every call it makes runs the target service’s **full hook pipeline** — authentication, validation, events, auditing — exactly like a REST call. There is no agent-only path that skips your rules; specs assert that HTTP, MCP tool mode, and MCP code mode make identical accept/reject decisions for the same hook chain. Four pieces work together: | Piece | What it does | | ------------------------------------------------ | --------------------------------------------------------------------------------- | | [`@mantlejs/mcp`](/packages/mcp/) | An MCP server over your services: deny-by-default expose map, one tool per method | | [`@mantlejs/mcp-code`](/packages/mcp-code/) | **Code mode**: a typed API the agent writes scripts against, run in a sandbox | | [`@mantlejs/auth`](/packages/auth/#agent-tokens) | Agent tokens — short-lived, capability-scoped, revocable, delegated by a user | | [`@mantlejs/audit`](/packages/audit/) | An audit-trail hook: who did what, as which identity, into any `Repository` | ## Tool mode [Section titled “Tool mode”](#tool-mode) ```typescript import { mcp } from "@mantlejs/mcp"; app.configure( mcp({ transport: "http", // mounts POST /mcp on the app's HTTP transport; or "stdio" via startMcp(app) services: { articles: true, // every method registered in app.use() users: ["find", "get"], // read-only over MCP }, }), ); ``` Nothing is exposed unless the expose map says so; an unknown path or method fails at startup. Each exposed method becomes a tool (`articles_find`, `users_get`, …) whose input schema comes from the service’s schema and its adapter’s `describe().capabilities` — an agent is never offered a query operator the adapter would reject. Errors come back as MCP tool errors carrying `MantleError.toJSON()`, `hint` included, so the agent can correct itself. ## Code mode [Section titled “Code mode”](#code-mode) With many methods, one tool per method crowds the model’s context, and every intermediate result round-trips through the model. **Code mode** replaces the per-method tools with a typed TypeScript API and two tools: * `search_api` returns the declarations for the services the agent needs, a piece at a time; * `execute` runs a script against them in a QuickJS sandbox and returns only the final result. ```typescript import { mcp } from "@mantlejs/mcp"; import { codeMode } from "@mantlejs/mcp-code"; app.configure( mcp({ transport: "http", services: { articles: ["find", "get"], comments: ["find"] }, codeMode: codeMode(), // mode defaults to "code"; "both" keeps the per-method tools too }), ); ``` An agent’s script: ```javascript const { data } = await mantle.articles.find({ where: { status: "published" }, limit: 100, select: ["id", "tags"] }); const counts = {}; for (const article of data) for (const tag of article.tags) counts[tag] = (counts[tag] ?? 0) + 1; return counts; ``` Each `mantle..()` call is a real service call through the hook pipeline. The sandbox has no network, filesystem, `process`, or timers; every execution starts fresh; and time, memory, stack, call count, and output size are all capped with typed errors. Code mode is **experimental** — see the [package page](/packages/mcp-code/) for limits, TypeScript input, the security model, and custom executors. ## Agent tokens [Section titled “Agent tokens”](#agent-tokens) A user delegates a slice of their access to an agent with a scoped token: ```typescript import type { AuthEngine } from "@mantlejs/auth"; const engine = app.get("auth"); const issued = await engine.issueAgentToken( { articles: ["find", "get"], comments: true }, // CapabilityScope: methods per path, or true for all user.id, // the delegating user { expiresIn: "15m" }, ); // later, before it expires if needed: await engine.revokeAgentToken(issued.id); ``` Routes opt in with the `authorizeAgent()` hook. It checks the token’s scope against the call’s path and method — the same deny-by-default matcher as the MCP expose map — and sets `context.agent = { id, scope, delegatingUserId }` for downstream hooks: ```typescript import { authorizeAgent } from "@mantlejs/auth"; app.service("articles").hooks({ before: { all: [authorizeAgent()] } }); ``` Agent tokens can’t be used as user JWTs: `authenticate("jwt")` rejects them outright, so a route without `authorizeAgent()` is unreachable with an agent token whatever its scope. In code mode, an agent session’s API listing is narrowed to its scope, and an out-of-scope call still fails in `authorizeAgent()` with the same catchable `Forbidden` tool mode returns. ## The audit trail [Section titled “The audit trail”](#the-audit-trail) ```typescript import { auditLog, type AuditRecord } from "@mantlejs/audit"; const audit = auditLog({ sink: new AuditRepository(app) }); // any Repository app.service("articles").hooks({ after: { all: [audit] }, error: { all: [audit] }, }); ``` Each call becomes a record — principal, agent id and scope when an agent made it, path, method, outcome — in a repository you choose, so “what did my agents do?” is a `find()` away. Calls from a code-mode script also carry the script’s `executionId` and a SHA-256 `scriptHash`, grouping a script’s calls together. A failed audit write is logged, never allowed to fail the call it records. # Authentication > JWT sessions with @mantlejs/auth, local email+password and OAuth sign-in, rotating refresh tokens, and what changes when you run more than one instance. Authentication is split in two: **`@mantlejs/auth`** is the engine — JWT issuance and verification, the `POST /authentication` endpoint, and the hooks — and **strategy packages** plug sign-in methods into it. | Strategy | Sign-in method | | ------------------------------------------------------- | ----------------------------------------- | | [`@mantlejs/auth-local`](/packages/auth-local/) | Email + password, hashed with Argon2id | | [`@mantlejs/auth-google`](/packages/auth-google/) | Google (authorization code + PKCE) | | [`@mantlejs/auth-github`](/packages/auth-github/) | GitHub | | [`@mantlejs/auth-facebook`](/packages/auth-facebook/) | Facebook | | [`@mantlejs/auth-apple`](/packages/auth-apple/) | Sign in with Apple (`form_post` callback) | | [`@mantlejs/auth-microsoft`](/packages/auth-microsoft/) | Microsoft Entra ID (PKCE) | | [`@mantlejs/auth-linkedin`](/packages/auth-linkedin/) | LinkedIn (OpenID Connect) | | [`@mantlejs/auth-twitter`](/packages/auth-twitter/) | Sign in with X (PKCE) | The OAuth strategies share one base, [`@mantlejs/auth-oauth`](/packages/auth-oauth/) — state handling, PKCE, and find-or-create of the local user — and none of them uses Passport.js. ## Local sign-in [Section titled “Local sign-in”](#local-sign-in) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { auth, authenticate, sanitizeUser } from "@mantlejs/auth"; import { localStrategy, hashPassword } from "@mantlejs/auth-local"; const app = mantle() .configure(express()) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure(localStrategy()); app.use("users", new UserService(new UserRepository(app)), { methods: ["find", "get", "create", "patch", "remove"], }); app.service("users").hooks({ before: { create: [hashPassword()], // registration stays public; the password is stored as an Argon2id hash patch: [authenticate("jwt"), hashPassword()], find: [authenticate("jwt")], get: [authenticate("jwt")], remove: [authenticate("jwt")], }, after: { all: [sanitizeUser()] }, // never send password hashes back }); ``` The flow over HTTP: 1. `POST /users` with `{ "email", "password" }` registers a user. 2. `POST /authentication` with `{ "strategy": "local", "email", "password" }` returns an access token, a refresh token, and the user. 3. Requests carry `Authorization: Bearer `; `authenticate("jwt")` verifies it and puts the decoded payload on `params.user`. Internal calls — a service calling another service without a `provider` — skip `authenticate("jwt")`, which is how strategies look up users without tripping the guards they protect. ## OAuth sign-in [Section titled “OAuth sign-in”](#oauth-sign-in) ```typescript import { googleStrategy } from "@mantlejs/auth-google"; app.configure( googleStrategy({ clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, redirectUrl: "https://app.example.com/", }), ); ``` Send the browser to `GET /auth/google` with a full-page navigation (a link, not `fetch`). After consent the provider calls back, the strategy finds or creates the user, and — with `redirectUrl` set — redirects to your frontend with `#accessToken=…&refreshToken=…` in the URL **fragment**, so tokens never reach a server log or a `Referer` header. The [`auth-provider`](/blocks/auth-provider/) and [`oauth-buttons`](/blocks/oauth-buttons/) UI blocks handle both ends of that for you. ## Refresh tokens [Section titled “Refresh tokens”](#refresh-tokens) Every login returns an access + refresh token pair. Exchange the refresh token on the same endpoint: ```json POST /authentication { "strategy": "refresh", "refreshToken": "eyJhbGciOiJIUzI1NiJ9..." } ``` Refresh tokens **rotate**: each exchange consumes the token and issues a new pair. Replaying a consumed token is treated as theft — every outstanding refresh token for that user is revoked. [`@mantlejs/client`](/packages/client/) does the exchange automatically on a `401`, with concurrent requests sharing a single refresh. ## Running more than one instance [Section titled “Running more than one instance”](#running-more-than-one-instance) The engine’s default stores are in memory: fine for one process, wrong behind a load balancer, where a refresh or an OAuth callback can land on a different instance than the one that issued the token or the state. Inject the Redis-backed stores from [`@mantlejs/auth-redis`](/packages/auth-redis/) — `redisRefreshTokenStore` into `auth({ refreshTokenStore })` and `redisStateStore` into each OAuth strategy’s `stateStore` option. Both consume entries atomically, so a replayed callback or a double-submitted refresh can’t succeed twice. ## Agents [Section titled “Agents”](#agents) Calls from AI agents get their own short-lived, capability-scoped tokens — distinct from user JWTs and opt-in per route. See [Agents](/guides/agents/). # Deployment > Running a Mantle app in production — Google Cloud Run as the reference target, and the settings that matter once there's more than one instance. A Mantle app is a plain Node.js (≥ 22) HTTP server: `app.listen(port)` on any host that runs a container or a Node process. The reference target is **Google Cloud Run** — it scales to zero, and pairs naturally with Cloud SQL for PostgreSQL. The guidance below is what changes between “works on my machine” and a horizontally scaled deployment. ## Configuration per environment [Section titled “Configuration per environment”](#configuration-per-environment) [`@mantlejs/config`](/packages/config/) merges `config/default.json`, `config/.json`, and environment variables, optionally validated against a TypeBox schema, so a container can be configured without a rebuild: ```typescript import { config } from "@mantlejs/config"; app.configure(config({ schema: AppConfigSchema })); const port = app.get("port"); app.listen(Number(process.env.PORT ?? port)); // Cloud Run injects PORT ``` Keep secrets — `JWT_SECRET`, database URLs, OAuth client secrets — in environment variables or a secret manager, never in the config files. ## Logging [Section titled “Logging”](#logging) [`@mantlejs/logger`](/packages/logger/) writes structured JSON to stdout, which Cloud Run collects as-is. Pass `gcp: true` so levels map to Cloud Logging severities and fields stay structured in the Logs Explorer: ```typescript import { logger, createLogger } from "@mantlejs/logger"; app.configure(logger(await createLogger({ gcp: process.env.NODE_ENV === "production" }))); ``` ## When there’s more than one instance [Section titled “When there’s more than one instance”](#when-theres-more-than-one-instance) Cloud Run adds instances under load, and any two requests may land on different ones. Everything that defaults to in-process memory has to move to a shared store: | Concern | Default | Multi-instance setting | | ----------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------- | | Refresh tokens | in-memory | `auth({ refreshTokenStore: redisRefreshTokenStore(redis) })` — [`@mantlejs/auth-redis`](/packages/auth-redis/) | | OAuth `state` / PKCE verifier | in-memory | each strategy’s `stateStore: redisStateStore(redis)` | | Agent-token revocation | in-memory | inject a shared `AgentTokenStore` into `auth({ agentTokenStore })` | | Realtime events | local process only | `sync({ adapter: redisAdapter(…) })` — [`@mantlejs/sync`](/packages/sync/) | | Uploaded files | local disk | `s3Storage()` / `gcsStorage()` — instances have no shared, persistent disk | Cloud Run’s filesystem is in-memory and per instance, so `diskStorage()` is for development only. ## Graceful shutdown [Section titled “Graceful shutdown”](#graceful-shutdown) Call `app.teardown()` on `SIGTERM` so adapters close their connection pools before the instance is stopped — Cloud Run sends `SIGTERM` and allows a grace period before killing the container: ```typescript process.on("SIGTERM", () => { void app.teardown().finally(() => process.exit(0)); }); ``` # Realtime > Service events over Socket.IO with channels, cross-instance fan-out with @mantlejs/sync, and live queries on the client. Every successful `create`, `update`, `patch`, and `remove` emits a service event — `created`, `updated`, `patched`, `removed` (plus any custom events a service declares). Three packages carry those events to clients: | Package | Role | | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | [`@mantlejs/socketio`](/packages/socketio/) | Socket.IO transport: service calls over the socket, events out through channels | | [`@mantlejs/sync`](/packages/sync/) | Relays events between app instances via Redis or Supabase Realtime | | [`@mantlejs/client`](/packages/client/) / [`@mantlejs/react`](/packages/react/) | Subscribe on the client; React hooks keep queries fresh | ## Socket.IO and channels [Section titled “Socket.IO and channels”](#socketio-and-channels) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { socketio } from "@mantlejs/socketio"; const app = mantle().configure(express()).configure(socketio()); // Who receives what: join each connection to channels, then publish events to channels. app.on("connection", (connection) => { app.channel("everyone").join(connection); }); app.publish(() => app.channel("everyone")); app.listen(3030); ``` Nothing is broadcast until a publisher says where an event goes — mutations still happen, but no client hears about them. Publishers can be global (`app.publish`) or per service (`app.service("messages").publish(…)`), and channels can be per user or filtered by a predicate, which is how you keep one user’s events away from another. Calls made over the socket (`socket.emit("find", "messages", …)`) are ordinary service calls: they run the full hook pipeline with `provider: "socket.io"`. ## More than one instance [Section titled “More than one instance”](#more-than-one-instance) Behind a load balancer, a client connected to instance A must still hear about a write handled by instance B. [`@mantlejs/sync`](/packages/sync/) publishes every local service event to a shared channel and re-emits events from other instances locally, where the Socket.IO transport fans them out: ```typescript import { sync, redisAdapter } from "@mantlejs/sync"; app.configure(sync({ adapter: redisAdapter({ url: process.env.REDIS_URL }) })); ``` Delivery is **at-most-once** — there’s no persistence or replay. Treat events as cache-invalidation hints, not as a source of truth, and refetch after a reconnect. ## On the client [Section titled “On the client”](#on-the-client) ```typescript import { mantle } from "@mantlejs/client"; const api = mantle({ url: "http://localhost:3030", socket: {} }); // socket.io-client is an optional peer api.service("messages").on("created", (message) => console.log("new message", message)); api.on("reconnect", () => console.log("reconnected — refetch anything stale")); ``` In React, [`@mantlejs/react`](/packages/react/)’s `useFind` and `useGet` subscribe automatically when the client has a socket, invalidate the affected queries on each event, and `MantleProvider` invalidates every query after a reconnect — so missed events cost a refetch, not stale data. To update a list in place with no request at all, opt `useFind` into patch mode, which writes each event straight into its cached result (a `Paginated`’s `total` included): ```typescript const { data } = useFind( "comments", { query: { articleId, $sort: { createdAt: 1 } } }, { realtime: { mode: "patch", matches: (comment) => comment.articleId === articleId, insert: "end" } }, ); ``` `matches` mirrors the query’s filter and `insert` its sort, so a created record lands where a refetch would have put it. The [`realtime-list`](/blocks/realtime-list/) UI block is built on exactly this. # File storage > Multipart uploads through a before hook, stored on local disk, Amazon S3 (or any S3-compatible store), or Google Cloud Storage. File uploads follow the same shape as everything else in Mantle: a plugin holds the configuration, and a hook does the work on exactly the service methods that accept files. | Package | Storage | | ------------------------------------------------- | --------------------------------------------------------------------- | | [`@mantlejs/storage`](/packages/storage/) | The `upload()` plugin, the `handleUpload()` hook, and `diskStorage()` | | [`@mantlejs/storage-s3`](/packages/storage-s3/) | `s3Storage()` — Amazon S3, MinIO, Cloudflare R2, other S3-compatibles | | [`@mantlejs/storage-gcs`](/packages/storage-gcs/) | `gcsStorage()` — Google Cloud Storage | ## Accepting uploads [Section titled “Accepting uploads”](#accepting-uploads) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { upload, diskStorage, handleUpload } from "@mantlejs/storage"; const app = mantle() .configure(express()) .configure( upload({ maxFileSize: 5 * 1024 * 1024, allowedMimeTypes: ["image/jpeg", "image/png"], storage: diskStorage({ destination: "./uploads" }), }), ); app.use("photos", new PhotoService(new PhotoRepository(app))); app.service("photos").hooks({ before: { create: [handleUpload("photo", { required: true })] }, }); ``` A `multipart/form-data` `POST /photos` with a `photo` part is parsed (with busboy) and stored before the service runs; `create` receives an `UploadedFile` descriptor in `context.data.photo` instead of the bytes — original name, MIME type, size, and the adapter’s `key` — plus any ordinary form fields. A file over `maxFileSize` or of a disallowed type fails the call with a `BadRequest`. `handleUpload()` reads the raw request the Express and Koa transports attach to `params.request`, so uploads need [`@mantlejs/express`](/packages/express/) or [`@mantlejs/koa`](/packages/koa/); the zero-dependency `@mantlejs/http` transport doesn’t support them yet. ## Cloud storage [Section titled “Cloud storage”](#cloud-storage) Swap the adapter; the hook doesn’t change: ```typescript import { s3Storage } from "@mantlejs/storage-s3"; app.configure( upload({ storage: s3Storage({ bucket: process.env.S3_BUCKET!, region: process.env.AWS_REGION!, keyPrefix: "uploads/" }), maxFileSize: 10 * 1024 * 1024, }), ); ``` Every adapter implements `store()`, `retrieve()`, and `delete()`; cloud adapters also implement `getSignedUrl()` for time-limited direct downloads. Feature-detect it — local disk has no such concept: ```typescript import type { UploadEngine } from "@mantlejs/storage"; const engine = app.get("upload"); if (engine.storage.getSignedUrl) { const url = await engine.storage.getSignedUrl(file.key, { expiresIn: 300 }); } ``` ## From the browser [Section titled “From the browser”](#from-the-browser) [`@mantlejs/client`](/packages/client/#file-uploads) sends uploads with `service.upload()` — a multipart `POST /:service` (or `PATCH /:service/:id` with `id`) carrying the same bearer auth, refresh-and-retry, and typed errors as every other call, with optional progress events: ```typescript const attachment = await api.service("attachments").upload(file, { fields: { articleId: "42" }, // merged into context.data by handleUpload() onProgress: ({ percent }) => setProgress(percent ?? 0), }); ``` The [`upload-dropzone`](/blocks/upload-dropzone/) UI block wraps it — drag-and-drop or a file picker, client-side type and size checks, per-file progress, and the server’s typed errors. # @mantlejs/audit > Audit-trail hook for Mantle JS. Records who did what, as which identity, with what result on every service call — a queryable audit trail, not a grep through logs. Audit-trail hook for [Mantle JS](https://github.com/mantlejs/mantle). Records *who did what, as which identity, with what result* on every service call — a queryable audit trail, not a grep through logs. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/audit ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Not the same job as `@mantlejs/logger` [Section titled “Not the same job as @mantlejs/logger”](#not-the-same-job-as-mantlejslogger) `@mantlejs/logger`’s `logRequest`/`logError` hooks are structured *observability* — request duration, status, correlation ids, meant for a log aggregator. `@mantlejs/audit`’s `auditLog()` is a *compliance/security* record scoped to identity and outcome — who (a user or an [agent](/packages/auth/#agent-tokens)), what (path + method), and what happened. Use both; they answer different questions. ### The sink is a `Repository` you already have [Section titled “The sink is a Repository\ you already have”](#the-sink-is-a-repositoryt-you-already-have) `auditLog({ sink })` writes each entry via `sink.save(record)`. `sink` is any `Repository` — `@mantlejs/memory` for prototyping, or a real adapter (`@mantlejs/knex`, `@mantlejs/mongodb`, `@mantlejs/supabase`, …) backed by a table/collection the deployment already runs. No new storage concept, and audit records are themselves queryable through the same `find`/`get` a normal Mantle service would give you if you wrap the sink in one. ### One hook, two registrations [Section titled “One hook, two registrations”](#one-hook-two-registrations) `auditLog()` returns a single hook function. Register the *same* returned value in both `after.all` and `error.all` — it records exactly once per call, whichever phase actually reaches it: ```typescript const audit = auditLog({ sink: auditRepository }); app.service("documents").hooks({ after: { all: [audit] }, error: { all: [audit] }, }); ``` A successful call never reaches `error.all`; a thrown call never reaches `after.all` — so there’s no risk of double-recording, and no need for a `before.all` registration (unlike `logRequest`, which needs one to measure duration — `auditLog()` doesn’t record duration, so it doesn’t need one). ### What’s in a record, and what’s deliberately left out [Section titled “What’s in a record, and what’s deliberately left out”](#whats-in-a-record-and-whats-deliberately-left-out) ```typescript interface AuditRecord { principal?: unknown; // ctx.params.user — undefined for an unauthenticated/internal call agentId?: string; // set only when the call was authorized via an agent token agentScope?: CapabilityScope; // present only alongside agentId delegatingUserId?: string; // present only alongside agentId path: string; method: string; params?: Record; // ctx.params.query — filters/pagination/sort/select mcp?: Record; // ctx.params.mcp — MCP origin metadata (code mode: executionId, scriptHash) status: "success" | "error"; resultSummary: string; // short outcome description, never the full result payload timestamp: string; // ISO 8601 } ``` `params` is `ctx.params.query` only — **never** `ctx.params.headers` or the raw `ctx.params.user` object. Headers can carry bearer tokens; identity already has its own field (`principal`). `resultSummary` is deliberately a short description (record count, an id, or an error name+message), not the full result — avoids duplicating potentially large or sensitive payloads into the audit trail. When the call was authorized via [`@mantlejs/auth`’s `authorizeAgent()`](/packages/auth/#agent-tokens) (`HookContext.agent` set), the entry additionally carries `agentId`, `agentScope`, and `delegatingUserId` — the “as which identity, delegated by whom” half of an agent-originated call. When the call came from an agent script under [`@mantlejs/mcp-code`](/packages/mcp-code/)’s code mode, the entry also carries `mcp: { mode: "code", executionId, scriptHash }` (plus `script` when the deployment opts in with `auditScriptSource: true`). Every service call one script made shares the same `executionId`, so “what did this script do” is one `find({ where: { "mcp.executionId": id } })` on the sink (on adapters that support nested paths), or a filter in your own query. ### Sink failures never fail the primary operation [Section titled “Sink failures never fail the primary operation”](#sink-failures-never-fail-the-primary-operation) If `sink.save()` throws, `auditLog()` catches it — the service call that triggered the audit entry already succeeded (or already failed for its own reason) and that outcome is never changed by an audit-sink outage. By default the failure is logged via `app.get("logger")` when `@mantlejs/logger` is configured; pass `onSinkError` for custom handling (e.g. a metric). *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { http } from "@mantlejs/http"; import { auth, authorizeAgent } from "@mantlejs/auth"; import { auditLog } from "@mantlejs/audit"; import { MemoryRepository } from "@mantlejs/memory"; import type { AuditRecord } from "@mantlejs/audit"; const app = mantle() .configure(http()) .configure(auth({ secret: process.env.JWT_SECRET! })); const auditRepository = new MemoryRepository(); // swap for a real adapter in production const audit = auditLog({ sink: auditRepository }); app.use("documents", new DocumentService(new DocumentRepository(app))); app.service("documents").hooks({ before: { all: [authorizeAgent()] }, // optional — only if agents may call this route after: { all: [audit] }, error: { all: [audit] }, }); app.listen(3030); ``` *** ## API [Section titled “API”](#api) ### `auditLog(options)` [Section titled “auditLog(options)”](#auditlogoptions) Returns a `HookFunction`. Register it in both `after.all` and `error.all` on the service(s) to audit. ```typescript auditLog({ sink: auditRepository, // required — any Repository onSinkError: (error, record, ctx) => { // optional — called instead of the default app.get("logger") fallback }, }); ``` #### `AuditLogOptions` [Section titled “AuditLogOptions”](#auditlogoptions-1) | Field | Type | Default | Description | | ------------- | ------------------------------ | ------------------------------------ | ------------------------------------------------------------------------ | | `sink` | `Repository` | — | Where audit records are written (required) | | `onSinkError` | `(error, record, ctx) => void` | logs via `app.get("logger")` | Called when `sink.save()` throws. The primary operation is never failed. | ### `AuditRecord` [Section titled “AuditRecord”](#auditrecord) See [What’s in a record](#whats-in-a-record-and-whats-deliberately-left-out) above for the full field-by-field breakdown. *** ## Types [Section titled “Types”](#types) ```typescript import type { AuditLogOptions, AuditRecord } from "@mantlejs/audit"; ``` | Type | Description | | ----------------- | ------------------------------------------------------ | | `AuditLogOptions` | Options passed to `auditLog()` | | `AuditRecord` | The shape of one recorded entry, and of the sink’s `T` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build audit # compile npx nx test audit # run tests npx nx lint audit # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build audit ``` First publish (scoped packages require `--access public`): ```bash cd packages/audit npm publish --access public ``` Subsequent releases — bump `version` in `packages/audit/package.json`, then: ```bash cd packages/audit npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/audit npm publish --registry http://localhost:4873 ``` # @mantlejs/auth > JWT authentication engine for Mantle JS. Provides token issuance, verification, a strategy runner, and hook utilities. Designed to be paired with a strategy package such as @mantlejs/auth-local. JWT authentication engine for [Mantle JS](https://github.com/mantlejs/mantle). Provides token issuance, verification, a strategy runner, and hook utilities. Designed to be paired with a strategy package such as [`@mantlejs/auth-local`](/packages/auth-local/). *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth jsonwebtoken ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### The authentication engine [Section titled “The authentication engine”](#the-authentication-engine) `auth()` is a Mantle plugin. It registers an `AuthEngine` on the application and mounts a built-in `authentication` service at `POST /authentication`. The engine is the single source of truth for JWT configuration and strategy dispatch — strategy packages (like `auth-local`) register themselves into it. ### Strategies [Section titled “Strategies”](#strategies) A strategy is an object with a `name` and an `authenticate(data, params)` method. The engine calls the matching strategy when `POST /authentication` arrives with a `strategy` field in the body. Strategy packages call `engine.registerStrategy(strategy)` during their own plugin registration. ### Hooks [Section titled “Hooks”](#hooks) `authenticate('jwt')` is a `before` hook that reads `Authorization: Bearer ` from the request headers, verifies the token, and writes the decoded payload to `params.user`. `authorizeAgent()` is a `before` hook for capability-scoped agent tokens (see [Agent tokens](#agent-tokens) below) — opt-in per route, and never a substitute for `authenticate('jwt')`. `sanitizeUser()` is an `after` hook that strips sensitive fields (password, passwordHash) from service results before they leave the server. ### Internal calls [Section titled “Internal calls”](#internal-calls) Hooks registered with `authenticate('jwt')` automatically skip when the call has no `provider` (i.e. calls that originate within the server rather than from HTTP). This lets auth strategies query user services without tripping their own guards. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { auth, authenticate, sanitizeUser } from "@mantlejs/auth"; import { localStrategy, hashPassword } from "@mantlejs/auth-local"; const app = mantle() .configure(express()) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure(localStrategy()); app.use("/users", new UserService(new UserRepository(app)), { methods: ["find", "get", "create", "patch", "remove"], }); app.service("users").hooks({ before: { create: [hashPassword()], find: [authenticate("jwt")], get: [authenticate("jwt")], patch: [authenticate("jwt")], remove: [authenticate("jwt")], }, after: { all: [sanitizeUser()], }, }); app.listen(3030); ``` **Register a user** ```http POST /users Content-Type: application/json { "email": "alice@example.com", "password": "hunter2" } ``` **Log in** ```http POST /authentication Content-Type: application/json { "strategy": "local", "email": "alice@example.com", "password": "hunter2" } ``` ```json { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiJ9...", "user": { "id": 1, "email": "alice@example.com" } } ``` **Call a protected endpoint** ```http GET /users Authorization: Bearer eyJhbGciOiJIUzI1NiJ9... ``` *** ## API [Section titled “API”](#api) ### `auth(config)` [Section titled “auth(config)”](#authconfig) Returns a `MantlePlugin`. Call via `app.configure(auth(config))`. ```typescript app.configure( auth({ secret: process.env.JWT_SECRET!, // required — signing key expiresIn: "7d", // optional — default "1d" algorithms: ["HS256"], // optional — default ["HS256"] issuer: "my-api", // optional audience: "my-client", // optional }), ); ``` Side effects: * Stores the `AuthEngine` at `app.get("auth")` * Registers `POST /authentication` via `app.use("authentication", ...)` #### `AuthConfig` [Section titled “AuthConfig”](#authconfig-1) | Field | Type | Default | Description | | ------------------- | -------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `secret` | `string` | — | JWT signing secret (required) | | `expiresIn` | `string \| number` | `"1d"` | Access-token lifetime. Strings use [ms](https://github.com/vercel/ms) format (`"2h"`, `"7d"`). Numbers are seconds. | | `algorithms` | `string[]` | `["HS256"]` | Verification algorithms | | `issuer` | `string` | — | Sets and verifies the `iss` claim | | `audience` | `string \| string[]` | — | Sets and verifies the `aud` claim | | `refreshExpiresIn` | `string \| number` | `"30d"` | Refresh-token lifetime | | `refreshTokenStore` | `RefreshTokenStore` | in-memory | Storage for outstanding refresh tokens. **Multi-instance deployments (Cloud Run) must inject a shared store** — the in-memory default cannot revoke tokens issued by another instance. Use [`redisRefreshTokenStore` from `@mantlejs/auth-redis`](/packages/auth-redis/). | | `agentTokenStore` | `AgentTokenStore` | in-memory | Storage for outstanding agent tokens, enabling `revokeAgentToken()` before JWT expiry. Same multi-instance caveat as `refreshTokenStore`. | *** ### `authenticate(strategy)` [Section titled “authenticate(strategy)”](#authenticatestrategy) A `before` hook factory. Returns a hook that authenticates the incoming request. ```typescript app.service("messages").hooks({ before: { all: [authenticate("jwt")], }, }); ``` **`authenticate("jwt")`** Reads `Authorization: Bearer ` from `params.headers`. On success writes the decoded JWT payload to `params.user`. On failure throws `NotAuthenticated`. Silently skips (passes through) when `params.provider` is undefined — internal service calls are trusted. **Resolving the full user record — `authenticate("jwt", { entity })`** By default `params.user` is just the raw JWT payload (`{ sub, iat, exp, ... }`). Pass `entity` to resolve the actual user record instead: ```typescript app.service("messages").hooks({ before: { all: [authenticate("jwt", { entity: "users" })], }, }); ``` With `entity` set, the hook calls `app.service(entity).get(payload.sub)` internally (no `provider`, so the target service’s own auth guards are bypassed for this lookup) and writes the result to `params.user`. The raw JWT payload moves to `params.authPayload` instead. If the subject no longer resolves to a user (e.g. deleted after the token was issued), the hook throws `NotAuthenticated("User for this token no longer exists")`. **`authenticate("custom")`** For non-JWT strategies: delegates to `engine.authenticate(strategyName, context.data, context.params)` and writes the result to `params.user`. Skips for internal calls. *** ### `authorizeAgent()` [Section titled “authorizeAgent()”](#authorizeagent) A `before` hook factory. Returns a hook that authorizes a capability-scoped agent token — see [Agent tokens](#agent-tokens) for issuance and revocation. ```typescript app.service("articles").hooks({ before: { all: [authorizeAgent()] }, }); ``` Reads `Authorization: Bearer ` from `params.headers`, verifies it, and checks it’s an unrevoked `type: "agent"` token whose `scope` covers `HookContext.path`+`HookContext.method` (`@mantlejs/mantle`’s `matchesCapabilityScope` — the same deny-by-default matcher `@mantlejs/mcp`’s expose map uses). On success sets `HookContext.agent = { id, scope, delegatingUserId }`, additive to `params.user`. Throws `Forbidden` for an out-of-scope call, `NotAuthenticated` for anything else (missing/expired/malformed/non-agent/revoked token, or auth not configured). Silently skips (passes through) when `params.provider` is undefined — internal service calls are trusted, same convention as `authenticate("jwt")`. Opt-in per route: `authenticate("jwt")` rejects a `type: "agent"` payload outright, so a route with no `authorizeAgent()` hook attached can never be reached with an agent token. *** ### `sanitizeUser(fields?)` [Section titled “sanitizeUser(fields?)”](#sanitizeuserfields) An `after` hook that removes sensitive fields from the service result. Works with single objects, arrays, and paginated results. ```typescript app.service("users").hooks({ after: { all: [sanitizeUser()], // removes password, passwordHash, password_hash get: [sanitizeUser(["secret", "apiKey"])], // custom field list }, }); ``` Default fields removed: `"password"`, `"passwordHash"`, `"password_hash"`. *** ### `AuthEngine` (advanced) [Section titled “AuthEngine (advanced)”](#authengine-advanced) Accessed via `app.get("auth")`. Useful when writing custom strategy packages. ```typescript const engine = app.get("auth"); // Create a token const token = engine.createJwt({ sub: String(user.id), role: "admin" }); // Verify a token const payload = engine.verifyJwt(token); // throws if invalid // Register a custom strategy engine.registerStrategy({ name: "magic-link", async authenticate(data, params) { // verify magic link token, find user, issue JWT... return { accessToken: engine.createJwt({ sub: userId }) }; }, }); ``` *** ### `POST /authentication` [Section titled “POST /authentication”](#post-authentication) The built-in authentication endpoint. Dispatches to a registered strategy based on the `strategy` field. **Request** ```json { "strategy": "local", "email": "alice@example.com", "password": "hunter2" } ``` **Response** (`201 Created`) ```json { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiJ9...", "user": { "id": 1, "email": "alice@example.com" } } ``` **Errors** | Status | Condition | | ----------------------- | ---------------------------------------------------------------- | | `400 Bad Request` | `strategy` field missing | | `400 Bad Request` | Strategy name not registered | | `401 Not Authenticated` | Strategy-specific failure (wrong password, user not found, etc.) | *** ### Refresh tokens [Section titled “Refresh tokens”](#refresh-tokens) Every login (local or OAuth) returns an access + refresh token pair issued by `engine.createTokenPair(sub)`. The refresh token carries `{ sub, type: "refresh", jti }`, lives `refreshExpiresIn` (default 30 days), and its `jti` is recorded in the `RefreshTokenStore`. Exchange it for a fresh pair via the built-in `refresh` strategy — same endpoint, no extra route: ```json POST /authentication { "strategy": "refresh", "refreshToken": "eyJhbGciOiJIUzI1NiJ9..." } ``` **Response** (`201 Created`): `{ "accessToken": "...", "refreshToken": "..." }` — no `user` field; a refresh proves possession of a token, not fresh credentials. Refresh tokens **rotate**: each successful exchange consumes the submitted token and issues a new one. Replaying an already-consumed token is treated as theft — **all** outstanding refresh tokens for that subject are revoked and the request fails with `401 NotAuthenticated("Refresh token reuse detected")`. Expired tokens, access tokens submitted as refresh tokens, and unknown signatures all fail with `401 NotAuthenticated("Invalid refresh token")`. Custom strategies should issue tokens through `engine.createTokenPair(sub, accessExtra?)` rather than `createJwt` directly, so their refresh tokens participate in rotation and revocation. *** ### Agent tokens [Section titled “Agent tokens”](#agent-tokens) Short-lived, capability-scoped tokens for AI-agent callers, delegated from a user rather than representing one. Distinct from user JWTs and refresh tokens — `authenticate("jwt")` rejects an agent token outright, and `authorizeAgent()` rejects anything that isn’t one. **Issuing a token** — typically from your own route/service, after checking the delegating user is allowed to grant the requested scope: ```typescript const engine = app.get("auth"); const issued = await engine.issueAgentToken( { articles: ["find", "get"], comments: true }, // CapabilityScope: methods per path, or `true` for all user.id, // delegatingUserId { expiresIn: "15m" }, // optional, default "15m" ); // issued: { accessToken, id, expiresAt } ``` **Authorizing agent calls** — attach `authorizeAgent()` to any route an agent may call. It’s opt-in per route: a route with no `authorizeAgent()` hook can never be reached with an agent token, no matter its scope, because `authenticate("jwt")` refuses `type: "agent"` payloads. ```typescript app.service("articles").hooks({ before: { all: [authorizeAgent()] }, }); ``` On success it sets `HookContext.agent = { id, scope, delegatingUserId }` — additive, `params.user` is untouched. On an out-of-scope `path`+`method` it throws `Forbidden`; on a missing, expired, malformed, non-agent, or revoked token it throws `NotAuthenticated`. **Revoking a token** before its natural expiry: ```typescript await engine.revokeAgentToken(issued.id); ``` Revocation is tracked in the `AgentTokenStore` (default: in-memory). **Multi-instance deployments must inject a shared store** — same rationale as `refreshTokenStore` above. *** ## Types [Section titled “Types”](#types) ```typescript import type { AgentPrincipal, AgentTokenOptions, AgentTokenStore, AuthConfig, AuthEngine, AuthResult, AuthStrategy, IssuedAgentToken, JwtPayload, RefreshTokenStore, TokenPair, } from "@mantlejs/auth"; ``` | Type | Description | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `AuthConfig` | Options passed to `auth()` | | `AuthEngine` | The engine stored at `app.get("auth")` | | `AuthResult` | `{ accessToken: string; [key: string]: unknown }` — returned by strategies | | `AuthStrategy` | Interface to implement for custom strategies | | `JwtPayload` | Decoded JWT payload shape | | `RefreshTokenStore` | `add(jti, sub, exp)` / `consume(jti)` / `revokeAll(sub)` — inject a shared implementation (e.g. `@mantlejs/auth-redis`) for multi-instance deployments | | `TokenPair` | `{ accessToken, refreshToken }` — returned by `engine.createTokenPair()` | | `AgentPrincipal` | `{ id, scope: CapabilityScope, delegatingUserId }` — an agent token’s decoded shape, and `HookContext.agent`’s type once `authorizeAgent()` accepts it | | `AgentTokenOptions` | `{ expiresIn? }` — passed to `engine.issueAgentToken()` | | `AgentTokenStore` | `add(id, exp)` / `isValid(id)` / `revoke(id)` — inject a shared implementation for multi-instance deployments | | `IssuedAgentToken` | `{ accessToken, id, expiresAt }` — returned by `engine.issueAgentToken()` | *** ## Writing a custom strategy [Section titled “Writing a custom strategy”](#writing-a-custom-strategy) ```typescript import type { MantlePlugin } from "@mantlejs/mantle"; import type { AuthEngine, AuthStrategy } from "@mantlejs/auth"; export function magicLinkStrategy(): MantlePlugin { return (app) => { const engine = app.get("auth"); if (!engine) throw new Error("@mantlejs/auth must be configured first"); const strategy: AuthStrategy = { name: "magic-link", async authenticate(data) { // 1. Validate data.token against your store // 2. Look up the user // 3. Return the JWT const accessToken = engine.createJwt({ sub: String(userId) }); return { accessToken, user }; }, }; engine.registerStrategy(strategy); }; } ``` Register it alongside your other plugins: ```typescript app.configure(auth({ secret: process.env.JWT_SECRET! })).configure(magicLinkStrategy()); ``` *** ## Error reference [Section titled “Error reference”](#error-reference) All errors extend `MantleError` and serialize to JSON automatically. | Error | Code | When thrown | | ------------------ | ---- | ---------------------------------------------------- | | `NotAuthenticated` | 401 | Missing/invalid/expired Bearer token | | `BadRequest` | 400 | `strategy` field missing from `POST /authentication` | | `BadRequest` | 400 | Named strategy not registered | *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth # compile npx nx test auth # run tests npx nx lint auth # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build auth ``` First publish (scoped packages require `--access public`): ```bash cd packages/auth npm publish --access public ``` Subsequent releases — bump `version` in `packages/auth/package.json`, then: ```bash cd packages/auth npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/auth npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/auth --registry http://localhost:4873 ``` # @mantlejs/auth-apple > Sign in with Apple strategy for Mantle JS. Implements the authorization code flow with responsemode=formpost — no Passport.js dependency. Registers GET /auth/apple and POST /auth/apple/callback on the HTTP transport, then finds or creates a user record and returns a Mantle JWT pair. Sign in with Apple strategy for [Mantle JS](https://github.com/mantlejs/mantle). Implements the authorization code flow with `response_mode=form_post` — no Passport.js dependency. Registers `GET /auth/apple` and `POST /auth/apple/callback` on the HTTP transport, then finds or creates a user record and returns a Mantle JWT pair. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth-apple ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### No static client secret [Section titled “No static client secret”](#no-static-client-secret) Apple does not issue a client secret. Instead, every token exchange is authenticated with a short-lived ES256 JWT signed from your Sign in with Apple private key (the `.p8` file). [Arctic](https://arcticjs.dev) signs this JWT per exchange (see [ADR-002](https://github.com/MantleJs/mantle/blob/main/docs/decisions/adr-002-arctic-oauth-internals.md)) — you configure `teamId`, `keyId`, and `privateKey` instead of a `clientSecret`. ### POST callback [Section titled “POST callback”](#post-callback) Whenever the `name`/`email` scopes are requested, Apple requires `response_mode=form_post`: the callback arrives as an `application/x-www-form-urlencoded` **POST** rather than a GET redirect. CSRF protection is the `state` round-trip through the `OAuthStateStore` (Apple does not support PKCE for this flow). ### Profile from the id_token [Section titled “Profile from the id_token”](#profile-from-the-id_token) Apple has no userinfo endpoint. The profile comes from the id_token returned by the token exchange: `sub` becomes the user ID, `email` the email. The id_token is obtained directly from Apple’s token endpoint over TLS, so its payload is decoded without a JWKS round-trip. ### First-login name capture [Section titled “First-login name capture”](#first-login-name-capture) Apple sends the user’s name in a `user` form field on the **first** authorization only. The plugin reads it into the profile, and find-or-create persists it when the user record is created — subsequent logins without the field lose nothing. ### Find-or-create [Section titled “Find-or-create”](#find-or-create) On callback the plugin searches the configured user service for a record where `appleId` (configurable) matches the id_token’s `sub` claim. If no record is found, it creates one with `{ appleId, email, name }` from the profile. The same user is returned on every subsequent sign-in. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { auth } from "@mantlejs/auth"; import { appleStrategy } from "@mantlejs/auth-apple"; const app = mantle() .configure(express()) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure( appleStrategy({ clientId: process.env.APPLE_CLIENT_ID!, // Services ID, e.g. "com.example.app.web" teamId: process.env.APPLE_TEAM_ID!, keyId: process.env.APPLE_KEY_ID!, privateKey: process.env.APPLE_PRIVATE_KEY!, // PEM contents of the .p8 key }), ); app.listen(3030); ``` **Sign-in flow:** 1. Redirect the browser to `GET /auth/apple` 2. User authenticates with Apple and consents 3. Apple POSTs `code`, `state` (and `user` on first login) to `/auth/apple/callback` 4. The plugin exchanges the code, decodes the id_token, finds or creates the user, and responds: ```json { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiJ9...", "user": { "id": "1", "appleId": "001234.abc...", "email": "alice@icloud.com", "name": "Alice Doe" } } ``` *** ## API [Section titled “API”](#api) ### `appleStrategy(config)` [Section titled “appleStrategy(config)”](#applestrategyconfig) ```typescript function appleStrategy(config: AppleStrategyConfig): MantlePlugin; type AppleStrategyConfig = { clientId: string; // Services ID (the OAuth client_id) teamId: string; // Apple Developer Team ID keyId: string; // Key ID of the Sign in with Apple private key privateKey: string; // PKCS#8 PEM contents of the .p8 key callbackUrl?: string; // Default: '/auth/apple/callback' scope?: string[]; // Default: ['name', 'email'] entity?: string; // Default: 'users' entityIdField?: string; // Default: 'appleId' }; ``` | Field | Type | Default | Description | | --------------- | ---------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `clientId` | `string` | — | Services ID from the Apple Developer portal (required) | | `teamId` | `string` | — | Apple Developer Team ID (required) | | `keyId` | `string` | — | Key ID of the Sign in with Apple key (required) | | `privateKey` | `string` | — | PEM contents of the downloaded `.p8` private key (required) | | `callbackUrl` | `string` | `'/auth/apple/callback'` | Callback path — must match the Return URL registered with Apple | | `scope` | `string[]` | `['name', 'email']` | Sign in with Apple scopes | | `entity` | `string` | `'users'` | Mantle service used to find or create users | | `entityIdField` | `string` | `'appleId'` | Field on the user record matched against the id_token’s `sub` claim | | `redirectUrl` | `string` | none — returns JSON | Frontend URL to redirect to on completion, with tokens (or an error) in the URL fragment — see [`@mantlejs/auth-oauth`](/packages/auth-oauth/#redirecting-back-to-a-frontend-redirecturl) | **Routes registered:** | Method | Path | Description | | ------ | ---------------------- | ---------------------------------------------------- | | `GET` | `/auth/apple` | Redirect to Apple’s sign-in page | | `POST` | `/auth/apple/callback` | Handle the form_post callback, issue Mantle JWT pair | **Must be configured after** an HTTP transport and `auth()`. *** ## Types [Section titled “Types”](#types) ```typescript import type { AppleStrategyConfig } from "@mantlejs/auth-apple"; ``` `AppleStrategyConfig` extends `OAuthPluginConfig` from `@mantlejs/auth-oauth`, replacing `clientSecret` with `teamId`/`keyId`/`privateKey`. *** ## Apple Developer setup [Section titled “Apple Developer setup”](#apple-developer-setup) 1. In [developer.apple.com](https://developer.apple.com/account), create an **App ID** with the Sign in with Apple capability 2. Create a **Services ID** (this is your `clientId`) and enable Sign in with Apple for it 3. Register your domain and Return URL: `https://your-domain.com/auth/apple/callback` (Apple requires HTTPS — no localhost) 4. Create a **Sign in with Apple key**, download the `.p8` file (one-time download), and note its **Key ID** 5. Find your **Team ID** on the membership page *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth-apple # compile npx nx test auth-apple # run tests npx nx lint auth-apple # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) ```bash npx nx build auth-apple cd packages/auth-apple npm publish --access public ``` # @mantlejs/auth-facebook > Facebook OAuth 2.0 strategy for Mantle JS. Implements the authorization code flow — no Passport.js dependency. Registers GET /auth/facebook and GET /auth/facebook/callback on the configured HTTP transport (@mantlejs/express, @mantlejs/koa, or @mantlejs/http), then finds or creates a user record and returns a Mantle JWT pair. Facebook OAuth 2.0 strategy for [Mantle JS](https://github.com/mantlejs/mantle). Implements the authorization code flow — no Passport.js dependency. Registers `GET /auth/facebook` and `GET /auth/facebook/callback` on the configured HTTP transport (`@mantlejs/express`, `@mantlejs/koa`, or `@mantlejs/http`), then finds or creates a user record and returns a Mantle JWT pair. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth-facebook ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Authorization code flow [Section titled “Authorization code flow”](#authorization-code-flow) Facebook Sign-In uses the standard OAuth 2.0 authorization code flow (without PKCE — Facebook does not require it for server-side apps). On redirect the plugin builds the authorization URL with a random `state` token to prevent CSRF. The state is verified on callback before the code is exchanged. URL construction and token exchange go through [Arctic](https://arcticjs.dev), which pins the Graph API version (see [ADR-002](https://github.com/MantleJs/mantle/blob/main/docs/decisions/adr-002-arctic-oauth-internals.md)). ### Find-or-create [Section titled “Find-or-create”](#find-or-create) On callback the plugin searches the configured user service for a record where `facebookId` (configurable) matches the `id` from Facebook’s Graph API `/me` endpoint. If no record is found, it creates one with `{ facebookId, email, name }` from the profile. The same user is returned on every subsequent sign-in. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { auth } from "@mantlejs/auth"; import { facebookStrategy } from "@mantlejs/auth-facebook"; const app = mantle() .configure(express()) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure( facebookStrategy({ clientId: process.env.FACEBOOK_APP_ID!, clientSecret: process.env.FACEBOOK_APP_SECRET!, }), ); app.listen(3030); ``` **Sign-in flow:** 1. Redirect the browser to `GET /auth/facebook` 2. User authenticates on Facebook and consents 3. Facebook redirects to `GET /auth/facebook/callback?code=...&state=...` 4. The plugin exchanges the code, fetches the profile, finds or creates the user, and responds: ```json { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiJ9...", "user": { "id": "1", "facebookId": "123456789", "email": "alice@example.com", "name": "Alice" } } ``` *** ## API [Section titled “API”](#api) ### `facebookStrategy(config)` [Section titled “facebookStrategy(config)”](#facebookstrategyconfig) ```typescript function facebookStrategy(config: FacebookStrategyConfig): MantlePlugin; type FacebookStrategyConfig = { clientId: string; clientSecret: string; callbackUrl?: string; // Default: '/auth/facebook/callback' scope?: string[]; // Default: ['email', 'public_profile'] entity?: string; // Default: 'users' entityIdField?: string; // Default: 'facebookId' }; ``` | Field | Type | Default | Description | | --------------- | ---------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `clientId` | `string` | — | Facebook App ID (required) | | `clientSecret` | `string` | — | Facebook App Secret (required) | | `callbackUrl` | `string` | `'/auth/facebook/callback'` | Callback path — must match the redirect URI registered in the Facebook Developer Console | | `scope` | `string[]` | `['email', 'public_profile']` | Facebook OAuth permission scopes | | `entity` | `string` | `'users'` | Mantle service used to find or create users | | `entityIdField` | `string` | `'facebookId'` | Field on the user record matched against Facebook’s `id` field | | `redirectUrl` | `string` | none — returns JSON | Frontend URL to redirect to on completion, with tokens (or an error) in the URL fragment — see [`@mantlejs/auth-oauth`](/packages/auth-oauth/#redirecting-back-to-a-frontend-redirecturl) | **Routes registered:** | Method | Path | Description | | ------ | ------------------------- | -------------------------------------- | | `GET` | `/auth/facebook` | Redirect to Facebook consent screen | | `GET` | `/auth/facebook/callback` | Handle callback, issue Mantle JWT pair | **Must be configured after** an HTTP transport (`@mantlejs/express`, `@mantlejs/koa`, or `@mantlejs/http`) and `auth()`. *** ## Types [Section titled “Types”](#types) ```typescript import type { FacebookStrategyConfig } from "@mantlejs/auth-facebook"; ``` `FacebookStrategyConfig` is an alias for `OAuthPluginConfig` from `@mantlejs/auth-oauth`. *** ## Facebook Developer Console setup [Section titled “Facebook Developer Console setup”](#facebook-developer-console-setup) 1. Go to [developers.facebook.com](https://developers.facebook.com) and create an app 2. Add the **Facebook Login** product to your app 3. Under **Facebook Login > Settings**, add the callback URL to **Valid OAuth Redirect URIs**: `https://your-domain.com/auth/facebook/callback` 4. Copy the **App ID** and **App Secret** from **Settings > Basic** into your environment *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth-facebook # compile npx nx test auth-facebook # run tests npx nx lint auth-facebook # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) ```bash npx nx build auth-facebook cd packages/auth-facebook npm publish --access public ``` # @mantlejs/auth-github > GitHub OAuth 2.0 strategy for Mantle JS. Implements the authorization code flow — no Passport.js dependency. Registers GET /auth/github and GET /auth/github/callback on the configured HTTP transport (@mantlejs/express, @mantlejs/koa, or @mantlejs/http), then finds or creates a user record and returns a Mantle JWT pair. GitHub OAuth 2.0 strategy for [Mantle JS](https://github.com/mantlejs/mantle). Implements the authorization code flow — no Passport.js dependency. Registers `GET /auth/github` and `GET /auth/github/callback` on the configured HTTP transport (`@mantlejs/express`, `@mantlejs/koa`, or `@mantlejs/http`), then finds or creates a user record and returns a Mantle JWT pair. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth-github ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Authorization code flow [Section titled “Authorization code flow”](#authorization-code-flow) GitHub Sign-In uses the standard authorization code flow (without PKCE — GitHub does not support it for server-side apps). On each redirect request the plugin generates a random `state` token stored server-side for CSRF protection and passed through the OAuth exchange. URL construction and token exchange go through [Arctic](https://arcticjs.dev) (see [ADR-002](https://github.com/MantleJs/mantle/blob/main/docs/decisions/adr-002-arctic-oauth-internals.md)); the token request authenticates with HTTP Basic credentials. ### Find-or-create [Section titled “Find-or-create”](#find-or-create) On callback the plugin searches the configured user service for a record where `githubId` (configurable) matches the `id` field from GitHub’s `/user` endpoint. If no record is found, it creates one with `{ githubId, email, name }` from the profile. The same user is returned on every subsequent sign-in. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { auth } from "@mantlejs/auth"; import { githubStrategy } from "@mantlejs/auth-github"; const app = mantle() .configure(express()) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure( githubStrategy({ clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, }), ); app.listen(3030); ``` **Sign-in flow:** 1. Redirect the browser to `GET /auth/github` 2. User authenticates on GitHub and consents 3. GitHub redirects to `GET /auth/github/callback?code=...&state=...` 4. The plugin exchanges the code, fetches the profile, finds or creates the user, and responds: ```json { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiJ9...", "user": { "id": "1", "githubId": "12345", "email": "alice@github.com", "name": "Alice" } } ``` *** ## API [Section titled “API”](#api) ### `githubStrategy(config)` [Section titled “githubStrategy(config)”](#githubstrategyconfig) ```typescript function githubStrategy(config: GitHubStrategyConfig): MantlePlugin; type GitHubStrategyConfig = { clientId: string; clientSecret: string; callbackUrl?: string; // Default: '/auth/github/callback' scope?: string[]; // Default: ['read:user', 'user:email'] entity?: string; // Default: 'users' entityIdField?: string; // Default: 'githubId' }; ``` | Field | Type | Default | Description | | --------------- | ---------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `clientId` | `string` | — | GitHub OAuth App client ID (required) | | `clientSecret` | `string` | — | GitHub OAuth App client secret (required) | | `callbackUrl` | `string` | `'/auth/github/callback'` | Callback path — must match the Authorization callback URL registered in your GitHub OAuth App | | `scope` | `string[]` | `['read:user', 'user:email']` | GitHub OAuth scopes | | `entity` | `string` | `'users'` | Mantle service used to find or create users | | `entityIdField` | `string` | `'githubId'` | Field on the user record matched against GitHub’s `id` value | | `redirectUrl` | `string` | none — returns JSON | Frontend URL to redirect to on completion, with tokens (or an error) in the URL fragment — see [`@mantlejs/auth-oauth`](/packages/auth-oauth/#redirecting-back-to-a-frontend-redirecturl) | **Routes registered:** | Method | Path | Description | | ------ | ----------------------- | -------------------------------------- | | `GET` | `/auth/github` | Redirect to GitHub consent screen | | `GET` | `/auth/github/callback` | Handle callback, issue Mantle JWT pair | **Must be configured after** an HTTP transport (`@mantlejs/express`, `@mantlejs/koa`, or `@mantlejs/http`) and `auth()`. *** ## Types [Section titled “Types”](#types) ```typescript import type { GitHubStrategyConfig } from "@mantlejs/auth-github"; ``` `GitHubStrategyConfig` is an alias for `OAuthPluginConfig` from `@mantlejs/auth-oauth`. *** ## GitHub OAuth App setup [Section titled “GitHub OAuth App setup”](#github-oauth-app-setup) 1. Go to **Settings → Developer settings → OAuth Apps** at [github.com](https://github.com) 2. Click **New OAuth App** 3. Set the **Authorization callback URL** to `https://your-domain.com/auth/github/callback` 4. Copy the **Client ID** and generate a **Client Secret** 5. Add both to your environment variables *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth-github # compile npx nx test auth-github # run tests npx nx lint auth-github # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) ```bash npx nx build auth-github cd packages/auth-github npm publish --access public ``` # @mantlejs/auth-google > Google OAuth 2.0 strategy for Mantle JS. Implements the authorization code flow with PKCE — no Passport.js dependency. Registers GET /auth/google and GET /auth/google/callback on the configured HTTP transport (@mantlejs/express, @mantlejs/koa, or @mantlejs/http), then finds or creates a user record and returns a Mantle JWT pair. Google OAuth 2.0 strategy for [Mantle JS](https://github.com/mantlejs/mantle). Implements the authorization code flow with PKCE — no Passport.js dependency. Registers `GET /auth/google` and `GET /auth/google/callback` on the configured HTTP transport (`@mantlejs/express`, `@mantlejs/koa`, or `@mantlejs/http`), then finds or creates a user record and returns a Mantle JWT pair. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth-google ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### PKCE [Section titled “PKCE”](#pkce) Google Sign-In uses the authorization code flow with PKCE (Proof Key for Code Exchange). On each redirect request the plugin generates a fresh `code_verifier` (via [Arctic](https://arcticjs.dev), which also derives the SHA-256 / base64url `code_challenge` — see [ADR-002](https://github.com/MantleJs/mantle/blob/main/docs/decisions/adr-002-arctic-oauth-internals.md)). The verifier is stored server-side against a random `state` token and passed to the token exchange on callback. ### Find-or-create [Section titled “Find-or-create”](#find-or-create) On callback the plugin searches the configured user service for a record where `googleId` (configurable) matches the `sub` claim from Google’s userinfo endpoint. If no record is found, it creates one with `{ googleId, email, name }` from the profile. The same user is returned on every subsequent sign-in. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { auth } from "@mantlejs/auth"; import { googleStrategy } from "@mantlejs/auth-google"; const app = mantle() .configure(express()) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure(googleStrategy({ clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, })); app.listen(3030); ``` **Sign-in flow:** 1. Redirect the browser to `GET /auth/google` 2. User authenticates on Google and consents 3. Google redirects to `GET /auth/google/callback?code=...&state=...` 4. The plugin exchanges the code, fetches the profile, finds or creates the user, and responds: ```json { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiJ9...", "user": { "id": "1", "googleId": "108...", "email": "alice@gmail.com", "name": "Alice" } } ``` *** ## API [Section titled “API”](#api) ### `googleStrategy(config)` [Section titled “googleStrategy(config)”](#googlestrategyconfig) ```typescript function googleStrategy(config: GoogleStrategyConfig): MantlePlugin; type GoogleStrategyConfig = { clientId: string; clientSecret: string; callbackUrl?: string; // Default: '/auth/google/callback' scope?: string[]; // Default: ['openid', 'profile', 'email'] entity?: string; // Default: 'users' entityIdField?: string; // Default: 'googleId' }; ``` | Field | Type | Default | Description | | --------------- | ---------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `clientId` | `string` | — | Google OAuth client ID (required) | | `clientSecret` | `string` | — | Google OAuth client secret (required) | | `callbackUrl` | `string` | `'/auth/google/callback'` | Callback path — must match the redirect URI registered in Google Cloud Console | | `scope` | `string[]` | `['openid', 'profile', 'email']` | Google OAuth scopes | | `entity` | `string` | `'users'` | Mantle service used to find or create users | | `entityIdField` | `string` | `'googleId'` | Field on the user record matched against Google’s `sub` claim | | `redirectUrl` | `string` | none — returns JSON | Frontend URL to redirect to on completion, with tokens (or an error) in the URL fragment — see [`@mantlejs/auth-oauth`](/packages/auth-oauth/#redirecting-back-to-a-frontend-redirecturl) | **Routes registered:** | Method | Path | Description | | ------ | ----------------------- | -------------------------------------- | | `GET` | `/auth/google` | Redirect to Google consent screen | | `GET` | `/auth/google/callback` | Handle callback, issue Mantle JWT pair | **Must be configured after** an HTTP transport (`@mantlejs/express`, `@mantlejs/koa`, or `@mantlejs/http`) and `auth()`. *** ## Types [Section titled “Types”](#types) ```typescript import type { GoogleStrategyConfig } from "@mantlejs/auth-google"; ``` `GoogleStrategyConfig` is an alias for `OAuthPluginConfig` from `@mantlejs/auth-oauth`. *** ## Google Cloud Console setup [Section titled “Google Cloud Console setup”](#google-cloud-console-setup) No API needs to be enabled — this strategy calls Google’s OpenID Connect userinfo endpoint directly for the `openid`/`profile`/`email` scopes, not the (long since shut down) Google+ API or the People API. 1. Create a project at [console.cloud.google.com](https://console.cloud.google.com) 2. Under **Google Auth Platform → Branding**, configure the app name and support email, then under **Audience** set the publishing status. `openid`, `email`, and `profile` are non-sensitive scopes that don’t require verification. If the app stays in **Testing** mode, add your own Google account under **Audience → Test users** or sign-in will be blocked. 3. Under **Google Auth Platform → Clients**, create a client (application type **Web application**) 4. Add the callback URL to **Authorized redirect URIs**: `https://your-domain.com/auth/google/callback` 5. Copy the **Client ID** and **Client Secret** into your environment (Google has renamed/reshuffled this console section before and may again — if these labels have drifted, look for “OAuth consent screen”/“Branding” and “Credentials”/“Clients”; the underlying steps are the same.) *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth-google # compile npx nx test auth-google # run tests npx nx lint auth-google # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) ```bash npx nx build auth-google cd packages/auth-google npm publish --access public ``` # @mantlejs/auth-linkedin > LinkedIn Sign-In (OpenID Connect) strategy for Mantle JS. Implements the authorization code flow — no Passport.js dependency. Registers GET /auth/linkedin and GET /auth/linkedin/callback on the HTTP transport, then finds or creates a user record and returns a Mantle JWT pair. LinkedIn Sign-In (OpenID Connect) strategy for [Mantle JS](https://github.com/mantlejs/mantle). Implements the authorization code flow — no Passport.js dependency. Registers `GET /auth/linkedin` and `GET /auth/linkedin/callback` on the HTTP transport, then finds or creates a user record and returns a Mantle JWT pair. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth-linkedin ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### OpenID Connect [Section titled “OpenID Connect”](#openid-connect) This strategy uses LinkedIn’s **“Sign In with LinkedIn using OpenID Connect”** product — the legacy `r_liteprofile`/v2 profile API is not touched. Enable this product on your LinkedIn developer app before using this package. ### No PKCE [Section titled “No PKCE”](#no-pkce) Unlike the Google and Microsoft strategies, LinkedIn’s authorization code flow authenticates the token exchange with the client secret only. CSRF protection comes from the `state` round-trip through the configured `OAuthStateStore` — the same posture as the GitHub and Facebook strategies. ### Find-or-create [Section titled “Find-or-create”](#find-or-create) On callback the plugin searches the configured user service for a record where `linkedinId` (configurable) matches the `sub` claim from LinkedIn’s OIDC userinfo endpoint. If no record is found, it creates one with `{ linkedinId, email, name }` from the profile. The same user is returned on every subsequent sign-in. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { auth } from "@mantlejs/auth"; import { linkedinStrategy } from "@mantlejs/auth-linkedin"; const app = mantle() .configure(express()) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure( linkedinStrategy({ clientId: process.env.LINKEDIN_CLIENT_ID!, clientSecret: process.env.LINKEDIN_CLIENT_SECRET!, }), ); app.listen(3030); ``` **Sign-in flow:** 1. Redirect the browser to `GET /auth/linkedin` 2. User authenticates on LinkedIn and consents 3. LinkedIn redirects to `GET /auth/linkedin/callback?code=...&state=...` 4. The plugin exchanges the code, fetches the profile, finds or creates the user, and responds: ```json { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiJ9...", "user": { "id": "1", "linkedinId": "AAAA...", "email": "alice@example.com", "name": "Alice" } } ``` *** ## API [Section titled “API”](#api) ### `linkedinStrategy(config)` [Section titled “linkedinStrategy(config)”](#linkedinstrategyconfig) ```typescript function linkedinStrategy(config: LinkedInStrategyConfig): MantlePlugin; type LinkedInStrategyConfig = OAuthPluginConfig; // no provider-specific fields ``` | Field | Type | Default | Description | | --------------- | ---------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `clientId` | `string` | — | LinkedIn app client ID (required) | | `clientSecret` | `string` | — | LinkedIn app client secret (required) | | `callbackUrl` | `string` | `'/auth/linkedin/callback'` | Callback path — must match the redirect URI registered on the LinkedIn app | | `scope` | `string[]` | `['openid', 'profile', 'email']` | LinkedIn OAuth scopes | | `entity` | `string` | `'users'` | Mantle service used to find or create users | | `entityIdField` | `string` | `'linkedinId'` | Field on the user record matched against LinkedIn’s `sub` claim | | `redirectUrl` | `string` | none — returns JSON | Frontend URL to redirect to on completion, with tokens (or an error) in the URL fragment — see [`@mantlejs/auth-oauth`](/packages/auth-oauth/#redirecting-back-to-a-frontend-redirecturl) | **Routes registered:** | Method | Path | Description | | ------ | ------------------------- | -------------------------------------- | | `GET` | `/auth/linkedin` | Redirect to LinkedIn sign-in | | `GET` | `/auth/linkedin/callback` | Handle callback, issue Mantle JWT pair | **Must be configured after** the transport (e.g. `express()`) and `auth()`. *** ## Types [Section titled “Types”](#types) ```typescript import type { LinkedInStrategyConfig } from "@mantlejs/auth-linkedin"; ``` `LinkedInStrategyConfig` is `OAuthPluginConfig` from `@mantlejs/auth-oauth` — LinkedIn has no provider-specific config. *** ## LinkedIn developer app setup [Section titled “LinkedIn developer app setup”](#linkedin-developer-app-setup) 1. Create an app at [linkedin.com/developers/apps](https://www.linkedin.com/developers/apps) 2. Under **Products**, request access to **“Sign In with LinkedIn using OpenID Connect”** 3. Under **Auth**, add an **Authorized redirect URL**: `https://your-domain.com/auth/linkedin/callback` 4. Copy the **Client ID** and **Client Secret** into your environment *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth-linkedin # compile npx nx test auth-linkedin # run tests npx nx lint auth-linkedin # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) ```bash npx nx build auth-linkedin cd packages/auth-linkedin npm publish --access public ``` # @mantlejs/auth-local > Local (email + password) authentication strategy for Mantle JS. Built on top of @mantlejs/auth. Passwords are hashed with Argon2id via @node-rs/argon2 — the OWASP-recommended algorithm. Local (email + password) authentication strategy for [Mantle JS](https://github.com/mantlejs/mantle). Built on top of [`@mantlejs/auth`](/packages/auth/). Passwords are hashed with [Argon2id](https://github.com/nicowillis/argon2) via `@node-rs/argon2` — the OWASP-recommended algorithm. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth-local @mantlejs/auth @node-rs/argon2 jsonwebtoken ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### `localStrategy()` [Section titled “localStrategy()”](#localstrategy) A Mantle plugin that registers a `"local"` strategy with the auth engine. When `POST /authentication` is called with `{ strategy: "local", email, password }`, it: 1. Queries the entity service (default: `"users"`) for a record matching the `usernameField` 2. Verifies the submitted password against the stored Argon2id hash 3. Issues an access + refresh token pair via `engine.createTokenPair(sub)` 4. Returns `{ accessToken, refreshToken, user }` The lookup is an internal call — it bypasses any `authenticate("jwt")` hooks on the users service. ### `hashPassword()` [Section titled “hashPassword()”](#hashpassword) A `before` hook that replaces a plain-text password field with its Argon2id hash. Run it on `create` (and `patch`/`update` if your app allows password changes) before the record reaches the database. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { knex } from "@mantlejs/knex"; import { auth, authenticate, sanitizeUser } from "@mantlejs/auth"; import { localStrategy, hashPassword } from "@mantlejs/auth-local"; const app = mantle() .configure(express()) .configure(knex({ client: "pg", connection: process.env.DATABASE_URL })) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure(localStrategy()); app.use("/users", new UserService(new UserRepository(app)), { methods: ["find", "get", "create", "patch", "remove"], }); app.service("users").hooks({ before: { create: [hashPassword()], // hash on registration patch: [authenticate("jwt"), hashPassword()], // protect + re-hash on update find: [authenticate("jwt")], get: [authenticate("jwt")], remove: [authenticate("jwt")], }, after: { all: [sanitizeUser()], // strip password from all responses }, }); app.listen(3030); ``` *** ## Usage [Section titled “Usage”](#usage) ### Register a user [Section titled “Register a user”](#register-a-user) ```http POST /users Content-Type: application/json { "email": "alice@example.com", "password": "hunter2" } ``` The `hashPassword()` hook runs first, so `"hunter2"` is stored as an Argon2id hash. ```json { "id": 1, "email": "alice@example.com" } ``` ### Log in [Section titled “Log in”](#log-in) ```http POST /authentication Content-Type: application/json { "strategy": "local", "email": "alice@example.com", "password": "hunter2" } ``` ```json { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiJ9...", "user": { "id": 1, "email": "alice@example.com" } } ``` Store `accessToken` client-side and include it in subsequent requests: ```http GET /users Authorization: Bearer eyJhbGciOiJIUzI1NiJ9... ``` *** ## API [Section titled “API”](#api) ### `localStrategy(config?)` [Section titled “localStrategy(config?)”](#localstrategyconfig) Returns a `MantlePlugin`. Must be called after `auth()`. ```typescript app.configure(localStrategy({ usernameField: "email", // field in the request body to match against (default: "email") passwordField: "password", // field in the request body containing the plain-text password (default: "password") entityService: "users", // Mantle service name used to look up the entity (default: "users") })); ``` #### `LocalStrategyConfig` [Section titled “LocalStrategyConfig”](#localstrategyconfig-1) | Field | Type | Default | Description | | --------------- | -------- | ------------ | --------------------------------------------------------------------------------------------------------------- | | `usernameField` | `string` | `"email"` | The field used to look up the user record. Must match the column name in your entity service. | | `passwordField` | `string` | `"password"` | The field containing the submitted plain-text password. Also used to locate the stored hash in the user record. | | `entityService` | `string` | `"users"` | Name of the registered Mantle service that holds user records. | *** ### `hashPassword(field?)` [Section titled “hashPassword(field?)”](#hashpasswordfield) A `before` hook that reads `context.data[field]`, hashes it with Argon2id, and writes the hash back in place. ```typescript // Default: hashes context.data.password app.service("users").hooks({ before: { create: [hashPassword()] }, }); // Custom field name app.service("accounts").hooks({ before: { create: [hashPassword("passphrase")] }, }); ``` The hook is a no-op when: * `context.data` is `undefined` * The target field is absent from `context.data` * The target field value is not a string *** ## Plugin order [Section titled “Plugin order”](#plugin-order) `localStrategy()` must be configured after `auth()` because it calls `app.get("auth")` at registration time to grab the engine and register the strategy: ```typescript // Correct app .configure(auth({ secret })) .configure(localStrategy()); // Wrong — throws "@mantlejs/auth must be configured before @mantlejs/auth-local" app .configure(localStrategy()) .configure(auth({ secret })); ``` *** ## Custom username field [Section titled “Custom username field”](#custom-username-field) Use any unique field as the login identifier: ```typescript app.configure(localStrategy({ usernameField: "username" })); ``` The strategy will query `app.service("users").find({ query: { username: value } })`. *** ## Multiple user entity services [Section titled “Multiple user entity services”](#multiple-user-entity-services) If you have more than one kind of entity that can authenticate (e.g. `users` and `admins`), register multiple strategy instances under different names by implementing a custom strategy using the `AuthEngine` directly. `localStrategy` always registers as `"local"`, so two instances would overwrite each other. *** ## Security notes [Section titled “Security notes”](#security-notes) * **Argon2id** is used instead of bcrypt. Argon2id is the winner of the [Password Hashing Competition](https://www.password-hashing.net/) and is [OWASP-recommended](https://cheats.owasp.org/cheatsheets/password_storage_cheat_sheet). Unlike bcrypt, it has no 72-character password truncation limit. * All authentication failures return the same `"Invalid credentials"` message regardless of whether the user was not found or the password was wrong. This prevents user enumeration. * The `sanitizeUser()` hook (from `@mantlejs/auth`) strips the `password` and `passwordHash` fields from all service results. Always apply it on the `after: all` phase of your entity service. *** ## Error reference [Section titled “Error reference”](#error-reference) | Error | Code | When thrown | | ------------------ | ---- | -------------------------------------------------------------- | | `NotAuthenticated` | 401 | User not found | | `NotAuthenticated` | 401 | Password does not match stored hash | | `NotAuthenticated` | 401 | Credentials missing from request body | | `Error` | — | `localStrategy()` called before `auth()` (config-time failure) | *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth-local # compile npx nx test auth-local # run tests npx nx lint auth-local # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build auth-local ``` First publish (scoped packages require `--access public`): ```bash cd packages/auth-local npm publish --access public ``` Subsequent releases — bump `version` in `packages/auth-local/package.json`, then: ```bash cd packages/auth-local npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/auth-local npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/auth-local --registry http://localhost:4873 ``` # @mantlejs/auth-microsoft > Microsoft (Entra ID) OAuth 2.0 strategy for Mantle JS. Implements the authorization code flow with PKCE — no Passport.js dependency. Registers GET /auth/microsoft and GET /auth/microsoft/callback on the HTTP transport, then finds or creates a user record and returns a Mantle JWT pair. Microsoft (Entra ID) OAuth 2.0 strategy for [Mantle JS](https://github.com/mantlejs/mantle). Implements the authorization code flow with PKCE — no Passport.js dependency. Registers `GET /auth/microsoft` and `GET /auth/microsoft/callback` on the HTTP transport, then finds or creates a user record and returns a Mantle JWT pair. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth-microsoft ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### PKCE [Section titled “PKCE”](#pkce) Microsoft Sign-In uses the authorization code flow with PKCE (Proof Key for Code Exchange). On each redirect request the plugin generates a fresh `code_verifier` (via [Arctic](https://arcticjs.dev), which also derives the SHA-256 / base64url `code_challenge` — see [ADR-002](https://github.com/MantleJs/mantle/blob/main/docs/decisions/adr-002-arctic-oauth-internals.md)). The verifier is stored server-side against a random `state` token and passed to the token exchange on callback. ### Tenant [Section titled “Tenant”](#tenant) Entra ID scopes sign-in to a tenant. The default, `"common"`, accepts both work/school (Entra ID) and personal Microsoft accounts. Set `tenant` to `"organizations"` (work/school only), `"consumers"` (personal only), or a specific tenant ID to restrict who can sign in — it must match the **Supported account types** of your app registration. ### Find-or-create [Section titled “Find-or-create”](#find-or-create) On callback the plugin searches the configured user service for a record where `microsoftId` (configurable) matches the `sub` claim from Microsoft Graph’s OIDC userinfo endpoint. If no record is found, it creates one with `{ microsoftId, email, name }` from the profile. The same user is returned on every subsequent sign-in. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { auth } from "@mantlejs/auth"; import { microsoftStrategy } from "@mantlejs/auth-microsoft"; const app = mantle() .configure(express()) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure( microsoftStrategy({ clientId: process.env.MICROSOFT_CLIENT_ID!, clientSecret: process.env.MICROSOFT_CLIENT_SECRET!, }), ); app.listen(3030); ``` **Sign-in flow:** 1. Redirect the browser to `GET /auth/microsoft` 2. User authenticates on Microsoft and consents 3. Microsoft redirects to `GET /auth/microsoft/callback?code=...&state=...` 4. The plugin exchanges the code, fetches the profile, finds or creates the user, and responds: ```json { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiJ9...", "user": { "id": "1", "microsoftId": "AAAA...", "email": "alice@outlook.com", "name": "Alice" } } ``` *** ## API [Section titled “API”](#api) ### `microsoftStrategy(config)` [Section titled “microsoftStrategy(config)”](#microsoftstrategyconfig) ```typescript function microsoftStrategy(config: MicrosoftStrategyConfig): MantlePlugin; interface MicrosoftStrategyConfig extends OAuthPluginConfig { tenant?: string; // Default: 'common' } ``` | Field | Type | Default | Description | | --------------- | ---------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `clientId` | `string` | — | Entra ID application (client) ID (required) | | `clientSecret` | `string` | — | Entra ID client secret (required) | | `tenant` | `string` | `'common'` | Entra tenant: `'common'`, `'organizations'`, `'consumers'`, or a tenant ID | | `callbackUrl` | `string` | `'/auth/microsoft/callback'` | Callback path — must match the redirect URI registered in the app registration | | `scope` | `string[]` | `['openid', 'profile', 'email']` | Microsoft OAuth scopes | | `entity` | `string` | `'users'` | Mantle service used to find or create users | | `entityIdField` | `string` | `'microsoftId'` | Field on the user record matched against Microsoft’s `sub` claim | | `redirectUrl` | `string` | none — returns JSON | Frontend URL to redirect to on completion, with tokens (or an error) in the URL fragment — see [`@mantlejs/auth-oauth`](/packages/auth-oauth/#redirecting-back-to-a-frontend-redirecturl) | **Routes registered:** | Method | Path | Description | | ------ | -------------------------- | -------------------------------------- | | `GET` | `/auth/microsoft` | Redirect to Microsoft sign-in | | `GET` | `/auth/microsoft/callback` | Handle callback, issue Mantle JWT pair | **Must be configured after** the transport (e.g. `express()`) and `auth()`. *** ## Types [Section titled “Types”](#types) ```typescript import type { MicrosoftStrategyConfig } from "@mantlejs/auth-microsoft"; ``` `MicrosoftStrategyConfig` extends `OAuthPluginConfig` from `@mantlejs/auth-oauth` with the optional `tenant` field. *** ## Microsoft Entra admin center setup [Section titled “Microsoft Entra admin center setup”](#microsoft-entra-admin-center-setup) 1. Register an application at [entra.microsoft.com](https://entra.microsoft.com) → **App registrations** → **New registration** 2. Choose the **Supported account types** matching your `tenant` setting 3. Add a **Web** redirect URI: `https://your-domain.com/auth/microsoft/callback` 4. Under **Certificates & secrets**, create a client secret 5. Copy the **Application (client) ID** and the secret value into your environment *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth-microsoft # compile npx nx test auth-microsoft # run tests npx nx lint auth-microsoft # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) ```bash npx nx build auth-microsoft cd packages/auth-microsoft npm publish --access public ``` # @mantlejs/auth-oauth > Shared OAuth 2.0 base for Mantle JS. Provides the state management, PKCE, find-or-create user logic, and route registration that all OAuth strategy packages (auth-google, auth-github, etc.) build on. Not used directly by application code. Shared OAuth 2.0 base for [Mantle JS](https://github.com/mantlejs/mantle). Provides the state management, PKCE, find-or-create user logic, and route registration that all OAuth strategy packages (`auth-google`, `auth-github`, etc.) build on. Not used directly by application code. Protocol plumbing — authorization URL construction, token exchange, PKCE derivation — is delegated to [Arctic](https://arcticjs.dev) as an implementation dependency (see [ADR-002](https://github.com/MantleJs/mantle/blob/main/docs/decisions/adr-002-arctic-oauth-internals.md)); strategy packages hand-write only profile normalization. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth-oauth ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Provider contract [Section titled “Provider contract”](#provider-contract) Each strategy package implements the `OAuthProvider` interface — three methods covering the provider-specific parts of the authorization code flow: ```typescript interface OAuthProvider { usePkce: boolean; defaultScope: string[]; callbackMethod?: "GET" | "POST"; // default "GET" — "POST" for form_post providers (e.g. Apple) buildAuthUrl(params: AuthUrlParams): string; exchangeCode(params: CodeExchangeParams): Promise; // returns provider access token fetchProfile(accessToken: string, extras?: CallbackExtras): Promise; // returns { id, email?, name? } } ``` `extras` is only populated for `callbackMethod: "POST"` providers — it carries the raw callback body (`{ body?: Record }`) for fields that only exist in a form-encoded POST payload, not the query string (e.g. Apple’s `user` JSON, only sent on first authorization). ### `createOAuthPlugin` [Section titled “createOAuthPlugin”](#createoauthplugin) The factory that wires a provider into Mantle. It registers two routes (`GET /auth/{providerKey}` and `/auth/{providerKey}/callback` — `GET` by default, or `POST` when `provider.callbackMethod` is `"POST"`) on the transport’s `http:router`, manages PKCE state (generating the code verifier via Arctic; the provider derives the challenge), performs find-or-create on the configured user service, and issues the Mantle JWT pair via `@mantlejs/auth`. ### State store [Section titled “State store”](#state-store) Pending OAuth state is kept in an `OAuthStateStore` keyed on the OAuth `state` parameter. Entries expire after 10 minutes and are cleaned up lazily on each redirect request. The `codeVerifier` (PKCE only) is stored alongside the state and passed to the token exchange. The callback handler consumes the entry via `consume(state)` — an atomic read-and-remove, not a separate `get()` then `delete()` — so a double-fired network request or a replayed callback URL for the same `state` can’t both pass the pending-state check before either removes it. The default store is an in-process, in-memory `Map` — fine for a single instance, **wrong for multi-instance deployments** (Cloud Run, horizontal scaling behind a load balancer), where the callback request can land on a different instance than the one that issued the state. Inject the Redis-backed store from [`@mantlejs/auth-redis`](/packages/auth-redis/) instead (store methods are sync-or-async, so any shared backend fits): ```typescript import { Redis } from "ioredis"; import { redisStateStore } from "@mantlejs/auth-redis"; const redis = new Redis(process.env.REDIS_URL!); app.configure(googleStrategy({ clientId, clientSecret, stateStore: redisStateStore(redis) })); ``` ### Find-or-create [Section titled “Find-or-create”](#find-or-create) On callback, the plugin calls `app.service(entity).find({ query: { [entityIdField]: profile.id } })`. If the result is empty, it calls `app.service(entity).create(...)` with the normalized profile fields. The resulting user record is used to mint the Mantle JWT pair. *** ## Quick start (writing a new strategy) [Section titled “Quick start (writing a new strategy)”](#quick-start-writing-a-new-strategy) Arctic ships clients for \~60 providers — pick the matching class (or fall back to its generic `OAuth2Client` with explicit endpoints) and hand-write only the profile normalization: ```typescript import type { MantlePlugin } from "@mantlejs/mantle"; import { GeneralError } from "@mantlejs/mantle"; import { Discord } from "arctic"; // or OAuth2Client for providers Arctic doesn't ship import { createOAuthPlugin } from "@mantlejs/auth-oauth"; import type { OAuthPluginConfig, OAuthProvider } from "@mantlejs/auth-oauth"; export type MyStrategyConfig = OAuthPluginConfig; const myProvider: OAuthProvider = { usePkce: false, // when true, buildAuthUrl receives a codeVerifier; Arctic derives the S256 challenge defaultScope: ["identify", "email"], buildAuthUrl({ clientId, redirectUri, scope, state }) { const client = new Discord(clientId, "", redirectUri); // secret unused for URL construction return client.createAuthorizationURL(state, scope).toString(); }, async exchangeCode({ code, clientId, clientSecret, redirectUri }) { const client = new Discord(clientId, clientSecret, redirectUri); try { const tokens = await client.validateAuthorizationCode(code); return tokens.accessToken(); } catch { throw new GeneralError("Token exchange failed"); } }, async fetchProfile(accessToken) { const res = await fetch("https://discord.com/api/users/@me", { headers: { Authorization: `Bearer ${accessToken}` }, }); if (!res.ok) throw new GeneralError("Profile fetch failed"); const data = (await res.json()) as Record; return { id: String(data["id"]), email: data["email"] as string }; }, }; export function myStrategy(config: MyStrategyConfig): MantlePlugin { return createOAuthPlugin("myprovider", myProvider, { ...config, entityIdField: config.entityIdField ?? "myProviderId", }); } ``` Register alongside the other plugins: ```typescript app .configure(express()) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure(myStrategy({ clientId: "...", clientSecret: "..." })); ``` This registers `GET /auth/myprovider` and `GET /auth/myprovider/callback` on the Express app. *** ## API [Section titled “API”](#api) ### `createOAuthPlugin(providerKey, provider, config)` [Section titled “createOAuthPlugin(providerKey, provider, config)”](#createoauthpluginproviderkey-provider-config) ```typescript function createOAuthPlugin(providerKey: string, provider: OAuthProvider, config: OAuthPluginConfig): MantlePlugin; ``` | Parameter | Description | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `providerKey` | Short identifier for the provider (`'google'`, `'github'`, `'facebook'`). Used in default route paths and the default `entityIdField`. | | `provider` | The `OAuthProvider` implementation for this strategy. | | `config` | User-supplied configuration (client ID, secret, optional overrides). | **Throws** at plugin registration time if `@mantlejs/auth` or an HTTP transport (`@mantlejs/express`, `@mantlejs/koa`, or `@mantlejs/http`) is not configured before this plugin. **Routes registered:** | Method | Path | Description | | ----------------- | --------------------- | ------------------------------------------------------------------------------------ | | `GET` | `/auth/{providerKey}` | Redirect to provider consent screen. Generates state + PKCE (if `provider.usePkce`). | | `GET` or `POST`\* | `config.callbackUrl` | Verify state, exchange code, fetch profile, find-or-create user, issue JWT pair. | \* `POST` when `provider.callbackMethod === "POST"` (e.g. Apple’s `response_mode=form_post`); `GET` otherwise. **Response on successful callback:** ```json { "accessToken": "...", "refreshToken": "...", "user": { "id": "...", "email": "...", "name": "..." } } ``` ### Redirecting back to a frontend (`redirectUrl`) [Section titled “Redirecting back to a frontend (redirectUrl)”](#redirecting-back-to-a-frontend-redirecturl) `/auth/{providerKey}` is meant to be opened via full-page navigation (an ``, not `fetch`) — the browser leaves your app to consent on the provider’s site and comes back on its own. Without `redirectUrl`, the callback’s JSON response above just strands the user on the API’s own origin. Set `redirectUrl` to send them back into the frontend instead: ```typescript app.configure(googleStrategy({ clientId, clientSecret, redirectUrl: "https://app.example.com/" })); ``` * Success redirects to `${redirectUrl}#accessToken=...&refreshToken=...` * Failure redirects to `${redirectUrl}#error=...` Tokens travel in the URL **fragment**, never the query string, so they never reach a server log or a `Referer` header. On the frontend, read `window.location.hash` on load, hand the tokens to `@mantlejs/client`’s `client.setTokens({ accessToken, refreshToken })`, and strip the fragment with `history.replaceState`. See `examples/knowledge-base/web/src/pages/AuthPage.tsx` for a complete example. #### `OAuthPluginConfig` [Section titled “OAuthPluginConfig”](#oauthpluginconfig) | Field | Type | Default | Description | | --------------- | ----------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------- | | `clientId` | `string` | — | OAuth application client ID (required) | | `clientSecret` | `string` | — | OAuth application client secret (required) | | `callbackUrl` | `string` | `/auth/{providerKey}/callback` | Path for the callback route | | `scope` | `string[]` | `provider.defaultScope` | OAuth scopes to request | | `entity` | `string` | `'users'` | Service used to find or create users | | `entityIdField` | `string` | `'{providerKey}Id'` | Field matched against the provider’s user ID | | `stateStore` | `OAuthStateStore` | in-memory | Store for pending OAuth state. Multi-instance deployments must inject a shared (e.g. Redis-backed) implementation | | `redirectUrl` | `string` | none — returns JSON | Frontend URL to send the browser back to once the callback completes (see below) | *** ## Types [Section titled “Types”](#types) ```typescript import type { OAuthProvider, OAuthProfile, OAuthPluginConfig, OAuthStateStore, OAuthStateData, AuthUrlParams, CodeExchangeParams, CallbackExtras, } from "@mantlejs/auth-oauth"; import { createStateStore } from "@mantlejs/auth-oauth"; ``` | Type | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `OAuthProvider` | Interface that each strategy package implements | | `OAuthProfile` | Normalized profile: `{ id: string; email?: string; name?: string }` | | `OAuthPluginConfig` | Shared config options for all OAuth strategies | | `OAuthStateStore` | Store for pending OAuth state — inject via `config.stateStore` | | `OAuthStateData` | Stored entry: `{ codeVerifier?: string; expiresAt: number }` | | `AuthUrlParams` | Params passed to `provider.buildAuthUrl()` | | `CodeExchangeParams` | Params passed to `provider.exchangeCode()` | | `CallbackExtras` | Raw callback extras passed to `provider.fetchProfile()`: `{ body?: Record }` — populated only for `callbackMethod: "POST"` providers | `createStateStore(ttlMs?)` — factory behind the default in-memory `OAuthStateStore`, exported in case you want the default backend with a non-default TTL (default `600_000` — 10 minutes) instead of writing a custom store: ```typescript app.configure(myStrategy({ clientId, clientSecret, stateStore: createStateStore(5 * 60 * 1000) })); ``` *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth-oauth # compile npx nx test auth-oauth # run tests npx nx lint auth-oauth # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) ```bash npx nx build auth-oauth cd packages/auth-oauth npm publish --access public ``` # @mantlejs/auth-redis > Redis-backed stores for Mantle JS authentication: a shared RefreshTokenStore for @mantlejs/auth and a shared OAuthStateStore for @mantlejs/auth-oauth. Both defaults are in-memory and single-instance only — in multi-instance deployments (Cloud Run, horizontal scaling behind a load balancer) every instance must see the same outstanding refresh tokens and pending OAuth states. This package backs both with Redis. Redis-backed stores for [Mantle JS](https://github.com/mantlejs/mantle) authentication: a shared `RefreshTokenStore` for [`@mantlejs/auth`](/packages/auth/) and a shared `OAuthStateStore` for [`@mantlejs/auth-oauth`](/packages/auth-oauth/). Both defaults are in-memory and single-instance only — in multi-instance deployments (Cloud Run, horizontal scaling behind a load balancer) every instance must see the same outstanding refresh tokens and pending OAuth states. This package backs both with Redis. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth-redis ioredis ``` The package has no hard dependency on a Redis client — the stores accept any client matching the small `RedisClientLike` interface (a subset of Redis commands shaped to match [`ioredis`](https://github.com/redis/ioredis), which is the recommended client). Requires Redis ≥ 6.2 (`GETDEL`). *** ## Concepts [Section titled “Concepts”](#concepts) ### Why a shared refresh-token store? [Section titled “Why a shared refresh-token store?”](#why-a-shared-refresh-token-store) `@mantlejs/auth` issues access + refresh token pairs and records each refresh token’s `jti` in a `RefreshTokenStore`. Rotation consumes the `jti` exactly once; a second consume of the same token is treated as theft and revokes the whole family. The in-memory default cannot see tokens issued by another instance, so rotation and revocation break fleet-wide. The Redis store keys each `jti` with a TTL matching the token’s own `exp` (Redis evicts stale entries itself — no sweeper) and uses `GETDEL` so concurrent consumers of the same token resolve atomically: exactly one wins. ### Why a shared OAuth state store? [Section titled “Why a shared OAuth state store?”](#why-a-shared-oauth-state-store) `@mantlejs/auth-oauth` stores the pending `state` (and PKCE code verifier) between the redirect to the provider and the callback. Behind a load balancer, the callback can land on a different instance than the one that started the flow. The Redis store writes each state with `SET … EX`, so entries expire server-side and `cleanup()` is a no-op. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { Redis } from "ioredis"; import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { auth } from "@mantlejs/auth"; import { googleStrategy } from "@mantlejs/auth-google"; import { redisRefreshTokenStore, redisStateStore } from "@mantlejs/auth-redis"; const redis = new Redis(process.env.REDIS_URL!); const app = mantle() .configure(express()) .configure( auth({ secret: process.env.JWT_SECRET!, refreshTokenStore: redisRefreshTokenStore(redis), }), ) .configure( googleStrategy({ clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, stateStore: redisStateStore(redis), }), ); app.listen(3030); ``` *** ## API [Section titled “API”](#api) ### `redisRefreshTokenStore(client, options?)` [Section titled “redisRefreshTokenStore(client, options?)”](#redisrefreshtokenstoreclient-options) Returns a `RefreshTokenStore` backed by Redis. Pass it as `refreshTokenStore` in the `auth()` config. Layout: each `jti` is a string key holding its subject, expiring with the token (`SET … EX`); each subject keeps a set of its outstanding `jti`s so `revokeAll` can find them. The set’s own TTL is re-armed to the newest token’s expiry on every `add` — under a fixed `refreshExpiresIn` the newest token always expires last, so the set outlives all its members. Stale members left behind by an individual `jti` key’s expiry are harmless: `consume` and `revokeAll` both treat a missing `jti` key as already gone. #### Options [Section titled “Options”](#options) | Option | Type | Default | Description | | -------- | -------- | ------------------------ | ----------------------------------------------- | | `prefix` | `string` | `"mantle:auth:refresh:"` | Prefix applied to all keys written by the store | ### `redisStateStore(client, options?)` [Section titled “redisStateStore(client, options?)”](#redisstatestoreclient-options) Returns an `OAuthStateStore` backed by Redis. Pass it as `stateStore` in any auth-oauth strategy config. `consume(state)` uses `GETDEL` — the same atomic read-and-remove pattern `redisRefreshTokenStore` uses for rotation — so two concurrent OAuth callback requests for the same `state` (a double-fired network request, or a replayed callback URL) resolve atomically: exactly one sees the pending entry and proceeds, the other sees it already gone. #### Options [Section titled “Options”](#options-1) | Option | Type | Default | Description | | -------- | -------- | ----------------------- | ----------------------------------------------- | | `ttlMs` | `number` | `600000` (10 minutes) | How long a pending authorization state is valid | | `prefix` | `string` | `"mantle:oauth:state:"` | Prefix applied to all keys written by the store | *** ## Types [Section titled “Types”](#types) ```typescript import type { RedisClientLike, RedisRefreshTokenStoreOptions, RedisStateStoreOptions } from "@mantlejs/auth-redis"; ``` | Type | Description | | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `RedisClientLike` | The Redis command subset the stores need (`set`/`get`/`getdel`/`del`/`sadd`/`srem`/`smembers`/`expire`) — satisfied by an `ioredis` instance | | `RedisRefreshTokenStoreOptions` | Options passed to `redisRefreshTokenStore()` | | `RedisStateStoreOptions` | Options passed to `redisStateStore()` | The stores implement `RefreshTokenStore` from `@mantlejs/auth` (`add(jti, sub, exp)` / `consume(jti)` / `revokeAll(sub)`) and `OAuthStateStore` from `@mantlejs/auth-oauth` (`set` / `get` / `delete` / `consume` / `cleanup`). *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth-redis # compile npx nx test auth-redis # run tests (uses ioredis-mock — no Redis server needed) npx nx lint auth-redis # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build auth-redis ``` First publish (scoped packages require `--access public`): ```bash cd packages/auth-redis npm publish --access public ``` Subsequent releases — bump `version` in `packages/auth-redis/package.json`, then: ```bash cd packages/auth-redis npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/auth-redis npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/auth-redis --registry http://localhost:4873 ``` # @mantlejs/auth-twitter > Sign in with X (Twitter) OAuth 2.0 strategy for Mantle JS. Implements the authorization code flow with PKCE — no Passport.js dependency. Registers GET /auth/twitter and GET /auth/twitter/callback on the HTTP transport, then finds or creates a user record and returns a Mantle JWT pair. Sign in with X (Twitter) OAuth 2.0 strategy for [Mantle JS](https://github.com/mantlejs/mantle). Implements the authorization code flow with PKCE — no Passport.js dependency. Registers `GET /auth/twitter` and `GET /auth/twitter/callback` on the HTTP transport, then finds or creates a user record and returns a Mantle JWT pair. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/auth-twitter ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### PKCE [Section titled “PKCE”](#pkce) Unlike `auth-github`/`auth-facebook`/`auth-linkedin`, X’s OAuth 2.0 implementation requires PKCE (Proof Key for Code Exchange) — same posture as `auth-google`/`auth-microsoft`. On each redirect request the plugin generates a fresh `code_verifier` (via [Arctic](https://arcticjs.dev), which also derives the SHA-256 / base64url `code_challenge` — see [ADR-002](https://github.com/MantleJs/mantle/blob/main/docs/decisions/adr-002-arctic-oauth-internals.md)). The verifier is stored server-side against a random `state` token and passed to the token exchange on callback. ### Scope [Section titled “Scope”](#scope) Defaults to `["users.read", "tweet.read"]`. X’s own API reference lists `users.read` and `tweet.read` as alternative scopes for `GET /2/users/me`, but X’s developer community has repeatedly reported `403 Forbidden` from that endpoint with `users.read` alone — both are requested by default to match what’s actually been confirmed working, not just what the reference documents. ### No email [Section titled “No email”](#no-email) X’s `/2/users/me` endpoint only returns an email address via the `confirmed_email` field, gated behind an app-level email access grant that most apps don’t have, and never includes it by default. This strategy doesn’t request `user.fields=confirmed_email` (an unapproved field risks the whole call failing rather than being silently omitted), so a user record created via `twitterStrategy` never has an `email` — plan your `users` schema accordingly if X is one of several strategies you support. ### Find-or-create [Section titled “Find-or-create”](#find-or-create) On callback the plugin searches the configured user service for a record where `twitterId` (configurable) matches the `id` field from X’s `/2/users/me` response. If no record is found, it creates one with `{ twitterId, name }` from the profile (no `email` — see above). The same user is returned on every subsequent sign-in. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { auth } from "@mantlejs/auth"; import { twitterStrategy } from "@mantlejs/auth-twitter"; const app = mantle() .configure(express()) .configure(auth({ secret: process.env.JWT_SECRET! })) .configure( twitterStrategy({ clientId: process.env.TWITTER_CLIENT_ID!, clientSecret: process.env.TWITTER_CLIENT_SECRET!, }), ); app.listen(3030); ``` **Sign-in flow:** 1. Redirect the browser to `GET /auth/twitter` 2. User authenticates on X and consents 3. X redirects to `GET /auth/twitter/callback?code=...&state=...` 4. The plugin exchanges the code, fetches the profile, finds or creates the user, and responds: ```json { "accessToken": "eyJhbGciOiJIUzI1NiJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiJ9...", "user": { "id": "1", "twitterId": "1234567890", "name": "Alice" } } ``` *** ## API [Section titled “API”](#api) ### `twitterStrategy(config)` [Section titled “twitterStrategy(config)”](#twitterstrategyconfig) ```typescript function twitterStrategy(config: TwitterStrategyConfig): MantlePlugin; type TwitterStrategyConfig = OAuthPluginConfig; ``` | Field | Type | Default | Description | | --------------- | ---------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `clientId` | `string` | — | X app’s OAuth 2.0 Client ID (required) | | `clientSecret` | `string` | — | X app’s OAuth 2.0 Client Secret (required) | | `callbackUrl` | `string` | `'/auth/twitter/callback'` | Callback path — must match a redirect URI registered in the X app’s settings | | `scope` | `string[]` | `['users.read', 'tweet.read']` | X OAuth 2.0 scopes | | `entity` | `string` | `'users'` | Mantle service used to find or create users | | `entityIdField` | `string` | `'twitterId'` | Field on the user record matched against X’s `id` field | | `redirectUrl` | `string` | none — returns JSON | Frontend URL to redirect to on completion, with tokens (or an error) in the URL fragment — see [`@mantlejs/auth-oauth`](/packages/auth-oauth/#redirecting-back-to-a-frontend-redirecturl) | **Routes registered:** | Method | Path | Description | | ------ | ------------------------ | -------------------------------------- | | `GET` | `/auth/twitter` | Redirect to X sign-in | | `GET` | `/auth/twitter/callback` | Handle callback, issue Mantle JWT pair | **Must be configured after** the transport (e.g. `express()`) and `auth()`. *** ## Types [Section titled “Types”](#types) ```typescript import type { TwitterStrategyConfig } from "@mantlejs/auth-twitter"; ``` `TwitterStrategyConfig` is a plain alias of `OAuthPluginConfig` from `@mantlejs/auth-oauth` — X needs no strategy-specific fields (unlike `auth-microsoft`’s `tenant` or `auth-apple`’s Sign in with Apple key fields). *** ## X Developer Portal setup [Section titled “X Developer Portal setup”](#x-developer-portal-setup) 1. Create a project and app at [developer.x.com](https://developer.x.com) → **Developer Portal** 2. Under your app’s **User authentication settings**, enable **OAuth 2.0** and set the app type to **Web App** 3. Add a callback URI: `https://your-domain.com/auth/twitter/callback` 4. Under **App permissions**, request at least **Read** access 5. Copy the **OAuth 2.0 Client ID** and generate a **Client Secret** into your environment *** ## Development [Section titled “Development”](#development) ```bash npx nx build auth-twitter # compile npx nx test auth-twitter # run tests npx nx lint auth-twitter # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) ```bash npx nx build auth-twitter cd packages/auth-twitter npm publish --access public ``` # @mantlejs/cli > Developer CLI for Mantle JS. Scaffold new projects and generate services, repositories, and hooks from the terminal. Developer CLI for [Mantle JS](https://github.com/mantlejs/mantle). Scaffold new projects and generate services, repositories, and hooks from the terminal. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install --global @mantlejs/cli ``` Or run without installing: ```bash npx @mantlejs/cli new my-api ``` *** ## Concepts [Section titled “Concepts”](#concepts) `@mantlejs/cli` has three top-level commands: * **`mantle new`** — scaffold a complete runnable Mantle project from scratch. Prompts for transport, database, auth strategy, and package manager, then writes all boilerplate files and runs install. * **`mantle generate` (alias `g`)** — add generated code into an existing project. Generates services, hooks, and repositories following Mantle’s layer conventions. * **`mantle add`** — install a Mantle package into an existing project and, for packages with known wiring, automatically add the import and `.configure()` call to `src/app.ts`. Generated service tests use `@mantlejs/memory` so they run without a database. *** ## Quick start [Section titled “Quick start”](#quick-start) ```bash # Scaffold a new project (interactive prompts) mantle new my-api # Scaffold with flags (non-interactive) mantle new my-api --database pg --auth local --package-manager npm # Scaffold with CORS and Redis-backed OAuth state/refresh-token storage mantle new my-api --database mongodb --auth google --cors --redis --package-manager npm # Generate a service, repository, schema, and spec mantle g service users # Generate a hook mantle g hook authenticate # Generate a repository only mantle g repository users ``` *** ## API [Section titled “API”](#api) ### `mantle new ` [Section titled “mantle new \”](#mantle-new-project-name) Scaffolds a new Mantle project in `.//`: ```plaintext / ├── src/ │ ├── app.ts # Application bootstrap │ ├── index.ts # Entry point — app.listen() │ └── services/ │ └── .gitkeep ├── config/ │ ├── default.json │ └── production.json ├── package.json ├── tsconfig.json ├── .env.example ├── .gitignore └── README.md ``` **Options:** | Option | Choices | Default | Description | | ------------------- | -------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------- | | `--transport` | `express` | `express` | HTTP transport | | `--database` | `pg`, `sqlite`, `mongodb`, `none` | prompted | Database adapter | | `--auth` | `local`, `google`, `github`, `facebook`, `apple`, `microsoft`, `linkedin`, `twitter`, `none` | prompted | Auth strategy | | `--cors` | — | `false` | Enable CORS on the transport (`@mantlejs/express`’s `cors` option) | | `--redis` | — | `false` | Wire `@mantlejs/auth-redis` state/refresh-token stores (ignored when `--auth none`) | | `--package-manager` | `npm`, `yarn`, `pnpm` | prompted | Package manager | | `--skip-install` | — | `false` | Skip running install after scaffold | When `--database`, `--auth`, or `--package-manager` are omitted, the CLI prompts interactively. `--cors` and `--redis` are flag-only (no prompt) — omit them to leave both off. `sqlite` scaffolds use Knex’s `better-sqlite3` client, matching the `better-sqlite3` driver dependency it installs. Every `--auth` choice takes `clientId`/`clientSecret`; `apple` additionally takes `teamId`/`keyId`/`privateKey` in place of `clientSecret` (Sign in with Apple has no static client secret, but still uses `clientId` as the Services ID). *** ### `mantle add ` [Section titled “mantle add \”](#mantle-add-package) Installs a Mantle package into an existing project and wires it up automatically where possible. ```bash mantle add @mantlejs/auth-local ``` 1. Detects the project’s package manager from its lockfile (`pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, otherwise npm) and installs ``. 2. If the package has known wiring (e.g. `@mantlejs/logger`, `@mantlejs/socketio`, `@mantlejs/koa`, `@mantlejs/auth` and its strategy packages, `@mantlejs/mongodb`, `@mantlejs/sync`, `@mantlejs/config`), rewrites `src/app.ts` to add the import and `.configure()` call, and prints any required `.env` additions. 3. For a package with no known wiring, or when `src/app.ts` can’t be automatically modified, prints the import and `.configure()` line to add manually instead of failing. *** ### `mantle generate ` (alias `g`) [Section titled “mantle generate \ \ (alias g)”](#mantle-generate-generator-name-alias-g) Generates code in `src/services//` by default. Override with `--directory `. | Generator | Alias | Files generated | | ---------------- | ------ | ----------------------------------------------------------------------------------------- | | `service` | `s` | `.service.ts`, `.repository.ts`, `.schema.ts`, `.service.spec.ts` | | `hook` | `h` | `.hook.ts`, `.hook.spec.ts` | | `repository` | `r` | `.repository.ts` | | `authentication` | `auth` | `src/authentication.ts` (detected auth strategy config) | | `migration` | `m` | `migrations/_.ts` (requires `@mantlejs/knex`) | `service`/`repository` detect the project’s database from `package.json` and generate a matching repository base class: `KnexRepository` (`@mantlejs/knex` installed), `MongoRepository` (`@mantlejs/mongodb` installed), or `MemoryRepository` as the fallback (`--database none`). **Examples:** ```bash mantle g service users mantle g s user-profile mantle g hook rate-limit --directory src/hooks mantle g repository messages ``` **Generated service test pattern** (uses `@mantlejs/memory` — no database needed): ```typescript import { MemoryRepository } from "@mantlejs/memory"; import { UsersService } from "./users.service.js"; describe("UsersService", () => { let repo: MemoryRepository; let service: UsersService; beforeEach(() => { repo = new MemoryRepository(); service = new UsersService(repo); }); it("creates a record", async () => { const record = await service.create({}, {}); expect(record.id).toBeDefined(); }); }); ``` *** ## Types [Section titled “Types”](#types) ```typescript import type { NewProjectOptions, GeneratorName } from "@mantlejs/cli"; ``` | Type | Description | | ------------------- | -------------------------------------------------------------------------------------------------------------- | | `NewProjectOptions` | Options accepted by `newProject()` | | `GeneratorName` | `"service" \| "hook" \| "repository" \| "authentication" \| "migration"` | | `Transport` | `"express"` | | `Database` | `"pg" \| "sqlite" \| "mongodb" \| "none"` | | `Auth` | `"local" \| "google" \| "github" \| "facebook" \| "apple" \| "microsoft" \| "linkedin" \| "twitter" \| "none"` | | `PackageManager` | `"npm" \| "yarn" \| "pnpm"` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build cli # compile npx nx test cli # run tests npx nx lint cli # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build cli ``` First publish (scoped packages require `--access public`): ```bash cd packages/cli npm publish --access public ``` Subsequent releases — bump `version` in `packages/cli/package.json`, then: ```bash cd packages/cli npm publish ``` # @mantlejs/client > Official JavaScript/TypeScript client SDK for Mantle JS — call Mantle services from browsers, Node.js (18+), and React Native over REST, with optional real-time events over Socket.IO. Official JavaScript/TypeScript client SDK for [Mantle JS](https://github.com/mantlejs/mantle) — call Mantle services from browsers, Node.js (18+), and React Native over REST, with optional real-time events over Socket.IO. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/client # Optional — only needed for real-time service events npm install socket.io-client ``` The client has zero required dependencies: REST calls use the native Fetch API. `socket.io-client` is an optional peer dependency loaded lazily on the first `.on()` call. *** ## Concepts [Section titled “Concepts”](#concepts) ### The same `Service` surface, on the wire [Section titled “The same Service\ surface, on the wire”](#the-same-servicet-surface-on-the-wire) A `ServiceClient` exposes the six method names the server-side `Service` contract uses — `find`, `get`, `create`, `update`, `patch`, `remove` — dispatched as REST calls (`GET/POST/PUT/PATCH/DELETE /:service`). Switching between server and client code involves no context switching. ### Query serialization [Section titled “Query serialization”](#query-serialization) `params.query` is serialized into the bracket-notation query string every Mantle HTTP transport parses back into the identical object (`parseQueryString` in `@mantlejs/mantle`): ```typescript await api.service("users").find({ query: { age: { $gt: 21 }, $limit: 10, $sort: { name: "asc" } } }); // → GET /users?age[$gt]=21&$limit=10&$sort[name]=asc ``` Values arrive server-side as strings — pair services with `RepositoryService` and a schema for type coercion. `undefined` values are dropped; `null` serializes as an empty string, so `IS NULL` queries need server-side coercion. ### Authentication and token rotation [Section titled “Authentication and token rotation”](#authentication-and-token-rotation) `client.authenticate({ strategy, ...credentials })` posts to `/authentication` and stores the returned `accessToken`/`refreshToken` in the configured `TokenStorage` (default: `localStorage` in the browser, in-memory elsewhere). Every REST request carries `Authorization: Bearer `. On a 401, the client attempts **one** token rotation — `POST /authentication` with `{ strategy: "refresh", refreshToken }` — then retries the original request. Concurrent 401s share a single refresh (the server’s rotation treats a reused refresh token as theft and revokes the whole family). If the refresh fails, tokens are cleared, `'logout'` is emitted, and the original 401 error is thrown. ### Restoring a session on startup [Section titled “Restoring a session on startup”](#restoring-a-session-on-startup) `getAccessToken()` is synchronous, reading only the **in-memory** copy — on a fresh page load (or a new `MantleClient` instance) that copy is empty until something hydrates it from `storage`, which normally only happens as a side effect of the first authenticated REST call. A UI that checks `getAccessToken()` before making any request — the natural thing to do to decide whether to render a logged-in or logged-out view on mount — will see `undefined` and show its logged-out state on every refresh, even with a valid session sitting in storage. Use `isAuthenticated()` instead, which hydrates first: ```typescript const [authenticated, setAuthenticated] = useState(undefined); // undefined = "checking" useEffect(() => { void api.isAuthenticated().then(setAuthenticated); }, []); ``` `setTokens({ accessToken, refreshToken })` hydrates the client from a token pair obtained outside `authenticate()` — e.g. an OAuth provider’s redirect-back callback landing tokens in a URL fragment. It stores them and emits `'authenticated'`, the same as a successful `authenticate()`. ### Real-time events [Section titled “Real-time events”](#real-time-events) When the `socket` option is configured, `service.on("created" | "updated" | "patched" | "removed", handler)` subscribes to the server’s Socket.IO broadcasts (`" "`). The socket connects lazily on the first `.on()` call, all services share one connection, and multiple handlers for the same event multiplex over a single underlying socket listener. Calling `.on()` without the `socket` option throws a `GeneralError`-shaped `MantleClientError`. Event delivery is at-most-once (see the `@mantlejs/sync` README) — the client emits a `'reconnect'` event on every re-connect so callers (e.g. `@mantlejs/react`) can refetch and bound the staleness from any missed events. ### Batch coalescing [Section titled “Batch coalescing”](#batch-coalescing) With the `batch` option enabled, service calls made within the same coalescing window (default: the same microtask tick) are queued and sent as **one** `POST /batch` request instead of N separate REST calls — the transports mount that endpoint by default. Each caller’s promise still resolves or rejects independently from its own entry in the batched response, so application code is unchanged: ```typescript const api = mantle({ url: "http://localhost:3030", batch: true }); // One HTTP round trip, three independent promises — the dominant AI-agent call pattern const [user, posts, tags] = await Promise.all([ api.service("users").get(1), api.service("posts").find({ query: { authorId: 1 } }), api.service("tags").find(), ]); ``` `windowMs` widens the window (`setTimeout`-based) beyond the same tick; queues longer than `maxSize` (default 25 — match the server’s max batch size) split into multiple requests. Calls with per-request `headers` bypass coalescing and go out individually, as does `similar()`. If every entry fails with a 401 (expired token), the client performs its usual single token refresh and retries just those entries once. ### File uploads [Section titled “File uploads”](#file-uploads) `service.upload(file, options?)` sends a `multipart/form-data` request to a service whose `create` (or, with `options.id`, `patch`) runs `@mantlejs/storage`’s `handleUpload()`. It gets the same guarantees as every other call: bearer auth, one refresh-and-retry on a 401, typed `MantleClientError`s. Uploads are never batched. ```typescript const attachment = await api.service("attachments").upload(file, { fields: { articleId: "42" }, // ordinary form fields, merged into context.data by handleUpload() onProgress: ({ percent }) => setProgress(percent ?? 0), }); ``` `fetch` can’t report upload progress, so when `onProgress` is set and `XMLHttpRequest` exists (browsers, React Native), the request goes over XHR instead. Elsewhere (Node), `fetch` is used and `onProgress` never fires. Pass `signal` to abort; the promise rejects with the signal’s reason. ### Errors [Section titled “Errors”](#errors) Non-2xx responses are deserialized into `MantleClientError` — `name` (`"BadRequest"`, `"NotFound"`, …), `code` (HTTP status), `data`, `errors`, and the server’s actionable `hint` when present. Non-JSON bodies (gateway errors) fall back to the HTTP status. Network failures propagate as the native fetch `TypeError`, unwrapped. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/client"; interface Message { id: number; text: string; } const api = mantle({ url: "http://localhost:3030", socket: {} }); // Authenticate (against @mantlejs/auth + a strategy such as auth-local) await api.authenticate({ strategy: "local", email: "alice@example.com", password: "secret" }); // CRUD — same names as the server-side Service const messages = api.service("messages"); const page = await messages.find({ query: { $limit: 10, $sort: { id: "desc" } } }); const one = await messages.get(1); const created = await messages.create({ text: "Hello" }); // Real-time messages.on("created", (message) => console.log("new message", message.text)); api.on("reconnect", () => console.log("socket reconnected — refetch anything stale")); ``` Vector search, against a service that registers the `similar` custom method (`VectorRepositoryService`): ```typescript const hits = await api.service("docs").similar({ vector: [0.1, 0.2, 0.3], topK: 5 }); // hits: Array ``` *** ## API [Section titled “API”](#api) ### `mantle(options)` [Section titled “mantle(options)”](#mantleoptions) Creates a `MantleClient`. Throws `TypeError` if `url` is missing. | Option | Type | Default | Description | | --------- | ------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------- | | `url` | `string` | — (required) | Base URL of the Mantle server, e.g. `"http://localhost:3030"` | | `storage` | `TokenStorage` | `localStorage` browser / in-memory | Token persistence. Any object with `getItem`/`setItem`/`removeItem` (sync or async) works | | `socket` | `SocketOptions` | `undefined` | Socket.IO connection options, passed to `io(url, options)`. Omit to disable real-time features | | `headers` | `Record` | `{}` | Default headers appended to every REST request (per-request `params.headers` win) | | `batch` | `boolean \| BatchOptions` | `false` | Coalesce same-window service calls into one `POST /batch` request (see Batch coalescing) | `SocketOptions.io` optionally overrides the socket factory itself — inject a stub in tests, or supply a pre-bundled `io` when dynamic import of the optional peer is undesirable. ### `MantleClient` [Section titled “MantleClient”](#mantleclient) | Member | Description | | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `service(path)` | Returns the (cached) `ServiceClient` for a service path | | `authenticate(credentials)` | `POST /authentication`, stores tokens, emits `'authenticated'`, returns `AuthResult`. A 2xx without an `accessToken` throws a `GeneralError` and stores nothing | | `setTokens(tokens)` | Stores a `{ accessToken, refreshToken? }` pair obtained outside `authenticate()`, emits `'authenticated'` | | `isAuthenticated()` | `Promise` — hydrates from storage first if needed. Use on startup, not `getAccessToken()` (see “Restoring a session on startup”) | | `logout()` | Clears tokens, emits `'logout'`, fires a best-effort `POST /authentication/logout` | | `url` | The server’s base URL (trailing slashes removed) — for links to non-service routes, e.g. downloads | | `getAccessToken()` | Current access token (synchronous, from the in-memory copy — may be `undefined` until something hydrates it, even with a valid session in storage) | | `on(event, handler)` | Client events: `'authenticated'`, `'logout'`, `'reconnect'` | | `off(event, handler)` | Remove a client event handler | ### `ServiceClient` [Section titled “ServiceClient\”](#serviceclientt) | Member | HTTP | Description | | --------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------- | | `find(params?)` | `GET /:service` | `Promise>` | | `get(id, params?)` | `GET /:service/:id` | `Promise` | | `create(data, params?)` | `POST /:service` | `Promise` | | `update(id, data, params?)` | `PUT /:service/:id` | `Promise` | | `patch(id, data, params?)` | `PATCH /:service/:id` | `Promise` | | `remove(id, params?)` | `DELETE /:service/:id` | `Promise` | | `similar(data, params?)` | `POST /:service/similar` | Vector-search convention — `Promise>` | | `upload(file, options?)` | `POST /:service` (or `PATCH /:service/:id` with `options.id`) | `multipart/form-data` for `handleUpload()` — see “File uploads” | | `on(event, handler)` | — | Subscribe to `'created' \| 'updated' \| 'patched' \| 'removed'` | | `off(event, handler)` | — | Unsubscribe; the socket listener detaches with the last handler | | `realtime` | — | `true` when the client has the `socket` option — `on()`/`off()` are available | *** ## Types [Section titled “Types”](#types) ```typescript interface ClientParams { query?: Record; // serialized into the URL headers?: Record; // per-request header overrides } interface TokenStorage { getItem(key: string): string | null | Promise; setItem(key: string, value: string): void | Promise; removeItem(key: string): void | Promise; } interface BatchOptions { windowMs?: number; // coalescing window; 0 (default) = same microtask tick maxSize?: number; // max calls per POST /batch; longer queues split. Default 25 } interface AuthCredentials { strategy: string; [key: string]: unknown; } interface AuthResult { accessToken: string; refreshToken?: string; user?: unknown; } interface Paginated { total: number; limit: number; skip: number; data: T[]; } interface UploadOptions { id?: Id; // PATCH /:service/:id instead of POST /:service field?: string; // multipart field handleUpload(field) reads. Default "file" fields?: Record; // ordinary form fields, sent before the file part filename?: string; // default: the File's name, or "blob" onProgress?: (progress: { loaded: number; total?: number; percent?: number }) => void; // XHR transport only signal?: AbortSignal; headers?: Record; } class MantleClientError extends Error { code: number; // HTTP status name: string; // server error class: "BadRequest", "NotFound", … className?: string; // kebab-case class name from the wire format data?: unknown; errors?: unknown[]; hint?: string; // server's actionable guidance, when present } ``` Tokens are stored under the keys `mantle-access-token` and `mantle-refresh-token`. *** ## Development [Section titled “Development”](#development) ```bash npx nx build client # compile npx nx test client # run tests npx nx lint client # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build client ``` First publish (scoped packages require `--access public`): ```bash cd packages/client npm publish --access public ``` Subsequent releases — bump `version` in `packages/client/package.json`, then: ```bash cd packages/client npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/client npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/client --registry http://localhost:4873 ``` # @mantlejs/config > Environment-aware configuration management for Mantle JS. Loads JSON config files from a config/ directory, merges environment-specific overrides, applies MANTLE env var overrides, and optionally validates the result against a TypeBox schema at startup. Environment-aware configuration management for [Mantle JS](https://github.com/mantlejs/mantle). Loads JSON config files from a `config/` directory, merges environment-specific overrides, applies `MANTLE_*` env var overrides, and optionally validates the result against a TypeBox schema at startup. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/config ``` For schema validation, also install the peer dependency: ```bash npm install @sinclair/typebox ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### File loading order [Section titled “File loading order”](#file-loading-order) Config is assembled by merging three sources in order — later values override earlier ones: 1. `config/default.json` — base config present in all environments 2. `config/{NODE_ENV}.json` — environment-specific overrides (e.g. `config/production.json`). If `NODE_ENV` (or a custom `envVar`) is unset, this defaults to `"development"`, so `config/development.json` is the overlay loaded by default. 3. `MANTLE_*` environment variables — highest priority, applied last ### Environment variable overrides [Section titled “Environment variable overrides”](#environment-variable-overrides) Any env var prefixed with `MANTLE_` (configurable via `envPrefix`) maps to a config key. Double underscore (`__`) navigates nested objects: ```plaintext MANTLE_PORT=8080 → config.port = 8080 MANTLE_DB__POOL__MAX=25 → config.db.pool.max = 25 ``` Use `envPrefix` to namespace overrides to your own app name: ```plaintext MYAPP_PORT=8080 → config.port = 8080 MYAPP_DB__POOL__MAX=25 → config.db.pool.max = 25 ``` Values are coerced to `number` or `boolean` when the base config value has that type. ### Schema validation [Section titled “Schema validation”](#schema-validation) Pass a TypeBox schema via `options.schema` and the plugin validates the merged config at startup. A mismatch throws `GeneralError('Invalid configuration', { errors })` before the server accepts any requests — fail fast, fail loud. *** ## Quick start [Section titled “Quick start”](#quick-start) ```plaintext config/ default.json production.json ``` config/default.json ```json { "port": 3030, "db": { "client": "pg", "pool": { "min": 2, "max": 10 } } } // config/production.json { "port": 8080, "db": { "pool": { "max": 25 } } } ``` ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { config } from "@mantlejs/config"; import { Type } from "@sinclair/typebox"; const AppConfigSchema = Type.Object({ port: Type.Number(), db: Type.Object({ client: Type.String(), pool: Type.Object({ min: Type.Number(), max: Type.Number() }), }), }); const app = mantle() .configure(express()) .configure(config({ schema: AppConfigSchema })); // Read the whole merged config const cfg = app.get<{ port: number; db: { client: string } }>("config"); // Top-level keys are also set individually for convenience const port = app.get("port"); // 3030 (or 8080 in production) ``` *** ## API [Section titled “API”](#api) ### `config(options?)` [Section titled “config(options?)”](#configoptions) Returns a `MantlePlugin`. Call via `app.configure(config(options))`. ```typescript import { config } from "@mantlejs/config"; app.configure( config({ directory: "config", // optional — default: process.cwd() + '/config' schema: AppConfigSchema, // optional — TypeBox schema for startup validation envVar: "NODE_ENV", // optional — env var that selects the overlay file envPrefix: "MYAPP_", // optional — default: 'MANTLE_' }), ); ``` Side effects: * Sets `app.set('config', mergedConfig)` — the full merged object * Sets `app.set(key, value)` for each top-level key in the merged config #### `ConfigOptions` [Section titled “ConfigOptions”](#configoptions-1) | Field | Type | Default | Description | | ----------- | --------- | --------------------------- | -------------------------------------------------------------------- | | `directory` | `string` | `process.cwd() + '/config'` | Directory containing config JSON files | | `schema` | `TSchema` | — | TypeBox schema; throws `GeneralError` if merged config is invalid | | `envVar` | `string` | `"NODE_ENV"` | Env var used to select the environment overlay file | | `envPrefix` | `string` | `"MANTLE_"` | Prefix for env var overrides — use your app name to avoid collisions | *** ## Accessing configuration [Section titled “Accessing configuration”](#accessing-configuration) ```typescript // Full merged config object const cfg = app.get("config"); // Individual top-level keys (set as a convenience by the plugin) const port = app.get("port"); const db = app.get("db"); // the whole "db" object — only top-level keys are set this way // Nested values require reading the full config const maxPool = app.get("config").db.pool.max; ``` *** ## Environment variable overrides [Section titled “Environment variable overrides”](#environment-variable-overrides-1) | Env var | Maps to | | -------------------------- | ----------------------------- | | `MANTLE_PORT=8080` | `config.port = 8080` | | `MANTLE_DB__CLIENT=mysql2` | `config.db.client = "mysql2"` | | `MANTLE_DB__POOL__MAX=25` | `config.db.pool.max = 25` | With `envPrefix: "MYAPP_"`: | Env var | Maps to | | ------------------------ | ------------------------- | | `MYAPP_PORT=8080` | `config.port = 8080` | | `MYAPP_DB__POOL__MAX=25` | `config.db.pool.max = 25` | Key segments are lowercased. Values are coerced: * If the existing value is a `number`, the env string is passed through `Number()`. * If the existing value is a `boolean`, `"true"` and `"1"` become `true`; everything else becomes `false`. * Otherwise the raw string is used. *** ## Types [Section titled “Types”](#types) ```typescript import type { ConfigOptions } from "@mantlejs/config"; ``` | Type | Description | | --------------- | ---------------------------- | | `ConfigOptions` | Options passed to `config()` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build config # compile npx nx test config # run tests npx nx lint config # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build config ``` First publish (scoped packages require `--access public`): ```bash cd packages/config npm publish --access public ``` Subsequent releases — bump `version` in `packages/config/package.json`, then: ```bash cd packages/config npm publish ``` # create-mantlejs > Project initializer for Mantle JS — scaffold a new Mantle application with a single npm create command. Project initializer for [Mantle JS](https://github.com/mantlejs/mantle) — scaffold a new Mantle application with a single `npm create` command. *** ## Installation [Section titled “Installation”](#installation) No installation needed. Run directly with your package manager: ```bash npm create mantlejs@latest my-app # or npx create-mantlejs my-app ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### npm create convention [Section titled “npm create convention”](#npm-create-convention) `create-mantlejs` follows the npm initializer convention. Running `npm create mantlejs my-app` resolves to `create-mantlejs` on the npm registry and invokes the `create-mantlejs` bin. This means users never need to install the package globally. `create-mantlejs` is a thin wrapper around `@mantlejs/cli`’s `newProject()` — see that package’s README for the full scaffold surface (database/auth/CORS/Redis options, what gets generated). *** ## Quick start [Section titled “Quick start”](#quick-start) ```bash # Scaffold a new project (interactive prompts) npm create mantlejs@latest my-app # Scaffold non-interactively (flags go after `--`) npm create mantlejs@latest my-app -- --database pg --auth local --package-manager npm # Move into the project cd my-app # Install dependencies npm install # Start the dev server npm run dev ``` *** ## API [Section titled “API”](#api) ### `newProject(name, options)` [Section titled “newProject(name, options)”](#newprojectname-options) Re-exported from `@mantlejs/cli`; the bin’s programmatic entry point. Returns a `Promise` that resolves once the project has been scaffolded (and installed, unless `skipInstall` is set). ```typescript import { newProject } from "create-mantlejs"; await newProject("my-app", { database: "pg", auth: "local", packageManager: "npm", }); ``` #### CLI flags [Section titled “CLI flags”](#cli-flags) | Flag | Values | Default | Description | | ------------------------ | -------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------ | | `--database ` | `pg`, `sqlite`, `mongodb`, `none` | prompted | Database adapter | | `--auth ` | `local`, `google`, `github`, `facebook`, `apple`, `microsoft`, `linkedin`, `twitter`, `none` | prompted | Auth strategy | | `--cors` | — | `false` | Enable CORS on the transport | | `--redis` | — | `false` | Wire `@mantlejs/auth-redis` state/refresh-token stores | | `--package-manager ` | `npm`, `yarn`, `pnpm` | prompted | Package manager | | `--skip-install` | — | `false` | Skip running install after scaffold | *** ## Types [Section titled “Types”](#types) ```typescript import type { NewProjectOptions, Database, Auth, PackageManager, Transport } from "create-mantlejs"; ``` | Type | Description | | ------------------- | -------------------------------------------------------------------------------------------------------------- | | `NewProjectOptions` | Options accepted by `newProject()` | | `Database` | `"pg" \| "sqlite" \| "mongodb" \| "none"` | | `Auth` | `"local" \| "google" \| "github" \| "facebook" \| "apple" \| "microsoft" \| "linkedin" \| "twitter" \| "none"` | | `PackageManager` | `"npm" \| "yarn" \| "pnpm"` | | `Transport` | `"express"` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build create-mantlejs # compile npx nx test create-mantlejs # run tests npx nx lint create-mantlejs # lint npx nx run create-mantlejs:e2e-scaffold # full scaffold → build → test → boot → CRUD → SIGTERM smoke test ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build create-mantlejs ``` First publish: ```bash cd packages/create-mantlejs npm publish --access public ``` Subsequent releases — bump `version` in `packages/create-mantlejs/package.json`, then: ```bash cd packages/create-mantlejs npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/create-mantlejs npm publish --registry http://localhost:4873 # Test the initializer npm create mantlejs my-test-app --registry http://localhost:4873 ``` # @mantlejs/dynamodb > Amazon DynamoDB adapter for Mantle JS. Provides a dynamodb() Mantle plugin and an abstract DynamoDbRepository base class that implements the Repository interface using the AWS SDK v3. Amazon DynamoDB adapter for [Mantle JS](https://github.com/mantlejs/mantle). Provides a `dynamodb()` Mantle plugin and an abstract `DynamoDbRepository` base class that implements the `Repository` interface using the AWS SDK v3. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/dynamodb @aws-sdk/client-dynamodb @aws-sdk/util-dynamodb ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Plugin [Section titled “Plugin”](#plugin) `dynamodb()` is a Mantle plugin. It creates an `@aws-sdk/client-dynamodb` `DynamoDBClient` instance and stores it on the application at `app.get("dynamodb")`. When the application tears down, the client is destroyed automatically. ### Repository [Section titled “Repository”](#repository) `DynamoDbRepository` implements the Mantle `Repository` contract against DynamoDB. Extend it with a concrete class, declare `tableName`, and the full set of methods (`findAll`, `findById`, `save`, `saveAll`, `updateById`, `patchById`, `deleteById`, `count`) work out of the box. The repository uses: * **GetItem** for `findById` * **PutItem** for `save` * **BatchWriteItem** for `saveAll` (auto-chunked at 25 items per DynamoDB limit) * **UpdateItem** with a condition expression for `updateById` and `patchById` * **DeleteItem** with a condition expression for `deleteById` * **Scan** for `findAll` (falls back to **Query** when a sort key is configured and the partition key is in the where clause) * **Scan with SELECT COUNT** for `count` ### Query translation [Section titled “Query translation”](#query-translation) `dynamodbify()` converts a Mantle `QueryParams.where` clause into a DynamoDB `FilterExpression`. Supported operators: | Operator | DynamoDB expression | | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `{ field: value }` | `#n = :v` | | `{ field: null }` | `(attribute_not_exists(#n) OR #n = :v)` — matches a missing attribute or an explicit DynamoDB `NULL`-type value | | `{ field: { $gt } }` | `#n > :v` | | `{ field: { $gte } }` | `#n >= :v` | | `{ field: { $lt } }` | `#n < :v` | | `{ field: { $lte } }` | `#n <= :v` | | `{ field: { $ne: value } }` | `#n <> :v` | | `{ field: { $ne: null } }` | `attribute_exists(#n)` | | `{ field: { $in: [...] } }` | `#n IN (...)` | | `{ field: [a, b] }` | `#n IN (...)` (shorthand) | | `{ field: { $nin: [...] } }` | `NOT (#n IN (...))` | | `{ field: { $begins: "prefix" } }` | `begins_with(#n, :v)` | | `{ field: { $contains: "str" } }` | `contains(#n, :v)` — scalar: substring/element match | | `{ field: { $contains: [...] } }` | `(contains(#n,:v0) AND contains(#n,:v1) ...)` — one ANDed `contains()` per element (every element required, matching the memory/supabase/knex-pg reference semantics) | | `{ field: { $contains: {...} } }` | flattened into ANDed leaf conditions, one per nested path — DynamoDB has no native nested-object containment function, so a superset check on an object operand is expressed as “every leaf key/value matches at its path” | | `{ "a.b.c": value }` | `#n0.#n1.#n2 = :v` — nested attribute path (each segment gets its own alias) | | `{ $or: [...] }` | `(expr OR expr)` | | `{ $and: [...] }` | `(expr AND expr)` | `$like` is deliberately unsupported (DynamoDB has no wildcard matching) and throws `BadRequest`. Nested dot-path fields (`"metadata.owner.name"`) address real nested attributes — DynamoDB supports this natively via multi-segment `ExpressionAttributeNames` aliases (`#n0.#n1.#n2`), and every operator above (including `$in`/`$nin`/null-checks) works the same whether the field is top-level or nested — DynamoDB’s expression language treats a nested document path like any other operand, unlike SQL dialects where `IN`/null-checks against a JSON path need special handling that isn’t always available. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { dynamodb, DynamoDbRepository } from "@mantlejs/dynamodb"; // ─── Domain entity ───────────────────────────────────────────────────────── interface Post extends Record { id: string; title: string; body: string; status: "draft" | "published"; } // ─── Infrastructure ──────────────────────────────────────────────────────── class PostRepository extends DynamoDbRepository { readonly tableName = "posts"; // partitionKey defaults to "id" /** Custom query: find all published posts */ async findPublished(): Promise { return this.findAll({ where: { status: "published" } }); } } // ─── Application ─────────────────────────────────────────────────────────── const app = mantle() .configure(express()) .configure(dynamodb({ region: "us-east-1" })); const posts = new PostRepository(app); app.use("/posts", new PostService(posts), { methods: ["find", "get", "create", "update", "patch", "remove"], }); app.listen(3030); ``` ### Local development (DynamoDB Local) [Section titled “Local development (DynamoDB Local)”](#local-development-dynamodb-local) ```typescript const app = mantle().configure( dynamodb({ clientConfig: { region: "us-east-1", endpoint: "http://localhost:8000", credentials: { accessKeyId: "local", secretAccessKey: "local" }, }, }), ); ``` ### Composite key (partition + sort key) [Section titled “Composite key (partition + sort key)”](#composite-key-partition--sort-key) ```typescript interface OrderItem extends Record { orderId: string; itemId: string; quantity: number; } class OrderItemRepository extends DynamoDbRepository { readonly tableName = "order-items"; override readonly partitionKey = "orderId"; override readonly sortKey = "itemId"; } // findById with composite key const item = await repo.findById({ pk: "ORDER#123", sk: "ITEM#456" }); // findAll — Query is used automatically when partitionKey is in the where clause const items = await repo.findAll({ where: { orderId: "ORDER#123" } }); ``` *** ## API [Section titled “API”](#api) ### `dynamodb(config?)` [Section titled “dynamodb(config?)”](#dynamodbconfig) Returns a `MantlePlugin`. Call via `app.configure(dynamodb(config))`. ```typescript app.configure(dynamodb({ region: "us-east-1", // optional — AWS region clientConfig: { ... }, // optional — full DynamoDBClientConfig (overrides region) })); ``` Side effects: * Stores the `DynamoDBClient` at `app.get("dynamodb")` * Wraps `app.teardown()` to call `client.destroy()` on shutdown #### `DynamoDbConfig` [Section titled “DynamoDbConfig”](#dynamodbconfig-1) | Field | Type | Default | Description | | -------------- | ---------------------- | ------- | ------------------------------------------------------------ | | `region` | `string` | — | AWS region. Falls back to `AWS_REGION` env var when omitted. | | `clientConfig` | `DynamoDBClientConfig` | — | Full AWS SDK client config. Takes precedence over `region`. | *** ### `DynamoDbRepository` [Section titled “DynamoDbRepository\”](#dynamodbrepositoryt-d) Abstract base class. Extend it, declare `tableName`, and override optional properties. ```typescript class UserRepository extends DynamoDbRepository { readonly tableName = "users"; // Optional overrides: override readonly partitionKey = "userId"; override readonly sortKey = "email"; override readonly timestamps = false; } ``` #### Properties [Section titled “Properties”](#properties) | Property | Type | Default | Description | | -------------- | --------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------- | | `tableName` | `string` | — | DynamoDB table name **(required)** | | `partitionKey` | `string` | `"id"` | Partition key attribute name | | `sortKey` | `string \| undefined` | `undefined` | Sort key attribute name (composite key tables) | | `timestamps` | `boolean` | `true` | Auto-write `createdAt` / `updatedAt` ISO-8601 strings | | `lastKey` | `Record \| undefined` | `undefined` | **Deprecated** — use `findPage()`. `LastEvaluatedKey` from the most recent paginated `findAll()` call | #### Methods (from `Repository`) [Section titled “Methods (from Repository\)”](#methods-from-repositoryt) | Method | Description | | ---------------------- | --------------------------------------------------------------------- | | `findAll(params?)` | Scan (or Query when PK is in where). Supports `skip`/`limit` offsets. | | `findPage(params?)` | One page via native cursor pagination — see below | | `findById(id)` | GetItem by partition key (or composite key) | | `save(data)` | PutItem — auto-generates UUID when no `partitionKey` is set | | `saveAll(data[])` | BatchWriteItem in chunks of 25 — auto-generates UUIDs as needed | | `updateById(id, data)` | UpdateItem (full replacement, throws NotFound if missing) | | `patchById(id, data)` | UpdateItem (partial, strips `undefined`, throws NotFound if missing) | | `deleteById(id)` | DeleteItem (throws NotFound if missing) | | `count(params?)` | Scan SELECT COUNT (paginates automatically) | | `withTransaction(fn)` | Execute writes atomically via TransactWriteItems (100-item limit) | #### Cursor pagination [Section titled “Cursor pagination”](#cursor-pagination) DynamoDB is natively cursor-paginated. Use `findPage()` — each page carries an opaque `cursor` (the `LastEvaluatedKey`, base64-JSON encoded); pass it back to fetch the next page: ```typescript const repo = new PostRepository(app); // First page const page1 = await repo.findPage({ limit: 20 }); // Next page — pass the cursor from the previous page if (page1.cursor) { const page2 = await repo.findPage({ limit: 20, cursor: page1.cursor }); } ``` `findPage()` is stateless — the cursor lives entirely in the returned page, so concurrent calls on one repository instance never interfere. It uses Query when a `sortKey` is defined and the where clause pins the partition key, otherwise Scan. `skip` and `sort` are rejected with `BadRequest` (offsets and cross-page ordering don’t compose with cursors — use `findAll()` for those). Note that with a filtering `where`, DynamoDB applies `limit` *before* filtering, so a page may hold fewer than `limit` items while `cursor` is still set. > **Deprecated:** the previous mechanism — reading `repo.lastKey` after `findAll()` and passing it back as `_startKey` — still works but shares mutable state on the repository instance. It will be removed in the next minor release. `skip` on `findAll()` performs in-memory offset (scans all pages) — use `findPage()` for large tables. #### Transactions [Section titled “Transactions”](#transactions) ```typescript await repo.withTransaction(async (tx) => { await tx.save({ id: "order-1", status: "pending", total: 99.99 }); await tx.save({ id: "item-1", orderId: "order-1", qty: 2 }); }); ``` In transaction mode, mutation methods return the input data immediately (DynamoDB `TransactWriteItems` does not support `ReturnValues`). All writes commit atomically when the callback resolves. *** ### `dynamodbify(where)` [Section titled “dynamodbify(where)”](#dynamodbifywhere) Converts a Mantle `where` clause to a DynamoDB `FilterExpression`. Useful when writing raw SDK commands inside a custom repository method. ```typescript import { dynamodbify } from "@mantlejs/dynamodb"; const { expression, names, values } = dynamodbify({ status: "active", age: { $gt: 18 }, }); // expression: "#n0 = :v0 AND #n1 > :v1" ``` ### `buildKeyCondition(partitionKey, sortKey, where)` [Section titled “buildKeyCondition(partitionKey, sortKey, where)”](#buildkeyconditionpartitionkey-sortkey-where) Splits a where clause into a `KeyConditionExpression` and an optional `FilterExpression` for use in `QueryCommand`. *** ### Error mapping [Section titled “Error mapping”](#error-mapping) AWS SDK errors are translated to typed `MantleError` subclasses by exception name: | DynamoDB exception name | Error thrown | | -------------------------------------------------------------------------------------- | ------------------------ | | Already a MantleError (e.g. thrown by a hook, or by a custom repository method) | passed through unchanged | | `ResourceNotFoundException` | `NotFound` | | `ConditionalCheckFailedException` | `NotFound` | | `ProvisionedThroughputExceededException`, `RequestLimitExceeded`, `ServiceUnavailable` | `Unavailable` | | `AccessDeniedException`, `UnauthorizedException` | `Forbidden` | | `ValidationException` | `BadRequest` | | `TransactionConflictException`, `TransactionCanceledException` | `Conflict` | | other (including a non-`Error` throw) | `GeneralError` | *** ## Types [Section titled “Types”](#types) ```typescript import type { DynamoDbConfig } from "@mantlejs/dynamodb"; import type { FilterExpression, WhereClause } from "@mantlejs/dynamodb"; ``` | Type | Description | | ------------------ | ---------------------------------------------------------------- | | `DynamoDbConfig` | Options passed to `dynamodb()` | | `WhereClause` | Input type for `dynamodbify()` and `buildKeyCondition()` | | `FilterExpression` | Return type of `dynamodbify()` — `{ expression, names, values }` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build dynamodb # compile npx nx test dynamodb # run tests npx nx lint dynamodb # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build dynamodb ``` First publish (scoped packages require `--access public`): ```bash cd packages/dynamodb npm publish --access public ``` Subsequent releases — bump `version` in `packages/dynamodb/package.json`, then: ```bash cd packages/dynamodb npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/dynamodb npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/dynamodb --registry http://localhost:4873 ``` # @mantlejs/embeddings > Auto-embed-on-write hook for Mantle JS. Calls a pluggable embedding provider and upserts the result into a VectorRepository after a create/update/patch — "one line instead of a hand-written trigger." Auto-embed-on-write hook for [Mantle JS](https://github.com/mantlejs/mantle). Calls a pluggable embedding provider and upserts the result into a `VectorRepository` after a create/update/patch — “one line instead of a hand-written trigger.” *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/embeddings ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Why this package exists [Section titled “Why this package exists”](#why-this-package-exists) `@mantlejs/pinecone`, `@mantlejs/qdrant`, `@mantlejs/mongodb`’s `MongoVectorRepository`, and `@mantlejs/knex`’s `KnexVectorRepository` are all deliberately model-agnostic: the caller must always supply an already-computed `number[]` vector — none of them generates an embedding from source text. `embed()` is the missing “text in, vector attached” step, as a hook rather than something every service hand-rolls in its own `create`/`update`/`patch` methods. ### Bring your own provider [Section titled “Bring your own provider”](#bring-your-own-provider) `@mantlejs/embeddings` never bundles a specific embedding model — no OpenAI/Cohere/etc. SDK dependency. `EmbeddingProvider` is a one-method interface: ```typescript interface EmbeddingProvider { dimensions?: number; embed(text: string): Promise; } ``` Wrap whichever provider you use behind it — an HTTP call to an embedding API, a local model, or (for demos) a deterministic hash-based stand-in. ### The cross-adapter write-consistency pattern, made concrete [Section titled “The cross-adapter write-consistency pattern, made concrete”](#the-cross-adapter-write-consistency-pattern-made-concrete) See the root [README’s “Services with multiple repositories”](https://github.com/MantleJs/mantle/blob/main/README.md#services-with-multiple-repositories) section for the general pattern; `embed()` is its reference implementation: * **Idempotent, keyed on the source record’s id.** `upsertVector(id, vector, data)` is an upsert on every adapter — re-running `embed()` for the same id (a retry, or a second edit) replaces the vector at that id rather than duplicating it. This comes from the adapter contract, not from anything `embed()` does itself. * **Safe to retry.** Because it’s an idempotent upsert, calling it again after a partial failure is always safe. * **Non-fatal on failure.** A provider error or a vector-store error is caught inside the hook and never rethrown — the primary write already succeeded (or already failed for its own reason), and a degraded embedding provider must never turn that into a failed request for the caller. ### Attach after create/update/patch — never after find/get/remove [Section titled “Attach after create/update/patch — never after find/get/remove”](#attach-after-createupdatepatch--never-after-findgetremove) `embed()` reads the just-written record off `ctx.result`. Attach it to `after.create`/`after.update`/`after.patch`, where a freshly-written single record is guaranteed to be there. It’s a no-op (skips) if `ctx.result` is an array or a paginated page — i.e. it’s safe to *not* worry about accidentally attaching it to `after.find`, but there’s still nothing useful for it to do there. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { http } from "@mantlejs/http"; import { knex, KnexVectorRepository } from "@mantlejs/knex"; import { embed } from "@mantlejs/embeddings"; import type { EmbeddingProvider } from "@mantlejs/embeddings"; const app = mantle() .configure(http()) .configure(knex({ client: "pg", connection: process.env.DATABASE_URL })); class ArticleVectorRepository extends KnexVectorRepository
{ readonly tableName = "articles"; readonly vectorColumn = "embedding"; } const provider: EmbeddingProvider = { dimensions: 1536, async embed(text) { const res = await fetch("https://api.openai.com/v1/embeddings", { method: "POST", headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ model: "text-embedding-3-small", input: text }), }); const { data } = await res.json(); return data[0].embedding; }, }; const articleVectors = new ArticleVectorRepository(app); const embedArticle = embed({ vectors: articleVectors, provider, field: ["title", "body"] }); app.use("articles", new ArticleService(new ArticleRepository(app))); app.service("articles").hooks({ after: { create: [embedArticle], update: [embedArticle], patch: [embedArticle], }, }); ``` *** ## API [Section titled “API”](#api) ### `embed(options)` [Section titled “embed(options)”](#embedoptions) Returns a `HookFunction`. | Field | Type | Default | Description | | ---------- | ------------------------------------------------------ | ------------------------------------ | ------------------------------------------------------------------------------------------ | | `vectors` | `VectorRepository` | — | Where the computed embedding is upserted (required) | | `provider` | `EmbeddingProvider` | — | Called with the extracted text to produce the vector (required) | | `field` | `keyof T \| Array \| ((record: T) => string)` | — | Source text: one field, several fields joined with `"\n"`, or a function (required) | | `onError` | `(error, record, ctx) => void` | logs via `app.get("logger")` | Called when extraction, the provider, or the upsert throws. Never fails the primary write. | ### `EmbeddingProvider` [Section titled “EmbeddingProvider”](#embeddingprovider) ```typescript interface EmbeddingProvider { dimensions?: number; embed(text: string): Promise; } ``` *** ## Types [Section titled “Types”](#types) ```typescript import type { EmbedOptions, EmbeddingProvider } from "@mantlejs/embeddings"; ``` | Type | Description | | ------------------- | ------------------------------------- | | `EmbedOptions` | Options passed to `embed()` | | `EmbeddingProvider` | The pluggable text-to-vector contract | *** ## Development [Section titled “Development”](#development) ```bash npx nx build embeddings # compile npx nx test embeddings # run tests npx nx lint embeddings # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build embeddings ``` First publish (scoped packages require `--access public`): ```bash cd packages/embeddings npm publish --access public ``` Subsequent releases — bump `version` in `packages/embeddings/package.json`, then: ```bash cd packages/embeddings npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/embeddings npm publish --registry http://localhost:4873 ``` # @mantlejs/express > Express HTTP transport adapter for Mantle JS. Mounts each registered service as a set of REST routes and converts MantleError instances to the correct HTTP status codes. Express HTTP transport adapter for Mantle JS. Mounts each registered service as a set of REST routes and converts `MantleError` instances to the correct HTTP status codes. ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/express express npm install --save-dev @types/express ``` ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; const app = mantle().configure(express()); app.use("users", new UserService(), { methods: ["find", "get", "create", "patch", "remove"], }); app.listen(3030, () => console.log("Listening on port 3030")); ``` ## API [Section titled “API”](#api) ### `express(existingApp?, options?)` [Section titled “express(existingApp?, options?)”](#expressexistingapp-options) Returns a `MantlePlugin`. When applied via `app.configure(express())`, it: * Creates (or reuses) an Express application and stores it under `app.get("http:router")` — the transport-neutral contract other Mantle packages (`@mantlejs/auth-oauth`, `@mantlejs/mcp`, `@mantlejs/openapi`) use to mount their own raw routes regardless of which HTTP transport is configured. * Patches `app.use()` so that every service registration automatically mounts REST routes. * Attaches an error handler after all routes are registered. * Adds `app.listen(port, callback?)` as a shorthand for `expressApp.listen(...)`. * Optionally mounts a `GET /_services` introspection endpoint (see below). #### `ExpressOptions` [Section titled “ExpressOptions”](#expressoptions) | Field | Type | Default | Description | | --------------- | ------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------- | | `introspection` | `boolean \| { path?: string }` | `false` | Mount the introspection endpoint; pass `{ path }` to customize it. | | `batch` | `boolean \| { path?: string; maxSize?: number }` | `true` | Mount the `POST /batch` endpoint; `false` disables it, `{ path, maxSize }` configures it. | | `cors` | `boolean \| CorsOptions` | `false` | Enable CORS via the `cors` npm package; `true` uses permissive defaults, an object customizes origin/methods/headers/credentials. | Pass an existing Express app if you need to share middleware or configure Express yourself: ```typescript import expressLib from "express"; import { express } from "@mantlejs/express"; const expressApp = expressLib(); expressApp.use(cors()); const app = mantle().configure(express(expressApp)); ``` Add raw Express middleware by passing a function to `app.use()`: ```typescript app.use(morgan("dev")); ``` *** ### Route mapping [Section titled “Route mapping”](#route-mapping) Each method in `options.methods` is mapped to an HTTP route: | Service method | HTTP route | Status | | -------------- | -------------------- | ------ | | `find` | `GET /path` | 200 | | `get` | `GET /path/:id` | 200 | | `create` | `POST /path` | 201 | | `update` | `PUT /path/:id` | 200 | | `patch` | `PATCH /path/:id` | 200 | | `remove` | `DELETE /path/:id` | 200 | | custom method | `POST /path/:method` | 200 | Custom methods (any name beyond the six standard ones) are mounted as `POST /path/:method` and routed through `ServiceHandle.dispatch()`. Query string parameters are passed to the service as `params.query`. Request headers are available as `params.headers`. `params.provider` is always `"rest"` for HTTP-originated calls. *** ### Introspection endpoint [Section titled “Introspection endpoint”](#introspection-endpoint) Opt in to a machine-readable service catalog: ```typescript app.configure(express(undefined, { introspection: true })); // or with a custom path: app.configure(express(undefined, { introspection: { path: "/__meta" } })); ``` `GET /_services` (or the custom path) then returns a `ServiceDescriptor[]` — one entry per registered service with `path`, `methods`, `events`, `schema`, the repository’s `capabilities` (when the service exposes them, e.g. `RepositoryService`), and `authRequired`. Off by default; without the option the route 404s. *** ### Batch endpoint [Section titled “Batch endpoint”](#batch-endpoint) `POST /batch` (mounted by default) dispatches an array of calls through `app.batch()` in one round trip — useful for any caller that fires several related requests at once, especially AI agents constructing raw HTTP: ```bash curl -X POST http://localhost:3030/batch -H "content-type: application/json" -d '[ { "service": "users", "method": "get", "id": 1 }, { "service": "messages", "method": "find", "params": { "query": { "$limit": 5 } } } ]' ``` Each call runs the target service’s **full hook pipeline** (including `authenticate("jwt")`) with the batch request’s headers — batch is not a way to bypass authentication or validation. The response is a `BatchResult[]` in the same order as the request array; calls execute concurrently and each entry independently reports `{ status: "success", result }` or `{ status: "error", error }` (no cross-call atomicity). Batches over `maxSize` (default 25) are rejected with `400 BadRequest` before any call executes. ```typescript app.configure(express(undefined, { batch: false })); // disable app.configure(express(undefined, { batch: { path: "/_batch", maxSize: 50 } })); ``` `@mantlejs/client` can coalesce same-tick service calls into this endpoint automatically — see its `batch` option. *** ### CORS [Section titled “CORS”](#cors) Disabled by default — consistent with Mantle’s secure-by-default posture elsewhere. Enable it with `cors: true` for permissive defaults, or pass a `CorsOptions` object to customize: ```typescript app.configure(express(undefined, { cors: true })); // reflects Origin, allows GET/POST/PUT/PATCH/DELETE, no credentials app.configure( express(undefined, { cors: { origin: ["https://app.example.com"], credentials: true }, }), ); ``` `CorsOptions` (exported from `@mantlejs/mantle`) is shared across `@mantlejs/express`, `@mantlejs/koa`, and `@mantlejs/http` so switching transports doesn’t require relearning CORS configuration: | Field | Type | Default | Description | | ---------------- | ------------------------------------------------------------------ | ------------------------------------------- | ---------------------------------------------------------- | | `origin` | `boolean \| string \| string[] \| ((origin) => boolean \| string)` | `true` (reflect the request’s `Origin`) | Allowed origin(s) for `Access-Control-Allow-Origin`. | | `methods` | `string[]` | `["GET", "POST", "PUT", "PATCH", "DELETE"]` | Allowed methods for `Access-Control-Allow-Methods`. | | `allowedHeaders` | `string[]` | reflects `Access-Control-Request-Headers` | Allowed request headers. | | `exposedHeaders` | `string[]` | none | Headers exposed to the browser. | | `credentials` | `boolean` | `false` | Allow credentials (cookies, `Authorization`) cross-origin. | | `maxAge` | `number` | none | Preflight cache duration in seconds. | `@mantlejs/express` wraps the `cors` npm package internally — it’s installed as a dependency of `@mantlejs/express`, not something you need to add yourself. *** ### `errorHandler()` [Section titled “errorHandler()”](#errorhandler) Standalone Express error-handling middleware. It is attached automatically by `express()`, but can also be used directly if you manage your own Express app: ```typescript import { errorHandler } from "@mantlejs/express"; expressApp.use(errorHandler()); ``` `MantleError` subclasses are serialized to JSON with the correct HTTP status code. Any other error becomes a 500 response. ## Development [Section titled “Development”](#development) ```bash npx nx build express # compile npx nx test express # run tests npx nx lint express # lint ``` ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build express ``` First publish (scoped packages require `--access public`): ```bash cd packages/express npm publish --access public ``` Subsequent releases — bump `version` in `packages/express/package.json`, then: ```bash cd packages/express npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/express npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/express --registry http://localhost:4873 ``` # @mantlejs/http > Zero-dependency HTTP transport adapter for Mantle JS. Exposes two handlers from a single http() plugin: a Node.js (req, res) handler for use with http.createServer, and a Fetch API (request: Request) => Promise handler for Cloudflare Workers, Vercel Edge Functions, AWS Lambda@Edge, and other edge runtimes. No Express or Koa required — route matching and body parsing are implemented from scratch using only Node.js built-ins. Zero-dependency HTTP transport adapter for [Mantle JS](https://github.com/mantlejs/mantle). Exposes two handlers from a single `http()` plugin: a Node.js `(req, res)` handler for use with `http.createServer`, and a Fetch API `(request: Request) => Promise` handler for Cloudflare Workers, Vercel Edge Functions, AWS Lambda\@Edge, and other edge runtimes. No Express or Koa required — route matching and body parsing are implemented from scratch using only Node.js built-ins. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/http ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Two handlers, one plugin [Section titled “Two handlers, one plugin”](#two-handlers-one-plugin) `http()` registers two handlers on the Mantle application: * **`httpHandler`** (`NodeHttpHandler`) — a `(req: IncomingMessage, res: ServerResponse) => void` callback compatible with Node.js `http.createServer`. Calling `app.listen(port)` automatically creates a server using this handler. * **`fetchHandler`** (`FetchHandler`) — a `(request: Request) => Promise` callback for edge runtimes that accept standard Fetch API objects. Both handlers run the full Mantle hook pipeline and set `params.provider = 'http'`. ### Zero dependencies [Section titled “Zero dependencies”](#zero-dependencies) `@mantlejs/http` only depends on `@mantlejs/mantle` and Node.js built-ins. Route matching and JSON body parsing are implemented from scratch — no Express, Koa, or other HTTP framework is pulled in. ### Edge runtime compatibility [Section titled “Edge runtime compatibility”](#edge-runtime-compatibility) `fetchHandler` is compatible with any runtime that supports the [WinterCG](https://wintercg.org/) minimum common API — `Request`, `Response`, `URL`, and `crypto.randomUUID()`. Tested platforms: | Platform | Runtime | Import style | | ----------------------- | ---------- | ---------------- | | Cloudflare Workers | V8 isolate | npm (bundled) | | Vercel Edge Functions | V8 isolate | npm (bundled) | | Supabase Edge Functions | Deno | `npm:` specifier | | Deno Deploy | Deno | `npm:` specifier | | AWS Lambda\@Edge | Node.js | npm | | Bun | Bun | npm | Edge runtimes are **stateless and ephemeral** — each isolate handles one request (or a small batch). This means `app.listen()` is irrelevant in these environments; retrieve the `fetchHandler` directly instead. Anything requiring a persistent connection (database pool, socket.io, Redis) must be re-established per request or routed through a connection proxy. ### Correlation IDs [Section titled “Correlation IDs”](#correlation-ids) Every request gets an `x-correlation-id` header in the response. If the incoming request supplies one, it is echoed back. The correlation ID is available inside hooks via `getContext().correlationId`. *** ## Quick start [Section titled “Quick start”](#quick-start) ### Node.js server [Section titled “Node.js server”](#nodejs-server) ```typescript import { mantle } from "@mantlejs/mantle"; import { http } from "@mantlejs/http"; const app = mantle().configure(http()); app.use("users", new UserService(new UserRepository(app))); app.listen(3030, () => console.log("Listening on :3030")); ``` ### Cloudflare Workers [Section titled “Cloudflare Workers”](#cloudflare-workers) ```typescript import { mantle } from "@mantlejs/mantle"; import { http } from "@mantlejs/http"; import type { FetchHandler } from "@mantlejs/http"; const app = mantle().configure(http()); app.use("users", new UserService(new UserRepository(app))); const handler = app.get("fetchHandler") as FetchHandler; export default { fetch: handler }; ``` ### Vercel Edge Functions [Section titled “Vercel Edge Functions”](#vercel-edge-functions) app/api/users/route.ts ```typescript import { mantle } from "@mantlejs/mantle"; import { http } from "@mantlejs/http"; import type { FetchHandler } from "@mantlejs/http"; export const runtime = "edge"; const app = mantle().configure(http()); app.use("users", new UserService(new UserRepository(app))); const handler = app.get("fetchHandler") as FetchHandler; export const GET = handler; export const POST = handler; export const PUT = handler; export const PATCH = handler; export const DELETE = handler; ``` ### Supabase Edge Functions [Section titled “Supabase Edge Functions”](#supabase-edge-functions) Supabase Edge Functions run on Deno. Import the packages using the `npm:` specifier: supabase/functions/api/index.ts ```typescript import { mantle } from "npm:@mantlejs/mantle"; import { http } from "npm:@mantlejs/http"; import type { FetchHandler } from "npm:@mantlejs/http"; const app = mantle().configure(http()); app.use("users", new UserService(new UserRepository(app))); const handler = app.get("fetchHandler") as FetchHandler; Deno.serve(handler); ``` ### Custom Node.js server [Section titled “Custom Node.js server”](#custom-nodejs-server) ```typescript import { createServer } from "node:http"; import { mantle } from "@mantlejs/mantle"; import { http } from "@mantlejs/http"; import type { NodeHttpHandler } from "@mantlejs/http"; const app = mantle().configure(http()); app.use("users", new UserService(new UserRepository(app))); const handler = app.get("httpHandler") as NodeHttpHandler; createServer(handler).listen(3030); ``` *** ## API [Section titled “API”](#api) ### `http(options?)` [Section titled “http(options?)”](#httpoptions) Returns a `MantlePlugin` that registers the HTTP transport on the application. #### `HttpOptions` [Section titled “HttpOptions”](#httpoptions-1) | Field | Type | Default | Description | | --------------- | ------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `introspection` | `boolean \| { path?: string }` | `false` | Mount the introspection endpoint; pass `{ path }` to customize it. | | `batch` | `boolean \| { path?: string; maxSize?: number }` | `true` | Mount the `POST /batch` endpoint; `false` disables it, `{ path, maxSize }` configures it. | | `cors` | `boolean \| CorsOptions` | `false` | Enable CORS via hand-rolled header logic; `true` uses permissive defaults, an object customizes origin/methods/headers/credentials. | With `introspection` enabled, `GET /_services` (or the custom path) returns a `ServiceDescriptor[]` — one entry per registered service with `path`, `methods`, `events`, `schema`, the repository’s `capabilities` (when the service exposes them, e.g. `RepositoryService`), and `authRequired`. Off by default; without the option the route 404s. ```typescript app.configure(http({ introspection: true })); ``` After calling `app.configure(http())`: | `app.get(key)` | Type | Description | | ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `'httpHandler'` | `NodeHttpHandler` | Node.js `(req, res) => void` handler for `http.createServer` | | `'fetchHandler'` | `FetchHandler` | Fetch API `(request) => Promise` handler for edge runtimes | | `'http:router'` | `HttpRouterLike` | The transport-neutral contract other Mantle packages (`@mantlejs/auth-oauth`, `@mantlejs/mcp`, `@mantlejs/openapi`) use to mount their own raw routes regardless of which HTTP transport is configured | | `'http:server'` | `http.Server` | Set after `app.listen()` is called | ### Route conventions [Section titled “Route conventions”](#route-conventions) | HTTP method | Path | Mantle method | | ----------- | ------------------ | ------------- | | GET | `/service` | `find` | | GET | `/service/:id` | `get` | | POST | `/service` | `create` | | PUT | `/service/:id` | `update` | | PATCH | `/service/:id` | `patch` | | DELETE | `/service/:id` | `remove` | | POST | `/service/:method` | custom method | ### Batch endpoint [Section titled “Batch endpoint”](#batch-endpoint) `POST /batch` (mounted by default, on both handlers) dispatches an array of calls through `app.batch()` in one round trip: ```bash curl -X POST http://localhost:3030/batch -H "content-type: application/json" -d '[ { "service": "users", "method": "get", "id": 1 }, { "service": "messages", "method": "find", "params": { "query": { "$limit": 5 } } } ]' ``` Each call runs the target service’s **full hook pipeline** (including `authenticate("jwt")`) with the batch request’s headers — batch is not a way to bypass authentication or validation. The response is a `BatchResult[]` in the same order as the request array; calls execute concurrently and each entry independently reports `{ status: "success", result }` or `{ status: "error", error }` (no cross-call atomicity). Batches over `maxSize` (default 25) are rejected with `400 BadRequest` before any call executes. ```typescript app.configure(http({ batch: false })); // disable app.configure(http({ batch: { path: "/_batch", maxSize: 50 } })); ``` `@mantlejs/client` can coalesce same-tick service calls into this endpoint automatically — see its `batch` option. ### CORS [Section titled “CORS”](#cors) Disabled by default — consistent with Mantle’s secure-by-default posture elsewhere. Enable it with `cors: true` for permissive defaults, or pass a `CorsOptions` object to customize: ```typescript app.configure(http({ cors: true })); // reflects Origin, allows GET/POST/PUT/PATCH/DELETE, no credentials app.configure( http({ cors: { origin: ["https://app.example.com"], credentials: true }, }), ); ``` `CorsOptions` (exported from `@mantlejs/mantle`) is shared across `@mantlejs/express`, `@mantlejs/koa`, and `@mantlejs/http` so switching transports doesn’t require relearning CORS configuration: | Field | Type | Default | Description | | ---------------- | ------------------------------------------------------------------ | ------------------------------------------- | ---------------------------------------------------------- | | `origin` | `boolean \| string \| string[] \| ((origin) => boolean \| string)` | `true` (reflect the request’s `Origin`) | Allowed origin(s) for `Access-Control-Allow-Origin`. | | `methods` | `string[]` | `["GET", "POST", "PUT", "PATCH", "DELETE"]` | Allowed methods for `Access-Control-Allow-Methods`. | | `allowedHeaders` | `string[]` | reflects `Access-Control-Request-Headers` | Allowed request headers. | | `exposedHeaders` | `string[]` | none | Headers exposed to the browser. | | `credentials` | `boolean` | `false` | Allow credentials (cookies, `Authorization`) cross-origin. | | `maxAge` | `number` | none | Preflight cache duration in seconds. | `@mantlejs/http` has no framework to delegate to, so CORS is hand-rolled: headers are set on every response for an allowed origin, and an `OPTIONS` preflight short-circuits with a `204` (on both `httpHandler` and `fetchHandler`) before the request reaches the router. ### `toErrorResponse(err)` [Section titled “toErrorResponse(err)”](#toerrorresponseerr) Utility exported for use in other adapters or middleware. Maps a `MantleError` (or any error) to `{ status: number; body: Record }`. *** ## Types [Section titled “Types”](#types) ```typescript import type { NodeHttpHandler, FetchHandler, HttpOptions } from "@mantlejs/http"; // Node.js handler type NodeHttpHandler = (req: IncomingMessage, res: ServerResponse) => void; // Edge / Fetch API handler type FetchHandler = (request: Request) => Promise; // Options accepted by http() interface HttpOptions { introspection?: boolean | { path?: string }; batch?: boolean | { path?: string; maxSize?: number }; cors?: boolean | CorsOptions; } ``` *** ## Development [Section titled “Development”](#development) ```bash npx nx build http # compile npx nx test http # run tests npx nx lint http # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build http ``` First publish (scoped packages require `--access public`): ```bash cd packages/http npm publish --access public ``` Subsequent releases — bump `version` in `packages/http/package.json`, then: ```bash cd packages/http npm publish ``` # @mantlejs/knex > SQL database adapter for Mantle JS via Knex.js. Provides a knex() plugin that registers a shared Knex instance, and KnexRepository — an abstract base class that implements Repository for any SQL table. SQL database adapter for Mantle JS via [Knex.js](https://knexjs.org). Provides a `knex()` plugin that registers a shared Knex instance, and `KnexRepository` — an abstract base class that implements `Repository` for any SQL table. Supported databases: **PostgreSQL** (primary), **MySQL/MariaDB**, **SQLite**, **MSSQL**. ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/knex knex # Add the driver for your database: npm install pg # PostgreSQL npm install mysql2 # MySQL / MariaDB npm install better-sqlite3 # SQLite npm install tedious # MSSQL ``` ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { knex, KnexRepository } from "@mantlejs/knex"; const app = mantle().configure( knex({ client: "pg", connection: process.env.DATABASE_URL }), ); class UserRepository extends KnexRepository { readonly tableName = "users"; } app.use("users", new UserService(new UserRepository(app))); ``` ## API [Section titled “API”](#api) ### `knex(config)` [Section titled “knex(config)”](#knexconfig) Returns a `MantlePlugin`. When applied via `app.configure(knex(...))`, it creates a Knex instance, stores it under `app.get("knex")`, and registers a teardown callback that destroys the connection pool on `app.teardown()`. ```typescript interface KnexConfig { client: string; // 'pg' | 'mysql2' | 'sqlite3' | 'mssql' | ... connection: Knex.Config["connection"]; // connection string or object pool?: { min?: number; max?: number }; // defaults: min 2, max 10 searchPath?: string | string[]; // PostgreSQL schema search path } ``` Examples: ```typescript // PostgreSQL app.configure(knex({ client: "pg", connection: process.env.DATABASE_URL })); // SQLite app.configure(knex({ client: "sqlite3", connection: { filename: "./dev.db" } })); // MySQL app.configure(knex({ client: "mysql2", connection: { host: "localhost", user: "root", database: "myapp" } })); ``` *** ### `KnexRepository` [Section titled “KnexRepository\”](#knexrepositoryt-d) Abstract base class implementing `Repository`. Extend it and set `tableName`. ```typescript abstract class KnexRepository> implements Repository { abstract readonly tableName: string; readonly idField: string = "id"; // override to use a different PK column readonly timestamps: boolean = true; // auto-manages createdAt / updatedAt } ``` #### Methods [Section titled “Methods”](#methods) | Method | Description | | ---------------------- | ----------------------------------------------- | | `findAll(params?)` | Returns all rows matching `QueryParams` | | `findById(id)` | Returns one row or `null` | | `save(data)` | Inserts a row, returns the inserted record | | `saveAll(data[])` | Bulk-inserts rows, returns all inserted records | | `updateById(id, data)` | Replaces a row, throws `NotFound` if missing | | `patchById(id, data)` | Merges into a row, throws `NotFound` if missing | | `deleteById(id)` | Deletes a row, throws `NotFound` if missing | | `count(params?)` | Returns the count matching `QueryParams` | `save`, `saveAll`, `updateById`, and `patchById` use `RETURNING *` on PostgreSQL, SQLite, and MSSQL. On MySQL/MariaDB they fall back to a follow-up `SELECT`. #### Custom queries [Section titled “Custom queries”](#custom-queries) Use the `db` getter for ad-hoc queries against the table: ```typescript class UserRepository extends KnexRepository { readonly tableName = "users"; async findByEmail(email: string): Promise { return (await this.db.where({ email }).first()) ?? null; } } ``` #### Transactions [Section titled “Transactions”](#transactions) ```typescript const repo = new UserRepository(app); await repo.withTransaction(async (txRepo) => { const user = await txRepo.save({ name: "Alice", email: "alice@example.com" }); await txRepo.save({ userId: user.id, role: "admin" }); }); ``` `withTransaction` creates a transaction-scoped copy of the repository. All calls inside the callback share the same transaction and roll back automatically on error. *** ### `KnexVectorRepository` (pgvector) [Section titled “KnexVectorRepository\ (pgvector)”](#knexvectorrepositoryt-d-pgvector) Extends `KnexRepository` with `VectorRepository` support on PostgreSQL via [pgvector](https://github.com/pgvector/pgvector). Set `vectorColumn` (default `"embedding"`) and optionally `distanceOperator` (`"<=>"` cosine, `"<->"` L2, `"<#>"` inner product). Every `findSimilar` result carries the computed pgvector distance as `_score` — **lower is more similar** (it is a distance, not a similarity, unlike the Pinecone/Qdrant adapters where higher wins). The same value is mirrored to the deprecated `_distance` field for one release. #### Vector methods [Section titled “Vector methods”](#vector-methods) | Method | Description | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `findSimilar(vector, topK, params?)` | Top-K nearest records ordered by `distanceOperator`; see `_score` note above. | | `upsertVector(id, vector, data)` | Inserts or updates a record and its embedding via `ON CONFLICT (idField) MERGE` — a real upsert, unlike the base `KnexRepository.save()`, which is create-only and throws `Conflict` on a duplicate id. | | `deleteVector(id)` | Deletes the record and its embedding by id. Delegates to `deleteById`. | All three methods throw `GeneralError` if the repository isn’t connected to a PostgreSQL (`pg`) client. *** ### `QueryParams` — supported `where` operators [Section titled “QueryParams — supported where operators”](#queryparams--supported-where-operators) ```typescript // Equality { email: "alice@example.com" } // Null check { deletedAt: null } // IS NULL { deletedAt: { $ne: null } } // IS NOT NULL // Comparison { age: { $gt: 18 } } // $lt | $lte | $gt | $gte // Inequality { status: { $ne: "banned" } } // Inclusion { role: { $in: ["admin", "editor"] } } { role: { $nin: ["guest"] } } // Pattern matching (all clients — Knex compiles LIKE/ILIKE per dialect) { name: { $like: "Alice%" } } { name: { $ilike: "alice%" } } // case-insensitive { name: { $notlike: "Bob%" } } // JSON containment (PostgreSQL: jsonb @>; MySQL: JSON_CONTAINS — both via whereJsonSupersetOf) { tags: { $contains: "blue" } } // contains element { tags: { $contains: ["red", "blue"] } } // contains every element { metadata: { $contains: { plan: "pro" } } } // object superset // On SQLite/MSSQL — no native JSON-superset function — $contains throws BadRequest naming the // operator and the connected client. // Nested (dot-path) fields — PostgreSQL, MySQL, SQLite, and MSSQL, via Knex's own // cross-dialect whereJsonPath (jsonb_path_query_first / JSON_EXTRACT / json_extract / JSON_VALUE) { "metadata.owner.name": "alice" } // equality { "metadata.level": { $gt: 4 } } // $lt | $lte | $gt | $gte { "metadata.owner.name": { $ne: "alice" } } // $ne { "metadata.owner.name": { $like: "al%" } } // $like | $notlike (all four clients) { "metadata.owner.name": { $ilike: "AL%" } } // PostgreSQL only — not standard SQL { "metadata.tags": { $contains: "a" } } // PostgreSQL + MySQL only, same restriction as above // $in/$nin and null-checks on a dot-path field have no clean SQL translation and throw // BadRequest — restructure the schema, or filter in application code after fetching. // Logical { $or: [{ role: "admin" }, { role: "editor" }] } { $and: [{ active: true }, { age: { $gte: 18 } }] } ``` `describe().capabilities` reports exactly which of the above the *connected* client supports — check it rather than assuming, since `$contains` and `$ilike`-on-a-dot-path in particular vary by client (see `CLAUDE.md`’s operator table for the full per-client breakdown). *** ### Error mapping [Section titled “Error mapping”](#error-mapping) Database-level errors are translated to typed `MantleError` subclasses: | PostgreSQL code prefix | Error thrown | | ---------------------- | --------------- | | `08`, `57` | `Unavailable` | | `22` | `BadRequest` | | `23505` (unique) | `Conflict` | | other `23` | `BadRequest` | | `28` | `Forbidden` | | `3D`, `3F`, `42` | `Unprocessable` | | other | `GeneralError` | ## Development [Section titled “Development”](#development) ```bash npx nx build knex # compile npx nx test knex # run tests npx nx lint knex # lint ``` ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build knex ``` First publish (scoped packages require `--access public`): ```bash cd packages/knex npm publish --access public ``` Subsequent releases — bump `version` in `packages/knex/package.json`, then: ```bash cd packages/knex npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/knex npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/knex --registry http://localhost:4873 ``` # @mantlejs/koa > Koa HTTP transport adapter for Mantle JS. Routes incoming Koa requests to registered Mantle services and sends back typed JSON responses. Koa HTTP transport adapter for [Mantle JS](https://github.com/mantlejs/mantle). Routes incoming Koa requests to registered Mantle services and sends back typed JSON responses. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/koa koa @koa/router @koa/bodyparser ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Transport adapter [Section titled “Transport adapter”](#transport-adapter) `koa()` is a Mantle plugin. It wraps a Koa application, mounts a REST router for every service registered with `app.use()`, and serialises the Mantle hook pipeline result as a JSON response. All HTTP concerns stay inside this package — services and hooks remain transport-agnostic. ### Route mapping [Section titled “Route mapping”](#route-mapping) Each Mantle service method maps to an HTTP verb and path: | Method | HTTP verb | Path | | -------- | --------- | ------------------ | | `find` | `GET` | `/path` | | `get` | `GET` | `/path/:id` | | `create` | `POST` | `/path` | | `update` | `PUT` | `/path/:id` | | `patch` | `PATCH` | `/path/:id` | | `remove` | `DELETE` | `/path/:id` | | custom | `POST` | `/path/methodName` | Only methods explicitly listed in the `app.use()` options are mounted. ### Provider [Section titled “Provider”](#provider) When a request arrives from Koa, `params.provider` is set to `"koa"`. Hooks can inspect this to distinguish HTTP calls from internal service calls (where `provider` is `undefined`). ### Correlation ID [Section titled “Correlation ID”](#correlation-id) Every response includes an `x-correlation-id` header. If the request supplies the header, it is echoed back. Otherwise a random UUID is generated. The ID is available inside hooks via `getContext().correlationId`. ### Error handling [Section titled “Error handling”](#error-handling) `MantleError` subclasses (`NotFound`, `BadRequest`, etc.) are mapped to their correct HTTP status codes. Unknown errors produce a `500 General Error` response. ### `@mantlejs/socketio` compatibility [Section titled “@mantlejs/socketio compatibility”](#mantlejssocketio-compatibility) After `app.listen()` the underlying `http.Server` is stored at `app.get("http:server")`. Socket.IO adapters can attach to this server without needing access to the Koa instance. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { koa } from "@mantlejs/koa"; const app = mantle().configure(koa()); app.use("/messages", new MessageService(new MessageRepository(app)), { methods: ["find", "get", "create", "patch", "remove"], }); app.listen(3030); ``` ```http GET /messages ``` ```json [{ "id": 1, "text": "Hello, world!" }] ``` ### Using an existing Koa instance [Section titled “Using an existing Koa instance”](#using-an-existing-koa-instance) ```typescript import Koa from "koa"; import { mantle } from "@mantlejs/mantle"; import { koa } from "@mantlejs/koa"; const koaApp = new Koa(); koaApp.use(myCustomMiddleware()); const app = mantle().configure(koa({ app: koaApp })); app.use("users", new UserService()); app.listen(3030); ``` *** ## API [Section titled “API”](#api) ### `koa(options?)` [Section titled “koa(options?)”](#koaoptions) Returns a `MantlePlugin`. Call via `app.configure(koa(options))`. ```typescript import Koa from "koa"; // Use a fresh Koa instance (default) app.configure(koa()); // Bring your own Koa instance app.configure(koa({ app: new Koa() })); ``` Side effects: * Stores a `@koa/router`-backed adapter under `app.get("http:router")` — the transport-neutral contract other Mantle packages (`@mantlejs/auth-oauth`, `@mantlejs/mcp`, `@mantlejs/openapi`) use to mount their own raw routes regardless of which HTTP transport is configured. * Stores the Koa application at `app.get("koa")` * Stores the `@koa/router` Router at `app.get("koa:router")` * Stores the `http.Server` at `app.get("http:server")` after `app.listen()` #### `KoaOptions` [Section titled “KoaOptions”](#koaoptions-1) | Field | Type | Default | Description | | --------------- | ------------------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------- | | `app` | `Koa` (Koa instance) | — | Existing Koa application. A new one is created if omitted. | | `introspection` | `boolean \| { path?: string }` | `false` | Mount the introspection endpoint; pass `{ path }` to customize it. | | `batch` | `boolean \| { path?: string; maxSize?: number }` | `true` | Mount the `POST /batch` endpoint; `false` disables it, `{ path, maxSize }` configures it. | | `cors` | `boolean \| CorsOptions` | `false` | Enable CORS via `@koa/cors`; `true` uses permissive defaults, an object customizes origin/methods/headers/credentials. | *** ### Introspection endpoint [Section titled “Introspection endpoint”](#introspection-endpoint) Opt in to a machine-readable service catalog: ```typescript app.configure(koa({ introspection: true })); // or with a custom path: app.configure(koa({ introspection: { path: "/__meta" } })); ``` `GET /_services` (or the custom path) then returns a `ServiceDescriptor[]` — one entry per registered service with `path`, `methods`, `events`, `schema`, the repository’s `capabilities` (when the service exposes them, e.g. `RepositoryService`), and `authRequired`. Off by default; without the option the route 404s. *** ### Batch endpoint [Section titled “Batch endpoint”](#batch-endpoint) `POST /batch` (mounted by default) dispatches an array of calls through `app.batch()` in one round trip: ```bash curl -X POST http://localhost:3030/batch -H "content-type: application/json" -d '[ { "service": "users", "method": "get", "id": 1 }, { "service": "messages", "method": "find", "params": { "query": { "$limit": 5 } } } ]' ``` Each call runs the target service’s **full hook pipeline** (including `authenticate("jwt")`) with the batch request’s headers — batch is not a way to bypass authentication or validation. The response is a `BatchResult[]` in the same order as the request array; calls execute concurrently and each entry independently reports `{ status: "success", result }` or `{ status: "error", error }` (no cross-call atomicity). Batches over `maxSize` (default 25) are rejected with `400 BadRequest` before any call executes. ```typescript app.configure(koa({ batch: false })); // disable app.configure(koa({ batch: { path: "/_batch", maxSize: 50 } })); ``` `@mantlejs/client` can coalesce same-tick service calls into this endpoint automatically — see its `batch` option. *** ### CORS [Section titled “CORS”](#cors) Disabled by default — consistent with Mantle’s secure-by-default posture elsewhere. Enable it with `cors: true` for permissive defaults, or pass a `CorsOptions` object to customize: ```typescript app.configure(koa({ cors: true })); // reflects Origin, allows GET/POST/PUT/PATCH/DELETE, no credentials app.configure( koa({ cors: { origin: ["https://app.example.com"], credentials: true }, }), ); ``` `CorsOptions` (exported from `@mantlejs/mantle`) is shared across `@mantlejs/express`, `@mantlejs/koa`, and `@mantlejs/http` so switching transports doesn’t require relearning CORS configuration: | Field | Type | Default | Description | | ---------------- | ------------------------------------------------------------------ | ------------------------------------------- | ---------------------------------------------------------- | | `origin` | `boolean \| string \| string[] \| ((origin) => boolean \| string)` | `true` (reflect the request’s `Origin`) | Allowed origin(s) for `Access-Control-Allow-Origin`. | | `methods` | `string[]` | `["GET", "POST", "PUT", "PATCH", "DELETE"]` | Allowed methods for `Access-Control-Allow-Methods`. | | `allowedHeaders` | `string[]` | reflects `Access-Control-Request-Headers` | Allowed request headers. | | `exposedHeaders` | `string[]` | none | Headers exposed to the browser. | | `credentials` | `boolean` | `false` | Allow credentials (cookies, `Authorization`) cross-origin. | | `maxAge` | `number` | none | Preflight cache duration in seconds. | `@mantlejs/koa` wraps `@koa/cors` internally — it’s installed as a dependency of `@mantlejs/koa`, not something you need to add yourself. *** ### `errorHandler` [Section titled “errorHandler”](#errorhandler) Koa middleware that catches `MantleError` subclasses and maps them to HTTP status codes. Installed automatically by `koa()` — you only need this export if you are assembling the middleware stack manually. ```typescript import { errorHandler } from "@mantlejs/koa"; koaApp.use(errorHandler); ``` *** ## Types [Section titled “Types”](#types) ```typescript import type { KoaOptions } from "@mantlejs/koa"; ``` | Type | Description | | ------------ | ---------------------------------- | | `KoaOptions` | Options object accepted by `koa()` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build koa # compile npx nx test koa # run tests npx nx lint koa # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build koa ``` First publish (scoped packages require `--access public`): ```bash cd packages/koa npm publish --access public ``` Subsequent releases — bump `version` in `packages/koa/package.json`, then: ```bash cd packages/koa npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/koa npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/koa --registry http://localhost:4873 ``` # @mantlejs/logger > Structured logging plugin for Mantle JS. Provides a pino adapter and two hook factories — logRequest and logError — that emit structured JSON records through any logger satisfying the Mantle Logger interface. Structured logging plugin for [Mantle JS](https://github.com/mantlejs/mantle). Provides a pino adapter and two hook factories — `logRequest` and `logError` — that emit structured JSON records through any logger satisfying the Mantle `Logger` interface. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/logger pino ``` `pino` is an optional peer dependency, loaded lazily by `createLogger()`. If you prefer winston, LogLayer, or another logger, skip it — see [Using a different logger](#using-a-different-logger). To pretty-print in development, also install `pino-pretty` (also optional — see [`createLogger(options?)`](#createloggeroptions)). *** ## Concepts [Section titled “Concepts”](#concepts) ### The Logger interface [Section titled “The Logger interface”](#the-logger-interface) `@mantlejs/mantle` defines a minimal `Logger` interface with four methods: `debug`, `info`, `warn`, and `error`. Every method accepts a message string and an optional context object: ```typescript interface Logger { debug(msg: string, context?: Record): void; info(msg: string, context?: Record): void; warn(msg: string, context?: Record): void; error(msg: string, context?: Record): void; } ``` Any object with these four methods can be registered on the app. Mantle packages emit logs via `app.get('logger')?.debug(...)` — if no logger is configured, all internal logging is silently skipped. ### LOG_LEVEL [Section titled “LOG_LEVEL”](#log_level) Verbosity is controlled by the `LOG_LEVEL` environment variable, not a `DEBUG=*` namespace. Set it before starting your process: ```bash LOG_LEVEL=debug npm start # debug + info + warn + error LOG_LEVEL=info npm start # info + warn + error (recommended for production) LOG_LEVEL=warn npm start # warn + error only ``` ### Component field [Section titled “Component field”](#component-field) Records emitted by Mantle packages include a `component` field — currently `mantle:core` (service registration/teardown), `mantle:request` and `mantle:error` (this package’s `logRequest`/`logError` hooks), and `mantle:sync` (`@mantlejs/sync`). Use this field in your log aggregation tool (CloudWatch, Datadog, Grafana Loki) to filter framework-internal logs without restarting the process. ### Correlation ID [Section titled “Correlation ID”](#correlation-id) When you use `pinoAdapter` with the `express()` transport, every log record emitted during a request automatically includes a `correlationId` field. No manual threading required. The `express()` plugin reads the `X-Correlation-ID` request header (or generates a UUID) and stores it in `@mantlejs/mantle`’s `AsyncLocalStorage` context via `withContext`. `pinoAdapter` calls `getContext()` on every log call and merges the result into the pino object. ```json { "correlationId": "a3f2c1d4-8b2e-4f1a-9c3d-1e2f3a4b5c6d", "component": "mantle:request", "method": "create", "path": "users", "provider": "rest", "durationMs": 12, "status": "ok" } ``` The `X-Correlation-ID` is echoed back in the response header so callers can correlate their requests with server-side log entries. Hooks and other code can read the correlation ID directly: ```typescript import { getContext } from "@mantlejs/mantle"; const hook = (ctx) => { const { correlationId } = getContext() ?? {}; // ... return ctx; }; ``` ### Output destination [Section titled “Output destination”](#output-destination) Destination is configured on the pino instance, not by Mantle. Common patterns: ```typescript // stdout only — cloud-native default, recommended for EKS / Cloud Run pino({ level: 'info' }) // file only pino({}, pino.destination('/var/log/app.log')) // stdout + file simultaneously pino({ level: 'info' }, pino.multistream([ { stream: process.stdout }, { stream: pino.destination('/var/log/app.log') }, ])) ``` > **Log rotation is not the application’s concern.** In containers, the kubelet and log agent (fluentd, fluent-bit) collect from stdout. If writing to files, use `logrotate` or a sidecar shipper. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { logger, createLogger, logRequest, logError } from "@mantlejs/logger"; const app = mantle() .configure(express()) .configure(logger(await createLogger({ gcp: process.env.NODE_ENV === "production" }))); const requestLogger = logRequest(); app.use("/users", new UserService(new UserRepository(app))); app.service("users").hooks({ before: { all: [requestLogger] }, after: { all: [requestLogger] }, error: { all: [requestLogger, logError()] }, }); app.listen(3030); ``` *** ## API [Section titled “API”](#api) ### `logger(adapter)` [Section titled “logger(adapter)”](#loggeradapter) Plugin factory. Registers a `Logger` adapter on the application. ```typescript function logger(adapter: Logger): MantlePlugin; ``` ```typescript app.configure(logger(pinoAdapter(pino({ level: "info" })))); // Equivalent to: app.set("logger", adapter) ``` *** ### `pinoAdapter(pinoInstance)` [Section titled “pinoAdapter(pinoInstance)”](#pinoadapterpinoinstance) Wraps a pino logger to satisfy the `Logger` interface. Pino uses `(object, message)` argument order; the `Logger` interface uses `(message, object)`. The adapter flips this internally. On every log call the adapter reads `getContext()` from `@mantlejs/mantle` and merges the current `RequestContext` (including `correlationId`) into the pino object. Per-call context fields take precedence over request context fields. When called outside an Express request (e.g. a startup log), no context is present and the field is simply omitted. ```typescript function pinoAdapter(pino: PinoLike): Logger; ``` ```typescript import pino from "pino"; import { pinoAdapter } from "@mantlejs/logger"; const adapter = pinoAdapter(pino({ level: process.env.LOG_LEVEL ?? "info" })); ``` `PinoLike` is a minimal duck-type — any object with `debug`, `info`, `warn`, and `error` methods accepting `(object, message)` will work. If the wrapped instance has a `child(bindings)` method (real pino instances do), the returned `Logger` gets one too — see [`Logger.child(bindings)`](#loggerchildbindings) below. *** ### `createLogger(options?)` [Section titled “createLogger(options?)”](#createloggeroptions) Builds a `Logger` backed by pino, with production-ready defaults baked in: environment-aware level, PII redaction, and Google Cloud severity mapping. `pino` is loaded lazily — nothing is required at module load time, only when `createLogger()` actually runs. ```typescript function createLogger(options?: CreateLoggerOptions): Promise; interface CreateLoggerOptions { /** Default: "info" when NODE_ENV === "production", else "debug". */ level?: "debug" | "info" | "warn" | "error"; /** Pino redact paths. Default: SENSITIVE_PATHS. Pass [] to disable. */ redact?: string[]; /** Pretty-print via pino-pretty when available. Default: false. Ignored in production. */ pretty?: boolean; /** Google Cloud structured logging: level -> `severity` labels, message key "message". Default: false. */ gcp?: boolean; /** Extra pino options merged last (escape hatch). */ pino?: Record; } ``` ```typescript import { logger, createLogger } from "@mantlejs/logger"; app.configure( logger( await createLogger({ pretty: process.env.NODE_ENV !== "production", gcp: process.env.NODE_ENV === "production", }), ), ); ``` * **`redact`** — defaults to the exported `SENSITIVE_PATHS` (`["password", "*.password", "*.accessToken", "*.refreshToken", "*.authorization", "*.cookie"]`), passed straight to pino’s `redact.paths` with `censor: "[Redacted]"`. Pass `[]` to disable pino-level redaction entirely (e.g. if you configure your own). * **`pretty`** — when `true` and not in production, `createLogger` checks whether `pino-pretty` is resolvable before setting `transport: { target: "pino-pretty" }`. If it isn’t installed, logging still works — output falls back to plain JSON and a one-time `console.warn` names the missing package. `pretty` has no effect in production. * **`gcp`** — sets `messageKey: "message"` and a `formatters.level` mapping (`debug`→`DEBUG`, `info`→`INFO`, `warn`→`WARNING`, `error`→`ERROR`) so [Cloud Logging](https://cloud.google.com/logging/docs/structured-logging) picks up the right severity and renders the message field. See [Deployment](#deployment) below. * **`pino`** — an escape hatch merged into the built options object last, so it can override any computed default (including `level` or `redact`). * If `pino` cannot be resolved (not installed), `createLogger()` throws a `GeneralError` naming the missing package, with `hint: "Install it with: npm install pino"`. *** ### `logRequest(options?)` [Section titled “logRequest(options?)”](#logrequestoptions) Returns a hook that logs service call duration. Register the **same returned function** in `before.all`, `after.all`, **and** `error.all`. The before phase records the start time; the after or error phase emits the record with elapsed duration and `status`. ```typescript function logRequest(options?: LogRequestOptions): HookFunction; interface LogRequestOptions { /** Log level for calls. Default: 'debug' */ level?: "debug" | "info"; /** * Include params in the log record. Default: false. * * WARNING: params can contain sensitive data — query strings, auth tokens, * pagination secrets, or any value a hook has attached. Only enable this * in development, or pair it with pino's `redact` option to strip known * sensitive paths before they reach the transport. */ includeParams?: boolean; /** Paths redacted from `params` when includeParams is true. Default: SENSITIVE_PATHS. */ redactParams?: string[]; } ``` `redactParams` runs a small own-code deep walk over `params` — replacing matched fields with `"[Redacted]"` — **before** the record reaches the logger. This works for every adapter, not just `pinoAdapter`; pino’s own `redact` option (configured via `createLogger`) remains defense in depth on top of it. ```typescript const requestLogger = logRequest(); app.service("users").hooks({ before: { all: [requestLogger] }, after: { all: [requestLogger] }, error: { all: [requestLogger, logError()] }, }); ``` **Success record** (message: `"Service call completed"`): ```json { "component": "mantle:request", "method": "get", "path": "users", "provider": "rest", "id": "42", "durationMs": 12, "status": "ok" } ``` `id` is only present for `get`, `update`, `patch`, and `remove` calls. `correlationId` is automatically included by `pinoAdapter` when inside an Express request. **Error record** (message: `"Service call failed"`): ```json { "component": "mantle:request", "method": "create", "path": "users", "provider": "rest", "durationMs": 1, "status": "error" } ``` *** ### `logError(options?)` [Section titled “logError(options?)”](#logerroroptions) Error hook. Logs structured error details from `ctx.error`. Maps `4xx` errors to `warn`, `5xx` to `error` by default. ```typescript function logError(options?: LogErrorOptions): HookFunction; interface LogErrorOptions { /** Log 4xx as 'warn', 5xx as 'error'. Default: true */ levelByCode?: boolean; /** Include stack trace. Default: true when NODE_ENV !== 'production' */ includeStack?: boolean; } ``` ```typescript app.service("users").hooks({ error: { all: [logError()] }, }); ``` **Emitted record** (message: `"Service error"`): ```json { "component": "mantle:error", "method": "create", "path": "users", "provider": "rest", "code": 409, "name": "Conflict", "message": "Email already exists", "data": { "field": "email" }, "stack": "..." } ``` `data` (the `MantleError`’s `data` payload) is only present when the error carries one, and is redacted with the same `SENSITIVE_PATHS` walk used by `logRequest`’s `redactParams`. *** ### `Logger.child(bindings)` [Section titled “Logger.child(bindings)”](#loggerchildbindings) `Logger` (from `@mantlejs/mantle`) declares `child` as **optional** — implementations may omit it, and callers must feature-check before use: ```typescript interface Logger { debug(msg: string, context?: Record): void; info(msg: string, context?: Record): void; warn(msg: string, context?: Record): void; error(msg: string, context?: Record): void; child?(bindings: Record): Logger; } ``` Both adapters this package ships implement it: * **`pinoAdapter`** — delegates to the wrapped instance’s own `pino.child(bindings)`, re-wrapped through `pinoAdapter` so `RequestContext` merging (`correlationId`, etc.) keeps working on the child logger too. If the wrapped object has no `child` method, the returned `Logger` won’t have one either. * **`loglayerAdapter`** — has no native LogLayer `child()` involved; bindings are closed over and folded into every subsequent `withMetadata()` call instead. ```typescript const requestLog = log.child?.({ requestId: "abc-123" }) ?? log; requestLog.info("handling request"); // every record includes requestId: "abc-123" ``` Mantle’s own hooks never call `child` unconditionally — it’s an opt-in for application code that wants scoped loggers (e.g. one per background job, per queue consumer). *** ### `loglayerAdapter(logLayerInstance)` [Section titled “loglayerAdapter(logLayerInstance)”](#loglayeradapterloglayerinstance) Wraps a [LogLayer](https://loglayer.dev) instance to satisfy the `Logger` interface. `loglayer` itself is **not** a dependency of any Mantle package — `LogLayerLike` is a duck-type, so any object shaped like a LogLayer instance works. ```typescript function loglayerAdapter(log: LogLayerLike): Logger; interface LogLayerLike { withMetadata(meta: Record): { debug(msg: string): void; info(msg: string): void; warn(msg: string): void; error(msg: string): void; }; child?(): unknown; } ``` ```typescript import { LogLayer, ConsoleTransport } from "loglayer"; import { logger, loglayerAdapter } from "@mantlejs/logger"; const log = new LogLayer({ transport: new ConsoleTransport({ logger: console }) }); app.configure(logger(loglayerAdapter(log))); ``` Like `pinoAdapter`, every call merges the current `RequestContext` into the metadata passed to `withMetadata()`. `child(bindings)` closes over the merged bindings rather than calling LogLayer’s own `child()` (whose semantics differ) — bindings are folded into every subsequent `withMetadata()` call, and nested `child()` calls compose. See [Using LogLayer](#using-loglayer) below for a transport fan-out example. *** ## PII and sensitive data [Section titled “PII and sensitive data”](#pii-and-sensitive-data) Mantle’s default log records are intentionally narrow — only service metadata (`method`, `path`, `provider`, `id`, `durationMs`, `status`) and error details reach the log output. The fields most likely to carry PII are **never logged by default**: | Field | Logged? | Notes | | ---------------------------- | ----------- | -------------------------------------------------------------- | | `ctx.data` (request body) | No | Passwords, email addresses, payment info | | `ctx.result` (response body) | No | Full entity records | | `ctx.params` | No (opt-in) | Query strings, auth tokens — see `includeParams` warning above | | `correlationId` | Yes | A UUID — not PII | | `ctx.id` | Yes | A resource identifier — usually not PII | ### Redacting fields with `createLogger` [Section titled “Redacting fields with createLogger”](#redacting-fields-with-createlogger) `createLogger({ redact })` is the recommended approach when using pino — it configures pino’s built-in [`redact`](https://getpino.io/#/docs/redaction) option (`fast-redact` with JSONPath patterns, applied before serialisation) and defaults to the exported `SENSITIVE_PATHS`: ```typescript import { logger, createLogger } from "@mantlejs/logger"; app.configure( logger( await createLogger({ redact: ["user.email", "user.password", "params.query.token", "*.creditCard"], }), ), ); ``` This applies globally to every record without any changes to Mantle hooks or the `Logger` interface. If you build a pino instance yourself instead of using `createLogger`, pass the same `redact` option directly to `pino({ ... })` before wrapping it with `pinoAdapter`. ### Redacting with `logRequest`/`logError` (works with any adapter) [Section titled “Redacting with logRequest/logError (works with any adapter)”](#redacting-with-logrequestlogerror-works-with-any-adapter) `logRequest({ redactParams })` and `logError()` run their own small deep-walk redaction (see [`redactPaths`](#redactpathsvalue-paths) below) over `params` and `error.data` respectively, before the record reaches *any* logger — pino, LogLayer, winston, or a custom one. This is defense in depth on top of pino-level redaction, and the only redaction available to non-pino adapters. ### Redacting with winston or a custom logger [Section titled “Redacting with winston or a custom logger”](#redacting-with-winston-or-a-custom-logger) Configure redaction at the transport or formatter level in your logger of choice. Mantle does not add a redaction layer on top of the underlying logger — except via `logRequest`/`logError` as described above. *** ## Using a different logger [Section titled “Using a different logger”](#using-a-different-logger) ### LogLayer [Section titled “LogLayer”](#loglayer) Use `loglayerAdapter` — see [`loglayerAdapter(logLayerInstance)`](#loglayeradapterloglayerinstance) above. ### Winston [Section titled “Winston”](#winston) Winston’s `debug`, `info`, `warn`, and `error` methods already match the `Logger` interface signature — no adapter needed. Skip installing `@mantlejs/logger` entirely if you only want winston: ```typescript import winston from "winston"; const winstonLogger = winston.createLogger({ level: process.env.LOG_LEVEL ?? "info", transports: [new winston.transports.Console()], }); app.set("logger", winstonLogger); ``` You can still use the `logRequest()` and `logError()` hook factories from `@mantlejs/logger` alongside a winston logger — they read the logger from `app.get('logger')` regardless of implementation. ### Custom / console logger [Section titled “Custom / console logger”](#custom--console-logger) Any object satisfying the four-method `Logger` interface works: ```typescript app.set("logger", { debug: (msg, ctx) => console.debug(msg, ctx), info: (msg, ctx) => console.info(msg, ctx), warn: (msg, ctx) => console.warn(msg, ctx), error: (msg, ctx) => console.error(msg, ctx), }); ``` *** ### `redactPaths(value, paths?)` [Section titled “redactPaths(value, paths?)”](#redactpathsvalue-paths) The own-code deep-walk redaction helper used internally by `logRequest` and `logError`. Exported so application code can reuse it. ```typescript function redactPaths(value: unknown, paths?: string[]): unknown; // default: SENSITIVE_PATHS ``` Path syntax is a small subset of pino’s `redact.paths`: a bare key (`"password"`) matches only at the top level; a `"*.key"` entry matches `key` at any depth. Returns a deep clone — the input is never mutated. ```typescript import { redactPaths, SENSITIVE_PATHS } from "@mantlejs/logger"; redactPaths({ user: { password: "hunter2" } }); // => { user: { password: "[Redacted]" } } SENSITIVE_PATHS; // ["password", "*.password", "*.accessToken", "*.refreshToken", "*.authorization", "*.cookie"] ``` *** ## Types [Section titled “Types”](#types) ```typescript import type { PinoLike, LogLayerLike, CreateLoggerOptions, LogRequestOptions, LogErrorOptions, } from "@mantlejs/logger"; import type { Logger } from "@mantlejs/mantle"; ``` | Type | Description | | --------------------- | -------------------------------------------------------------------------- | | `Logger` | Core interface — re-exported from `@mantlejs/mantle`. `child` is optional. | | `PinoLike` | Duck-type accepted by `pinoAdapter()` | | `LogLayerLike` | Duck-type accepted by `loglayerAdapter()` | | `CreateLoggerOptions` | Options for `createLogger()` | | `LogRequestOptions` | Options for `logRequest()` | | `LogErrorOptions` | Options for `logError()` | *** ## Deployment [Section titled “Deployment”](#deployment) ### Cloud Run [Section titled “Cloud Run”](#cloud-run) Pass `gcp: true` to `createLogger()` when running on Cloud Run (or any GKE/Cloud Logging environment). It maps Mantle’s four levels to Cloud Logging `severity` labels and renames the message field, so structured fields survive intact in the Logs Explorer instead of being flattened into `textPayload`: ```typescript const log = await createLogger({ gcp: process.env.NODE_ENV === "production" }); ``` Write to **stdout only** — Cloud Run collects container stdout/stderr automatically. Don’t configure file transports; there’s no persistent disk, and a second write target only adds latency. This is also why `pretty` is ignored in production: pretty-printed multi-line output does not parse as structured JSON by the log agent. ### Level configuration via `@mantlejs/config` [Section titled “Level configuration via @mantlejs/config”](#level-configuration-via-mantlejsconfig) Read the log level from `@mantlejs/config` instead of hardcoding it, so it can be overridden per environment without a redeploy. `config()` and `logger()` both call `app.set("logger", ...)` — `config()` sets it to the raw config object, `logger()` overwrites it with the actual `Logger` instance — so **configure `config()` before `logger()`**: ```typescript import { config } from "@mantlejs/config"; import { logger, createLogger } from "@mantlejs/logger"; import type { CreateLoggerOptions } from "@mantlejs/logger"; app.configure(config()); // reads config/default.json + config/{NODE_ENV}.json + MANTLE_* env vars const { level } = app.get<{ logger?: { level?: string } }>("config").logger ?? {}; app.configure(logger(await createLogger({ level: level as CreateLoggerOptions["level"] }))); ``` config/default.json ```jsonc { "logger": { "level": "info" } } ``` ```plaintext MANTLE_LOGGER__LEVEL=debug npm start # env override, no redeploy ``` ### Process-level error handlers [Section titled “Process-level error handlers”](#process-level-error-handlers) Pino/LogLayer only capture errors that flow through a service call. Wire the two Node.js process-level events too, so nothing crashes silently: ```typescript const log = await createLogger(); process.on("uncaughtException", (err) => { log.error("Uncaught exception", { component: "mantle:process", name: err.name, message: err.message, stack: err.stack }); process.exit(1); }); process.on("unhandledRejection", (reason) => { const err = reason instanceof Error ? reason : new Error(String(reason)); log.error("Unhandled rejection", { component: "mantle:process", name: err.name, message: err.message, stack: err.stack }); }); ``` ### Log-based metrics [Section titled “Log-based metrics”](#log-based-metrics) Every record `logRequest`/`logError` emit carries a `component` field (`mantle:request` or `mantle:error`). Build log-based metrics/alerts (Cloud Monitoring, Datadog, Grafana Loki) filtered on `component`, without needing a separate metrics pipeline: * `component="mantle:request" AND status="error"` — error rate by `path`/`method` * `component="mantle:request"` — p50/p95 `durationMs` by `path` * `component="mantle:error" AND code>=500` — 5xx volume, paged separately from 4xx noise ### Using LogLayer [Section titled “Using LogLayer”](#using-loglayer) `loglayerAdapter` lets LogLayer’s own transport fan-out do double duty — one Mantle `Logger` writing to multiple destinations at once: ```typescript import { LogLayer, MultiTransport, ConsoleTransport, DatadogTransport } from "loglayer"; import { logger, loglayerAdapter } from "@mantlejs/logger"; const log = new LogLayer({ transport: new MultiTransport([ new ConsoleTransport({ logger: console }), new DatadogTransport({ apiKey: process.env.DATADOG_API_KEY! }), ]), }); app.configure(logger(loglayerAdapter(log))); ``` `logRequest` and `logError` work unchanged — they only depend on the `Logger` interface, never on LogLayer directly. *** ## Development [Section titled “Development”](#development) ```bash npx nx build logger # compile npx nx test logger # run tests npx nx lint logger # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build logger ``` First publish (scoped packages require `--access public`): ```bash cd packages/logger npm publish --access public ``` Subsequent releases — bump `version` in `packages/logger/package.json`, then: ```bash cd packages/logger npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/logger npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/logger --registry http://localhost:4873 ``` # @mantlejs/mantle > The framework kernel for Mantle JS. Zero external dependencies. Defines all core interfaces, the application factory, and typed error classes used across every other package. The framework kernel for Mantle JS. Zero external dependencies. Defines all core interfaces, the application factory, and typed error classes used across every other package. ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/mantle ``` ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; const app = mantle(); app.use("users", new UserService()); app.service("users").hooks({ before: { create: [validateUser()] }, after: { all: [sanitize()] }, }); ``` ## API [Section titled “API”](#api) ### `mantle(options?)` [Section titled “mantle(options?)”](#mantleoptions) Creates a `MantleApplication` instance. ```typescript const app = mantle(); ``` #### `app.use(path, service, options?)` [Section titled “app.use(path, service, options?)”](#appusepath-service-options) Registers a service at a path. `options.methods` restricts which of the six standard methods are exposed (defaults to all six). ```typescript app.use("users", new UserService(), { methods: ["find", "get", "create", "patch", "remove"], }); ``` #### `app.service(path)` [Section titled “app.service(path)”](#appservicepath) Returns the `ServiceHandle` for a registered path. Throws `NotFound` if the path is not registered. ```typescript const result = await app.service("users").find({ query: { role: "admin" } }); ``` #### `app.configure(plugin)` [Section titled “app.configure(plugin)”](#appconfigureplugin) Applies a plugin (e.g. `express()`, `knex()`) to the application. ```typescript app.configure(express()).configure(knex({ client: "pg", connection: DATABASE_URL })); ``` #### `app.batch(calls, params?, options?)` [Section titled “app.batch(calls, params?, options?)”](#appbatchcalls-params-options) Dispatches many service calls concurrently (`Promise.allSettled` semantics) and returns one `BatchResult` per call, **in input order**. The HTTP transports expose this as `POST /batch`. ```typescript const results = await app.batch([ { service: "users", method: "get", id: 1 }, { service: "messages", method: "find", params: { query: { $limit: 5 } } }, { service: "messages", method: "create", data: { text: "Hello" } }, ]); // results[i] is { status: "success", result } or { status: "error", error: { name, message, code, … } } ``` * Every call runs the target service’s **full hook pipeline** — batch is not a way to bypass authentication or validation. `params` (typically the outer HTTP request’s `headers`, `user`, `provider`) is inherited by every call and merged with the call’s own `params.query`. * One call failing does not fail the batch, and there is no cross-call atomicity — a batch can span services backed by different databases. Use `withTransaction()` inside a single custom service method when you need same-repository atomicity. * Batches larger than `options.maxSize` (default 25, `DEFAULT_MAX_BATCH_SIZE`) are rejected with `BadRequest` before any call executes. Malformed entries (missing `service`, a method outside the six standard ones) become per-call `BadRequest` error entries. #### `app.set(key, value)` / `app.get(key)` [Section titled “app.set(key, value) / app.get(key)”](#appsetkey-value--appgetkey) Typed key-value store used by plugins to share instances (e.g. the knex instance is stored under `"knex"`). #### `app.teardown()` [Section titled “app.teardown()”](#appteardown) Gracefully shuts down all registered resources. Call on process exit. *** ### `Service` [Section titled “Service\”](#servicet-d) The contract every service must satisfy. All methods are optional — only implement what you need and restrict the rest via `options.methods`. ```typescript interface Service> { find(params?: ServiceParams): Promise>; get(id: Id, params?: ServiceParams): Promise; create(data: D, params?: ServiceParams): Promise; update(id: Id, data: D, params?: ServiceParams): Promise; patch(id: Id, data: D, params?: ServiceParams): Promise; remove(id: Id, params?: ServiceParams): Promise; } ``` Custom methods beyond these six must be explicitly listed in `options.methods` when calling `app.use()`. *** ### `Repository` [Section titled “Repository\”](#repositoryt-d) Interface for data access implementations (e.g. `KnexRepository`). Lives in the Infrastructure layer and is never imported by services directly. ```typescript interface Repository> { findAll(params?: QueryParams): Promise; findById(id: Id): Promise; save(data: D): Promise; saveAll(data: D[]): Promise; updateById(id: Id, data: D): Promise; patchById(id: Id, data: D): Promise; deleteById(id: Id): Promise; count(params?: QueryParams): Promise; findPage?(params?: QueryParams & { cursor?: string }): Promise>; // optional cursor pagination describe?(): RepositoryCapabilities; // optional capability introspection } interface CursorPage { data: T[]; cursor?: string; // opaque adapter token — pass back as params.cursor for the next page; absent on the last page } ``` `findPage()` is implemented by adapters whose backend is natively cursored (DynamoDB, Qdrant, Pinecone) — `describe().pagination` reports `"cursor"` or `"both"` when it is available. It is stateless: the cursor lives entirely in the returned page, so concurrent calls on one repository instance never interfere. *** ### Introspection — `describe()` [Section titled “Introspection — describe()”](#introspection--describe) Machine-readable self-description at both layers, so tooling (OpenAPI generation, the transports’ opt-in `/_services` endpoint, AI agents) can discover capabilities instead of probing by trial and error. #### `Repository.describe()` → `RepositoryCapabilities` [Section titled “Repository.describe() → RepositoryCapabilities”](#repositorydescribe--repositorycapabilities) Every @mantlejs adapter implements the optional `describe()`; user repositories may omit it. ```typescript interface RepositoryCapabilities { adapter: string; // package name, e.g. "@mantlejs/knex" operators: string[]; // exactly the $-operators the adapter's where translator accepts pagination: "offset" | "cursor" | "both"; fullTextSearch: boolean; scanning?: (where: Record) => boolean; // e.g. DynamoDB: true when the where clause forces a full Scan } ``` #### `ServiceHandle.describe()` → `ServiceDescriptor` [Section titled “ServiceHandle.describe() → ServiceDescriptor”](#servicehandledescribe--servicedescriptor) ```typescript const descriptor = app.service("users").describe(); interface ServiceDescriptor { path: string; methods: string[]; events: string[]; // standard created/updated/patched/removed filtered by methods, plus custom ServiceOptions.events schema?: unknown; // the stored ServiceOptions.schema (JSON Schema — a TypeBox schema works) capabilities?: RepositoryCapabilities; // present when the service exposes describe() — RepositoryService wires this through authRequired?: boolean; // true when a before.all hook carries an authStrategy marker, e.g. authenticate("jwt") } ``` The HTTP transports (`@mantlejs/express`, `@mantlejs/koa`, `@mantlejs/http`) can serve every registered service’s descriptor at `GET /_services` via their opt-in `introspection` option. *** ### `RepositoryService` [Section titled “RepositoryService\”](#repositoryservicet-d) A ready-made `Service` over any `Repository` — the framework-owned bridge from raw HTTP query strings to typed `QueryParams`. Use it when a service is a straight CRUD facade over a repository; add hooks for everything else. ```typescript import { RepositoryService } from "@mantlejs/mantle"; app.use( "users", new RepositoryService(new UserRepository(app), { schema: userSchema, // any object with { properties: { field: { type } } } — a TypeBox schema works fields: ["name", "age", "active"], // optional whitelist of queryable fields paginate: { default: 25, max: 100 }, }), ); ``` #### Query conventions [Section titled “Query conventions”](#query-conventions) Reserved keys inside `query` (everything else becomes the repository `where` clause): | Key | Example | Meaning | | --------- | ------------------ | --------------------------------------------- | | `$limit` | `?$limit=10` | page size (capped at `paginate.max` when set) | | `$skip` | `?$skip=20` | offset | | `$sort` | `?$sort[name]=asc` | sort order — accepts `asc`/`desc`/`1`/`-1` | | `$select` | `?$select[]=name` | field projection | `GET /users?age[$gt]=21&$limit=10&$sort[name]=asc` becomes `repository.findAll({ where: { age: { $gt: 21 } }, limit: 10, sort: { name: "asc" } })`. #### Behavior guarantees [Section titled “Behavior guarantees”](#behavior-guarantees) * **`find()` always returns `Paginated`** — `{ total, limit, skip, data }`, with `total` from `repository.count({ where })`. No `T[] | Paginated` branching for consumers. * **Field whitelist** — with `options.fields`, a where/sort/select key outside the list throws `BadRequest` naming the field and the allowed set. * **String coercion** — query values arrive as strings over HTTP. With `options.schema`, values are coerced to each field’s declared type (`number`, `integer`, `boolean`), including inside operator objects, `$in`/`$nin` arrays, and `$or`/`$and` branches. **Without a schema, strings pass through unchanged** — `age[$gt]=21` then compares against the string `"21"`, so provide a schema for any non-string field you query. * `update`/`patch`/`remove` propagate the repository’s `NotFound` untouched; `get()` maps a `null` lookup to `NotFound`. *** ### `VectorRepositoryService` and the `similar` convention [Section titled “VectorRepositoryService\ and the similar convention”](#vectorrepositoryservicet-d-and-the-similar-convention) Services backed by a `VectorRepository` expose vector search as a **custom method named `similar`**, registered in `app.use()` so transports mount it (custom methods dispatch as `POST //similar` and run the full hook pipeline). `VectorRepositoryService` is a `RepositoryService` with the method pre-wired to `repository.findSimilar()`: ```typescript import { VectorRepositoryService } from "@mantlejs/mantle"; app.use( "docs", new VectorRepositoryService(new DocRepository(app), { fields: ["category", "tags"], // whitelist applies to similar()'s where too topK: { default: 10, max: 100 }, // missing topK gets default; requests are capped at max }), { methods: ["find", "get", "create", "update", "patch", "remove", "similar"], }, ); ``` ```plaintext POST /docs/similar { "vector": [0.12, -0.5, …], "topK": 5, "where": { "category": "tech" } } ``` Every result carries the adapter’s native match metric as `_score` — whether higher or lower means “more similar” is adapter-specific; see the adapter README. A missing/non-numeric `vector`, a non-positive `topK`, or a `where` key outside `options.fields` throws `BadRequest` with a hint. Writing your own service instead? Follow the same convention: register `similar(data, params)` as a custom method and forward to `repository.findSimilar(data.vector, data.topK, { where: data.where })`. *** ### Hooks [Section titled “Hooks”](#hooks) Hooks are plain functions — no classes. They run in three phases: `before`, `after`, and `error`. The `all` key applies to every method in that phase. ```typescript type HookFunction = (context: HookContext) => Promise> | HookContext; ``` Register hooks via `app.service(path).hooks(config)`: ```typescript app.service("users").hooks({ before: { all: [authenticate("jwt")], create: [hashPassword()], }, after: { all: [sanitizeUser()], }, error: { all: [logError()], }, }); ``` #### `HookContext` [Section titled “HookContext\”](#hookcontextt) | Field | Type | Description | | ---------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------- | | `app` | `MantleApplication` | The application instance | | `service` | `Partial>` | The service being called | | `path` | `string` | Registered path, e.g. `"users"` | | `method` | `string` | Method name, e.g. `"create"` | | `provider` | `string \| undefined` | The transport: `"rest"` (Express), `"koa"`, `"http"`, `"socket.io"`, `"mcp"`; `undefined` internally | | `params` | `ServiceParams` | Query, headers, user, etc. | | `data` | `Partial \| undefined` | Request body (write methods) | | `id` | `Id \| undefined` | Record identifier (get/update/patch/remove) | | `result` | `T \| T[] \| Paginated \| undefined` | Set by the service or an `after` hook | | `error` | `Error \| undefined` | Set when an error occurs | Setting `context.result` in a `before` hook skips the service call entirely. *** ### Error classes [Section titled “Error classes”](#error-classes) Always throw a typed error — never a plain `new Error()`. | Class | HTTP status | `className` | | ------------------ | ----------- | -------------------- | | `BadRequest` | 400 | `bad-request` | | `NotAuthenticated` | 401 | `not-authenticated` | | `Forbidden` | 403 | `forbidden` | | `NotFound` | 404 | `not-found` | | `MethodNotAllowed` | 405 | `method-not-allowed` | | `Conflict` | 409 | `conflict` | | `Unprocessable` | 422 | `unprocessable` | | `TooManyRequests` | 429 | `too-many-requests` | | `GeneralError` | 500 | `general-error` | | `NotImplemented` | 501 | `not-implemented` | | `Unavailable` | 503 | `unavailable` | All errors accept `(message?, data?, errors?, hint?)` and serialize via `.toJSON()`. ```typescript throw new Conflict("Email already exists", { field: "email" }); ``` The optional `hint` is actionable guidance for the caller — what to change so the request succeeds. It is included in `toJSON()`, so HTTP error responses carry it to clients (human or agent): ```typescript throw new BadRequest( "Operator $regex is not supported by knex", undefined, undefined, "Rewrite the where clause using only the supported operators, or filter in application code.", ); ``` *** ### `Paginated` [Section titled “Paginated\”](#paginatedt) ```typescript interface Paginated { total: number; limit: number; skip: number; data: T[]; } ``` Return this from `find()` when the caller requests a page. *** ### `QueryParams` [Section titled “QueryParams”](#queryparams) Used by `Repository` methods to filter, sort, and paginate queries. ```typescript interface QueryParams { where?: Record; limit?: number; skip?: number; sort?: Record; select?: string[]; } ``` ## Development [Section titled “Development”](#development) ```bash npx nx build mantle # compile npx nx test mantle # run tests npx nx lint mantle # lint ``` ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build mantle ``` First publish (scoped packages require `--access public`): ```bash cd packages/mantle npm publish --access public ``` Subsequent releases — bump `version` in `packages/mantle/package.json`, then: ```bash cd packages/mantle npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) The workspace includes a local registry to smoke-test a publish before pushing to npm: ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/mantle npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/mantle --registry http://localhost:4873 ``` # @mantlejs/mcp > Expose Mantle services as Model Context Protocol tools — an AI agent gets one tool per exposed service method, with input schemas derived from your TypeBox schemas and query capabilities, and every call runs the service's full hook pipeline. There is no bypass: authentication, validation, and events apply to agents exactly as they do to REST callers. Expose Mantle services as [Model Context Protocol](https://modelcontextprotocol.io) tools — an AI agent gets one tool per exposed service method, with input schemas derived from your TypeBox schemas and query capabilities, and every call runs the service’s **full hook pipeline**. There is no bypass: authentication, validation, and events apply to agents exactly as they do to REST callers. ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/mcp ``` `@mantlejs/mantle` is a peer dependency. For `transport: "http"`, an HTTP transport (`@mantlejs/express`, `@mantlejs/koa`, or `@mantlejs/http`) must be configured first. *** ## Concepts [Section titled “Concepts”](#concepts) ### Deny-by-default expose map [Section titled “Deny-by-default expose map”](#deny-by-default-expose-map) Nothing is exposed unless you say so. `services` is required and maps a service path to the methods that become tools: ```typescript mcp({ transport: "http", services: { articles: true, // every method registered in app.use() users: ["find", "get"], // read-only over MCP }, }); ``` * `true` exposes all of the service’s registered methods — never more than `app.use()` registered. * An unknown path or method fails the boot (`listen()` / `startMcp()`) with a `BadRequest` naming it. * `services: "*"` exposes every service registered **after** `mcp()` — a deliberate, greppable escape hatch for prototyping. * With `events: true`, only expose-map services get an event resource; hidden services never leak. ### One tool per method [Section titled “One tool per method”](#one-tool-per-method) Tools are generated from `ServiceHandle.describe()` and named `{path}_{method}` (`users_find`, `blog_posts_create`, custom methods included): * **`find`** takes a structured `query` (`where` / `limit` / `skip` / `sort` / `select`). The `where` operator set is constrained to the adapter’s `describe().capabilities.operators` — an agent is never offered `$ilike` against an adapter that rejects it. * **`create`/`update`/`patch`** take `data` typed by the schema attached in `app.use()` (`patch` gets the all-optional variant); services without a schema still get tools with a generic object schema. * **`get`/`remove`** take `id`; `update`/`remove` descriptions carry destructive-operation notes. ### Hook-pipeline dispatch [Section titled “Hook-pipeline dispatch”](#hook-pipeline-dispatch) Every tool call goes through `service.dispatch()` — the same entry point the HTTP transports use — with `params.provider = "mcp"`. Hooks can therefore apply agent-specific policy: ```typescript app.service("articles").hooks({ before: { remove: [ (ctx) => { if (ctx.params.provider === "mcp") throw new Forbidden("Agents may not delete articles"); return ctx; }, ], }, }); ``` Errors come back as MCP **tool errors** carrying `MantleError.toJSON()` — name, message, code, `data`, and `hint` — so an agent can read what went wrong and adjust. ### Result limits [Section titled “Result limits”](#result-limits) `find` results are clamped: `limit = min(requested ?? defaultLimit, maxLimit)` (defaults 25 / 100, configurable via `query`). The clamp is advertised in the generated schema (`maximum`, default in the description), and a truncated page carries a note telling the agent to page with `skip`/`limit` and trim fields with `select`. ### Authentication [Section titled “Authentication”](#authentication) * **HTTP:** a `Bearer` token on the request is verified with the configured auth engine (`@mantlejs/auth`); the resolved user lands on `params.user` for every call in the session, and the `authorization` header is passed through so `authenticate("jwt")` hooks work unchanged. No/invalid token → the session is anonymous and each service’s own auth hooks reject. The MCP layer makes no auth decisions of its own. * **stdio:** carries no HTTP request — pass `startMcp(app, { params: { headers: { authorization: "Bearer …" } } })` to authenticate the session, otherwise it is anonymous. *** ## Quick start [Section titled “Quick start”](#quick-start) ### HTTP [Section titled “HTTP”](#http) ```typescript import { mantle, RepositoryService } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { mcp } from "@mantlejs/mcp"; const app = mantle() .configure(express()) .configure( mcp({ transport: "http", // mounts POST /mcp (path configurable) services: { users: ["find", "get"], articles: true }, events: true, // mantle://events/{path} resources }), ); app.use("users", new RepositoryService(new UserRepository(app)), { schema: userSchema }); app.listen(3030); ``` Point an MCP client at `http://localhost:3030/mcp` (streamable HTTP, JSON responses; SSE streaming is not supported — poll event resources instead). ### stdio [Section titled “stdio”](#stdio) ```typescript // mcp.ts — separate entry point: build the app, don't listen() import { startMcp } from "@mantlejs/mcp"; import { buildApp } from "./app.js"; const app = buildApp(); // configure(mcp({ transport: "stdio", services: { … } })) await startMcp(app); ``` ### Custom (composite) tools [Section titled “Custom (composite) tools”](#custom-composite-tools) Task-level tools beyond CRUD — chain service calls with the session’s identity; every inner `dispatch` runs the target’s hook pipeline: ```typescript mcp({ transport: "http", services: { articles: ["find", "get"] }, tools: [ { name: "publish_article", description: "Publish an article and record the activity.", inputSchema: { type: "object", required: ["articleId"], properties: { articleId: { type: "string" } } }, handler: async (args, { app, params }) => { const { articleId } = args as { articleId: string }; const article = await app.service("articles").dispatch("patch", { status: "published" }, articleId, params); await app.service("activity").dispatch("create", { type: "published", articleId }, undefined, params); return article; }, }, ], }); ``` A custom tool name colliding with a generated tool fails the boot. ### Custom resources [Section titled “Custom resources”](#custom-resources) Read-only reference content an agent should load as context rather than query as a tool — schema docs, enum values, usage guides. `read` receives the same session context as tools, so inner `dispatch()` calls run the full hook pipeline: ```typescript mcp({ transport: "http", services: { users: ["find"] }, resources: [ { uri: "mantle://docs/usage", name: "API usage guide", mimeType: "text/markdown", read: async () => readFile("docs/agent-guide.md", "utf8"), }, ], }); ``` The `mantle://events/` namespace is reserved for event resources; duplicate or reserved URIs fail at configure time. Custom resources have no update signal — subscribing is an accepted no-op (event resources keep live notifications on stdio). ### Prompts [Section titled “Prompts”](#prompts) Prompt templates that MCP clients surface as user-invokable commands (e.g. slash commands). `get` may return a plain string (shorthand for a single user message) or the full `{ description?, messages }` shape, and receives the session context: ```typescript mcp({ transport: "http", services: { articles: ["find"] }, prompts: [ { name: "triage_articles", description: "Review articles awaiting moderation.", arguments: [{ name: "focus", required: false }], get: async (args, { app, params }) => { const page = await app.service("articles").dispatch("find", undefined, undefined, { ...params, query: { status: "pending", $limit: 10 }, }); return `Review these pending articles${args["focus"] ? ` (focus: ${args["focus"]})` : ""}: ${JSON.stringify(page)}`; }, }, ], }); ``` The `prompts` capability is only declared when at least one prompt is defined; duplicate names fail at configure time. ### Code mode [Section titled “Code mode”](#code-mode) Instead of one tool per method, a server can give the agent a typed API it writes a short script against. Many service calls then run in one round trip, and intermediate results stay out of the model’s context. The sandbox lives in a separate package, **[`@mantlejs/mcp-code`](/packages/mcp-code/)** (experimental), which plugs in through the `codeMode` option — `mcp({ …, codeMode: codeMode() })`. `@mantlejs/mcp` itself has no sandbox dependency. | `mode` | Tools listed | | --------- | ------------------------------------------------------------------------------------------------------------- | | `"tools"` | One generated tool per exposed method. The default when no `codeMode` provider is set, and today’s behavior. | | `"code"` | The provider’s tools only; the generated per-method tools are suppressed. The default when `codeMode` is set. | | `"both"` | Generated tools plus the provider’s tools. | App-authored `tools`, `resources`, `prompts`, and event resources are registered in every mode. `mode: "code"` or `"both"` without a provider fails at configure time with a `BadRequest`. A provider implements `McpCodeModeProvider`. Its `build()` runs once, when the expose map is resolved (at `listen()` for HTTP, at `startMcp()` for stdio): ```typescript interface McpCodeModeProvider { build(input: McpCodeModeInput): McpCodeModeSurface; } interface McpCodeModeInput { app: MantleApplication; services: McpExposedService[]; // resolved expose map: { path, methods, descriptor }, never "*" query: { defaultLimit: number; maxLimit: number }; // effective find() guardrails } interface McpCodeModeSurface { tools: McpToolDefinition[]; resources?: McpResourceDefinition[]; } ``` * **Same rules as app-authored tools and resources.** A provider tool whose name collides with any registered tool fails the boot, and so does a provider resource URI that duplicates another or uses `mantle://events/`. In `"code"` mode the generated tools aren’t registered, so their names are free. * **Same metadata and dispatch as tool mode.** `describeServiceMethod(descriptor, method, query)` and `buildQuerySchema(operators, limits)` are exported — the exact helpers the generated tools are built from, so a provider’s typed API cannot drift from the tool schemas. `createServiceMethodRunner(app, descriptor, method, query)` is the runner every generated tool executes (find-limit clamp, structured-query translation, id checks); a provider dispatching through it behaves exactly like tool mode. * **Per-session behavior lives in the handlers.** The surface is built once and shared by every session. Tool handlers and resource `read` functions receive the session’s `McpToolContext.params` on every call, which is where a provider narrows what a given agent sees. Enforcement still happens in the hook pipeline, because every inner `dispatch()` runs the target service’s hooks. *** ## API [Section titled “API”](#api) ### `mcp(options: McpOptions): MantlePlugin` [Section titled “mcp(options: McpOptions): MantlePlugin”](#mcpoptions-mcpoptions-mantleplugin) | Option | Type | Default | Description | | ------------ | ----------------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------- | | `services` | `Record \| "*"` | — (required) | Deny-by-default expose map. | | `transport` | `"stdio" \| "http"` | — (required) | `"http"` mounts on the app’s transport; `"stdio"` pairs with `startMcp()`. | | `path` | `string` | `"/mcp"` | HTTP mount path. | | `serverInfo` | `{ name?, version? }` | `{ name: "mantle", version: "0.0.0" }` | Identity reported during MCP initialization. | | `events` | `boolean` | `false` | Event resources (`mantle://events/{path}`, last 50 events) for exposed services. | | `tools` | `McpToolDefinition[]` | `[]` | App-authored composite tools. | | `resources` | `McpResourceDefinition[]` | `[]` | App-authored read-only resources. | | `prompts` | `McpPromptDefinition[]` | `[]` | App-authored prompt templates. | | `query` | `{ defaultLimit?, maxLimit? }` | `{ defaultLimit: 25, maxLimit: 100 }` | `find` result guardrails. | | `mode` | `"tools" \| "code" \| "both"` | `"code"` with `codeMode`, else `"tools"` | Tool surface — see [Code mode](#code-mode). | | `codeMode` | `McpCodeModeProvider` | — | Code-mode provider (e.g. from `@mantlejs/mcp-code`). | ### `startMcp(app, options?): Promise` [Section titled “startMcp(app, options?): Promise\”](#startmcpapp-options-promiseserver) Connects the configured server to stdio. `options.params` merges extra `ServiceParams` (e.g. an `authorization` header) into every call of the session. There is deliberately **no batch tool** — agents issue parallel tool calls natively, and composite needs are served by `tools`. ## Configure order [Section titled “Configure order”](#configure-order) `mcp()` reads the `"http:router"` contract and (for `"*"`) tracks registrations, so configure it **after** the HTTP transport and **before** registering services. # @mantlejs/mcp-code > MCP code mode for Mantle JS. Instead of one MCP tool per service method, the agent gets a typed TypeScript API of your exposed services and writes a short script against it. The script runs in a QuickJS sandbox. Many service calls happen in one round trip, filtering and joining happen in code, and only the final answer goes back to the model. MCP **code mode** for [Mantle JS](https://github.com/mantlejs/mantle). Instead of one MCP tool per service method, the agent gets a typed TypeScript API of your exposed services and writes a short script against it. The script runs in a QuickJS sandbox. Many service calls happen in one round trip, filtering and joining happen in code, and only the final answer goes back to the model. Every `mantle..()` call inside a script runs the service’s **full hook pipeline** as the MCP session — `authenticate`, `authorizeAgent()`, validation, `@mantlejs/audit` — exactly like [`@mantlejs/mcp`](/packages/mcp/)’s per-method tools. Code mode changes how the agent calls your API, not what it’s allowed to do. > **Experimental.** Ships in the `experimental` release group (`0.3.0-experimental`, install with `npm install @mantlejs/mcp-code@experimental`); the API may change before it’s promoted. ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/mcp @mantlejs/mcp-code ``` `@mantlejs/mantle` and `@mantlejs/mcp` are peer dependencies. Requires Node ≥ 22 (TypeScript input needs ≥ 22.13 — see [TypeScript input](#typescript-input)). *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle, RepositoryService } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { mcp } from "@mantlejs/mcp"; import { codeMode } from "@mantlejs/mcp-code"; const app = mantle() .configure(express()) .configure( mcp({ transport: "http", services: { articles: ["find", "get"], comments: ["find"] }, // deny-by-default, as always codeMode: codeMode(), }), ); app.use("articles", new RepositoryService(new ArticleRepository(app)), { schema: articleSchema }); app.use("comments", new RepositoryService(new CommentRepository(app)), { schema: commentSchema }); app.listen(3030); ``` With a `codeMode` provider, `mcp()` defaults to `mode: "code"`: | `mode` | What the agent sees | | --------- | -------------------------------------------------------------------------- | | `"code"` | `search_api` + `execute` + the `mantle://code/api.d.ts` resource (default) | | `"both"` | The above plus the per-method tools (`articles_find`, …) | | `"tools"` | Per-method tools only — the provider isn’t used | App-authored `tools`, `resources`, `prompts`, and event resources are registered in every mode. *** ## What the agent sees [Section titled “What the agent sees”](#what-the-agent-sees) ### `search_api({ query?, paths? })` [Section titled “search_api({ query?, paths? })”](#search_api-query-paths-) Loads the typed API a piece at a time. With no arguments it returns a one-line index (`articles — find, get`); `query` keyword-matches service paths, method names, and descriptions; `paths` selects exact services. The result is a self-contained TypeScript declaration module: ```typescript type Id = string | number; interface Paginated { total: number; limit: number; skip: number; data: T[]; } /** A `articles` record. */ type ArticlesRecord = { id?: string; title: string; tags: string[]; status: "draft" | "published" }; /** Query for `mantle.articles.find()`. */ type ArticlesQuery = { where?: { $or?: { [key: string]: unknown }[] } & { [key: string]: unknown | { $lt?: unknown; $like?: unknown } }; /** Maximum records to return. Default 25; requests above 100 are clamped. Page with skip. */ limit?: number; skip?: number; sort?: { [key: string]: "asc" | "desc" }; select?: string[]; }; declare const mantle: { articles: { /** Find articles records matching a query. Results are paginated — … */ find(query?: ArticlesQuery): Promise>; /** Get a single articles record by id. */ get(id: Id, query?: ArticlesIdQuery): Promise; }; }; ``` The same module (for the session) is served as the **`mantle://code/api.d.ts`** resource, for clients that load resources up front. * **One source of truth.** Every parameter and return type is converted from the exact JSON Schema `@mantlejs/mcp` lists the per-method tool with (`describeServiceMethod()`), so code mode and tool mode can’t drift. Record types come from the schema attached in `app.use()`; services without one get `{ [key: string]: unknown }`. * **Operators match the adapter.** The `where` type offers exactly the operators the repository’s `describe().capabilities.operators` reports. * **Paths are keys, verbatim.** `mantle.articles`, `mantle["blog-posts"]`, `mantle["admin/reports"]` — no aliasing, so there’s never a collision or a renamed path to guess. * **Signatures:** `find(query?)`, `get(id, query?)`, `remove(id, query?)`, `create(data)`, `update(id, data)`, `patch(id, data)`, custom methods `(data?)` — the same argument objects tool mode takes, positionally. ### `execute({ code })` [Section titled “execute({ code })”](#execute-code-) Runs a script — the **body of an async function**: top-level `await` works and `return` sends back the result. ```javascript const { data } = await mantle.articles.find({ where: { status: "published" }, limit: 100, select: ["id", "tags"] }); const counts = {}; for (const article of data) for (const tag of article.tags) counts[tag] = (counts[tag] ?? 0) + 1; console.log("articles scanned:", data.length); return counts; ``` returns ```json { "result": { "guide": 2, "start": 1 }, "executionId": "…", "calls": 1, "logs": ["[log] articles scanned: 2"] } ``` plus `notes` when something was cut short (a partial `find` page, dropped log lines). * **Values cross as JSON** in both directions — `Date` becomes an ISO string, functions and `undefined` are dropped. * **Run independent calls with `Promise.all`** — bridged calls run concurrently. * **Service errors are catchable.** A rejected call throws an `Error` carrying the service error’s `name`, `message`, `code`, `className`, `data`, and `hint`: ```javascript try { return await mantle.articles.get(id); } catch (e) { if (e.name === "NotFound") return null; throw e; } ``` * **Uncaught errors** come back as an MCP tool error with the same `MantleError.toJSON()` shape tool mode uses. A service error keeps its class (`NotFound`, `Forbidden`, …); the script’s own bugs (a `TypeError`, a syntax error) are `CodeScriptError`. `data.execution` carries `{ executionId, calls, logs }`, and the original `data`, if any, moves to `data.cause`. *** ## Limits [Section titled “Limits”](#limits) Every execution gets a **fresh sandbox** — nothing carries over between calls — and every limit fails with a typed error. None can take down the host process. | Limit | Default | Error | Enforcement | | ----------------- | ------- | -------------------------- | -------------------------------------------------------------------------------------------------- | | `timeoutMs` | 10 000 | `CodeTimeout` (408) | Interrupt handler for CPU loops **and** a host timer for a bridged call that never settles | | `memoryBytes` | 32 MiB | `CodeLimitExceeded` (422) | QuickJS’s own limit — soft, it undercounts some allocations | | `wasmMemoryBytes` | 64 MiB | `CodeLimitExceeded` (422) | Bounded `WebAssembly.Memory` per execution — the hard cap. Minimum 16 MiB | | `stackBytes` | 192 KiB | `CodeLimitExceeded` (422) | QuickJS shares the host thread’s stack — keep it under \~¼ of it (\~256 KiB on Node’s main thread) | | `maxCalls` | 50 | `CodeLimitExceeded` (422) | Bridged calls per execution. Fails the execution even if the script catches the error | | `maxOutputBytes` | 64 KiB | `CodeOutputTooLarge` (413) | JSON result size; log output past it is dropped with a note | ```typescript codeMode({ limits: { timeoutMs: 5_000, maxCalls: 20 } }); ``` A stack limit above the safe ceiling can overflow the host’s native stack; that’s contained as `CodeExecutorFault` (500) and the sandbox instance is discarded. A bridged service call already in flight when a timeout fires runs to completion on the host — it can’t be cancelled — but its result is discarded. *** ## TypeScript input [Section titled “TypeScript input”](#typescript-input) On Node ≥ 22.13 the script may use TypeScript annotations — types are stripped host-side with `module.stripTypeScriptTypes` (positions preserved, so error line numbers match the script). Only erasable syntax is supported: `enum` and `namespace` fail with a `BadRequest` and a hint. Agent code is not type-checked. On older Node versions scripts must be plain JavaScript, and the `execute` description says so. Node prints a one-time `ExperimentalWarning` the first time the stripper runs. *** ## Security model [Section titled “Security model”](#security-model) * **The hook pipeline is the authority.** Each bridged call is `createServiceMethodRunner()` from `@mantlejs/mcp` — the identical path tool mode uses — with the session’s params, `provider: "mcp"`, the session’s `authorization` header, and fresh params per call. Hooks see the same request either way; `HookContext.provider` is `"mcp"` for both. * **The expose map is the hard boundary.** Only the exposed methods exist inside the sandbox. A registered-but-unexposed service or method isn’t there to call. * **Agent narrowing is display only.** When the session authenticated with an [agent token](/packages/auth/#agent-tokens), `search_api` and the resource show only the methods its capability scope grants. Enforcement stays in `authorizeAgent()`: an exposed-but-out-of-scope call still reaches the hook and throws a catchable `Forbidden` — the same denial tool mode returns. * **No ambient capabilities.** No `fetch`, `XMLHttpRequest`, `WebSocket`, `require`/`import`, `process`, filesystem, or timers; the guest holds no host object references. `Date` and `Math.random` behave normally. * **Every call is attributable.** Each bridged call carries `params.mcp = { mode: "code", executionId, scriptHash }`. With [`@mantlejs/audit`](/packages/audit/), a script that made N calls produces N audit records sharing the `executionId`. The script source itself is recorded only with `codeMode({ auditScriptSource: true })` — off by default, since scripts can embed data; the SHA-256 `scriptHash` is always there to match a record to a script. *** ## Custom executors [Section titled “Custom executors”](#custom-executors) The sandbox is pluggable. Implement `CodeExecutor`: ```typescript interface CodeExecutor { execute(code: string, bridge: CodeBridge, limits: CodeLimits): Promise; } codeMode({ executor: myWorkerExecutor() }); ``` The contract — fresh state per execution, JSON-only values, the `mantle` API built from `bridge.services`, no host capabilities, every limit typed, never rejecting — is published as data, so any executor can run the reference suite: ```typescript import { CODE_EXECUTOR_CONFORMANCE_CASES, conformanceBridge, DEFAULT_CODE_LIMITS } from "@mantlejs/mcp-code"; for (const testCase of CODE_EXECUTOR_CONFORMANCE_CASES) { it(testCase.name, async () => { const bridge = conformanceBridge(); const result = await myExecutor.execute(testCase.code, bridge, { ...DEFAULT_CODE_LIMITS, ...testCase.limits }); // compare with testCase.expect and testCase.check — see quickjs-executor.spec.ts for the full loop }); } ``` *** ## When to prefer tool mode [Section titled “When to prefer tool mode”](#when-to-prefer-tool-mode) * **Small APIs** — a handful of methods costs little context as tools. * **Clients that don’t write code well** — some models are much better at choosing a tool than at writing a correct script. * **Interactive, one-call-at-a-time work** where intermediate results should be shown to the user anyway. `mode: "both"` serves both surfaces from one endpoint, or mount two `mcp()` endpoints with different `path`s (see the [knowledge-base example](https://github.com/MantleJs/mantle/blob/main/examples/knowledge-base/README.md#mcp-code-mode)). *** ## API [Section titled “API”](#api) | Export | Description | | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `codeMode(options?)` | The `McpCodeModeProvider` for `mcp({ codeMode })`. Options: `executor`, `limits`, `auditScriptSource` | | `quickJsExecutor()` | The default `CodeExecutor` (QuickJS sync WASM build, fresh bounded instance per execution) | | `DEFAULT_CODE_LIMITS`, `MIN_WASM_MEMORY_BYTES` | Limit defaults and the `wasmMemoryBytes` floor | | `CodeTimeout`, `CodeLimitExceeded`, `CodeOutputTooLarge`, `CodeScriptError`, `CodeExecutorFault` | Typed errors (`MantleError` subclasses) | | `CODE_EXECUTOR_CONFORMANCE_CASES`, `conformanceBridge()` | The executor contract as data | | `API_RESOURCE_URI` | `"mantle://code/api.d.ts"` | | Types | `CodeModeOptions`, `CodeModeCallMetadata`, `CodeExecutionReport`, `CodeExecutor`, `CodeBridge`, `CodeLimits`, `CodeExecutionResult`, `ConformanceCase`, `ConformanceBridge`, `BridgeCall` | # @mantlejs/memory > In-memory Repository implementation for Mantle JS. Backed by a Map, it supports all QueryParams operators with the same semantics as KnexRepository. Designed for unit testing services without a database — swap it in for any Repository. In-memory `Repository` implementation for [Mantle JS](https://github.com/mantlejs/mantle). Backed by a `Map`, it supports all `QueryParams` operators with the same semantics as `KnexRepository`. Designed for unit testing services without a database — swap it in for any `Repository`. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/memory ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### MemoryRepository [Section titled “MemoryRepository”](#memoryrepository) `MemoryRepository` implements the `Repository` interface from `@mantlejs/mantle`. It stores records in a `Map` keyed by ID, auto-generates UUIDs, and manages `createdAt`/`updatedAt` timestamps — the same behaviour your production `KnexRepository` provides. ### Test helpers [Section titled “Test helpers”](#test-helpers) Three helpers make it easy to set up and inspect state in tests: * `repo.seed(records)` — pre-populate the store with known records (bypasses auto-ID if `id` is provided) * `repo.clear()` — empty the store between tests * `repo.store` — read-only `Map` for assertions on internal state Both `seed()` and `clear()` return `this` for chaining. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { MemoryRepository } from "@mantlejs/memory"; interface User { id: string; name: string; email: string; } // In your test const repo = new MemoryRepository(); // Seed known state repo.seed([ { id: "1", name: "Alice", email: "alice@example.com" }, { id: "2", name: "Bob", email: "bob@example.com" }, ]); const user = await repo.findById("1"); // → { id: "1", name: "Alice", email: "alice@example.com", createdAt: "...", updatedAt: "..." } const active = await repo.findAll({ where: { name: { $like: "A%" } } }); // → [{ id: "1", name: "Alice", ... }] ``` ### Injecting into a service [Section titled “Injecting into a service”](#injecting-into-a-service) ```typescript import { mantle } from "@mantlejs/mantle"; import { MemoryRepository } from "@mantlejs/memory"; import { UserService } from "../src/user.service.js"; const app = mantle(); const repo = new MemoryRepository(); app.use("users", new UserService(repo)); repo.seed([{ id: "1", name: "Alice", email: "alice@example.com" }]); const users = await app.service("users").find(); // → [{ id: "1", name: "Alice", ... }] ``` *** ## API [Section titled “API”](#api) ### `new MemoryRepository(options?)` [Section titled “new MemoryRepository\(options?)”](#new-memoryrepositorytoptions) ```typescript const repo = new MemoryRepository({ idField: "id", // default — primary key field name autoId: true, // default — auto-generate UUID when id is absent on save timestamps: true, // default — manage createdAt / updatedAt }); ``` #### `MemoryRepositoryOptions` [Section titled “MemoryRepositoryOptions”](#memoryrepositoryoptions) | Field | Type | Default | Description | | ------------ | --------- | ------- | ----------------------------------------------------------- | | `idField` | `string` | `"id"` | Primary key field name | | `autoId` | `boolean` | `true` | Auto-generate `crypto.randomUUID()` when `id` is absent | | `timestamps` | `boolean` | `true` | Set `createdAt` on save, update `updatedAt` on patch/update | *** ### Repository methods [Section titled “Repository methods”](#repository-methods) All methods mirror the `Repository` interface and return `Promise` or `Promise`. | Method | Description | | ---------------------- | ------------------------------------------------------------- | | `findAll(params?)` | Return all records matching `QueryParams` | | `findById(id)` | Return a single record or `null` | | `save(data)` | Insert a new record; throws `Conflict` if `id` already exists | | `saveAll(data[])` | Insert multiple records | | `updateById(id, data)` | Full replace (all fields); throws `NotFound` if absent | | `patchById(id, data)` | Partial update (merge); throws `NotFound` if absent | | `deleteById(id)` | Remove and return the record; throws `NotFound` if absent | | `count(params?)` | Count records matching `QueryParams.where` | *** ### Test helpers [Section titled “Test helpers”](#test-helpers-1) | Member | Description | | --------------- | ----------------------------------------------------- | | `seed(records)` | Pre-populate the store; returns `this` | | `clear()` | Empty the store; returns `this` | | `store` | Read-only `Map` — direct access for assertions | *** ### QueryParams operators [Section titled “QueryParams operators”](#queryparams-operators) `findAll()` and `count()` accept a `QueryParams` object. All operators match the behaviour of `KnexRepository`: | Operator | Example | SQL equivalent | | ------------------------------- | ------------------------------------- | ---------------------------------------- | | Equality | `{ field: value }` | `field = value` | | Null | `{ field: null }` | `IS NULL` | | `$lt` / `$lte` / `$gt` / `$gte` | `{ age: { $gt: 18 } }` | `age > 18` | | `$ne` | `{ status: { $ne: "inactive" } }` | `status != 'inactive'` | | `$in` / `$nin` | `{ role: { $in: ["admin", "mod"] } }` | `role IN (...)` | | `$or` / `$and` | `{ $or: [{ a: 1 }, { b: 2 }] }` | `(a = 1 OR b = 2)` | | `$like` / `$notlike` | `{ name: { $like: "A%" } }` | `name LIKE 'A%'` | | `$ilike` | `{ name: { $ilike: "alice" } }` | Case-insensitive match | | `$contains` | `{ tags: { $contains: "blue" } }` | `tags @> '["blue"]'` (jsonb containment) | Where-clause keys may be dot-paths into nested objects: `{ "metadata.owner.name": "alice" }`. `$contains` follows PostgreSQL jsonb `@>` semantics — array operand: field contains every element; scalar operand: field contains that element; object operand: field is a recursive superset. This adapter is the executable reference for the shared nested-path/`$contains` conformance fixture exported from `@mantlejs/mantle` (`NESTED_QUERY_RECORDS` / `NESTED_QUERY_CASES`). *** ## Types [Section titled “Types”](#types) ```typescript import type { MemoryRepositoryOptions } from "@mantlejs/memory"; ``` | Type | Description | | ------------------------- | ------------------------------------------ | | `MemoryRepositoryOptions` | Constructor options for `MemoryRepository` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build memory # compile npx nx test memory # run tests npx nx lint memory # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build memory ``` First publish (scoped packages require `--access public`): ```bash cd packages/memory npm publish --access public ``` Subsequent releases — bump `version` in `packages/memory/package.json`, then: ```bash cd packages/memory npm publish ``` # @mantlejs/mongodb > MongoDB adapter for Mantle JS. Provides MongoRepository — an abstract Repository base class over the official MongoDB Node.js driver (no Mongoose) — and MongoVectorRepository, a VectorRepository implementation on top of MongoDB Atlas Vector Search. Primary deployment target is MongoDB Atlas; works unchanged against any MongoDB 6+ server. MongoDB adapter for [Mantle JS](https://github.com/mantlejs/mantle). Provides `MongoRepository` — an abstract `Repository` base class over the official MongoDB Node.js driver (no Mongoose) — and `MongoVectorRepository`, a `VectorRepository` implementation on top of **MongoDB Atlas Vector Search**. Primary deployment target is MongoDB Atlas; works unchanged against any MongoDB 6+ server. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/mongodb mongodb ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Document storage [Section titled “Document storage”](#document-storage) MongoDB stores records as BSON documents in named collections. Mongo’s native `_id` is an `ObjectId`; `MongoRepository` accepts and returns `id` as a `string` (24-char hex) at the `Repository` boundary, converting to/from `ObjectId` internally — services and hooks never see a raw `ObjectId`. Malformed ids are rejected with `BadRequest`. ### Collections [Section titled “Collections”](#collections) Subclasses declare `readonly collectionName: string`. Collections are created lazily by MongoDB on first write — no manual setup required for development or testing. ### Query translation [Section titled “Query translation”](#query-translation) Mantle `QueryParams.where` operators map almost directly onto MongoDB query operators — `$lt`/`$lte`/`$gt`/`$gte`/`$ne`/`$in`/`$nin`/`$or`/`$and` are native MongoDB syntax already, and dot-path keys (`"metadata.owner.name"`) are native too. `$contains` is translated to jsonb-`@>`-equivalent MongoDB conditions. `$like`/`$ilike`/`$notlike` are **not** supported by this adapter and throw `BadRequest` — use the raw `collection` escape hatch with `$regex` for pattern matching. Unsupported operators are rejected loudly via `assertOperators`. ### Vector search (Atlas) [Section titled “Vector search (Atlas)”](#vector-search-atlas) `MongoVectorRepository` implements Mantle’s `VectorRepository` (`findSimilar` / `upsertVector` / `deleteVector`) via the `$vectorSearch` aggregation stage. This is an **Atlas feature**, not core MongoDB — it requires an Atlas cluster (the free M0 tier works) and an [Atlas Vector Search index](https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-type/) on the collection. Fields you want to filter on in `findSimilar` must be declared as `type: "filter"` in that index. Register it with `VectorRepositoryService` from `@mantlejs/mantle` to expose `POST //similar`, exactly like the Pinecone/Qdrant adapters. ### Transactions [Section titled “Transactions”](#transactions) `withTransaction()` runs a block of repository calls inside one MongoDB session/transaction via `client.startSession().withTransaction()`. This requires the deployment to be a replica set — true for every Atlas cluster including free-tier M0. Standalone self-hosted MongoDB without a replica set throws the driver’s native error; this is a deployment requirement, not something Mantle works around. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { mongodb, MongoRepository } from "@mantlejs/mongodb"; const app = mantle().configure(mongodb({ uri: process.env.MONGODB_URI!, dbName: "app" })); interface User extends Record { id: string; name: string; email: string; } class UserRepository extends MongoRepository { readonly collectionName = "users"; // Escape hatch — driver-native queries via this.collection async findByEmailDomain(domain: string): Promise { const docs = await this.collection.find({ email: { $regex: `@${domain}$` } }).toArray(); return docs.map((doc) => this.fromDocument(doc)); } } const repo = new UserRepository(app); const user = await repo.save({ name: "Alice", email: "alice@example.com" }); const admins = await repo.findAll({ where: { role: "admin" }, sort: { name: "asc" }, limit: 10 }); // Transaction (requires a replica set / Atlas) await repo.withTransaction(async (txRepo) => { const alice = await txRepo.save({ name: "Alice", email: "alice@example.com" }); await txRepo.patchById(alice.id, { name: "Alice A." }); }); ``` ### Vector search quick start [Section titled “Vector search quick start”](#vector-search-quick-start) ```typescript import { VectorRepositoryService } from "@mantlejs/mantle"; import { MongoVectorRepository } from "@mantlejs/mongodb"; interface Doc extends Record { id: string; text: string; category: string; } class DocRepository extends MongoVectorRepository { readonly collectionName = "docs"; override readonly vectorIndexName = "docs_vector_index"; // Atlas Vector Search index name override readonly vectorField = "embedding"; // field the index covers } const docs = new DocRepository(app); await docs.upsertVector(id, embedding, { text: "MongoDB is a document database", category: "db" }); // Top-5 nearest neighbours, optionally pre-filtered ("category" must be a filter field in the index) const hits = await docs.findSimilar(queryEmbedding, 5, { where: { category: "db" } }); // → [{ id, text, category, _score: 0.97 }, …] — HIGHER _score is more similar // Or expose it as a service: POST /docs/similar { "vector": [...], "topK": 5 } app.use("docs", new VectorRepositoryService(docs), { methods: ["find", "get", "create", "update", "patch", "remove", "similar"], }); ``` *** ## API [Section titled “API”](#api) ### `mongodb(config)` [Section titled “mongodb(config)”](#mongodbconfig) Returns a `MantlePlugin`. Call via `app.configure(mongodb(config))`. ```typescript app.configure(mongodb({ uri: "mongodb+srv://…", dbName: "app" })); ``` Side effects: * Creates one `MongoClient` (connects lazily on first operation) and stores it at `app.get("mongoClient")` * Stores the target `Db` at `app.get("mongoDb")` * Wraps `app.teardown()` to close the client #### `MongoConfig` [Section titled “MongoConfig”](#mongoconfig) | Field | Type | Default | Description | | --------------- | -------------------- | ------- | ---------------------------------------------------- | | `uri` | `string` | — | Atlas or self-hosted connection string (required) | | `dbName` | `string` | — | Database to open collections against (required) | | `clientOptions` | `MongoClientOptions` | `{}` | Full driver options (TLS, pool sizing) — passthrough | *** ### `MongoRepository` (abstract class) [Section titled “MongoRepository\ (abstract class)”](#mongorepositoryt-d-abstract-class) Implements `Repository` for MongoDB. Subclasses must declare `collectionName`. | Method | Description | | ---------------------- | ------------------------------------------------------------------------------------------ | | `findAll(params?)` | Query documents with filtering, sorting, offset pagination, projection | | `findById(id)` | Fetch a single record by id; returns `null` if not found | | `save(data)` | Insert a document (`_id` auto-generated unless `data.id` is given) | | `saveAll(data[])` | Batch insert (single `insertMany` call) | | `updateById(id, data)` | Replace the full document (`findOneAndReplace`); throws `NotFound` if absent | | `patchById(id, data)` | Merge partial fields (`$set`); `undefined` values are dropped; throws `NotFound` if absent | | `deleteById(id)` | Delete a record and return it; throws `NotFound` if absent | | `count(params?)` | Count documents matching optional `QueryParams` | | `withTransaction(fn)` | Run repository calls in one MongoDB transaction (replica set required) | | `describe()` | `RepositoryCapabilities` — adapter name, operator set, pagination style | #### Instance properties [Section titled “Instance properties”](#instance-properties) | Property | Type | Default | Description | | ---------------- | ------------ | ------- | --------------------------------------------------------------- | | `collectionName` | `string` | — | Collection to target (abstract — must be declared) | | `timestamps` | `boolean` | `true` | Maintain `createdAt`/`updatedAt` as BSON `Date` fields | | `collection` | `Collection` | — | Protected escape hatch — the driver collection (`$regex`, agg…) | #### `QueryParams.where` operators [Section titled “QueryParams.where operators”](#queryparamswhere-operators) Equality, `null`, `$lt`, `$lte`, `$gt`, `$gte`, `$ne`, `$in`, `$nin`, `$or`, `$and`, `$contains`, and dot-path keys. `$contains` follows the shared jsonb-`@>` conformance semantics (`NESTED_QUERY_CASES` in `@mantlejs/mantle`): scalar operand → array-element match, array operand → `$all`, object operand → recursive dot-path superset. `$like`/`$ilike`/`$notlike` throw `BadRequest`. #### Error mapping [Section titled “Error mapping”](#error-mapping) Driver errors are translated to typed `MantleError` subclasses: | Condition | Error thrown | | --------------------------------------------------------------------------------- | ------------------------ | | Already a `MantleError` (e.g. thrown by a hook, or by a custom repository method) | passed through unchanged | | Duplicate key (`code` `11000` or `11001`) | `Conflict` | | `MongoNetworkError`, `MongoServerSelectionError` | `Unavailable` | | anything else (including a non-`Error` throw) | `GeneralError` | *** ### `MongoVectorRepository` (abstract class) [Section titled “MongoVectorRepository\ (abstract class)”](#mongovectorrepositoryt-d-abstract-class) Extends `MongoRepository` and implements `VectorRepository` via Atlas Vector Search. #### `VectorRepository` methods [Section titled “VectorRepository\ methods”](#vectorrepositoryt-methods) | Method | Description | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | | `findSimilar(vector, topK, params?)` | `$vectorSearch` top-K nearest neighbours; results carry `_score` (higher = more similar) and exclude the embedding field | | `upsertVector(id, vector, data)` | Upsert a record with its embedding (`findOneAndUpdate` + `upsert`) | | `deleteVector(id)` | Delete the record (and its embedding) — same as `deleteById` | #### Additional instance properties [Section titled “Additional instance properties”](#additional-instance-properties) | Property | Type | Default | Description | | --------------------- | -------- | ---------------- | ----------------------------------------------------------------------- | | `vectorIndexName` | `string` | `"vector_index"` | Name of the Atlas Vector Search index on this collection | | `vectorField` | `string` | `"embedding"` | Document field storing the embedding | | `candidateMultiplier` | `number` | `10` | ANN pool: `numCandidates = topK × multiplier` (capped at Atlas’s 10000) | *** ## Types [Section titled “Types”](#types) ```typescript import type { MongoConfig, WhereClause } from "@mantlejs/mongodb"; import { MONGO_OPERATORS, toMongoFilter, toMongoSort, toMongoProjection } from "@mantlejs/mongodb"; ``` | Export | Description | | ------------------- | ---------------------------------------------------------------- | | `MongoConfig` | Options passed to the `mongodb()` plugin | | `MONGO_OPERATORS` | Exactly the `$`-operators the where-clause translator accepts | | `toMongoFilter` | `QueryParams.where` → MongoDB filter document translator | | `toMongoSort` | `QueryParams.sort` → MongoDB sort document (`asc`/`desc` → 1/-1) | | `toMongoProjection` | `QueryParams.select` → MongoDB inclusion projection | *** ## Development [Section titled “Development”](#development) ```bash npx nx build mongodb # compile npx nx test mongodb # run tests npx nx lint mongodb # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build mongodb ``` First publish (scoped packages require `--access public`): ```bash cd packages/mongodb npm publish --access public ``` Subsequent releases — bump `version` in `packages/mongodb/package.json`, then: ```bash cd packages/mongodb npm publish ``` # @mantlejs/neo4j > Neo4j graph database adapter for Mantle JS. Provides Neo4jRepository — an abstract GraphRepository base class that maps Mantle entities to Neo4j nodes, with Cypher query generation from QueryParams.where. Neo4j graph database adapter for [Mantle JS](https://github.com/mantlejs/mantle). Provides `Neo4jRepository` — an abstract `GraphRepository` base class that maps Mantle entities to Neo4j nodes, with Cypher query generation from `QueryParams.where`. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/neo4j neo4j-driver ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Graph model [Section titled “Graph model”](#graph-model) Neo4j stores data as nodes and directed relationships. `Neo4jRepository` maps one Mantle entity type to one Neo4j node label. Each node stores a UUID `id` property plus all other entity fields as node properties. ### QueryParams.where → Cypher WHERE [Section titled “QueryParams.where → Cypher WHERE”](#queryparamswhere--cypher-where) `findNodes(params?)` translates `QueryParams.where` to a parameterised Cypher `WHERE` clause. All standard Mantle operators are supported: equality, `$lt`/`$lte`/`$gt`/`$gte`, `$ne`, `$in`/`$nin`, `$like`/`$ilike`/`$notlike`, `$or`, `$and`. ### Relationships and traversal [Section titled “Relationships and traversal”](#relationships-and-traversal) `createRelationship` creates a directed `(a)-[:TYPE]->(b)` edge between two nodes of the same label. `traverse` walks relationships up to a configurable depth and returns the reached nodes. ### Transactions [Section titled “Transactions”](#transactions) `withTransaction(fn)` wraps a callback in a Neo4j write transaction. A transaction-scoped repository instance is passed to the callback so all operations inside share the same transaction. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { neo4j, Neo4jRepository } from "@mantlejs/neo4j"; interface Person extends Record { id: string; name: string; age: number; } class PersonRepository extends Neo4jRepository { readonly label = "Person"; } const app = mantle().configure( neo4j({ uri: process.env.NEO4J_URI, auth: { username: "neo4j", password: process.env.NEO4J_PASSWORD! }, }), ); const repo = new PersonRepository(app); // Create a node const alice = await repo.createNode({ name: "Alice", age: 30 }); const bob = await repo.createNode({ name: "Bob", age: 25 }); // Create a relationship await repo.createRelationship(alice.id, bob.id, "KNOWS", { since: "2024" }); // Traverse const aliceFriends = await repo.traverse(alice.id, "KNOWS", 1); // Filter nodes const seniors = await repo.findNodes({ where: { age: { $gte: 30 } } }); // Raw Cypher via the adapter-neutral raw() escape hatch const result = await repo.raw( "MATCH (n:Person) WHERE n.name STARTS WITH $prefix RETURN n", { prefix: "A" }, ); ``` *** ## API [Section titled “API”](#api) ### `neo4j(options)` [Section titled “neo4j(options)”](#neo4joptions) Returns a `MantlePlugin`. Call via `app.configure(neo4j(options))`. ```typescript app.configure( neo4j({ uri: "bolt://localhost:7687", // optional — default bolt://localhost:7687 auth: { username: "neo4j", password: "…" }, // required database: "neo4j", // optional — default "neo4j" }), ); ``` Side effects: * Opens a Neo4j `Driver` and stores it at `app.get("neo4j")` * Stores the resolved database name at `app.get("neo4j:database")` #### `Neo4jOptions` [Section titled “Neo4jOptions”](#neo4joptions-1) | Field | Type | Default | Description | | ---------- | ---------------------------------------- | ------------------------------------------ | ---------------------------- | | `uri` | `string` | `NEO4J_URI` env or `bolt://localhost:7687` | Bolt connection URI | | `auth` | `{ username: string; password: string }` | — | Neo4j credentials (required) | | `database` | `string` | `NEO4J_DATABASE` env or `"neo4j"` | Target database name | *** ### `Neo4jRepository` (abstract class) [Section titled “Neo4jRepository\ (abstract class)”](#neo4jrepositoryt-abstract-class) Implements `GraphRepository` from `@mantlejs/mantle`. ```typescript abstract class Neo4jRepository> implements GraphRepository { abstract readonly label: string; } ``` Subclasses must declare `label`. All `GraphRepository` methods are provided as concrete implementations. #### `GraphRepository` methods [Section titled “GraphRepository\ methods”](#graphrepositoryt-methods) | Method | Cypher pattern | | ------------------------------------------------ | ---------------------------------------------------------------------------------- | | `createNode(data)` | `CREATE (n:Label $props) RETURN n` | | `findNodeById(id)` | `MATCH (n:Label {id: $id}) RETURN n` | | `findNodes(params?)` | `MATCH (n:Label) WHERE … RETURN n ORDER BY … SKIP … LIMIT …` | | `createRelationship(fromId, toId, type, props?)` | `MATCH (a:Label {id: $from}), (b:Label {id: $to}) CREATE (a)-[r:TYPE $props]->(b)` | | `traverse(startId, relation, depth?)` | `MATCH (start)-[r:TYPE*1..depth]->(n) RETURN n` | | `deleteNode(id)` | `MATCH (n:Label {id: $id}) DETACH DELETE n` — throws `NotFound` if absent | | `raw(query, params?)` | Raw Cypher passthrough (the `GraphRepository` escape hatch) | #### Error mapping [Section titled “Error mapping”](#error-mapping) The Neo4j driver doesn’t expose a rich exception taxonomy the way DynamoDB or MongoDB do, so `wrapError()` is intentionally simple: | Thrown value | Error thrown | | ------------------------------------------------------------------------------------------------------------ | ------------------------ | | Already a MantleError (e.g. `NotFound` from a missing node, or thrown by a hook or custom repository method) | passed through unchanged | | Any other `Error` | `GeneralError` | | A non-`Error` throw | `GeneralError` | #### `withTransaction(fn)` [Section titled “withTransaction(fn)”](#withtransactionfn) ```typescript await repo.withTransaction(async (txRepo) => { const alice = await txRepo.createNode({ name: "Alice", age: 30 }); await txRepo.createNode({ name: "Bob", age: 25 }); await txRepo.createRelationship(alice.id, "…", "KNOWS"); }); ``` #### Instance properties [Section titled “Instance properties”](#instance-properties) | Property | Type | Default | Description | | ------------ | --------- | ------- | ---------------------------------------------------- | | `label` | `string` | — | **Required.** Neo4j node label for this repository | | `idField` | `string` | `"id"` | Node property used as the entity identifier | | `timestamps` | `boolean` | `true` | Auto-write `createdAt` / `updatedAt` ISO-8601 fields | #### `QueryParams.where` operators [Section titled “QueryParams.where operators”](#queryparamswhere-operators) | Mantle operator | Cypher equivalent | | ------------------------- | ---------------------------------------- | | `{ field: value }` | `n.field = $p` | | `{ field: null }` | `n.field IS NULL` | | `{ field: [a, b] }` | `n.field IN $p` | | `$gt / $gte / $lt / $lte` | `>`, `>=`, `<`, `<=` | | `$ne: value` | `n.field <> $p` | | `$ne: null` | `n.field IS NOT NULL` | | `$in` | `n.field IN $p` | | `$nin` | `NOT n.field IN $p` | | `$like: '%x%'` | `CONTAINS` / `STARTS WITH` / `ENDS WITH` | | `$ilike` | `toLower(n.field) CONTAINS $p` | | `$notlike` | `NOT (n.field CONTAINS $p)` | | `$or` | `(a OR b OR …)` | | `$and` | `(a AND b AND …)` | `$contains` and dot-path nested fields (`"metadata.owner.name"`) are **not supported** — architectural, not a missing translator feature: Neo4j node properties can only hold flat scalars/scalar-arrays, never nested objects, so there’s no nested structure to address or check containment against in the first place. `describe().capabilities.nestedPaths` reports `false` accordingly. *** ### `toNeo4jWhere(where, alias?)` [Section titled “toNeo4jWhere(where, alias?)”](#toneo4jwherewhere-alias) Converts a Mantle `QueryParams.where` clause into a parameterised Cypher `WHERE` expression and parameter map. Used internally by `findNodes()`; exported for writing raw Cypher in a custom repository method. The node alias defaults to `"n"`. ```typescript import { toNeo4jWhere } from "@mantlejs/neo4j"; const { clause, params } = toNeo4jWhere({ age: { $gte: 30 } }); // clause: "n.age >= $_w_0" // params: { _w_0: 30 } ``` *** ## Types [Section titled “Types”](#types) ```typescript import type { Neo4jOptions, WhereClause, WhereResult } from "@mantlejs/neo4j"; ``` | Type | Description | | -------------- | ----------------------------------------------------- | | `Neo4jOptions` | Options passed to `neo4j()` plugin | | `WhereClause` | `QueryParams.where` clause type | | `WhereResult` | Return value of `toNeo4jWhere` — `{ clause, params }` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build neo4j # compile npx nx test neo4j # run tests npx nx lint neo4j # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build neo4j ``` First publish (scoped packages require `--access public`): ```bash cd packages/neo4j npm publish --access public ``` Subsequent releases — bump `version` in `packages/neo4j/package.json`, then: ```bash cd packages/neo4j npm publish ``` # @mantlejs/openapi > OpenAPI 3.1 document generation for Mantle JS — walks your registered services and serves a live spec (and optional Swagger UI) with zero configuration. OpenAPI 3.1 document generation for Mantle JS — walks your registered services and serves a live spec (and optional Swagger UI) with zero configuration. ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/openapi ``` `@mantlejs/mantle` is a peer dependency, and an HTTP transport (`@mantlejs/express`, `@mantlejs/koa`, or `@mantlejs/http`) must be configured first. *** ## Concepts [Section titled “Concepts”](#concepts) ### Introspection-driven [Section titled “Introspection-driven”](#introspection-driven) The plugin consumes `ServiceHandle.describe()` — the same machinery behind the transports’ `/_services` endpoint. For every registered service it reads: * **`methods`** → REST operations (`find` → `GET /path`, `get` → `GET /path/{id}`, `create` → `POST /path`, `update` → `PUT /path/{id}`, `patch` → `PATCH /path/{id}`, `remove` → `DELETE /path/{id}`, custom methods → `POST /path/{method}`). * **`schema`** → the entity schema under `components.schemas` (a TypeBox schema stored via `ServiceOptions.schema` is JSON Schema and is embedded as-is). Services without a schema still appear with a generic `object` schema — missing coverage never skips a service or errors. * **`authRequired`** → operations are marked with `bearerAuth` security when an auth hook (e.g. `authenticate("jwt")` from `@mantlejs/auth`) is registered in `before.all`. The `bearerAuth` security scheme is added to `components.securitySchemes` when at least one service requires it. * **`capabilities`** → when the service exposes repository capabilities (e.g. `RepositoryService`), `find` responses are documented as the `Paginated` envelope and the supported query operators are listed in the operation description. ### Live document [Section titled “Live document”](#live-document) The document is rebuilt on every request to `specPath`, so hooks and services registered after startup are always reflected. ### Configure order [Section titled “Configure order”](#configure-order) `openapi()` mounts its routes through the transport-neutral `"http:router"` contract, so configure it **after** the transport and **before** registering services: ```typescript const app = mantle() .configure(express()) .configure(openapi()); ``` *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle, RepositoryService } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { openapi } from "@mantlejs/openapi"; const app = mantle() .configure(express()) .configure( openapi({ docsPath: "/docs", info: { title: "My API", version: "1.0.0" }, }), ); app.use("users", new RepositoryService(new UserRepository(app), { schema: userSchema }), { schema: userSchema, }); app.listen(3030); // GET /openapi.json → OpenAPI 3.1 document // GET /docs → Swagger UI ``` *** ## API [Section titled “API”](#api) ### `openapi(options?)` [Section titled “openapi(options?)”](#openapioptions) Returns a `MantlePlugin`. Mounts `GET ` serving the OpenAPI 3.1 JSON document, and optionally `GET ` serving a Swagger UI page (loaded from the unpkg CDN — the package itself stays dependency-free). #### `OpenApiOptions` [Section titled “OpenApiOptions”](#openapioptions-1) | Field | Type | Default | Description | | ---------- | ------------------------------------------------------------ | ------------------------------------------- | ------------------------------------------ | | `specPath` | `string` | `"/openapi.json"` | Route the JSON document is served from. | | `docsPath` | `string` | — | When set, serves Swagger UI at this route. | | `info` | `{ title?: string; version?: string; description?: string }` | `{ title: "Mantle API", version: "0.0.0" }` | OpenAPI `info` object. | Throws `GeneralError` at configure time when no HTTP transport has registered `"http:router"`. ### `buildOpenApiDocument(descriptors, info?)` [Section titled “buildOpenApiDocument(descriptors, info?)”](#buildopenapidocumentdescriptors-info) The pure document assembler, exported for tooling that wants the document without mounting routes: ```typescript import { buildOpenApiDocument } from "@mantlejs/openapi"; const doc = buildOpenApiDocument( ["users", "orders"].map((path) => app.service(path).describe()), { title: "My API", version: "1.0.0" }, ); ``` Every document also includes a `MantleError` schema referenced by each operation’s `default` error response. *** ## Types [Section titled “Types”](#types) ```typescript import type { OpenApiOptions, OpenApiInfo } from "@mantlejs/openapi"; ``` | Type | Description | | ---------------- | --------------------------------------------- | | `OpenApiOptions` | Options object accepted by `openapi()` | | `OpenApiInfo` | The document `info` metadata (title, version) | *** ## Development [Section titled “Development”](#development) ```bash npx nx build openapi # compile npx nx test openapi # run tests npx nx lint openapi # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build openapi ``` First publish (scoped packages require `--access public`): ```bash cd packages/openapi npm publish --access public ``` Subsequent releases — bump `version` in `packages/openapi/package.json`, then: ```bash cd packages/openapi npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/openapi npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/openapi --registry http://localhost:4873 ``` # @mantlejs/pinecone > Pinecone vector database adapter for Mantle JS. Provides PineconeRepository — an abstract Repository base class that stores entities as Pinecone vectors with metadata. Pinecone vector database adapter for [Mantle JS](https://github.com/mantlejs/mantle). Provides `PineconeRepository` — an abstract `Repository` base class that stores entities as Pinecone vectors with metadata. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/pinecone @pinecone-database/pinecone ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### The `pinecone()` plugin [Section titled “The pinecone() plugin”](#the-pinecone-plugin) `pinecone(config)` is a Mantle plugin. It creates a `Pinecone` client (from `@pinecone-database/pinecone`) and stores it on the application at `app.get("pinecone")`. `PineconeRepository` reads this client from the app in its constructor, so `.configure(pinecone(...))` must run before any repository is instantiated. ### Vector storage [Section titled “Vector storage”](#vector-storage) Pinecone is a managed vector database. Each record is stored as a high-dimensional float array (the “embedding”) plus a metadata object. `PineconeRepository` maps a Mantle entity to a Pinecone vector by serialising all non-`idField` fields as metadata. ### Embedding generation [Section titled “Embedding generation”](#embedding-generation) `save()` and `saveAll()` write a **zero-vector placeholder** (sized to `vectorDimension`) — they never call an embedding model. Attach a real embedding separately with `upsertVector(id, vector, data)`, using a vector from your embedding model of choice (OpenAI, Cohere, a local model, etc.). This keeps the adapter model-agnostic: it never generates embeddings itself. ### Namespace [Section titled “Namespace”](#namespace) A single Pinecone index can be partitioned into isolated namespaces. Declare `namespace` as a required property on your repository subclass to scope all operations to that namespace. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { pinecone, PineconeRepository } from "@mantlejs/pinecone"; interface Document extends Record { id: string; title: string; body: string; } class DocumentRepository extends PineconeRepository { readonly indexName = "documents"; readonly namespace = "prod"; readonly vectorDimension = 1536; // must match your embedding model's output size } const app = mantle().configure(pinecone({ apiKey: process.env.PINECONE_API_KEY })); const repo = new DocumentRepository(app); app.use("/documents", new DocumentService(repo)); app.listen(3030); // save() only writes a zero-vector placeholder — attach the real embedding separately: const doc = await repo.save({ title: "Hello", body: "World" }); const vector = await myEmbeddingModel.embed(`${doc.title} ${doc.body}`); await repo.upsertVector(doc.id, vector, doc); ``` *** ## API [Section titled “API”](#api) ### `pinecone(config?)` [Section titled “pinecone(config?)”](#pineconeconfig) Returns a `MantlePlugin`. Call via `app.configure(pinecone(config))`. ```typescript app.configure( pinecone({ apiKey: process.env.PINECONE_API_KEY, // optional — falls back to the PINECONE_API_KEY env var }), ); ``` Side effects: * Stores the `Pinecone` client at `app.get("pinecone")` #### `PineconeConfig` [Section titled “PineconeConfig”](#pineconeconfig-1) `Partial` — every option accepted by the underlying `@pinecone-database/pinecone` client’s constructor (`apiKey`, custom fetch implementation, etc.) is passed straight through. *** ### `PineconeRepository` (abstract class) [Section titled “PineconeRepository\ (abstract class)”](#pineconerepositoryt-d-abstract-class) An abstract base class that implements `VectorRepository` for Pinecone. Requires `pinecone()` to be configured first — the constructor reads the client via `app.get("pinecone")`. ```typescript abstract class PineconeRepository, D = Partial> implements VectorRepository { constructor(app: MantleApplication); abstract readonly indexName: string; abstract readonly namespace: string; abstract readonly vectorDimension: number; } ``` #### Properties [Section titled “Properties”](#properties) | Property | Type | Default | Description | | ----------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------------------- | | `indexName` | `string` | — | Pinecone index name **(required)** | | `namespace` | `string` | — | Pinecone namespace to scope operations to **(required)** | | `vectorDimension` | `number` | — | Vector dimensionality of the index — sizes the zero-vector placeholder written by `save()`/`saveAll()` **(required)** | | `idField` | `string` | `"id"` | Metadata key that holds the record id | | `timestamps` | `boolean` | `true` | Auto-write `createdAt` / `updatedAt` ISO-8601 strings | #### `VectorRepository` methods [Section titled “VectorRepository\ methods”](#vectorrepositoryt-methods) | Method | Description | | ------------------------------------ | ------------------------------------------------------------ | | `findSimilar(vector, topK, params?)` | ANN search — returns the top-K most similar records | | `upsertVector(id, vector, data)` | Attach (or replace) the embedding for a record | | `deleteVector(id)` | Delete the record and its embedding (alias for `deleteById`) | Every `findSimilar` result carries the Pinecone match score as `_score` — **higher is more similar** (similarity, not distance, for the common `cosine`/`dotproduct` metrics). #### Inherited from `Repository` [Section titled “Inherited from Repository\”](#inherited-from-repositoryt-d) | Method | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `findAll(params?)` | List all records, with optional `QueryParams` filtering | | `findPage(params?)` | One page via Pinecone’s native `paginationToken` as the `cursor`. `where`/`skip`/`sort` are rejected with `BadRequest` — the list API cannot filter or order | | `findById(id)` | Fetch a single record by ID; returns `null` if not found | | `save(data)` | Insert a new record with a **zero-vector placeholder** (sized to `vectorDimension`) — does not generate a real embedding; call `upsertVector()` for that | | `saveAll(data[])` | Batch insert multiple records, each with a zero-vector placeholder | | `updateById(id, data)` | Replace all metadata for a record; throws `NotFound` if absent | | `patchById(id, data)` | Update individual metadata fields for a record; throws `NotFound` if absent | | `deleteById(id)` | Delete a record; throws `NotFound` if absent | | `count(params?)` | Count records matching optional `QueryParams` | #### Error mapping [Section titled “Error mapping”](#error-mapping) Pinecone’s SDK doesn’t expose a rich exception taxonomy the way DynamoDB or MongoDB do, so `wrapError()` is intentionally simple: | Thrown value | Error thrown | | -------------------------------------------------------------------------------------------------------------- | ------------------------ | | Already a MantleError (e.g. `NotFound` from a missing record, or thrown by a hook or custom repository method) | passed through unchanged | | Any other `Error` | `GeneralError` | | A non-`Error` throw | `GeneralError` | *** ### `toPineconeFilter(where)` [Section titled “toPineconeFilter(where)”](#topineconefilterwhere) Converts a Mantle `QueryParams.where` clause into a Pinecone metadata filter object. Used internally by `findAll()` and `findSimilar()`; exported for writing raw SDK calls inside a custom repository method. | Operator | Pinecone filter | | ---------------------------------- | ---------------------------- | | `{ field: value }` | `{ field: { $eq: value } }` | | `{ field: null }` | `{ field: { $eq: null } }` | | `{ field: [a, b] }` | `{ field: { $in: [a, b] } }` | | `{ field: { $lt/$lte/$gt/$gte } }` | passed through unchanged | | `{ field: { $ne/$in/$nin } }` | passed through unchanged | | `{ $or: [...] }` | `{ $or: [mapped…] }` | | `{ $and: [...] }` | `{ $and: [mapped…] }` | `$like` and other pattern-matching operators are unsupported — Pinecone metadata filters have no wildcard matching — and throw `BadRequest` naming the operator. `$contains` and dot-path nested fields (`"metadata.owner.name"`) are also **not supported** — architectural, not a missing translator feature: Pinecone metadata values are flat scalars or scalar arrays only, so there’s no nested structure to address or check containment against. `describe().capabilities.nestedPaths` reports `false` accordingly. ```typescript import { toPineconeFilter } from "@mantlejs/pinecone"; const filter = toPineconeFilter({ category: "docs", views: { $gt: 100 } }); // { category: { $eq: "docs" }, views: { $gt: 100 } } ``` *** ## Types [Section titled “Types”](#types) ```typescript import type { PineconeConfig, WhereClause } from "@mantlejs/pinecone"; ``` | Type | Description | | ---------------- | ----------------------------------- | | `PineconeConfig` | Options passed to `pinecone()` | | `WhereClause` | Input type for `toPineconeFilter()` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build pinecone # compile npx nx test pinecone # run tests npx nx lint pinecone # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build pinecone ``` First publish (scoped packages require `--access public`): ```bash cd packages/pinecone npm publish --access public ``` Subsequent releases — bump `version` in `packages/pinecone/package.json`, then: ```bash cd packages/pinecone npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/pinecone npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/pinecone --registry http://localhost:4873 ``` # @mantlejs/qdrant > Qdrant vector database adapter for Mantle JS. Provides QdrantRepository — an abstract VectorRepository base class that stores entities as Qdrant points with payload metadata and auto-creates collections on first write. Qdrant vector database adapter for [Mantle JS](https://github.com/mantlejs/mantle). Provides `QdrantRepository` — an abstract `VectorRepository` base class that stores entities as Qdrant points with payload metadata and auto-creates collections on first write. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/qdrant @qdrant/js-client-rest ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Vector storage [Section titled “Vector storage”](#vector-storage) Qdrant is an open-source, self-hosted vector database optimised for high-performance similarity search. Each record is stored as a Qdrant point: a high-dimensional float array (the embedding) plus a JSON payload. `QdrantRepository` maps a Mantle entity to a Qdrant point by using the entity `id` as the point ID and serialising all other fields as the point payload. ### Zero-vector placeholders [Section titled “Zero-vector placeholders”](#zero-vector-placeholders) Embedding generation is intentionally decoupled from the repository. Calling `save()` or `saveAll()` inserts a zero-vector placeholder so the record is immediately findable by payload filters. Call `upsertVector(id, vector, data)` to attach the real embedding once it is available. ### Collections [Section titled “Collections”](#collections) A Qdrant database is organised into named collections. Subclasses declare `readonly collectionName: string`. The collection is created automatically with cosine-distance configuration on the first write — no manual setup required for development or testing. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { qdrant, QdrantRepository } from "@mantlejs/qdrant"; const app = mantle().configure( qdrant({ url: process.env.QDRANT_URL, apiKey: process.env.QDRANT_API_KEY }), ); interface Document extends Record { id: string; title: string; body: string; } class DocumentRepository extends QdrantRepository { readonly collectionName = "documents"; readonly vectorSize = 1536; // must match the embedding model output } const repo = new DocumentRepository(app); // Insert a record (zero-vector placeholder) const doc = await repo.save({ title: "Intro to Qdrant", body: "..." }); // Attach a real embedding once generated await repo.upsertVector(doc.id, embeddingVector, { title: doc.title, body: doc.body }); // Similarity search const similar = await repo.findSimilar(queryVector, 10, { where: { category: "guide" } }); ``` *** ## API [Section titled “API”](#api) ### `qdrant(config?)` [Section titled “qdrant(config?)”](#qdrantconfig) Returns a `MantlePlugin`. Call via `app.configure(qdrant(config))`. ```typescript app.configure( qdrant({ url: process.env.QDRANT_URL, // optional — default "http://localhost:6333" apiKey: process.env.QDRANT_API_KEY, // optional — for Qdrant Cloud }), ); ``` Side effects: * Creates a `QdrantClient` and stores it at `app.get("qdrant")` #### `QdrantConfig` [Section titled “QdrantConfig”](#qdrantconfig-1) | Field | Type | Default | Description | | -------- | -------- | ------------------------------------------- | ----------------------------------------- | | `url` | `string` | `QDRANT_URL` env or `http://localhost:6333` | Qdrant server URL | | `apiKey` | `string` | `QDRANT_API_KEY` env | API key for Qdrant Cloud (omit for local) | All other fields are passed through to `QdrantClient` from `@qdrant/js-client-rest`. *** ### `QdrantRepository` (abstract class) [Section titled “QdrantRepository\ (abstract class)”](#qdrantrepositoryt-d-abstract-class) Implements `VectorRepository` for Qdrant. ```typescript abstract class QdrantRepository, D = Partial> implements VectorRepository { abstract readonly collectionName: string; abstract readonly vectorSize: number; } ``` Subclasses must declare `collectionName` and `vectorSize`. All `VectorRepository` and `Repository` methods are provided as concrete implementations. #### `VectorRepository` methods [Section titled “VectorRepository\ methods”](#vectorrepositoryt-methods) | Method | Description | | ------------------------------------ | ------------------------------------------------------------ | | `findSimilar(vector, topK, params?)` | ANN search — returns the top-K most similar records | | `upsertVector(id, vector, data)` | Attach (or replace) the embedding for a record | | `deleteVector(id)` | Delete the record and its embedding (alias for `deleteById`) | Every `findSimilar` result carries the Qdrant match score as `_score`. **Higher is more similar** for `Cosine` and `Dot` collections (the default here is `Cosine`); for `Euclid` collections Qdrant reports a distance, where lower is more similar. #### `Repository` methods [Section titled “Repository\ methods”](#repositoryt-methods) | Method | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `findAll(params?)` | Scroll through records with optional filtering & sorting | | `findPage(params?)` | One page via native scroll cursors — pass the returned `cursor` back for the next page. `skip`/`sort` are rejected with `BadRequest` (Qdrant cannot combine `order_by` with scroll cursors) | | `findById(id)` | Fetch a single record by ID; returns `null` if not found | | `save(data)` | Insert with a zero-vector placeholder | | `saveAll(data[])` | Batch insert (single Qdrant upsert call) | | `updateById(id, data)` | Replace the full payload, preserving the stored vector; throws `NotFound` if absent | | `patchById(id, data)` | Merge partial fields, preserving the stored vector; throws `NotFound` if absent | | `deleteById(id)` | Delete a record; throws `NotFound` if absent | | `count(params?)` | Exact count of records matching optional `QueryParams` | #### Error mapping [Section titled “Error mapping”](#error-mapping) Qdrant’s client doesn’t expose a rich exception taxonomy the way DynamoDB or MongoDB do, so `wrapError()` is intentionally simple: | Thrown value | Error thrown | | -------------------------------------------------------------------------------------------------------------- | ------------------------ | | Already a MantleError (e.g. `NotFound` from a missing record, or thrown by a hook or custom repository method) | passed through unchanged | | Any other `Error` | `GeneralError` | | A non-`Error` throw | `GeneralError` | #### Instance properties [Section titled “Instance properties”](#instance-properties) | Property | Type | Default | Description | | ---------------- | --------- | ------- | ------------------------------------------- | | `collectionName` | `string` | — | **Required.** Qdrant collection name | | `vectorSize` | `number` | — | **Required.** Embedding dimensionality | | `idField` | `string` | `"id"` | Payload key used as the point ID | | `timestamps` | `boolean` | `true` | Auto-write `createdAt` / `updatedAt` fields | #### `QueryParams.where` operators [Section titled “QueryParams.where operators”](#queryparamswhere-operators) | Mantle operator | Qdrant filter | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `{ field: value }` | `match.value` | | `{ field: null }` | `is_null` | | `{ field: [a, b] }` | `match.any` | | `$gt / $gte / $lt / $lte` | `range` | | `$ne` | `must_not match.value` | | `$ne: null` | `must_not is_null` | | `$in` | `match.any` | | `$nin` | `must_not match.any` | | `$contains` (scalar) | `match.value` — Qdrant already matches “any element equals this value” when the field is an array, so this is the same shape as plain equality | | `$contains` (array) | one ANDed `match.value` per element — every element required, not “any of” | | `$contains` (object) | recurses into each key as a nested dot-path, flattening to leaf `match.value`/ANDed-array conditions the same way | | `$or` | `should` | | `$and` | `must` | Nested fields (`"metadata.owner.name"`) are addressed natively — Qdrant’s payload filter `key` accepts dot-path strings directly, for every operator above, no translation needed. *** ## Types [Section titled “Types”](#types) ```typescript import type { QdrantConfig, WhereClause } from "@mantlejs/qdrant"; ``` | Type | Description | | -------------- | ----------------------------------- | | `QdrantConfig` | Options passed to `qdrant()` plugin | | `WhereClause` | `QueryParams.where` clause type | *** ## Development [Section titled “Development”](#development) ```bash npx nx build qdrant # compile npx nx test qdrant # run tests npx nx lint qdrant # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build qdrant ``` First publish (scoped packages require `--access public`): ```bash cd packages/qdrant npm publish --access public ``` Subsequent releases — bump `version` in `packages/qdrant/package.json`, then: ```bash cd packages/qdrant npm publish ``` # @mantlejs/react > React hooks for Mantle JS services, built on TanStack Query v5 over the @mantlejs/client SDK. React hooks for [Mantle JS](https://github.com/mantlejs/mantle) services, built on [TanStack Query v5](https://tanstack.com/query/latest) over the [`@mantlejs/client`](/packages/client/) SDK. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/react @mantlejs/client @tanstack/react-query react ``` `react`, `@tanstack/react-query`, and `@mantlejs/client` are peer dependencies — the package ships no React runtime or query cache of its own. *** ## Concepts [Section titled “Concepts”](#concepts) ### Bindings, not a client [Section titled “Bindings, not a client”](#bindings-not-a-client) `@mantlejs/react` does not talk to a Mantle server itself. It wraps a `MantleClient` instance (from `@mantlejs/client`) and exposes it to a React component tree: query hooks for `find`/`get`, mutation hooks for `create`/`update`/`patch`/`remove`, all dispatched through the client’s REST transport (auth headers, 401 refresh-retry, and typed errors included). ### Provider at the root, hooks in the tree [Section titled “Provider at the root, hooks in the tree”](#provider-at-the-root-hooks-in-the-tree) `MantleProvider` owns the client instance and a TanStack `QueryClient` (it creates a default one when none is passed). Hooks anywhere below it resolve both from context — no prop drilling, no module-level singletons. ### Deterministic query keys [Section titled “Deterministic query keys”](#deterministic-query-keys) Query keys follow `[service, method, ...identifiers]`: | Hook call | Query key | | ---------------------------------- | ----------------------------------- | | `useFind("messages")` | `["messages", "find"]` | | `useFind("messages", params)` | `["messages", "find", params]` | | `useGet("messages", "42")` | `["messages", "get", "42"]` | | `useGet("messages", "42", params)` | `["messages", "get", "42", params]` | Invalidation always targets the `[service]` prefix, so `find` and `get` caches for a service invalidate together. ### Real-time cache invalidation [Section titled “Real-time cache invalidation”](#real-time-cache-invalidation) When the client was created with the `socket` option, `useFind` and `useGet` subscribe to the service’s `created` / `updated` / `patched` / `removed` events and call `queryClient.invalidateQueries({ queryKey: [service] })` when one arrives. Subscriptions are reference-counted per service — one listener set no matter how many hooks are mounted, removed when the last hook unmounts. Opt out per hook with `realtime: false`. Event delivery is at-most-once (see the [`@mantlejs/sync` README](/packages/sync/)): events emitted while the socket is disconnected are gone. To bound staleness, `MantleProvider` listens for the client’s `'reconnect'` event and invalidates **every** query on re-connect, forcing mounted queries to refetch. ### Mutations do not invalidate [Section titled “Mutations do not invalidate”](#mutations-do-not-invalidate) `useCreate` / `useUpdate` / `usePatch` / `useRemove` rely on the server’s socket event to trigger invalidation. Without a socket, or for optimistic updates, pass TanStack’s `onSuccess` / `onMutate` callbacks. *** ## Quick start [Section titled “Quick start”](#quick-start) ```tsx import { mantle } from "@mantlejs/client"; import { MantleProvider, useCreate, useFind } from "@mantlejs/react"; const client = mantle({ url: "http://localhost:3030", socket: {} }); export function App() { return ( ); } interface Message { id: number; text: string; } function Messages() { const { data, isPending } = useFind("messages", { query: { $limit: 25, $sort: { id: "desc" } } }); const create = useCreate("messages"); if (isPending) return

Loading…

; const messages = Array.isArray(data) ? data : (data?.data ?? []); return ( <>
    {messages.map((m) => (
  • {m.text}
  • ))}
); } ``` When another client creates a message, the server broadcasts `messages created` over Socket.IO, the hook invalidates `["messages"]`, and the list refetches — no wiring needed. *** ## API [Section titled “API”](#api) ### `MantleProvider` [Section titled “MantleProvider”](#mantleprovider) Wraps children in a `QueryClientProvider` and stores the `MantleClient` in context. | Prop | Type | Default | Description | | ------------- | -------------- | ------------------- | ------------------------------------------------------ | | `client` | `MantleClient` | required | The `@mantlejs/client` instance hooks dispatch through | | `queryClient` | `QueryClient` | `new QueryClient()` | Custom TanStack Query client | ### `useMantleClient(): MantleClient` [Section titled “useMantleClient(): MantleClient”](#usemantleclient-mantleclient) The client from the nearest `MantleProvider`. Throws a descriptive `Error` when used outside one. Use it for anything the hooks don’t cover — `client.authenticate(...)`, `client.service("docs").similar(...)`, auth events. ### Query hooks [Section titled “Query hooks”](#query-hooks) ```typescript useFind(service, params?, options?); // GET /:service → UseQueryResult, MantleClientError> useGet(service, id, params?, options?); // GET /:service/:id → UseQueryResult ``` `options` accepts every TanStack `useQuery` option except `queryKey`/`queryFn`, plus: | Option | Type | Default | Description | | ---------- | ---------------------------------------------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------- | | `realtime` | `boolean` (`useGet`), `boolean \| RealtimePatchOptions` (`useFind`) | `true` when a socket is configured | Socket-driven cache invalidation for this hook, or in-place patching (below) | #### In-place realtime (`useFind` only) [Section titled “In-place realtime (useFind only)”](#in-place-realtime-usefind-only) The default realtime behavior invalidates the service’s queries, so every event triggers a refetch. With `realtime: { mode: "patch" }`, `useFind` writes `created` / `updated` / `patched` / `removed` events straight into its own cached result instead. The list changes in place with no request, and a `Paginated` envelope’s `total` stays in step. ```typescript const { data } = useFind( "comments", { query: { articleId, $sort: { createdAt: 1 } } }, { realtime: { mode: "patch", matches: (comment) => comment.articleId === articleId, // mirror the query's filter insert: "end", // mirror its $sort: where created records go idField: "id", // default }, }, ); ``` Events carry the full record, but the client can’t re-run the server’s `where`/`$sort`, which is what `matches` and `insert` are for. A changed record that stops matching is removed, and one that starts matching is inserted. Only this hook’s cache entry is touched. Events missed while the socket was disconnected are recovered by the provider’s invalidate-everything on reconnect. ### Mutation hooks [Section titled “Mutation hooks”](#mutation-hooks) ```typescript useCreate(service, options?); // variables: Partial → POST /:service useUpdate(service, options?); // variables: { id: Id; data: Partial } → PUT /:service/:id usePatch(service, options?); // variables: { id: Id; data: Partial } → PATCH /:service/:id useRemove(service, options?); // variables: Id → DELETE /:service/:id ``` `options` accepts every TanStack `useMutation` option except `mutationFn`. Errors are typed `MantleClientError`. *** ## Types [Section titled “Types”](#types) | Type | Description | | ------------------------- | ---------------------------------------------------------------------------------- | | `MantleProviderProps` | Props for `MantleProvider` (`client`, `queryClient?`, `children`) | | `MantleQueryOptions` | The `realtime?: boolean` extension accepted by `useGet` | | `MantleFindOptions` | `useFind`’s extension: `realtime?: boolean \| RealtimePatchOptions` | | `RealtimePatchOptions` | `{ mode: "patch"; idField?; matches?; insert? }` — in-place realtime for `useFind` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build react # compile npx nx test react # run tests npx nx lint react # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build react ``` First publish (scoped packages require `--access public`): ```bash cd packages/react npm publish --access public ``` Subsequent releases — bump `version` in `packages/react/package.json`, then: ```bash cd packages/react npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/react npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/react --registry http://localhost:4873 ``` # @mantlejs/schema > TypeBox schema definition, Ajv validation, and data resolution for Mantle JS. Re-exports the TypeBox builder so a single schema definition produces both a TypeScript type and a validated runtime contract. Uses Ajv with ajv-formats for RFC-compliant string format validation out of the box. TypeBox schema definition, Ajv validation, and data resolution for [Mantle JS](https://github.com/mantlejs/mantle). Re-exports the TypeBox builder so a single schema definition produces both a TypeScript type and a validated runtime contract. Uses Ajv with `ajv-formats` for RFC-compliant string format validation out of the box. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/schema ``` `@sinclair/typebox`, `ajv`, and `ajv-formats` ship as direct dependencies — no separate install needed. *** ## Concepts [Section titled “Concepts”](#concepts) ### Why TypeBox + Ajv [Section titled “Why TypeBox + Ajv”](#why-typebox--ajv) The two libraries split the job along their strengths instead of overlapping: **TypeBox is the authoring surface** — one schema definition yields both the TypeScript type and a JSON Schema object, with no codegen step. **Ajv is the validation engine** — it JIT-compiles each schema into an optimized validator function (cached per schema, see `validate()` below) and implements the JSON Schema spec, including string formats, more completely than TypeBox’s own built-in `Value.Check` runtime checker, which skips format validation entirely. Using TypeBox alone would mean either no format validation or hand-rolling it; using Ajv alone would mean hand-writing JSON Schema with no inferred TypeScript type. Combined, you define a schema once and get compile-time types plus spec-compliant, high-performance runtime validation from it — the trade-off is that Ajv becomes a hard runtime dependency (see the BYOV escape hatch below if that’s unacceptable). ### One schema, two outputs [Section titled “One schema, two outputs”](#one-schema-two-outputs) TypeBox generates a TypeScript type and a JSON Schema object from the same definition, with no build step: ```typescript import { Type, Static } from "@mantlejs/schema"; const UserSchema = Type.Object({ id: Type.String({ format: "uuid" }), email: Type.String({ format: "email" }), name: Type.String({ minLength: 1, maxLength: 100 }), password: Type.Optional(Type.String({ minLength: 8 })), createdAt: Type.String({ format: "date-time" }), updatedAt: Type.String({ format: "date-time" }), }); type User = Static; // ^ { id: string; email: string; name: string; password?: string; createdAt: string; updatedAt: string } ``` ### RFC-compliant format validation [Section titled “RFC-compliant format validation”](#rfc-compliant-format-validation) `@mantlejs/schema` uses **Ajv** with **`ajv-formats`** for validation. Common JSON Schema string formats are validated against the relevant RFCs out of the box: | Format | RFC | | --------------- | ------------------ | | `email` | RFC 5321/5322 | | `date-time` | RFC 3339 | | `date` | ISO 8601 | | `uuid` | RFC 4122 | | `uri` | RFC 3986 | | `ipv4` / `ipv6` | RFC 791 / RFC 4291 | No setup required — formats are validated automatically when you add them to a TypeBox schema. ### Validation vs resolution [Section titled “Validation vs resolution”](#validation-vs-resolution) * **`validate()`** — runs in a `before` hook. Validates incoming data against the schema using Ajv. Throws `Unprocessable` with field-level errors on failure. * **`resolver()`** — runs in an `after` hook. Transforms the result field-by-field: strip sensitive data, compute derived fields, or join related records. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { Type, Static, validate, resolver } from "@mantlejs/schema"; const UserSchema = Type.Object({ id: Type.String({ format: "uuid" }), email: Type.String({ format: "email" }), name: Type.String({ minLength: 1 }), password: Type.Optional(Type.String()), }); type User = Static; app.use("/users", new UserService(new UserRepository(app)), { methods: ["find", "get", "create", "update", "patch", "remove"], schema: UserSchema, }); app.service("users").hooks({ before: { create: [validate(UserSchema)], update: [validate(UserSchema)], }, after: { all: [ resolver({ password: () => undefined, // returning undefined removes the field }), ], }, }); ``` > `UserSchema` appears in three places above, but only one of them does anything at request time. The `schema` passed to `app.use()` is **inert metadata** — it’s stored for tooling introspection (`describe()`, `GET /_services`, OpenAPI/MCP generation later) and never validates or transforms a request on its own. Actual enforcement comes only from the `validate(UserSchema)` hooks in `before`, and the `password` strip only happens because of the explicit `resolver()` in `after`. Passing `schema` to `app.use()` without also wiring `validate()` hooks means nothing is validated. (Exception: `RepositoryService` reads `schema.properties[key].type` to coerce `where`-clause query values — see “String coercion” under `RepositoryService` in the `@mantlejs/mantle` README — but that’s query coercion, not body validation.) *** ## API [Section titled “API”](#api) ### `validate(schema, options?)` [Section titled “validate(schema, options?)”](#validateschema-options) Before hook. Validates `context.data` (default), `context.result`, or `context.params.query` against a TypeBox schema using Ajv. Throws `Unprocessable` with field-level error details on failure. Compiled validators are cached per schema object reference — Ajv compiles once on first use, not on every request. ```typescript function validate(schema: T, options?: ValidateOptions): HookFunction; interface ValidateOptions { /** What to validate. Default: 'data' */ target?: "data" | "result" | "query"; /** Coerce string inputs to their schema types (e.g. "42" → 42). Default: false */ coerce?: boolean; /** Strip properties not declared in the schema. Default: false */ stripAdditional?: boolean; } ``` `coerce` uses TypeBox’s `Value.Convert` and `stripAdditional` uses `Value.Clean` as pre-processing steps before Ajv validates. The transformed value is written back to the context target. ```typescript app.service("users").hooks({ before: { create: [validate(UserSchema)], // coerce query string params ("limit=10") to numbers before validating find: [validate(QuerySchema, { target: "query", coerce: true })], }, }); ``` **Validation error format:** ```typescript // HTTP 422 Unprocessable Entity { name: "Unprocessable", code: 422, message: "Validation failed", data: { errors: [ { field: "/email", message: "must match format \"email\"" }, { field: "/name", message: "must NOT have fewer than 1 characters" }, ], }, } ``` *** ### `validate(validator, options?)` — BYOV [Section titled “validate(validator, options?) — BYOV”](#validatevalidator-options--byov) Pass a function as the first argument to bypass Ajv entirely and use any validation library. The function receives the raw data and returns either an array of `{ field, message }` errors (triggers `Unprocessable`) or `null`/`undefined`/empty array (passes). ```typescript type ValidatorFn = (data: unknown) => Array<{ field: string; message: string }> | null | undefined; function validate(validator: ValidatorFn, options?: Pick): HookFunction; ``` `coerce` and `stripAdditional` do not apply — the custom validator handles any transforms. ```typescript import { z } from "zod"; import type { ValidatorFn } from "@mantlejs/schema"; const UserZod = z.object({ email: z.string().email(), name: z.string().min(1) }); const zodValidator: ValidatorFn = (data) => { const result = UserZod.safeParse(data); if (result.success) return null; return result.error.issues.map((i) => ({ field: "/" + i.path.join("/"), message: i.message, })); }; app.service("users").hooks({ before: { create: [validate(zodValidator)] }, }); ``` > Ajv is a hard dependency of `@mantlejs/schema` and is always loaded. BYOV replaces the validation logic, not the package dependency. If removing Ajv entirely is a requirement, skip `@mantlejs/schema` and throw `Unprocessable` from a plain hook function. *** ### `querySyntax(entitySchema, options?)` [Section titled “querySyntax(entitySchema, options?)”](#querysyntaxentityschema-options) Derives a **query** schema from an entity schema, following the `RepositoryService` query convention: every entity field accepts either a bare value or an operator object (`$gt`, `$gte`, `$lt`, `$lte`, `$ne`, `$in`, `$nin` — plus `$like`, `$notlike`, `$ilike` on string fields), and the reserved keys `$limit`, `$skip`, `$sort`, `$select`, `$or`, `$and` are typed to match. Unknown fields and unknown operators are rejected. A plain entity schema rejects operator objects (`{ age: { $gt: 21 } }` is not a valid `User`) — use `querySyntax` for the `query` target instead: ```typescript import { querySyntax, validate } from "@mantlejs/schema"; const UserQuery = querySyntax(UserSchema, { maxLimit: 100 }); app.service("users").hooks({ before: { find: [validate(UserQuery, { target: "query", coerce: true })] }, }); ``` With `coerce: true`, query-string values are converted to the field’s declared type before validation — `?age[$gt]=21&$limit=10` validates with `21` and `10` as numbers, ready for `RepositoryService`. | Option | Type | Default | Description | | ---------- | -------- | --------- | -------------------------------- | | `maxLimit` | `number` | unlimited | Upper bound enforced on `$limit` | *** ### `resolver(map, options?)` [Section titled “resolver(map, options?)”](#resolvermap-options) After hook. Iterates the field map and calls each resolver with the current field value, the full record, the hook context, and an optional shared context. Returning `undefined` removes the field entirely. Supports single records (`T`), arrays (`T[]`), and paginated results (`Paginated`). ```typescript type FieldResolver = ( value: T[K] | undefined, data: T, context: HookContext, shared: C, ) => Promise | T[K] | undefined; type ResolverMap = { [K in keyof T]?: FieldResolver; }; interface ResolverOptions { createContext?: (record: T, ctx: HookContext) => Promise | C; } function resolver(map: ResolverMap, options?: ResolverOptions): HookFunction; ``` ```typescript app.service("users").hooks({ after: { all: [ resolver({ password: () => undefined, // strip sensitive field fullName: (_, data) => `${data.firstName} ${data.lastName}`, // compute derived field avatar: async (_, data) => fetchAvatar(data.id), // async lookup }), ], }, }); ``` #### Shared context [Section titled “Shared context”](#shared-context) `createContext` is called once per record before field resolvers run. Its return value is passed to every field resolver as the fourth argument. Use this to perform a single expensive async lookup shared across multiple fields: ```typescript resolver( { role: (_, data, ctx, shared) => shared.isAdmin ? data.role : "viewer", badge: (_, data, ctx, shared) => shared.isAdmin ? "admin" : null, }, { createContext: async (record) => ({ isAdmin: await checkAdmin(record.id) }), }, ) ``` For array and paginated results, `createContext` is called once per element. Existing field resolvers that ignore the fourth argument continue to work — TypeScript allows functions with fewer parameters. *** ## Schema registration on a service [Section titled “Schema registration on a service”](#schema-registration-on-a-service) Pass `schema` to `app.use()` to store it for tooling introspection (CLI code generation, future OpenAPI output): ```typescript app.use("/users", new UserService(repo), { methods: ["find", "get", "create", "update", "patch", "remove"], schema: UserSchema, }); app.service("users").schema; // → TSchema | undefined ``` *** ## TypeBox re-exports [Section titled “TypeBox re-exports”](#typebox-re-exports) `@mantlejs/schema` re-exports the TypeBox builder and common type helpers: ```typescript import { Type, Static, FormatRegistry } from "@mantlejs/schema"; import type { TSchema, TObject, TString, TNumber, TBoolean, TArray, TOptional } from "@mantlejs/schema"; ``` *** ## Types [Section titled “Types”](#types) ```typescript import type { ValidateOptions, ValidatorFn, ResolverMap, FieldResolver, ResolverOptions } from "@mantlejs/schema"; import type { TSchema, Static } from "@mantlejs/schema"; ``` | Type | Description | | ------------------------ | --------------------------------------------------------- | | `TSchema` | Base TypeBox schema type | | `Static` | Infers the TypeScript type from a schema | | `ValidateOptions` | Options for the TypeBox + Ajv overload of `validate()` | | `ValidatorFn` | Custom validator function signature for the BYOV overload | | `ResolverMap` | Field-keyed map of resolver functions | | `FieldResolver` | Single field resolver function signature | | `ResolverOptions` | Options for `resolver()`, including `createContext` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build schema # compile npx nx test schema # run tests npx nx lint schema # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build schema ``` First publish (scoped packages require `--access public`): ```bash cd packages/schema npm publish --access public ``` Subsequent releases — bump `version` in `packages/schema/package.json`, then: ```bash cd packages/schema npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/schema npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/schema --registry http://localhost:4873 ``` # @mantlejs/socketio > Socket.IO transport adapter for Mantle JS. Bridges Mantle services to Socket.IO clients, enabling real-time bidirectional communication over WebSockets with the same hook pipeline used by REST adapters. Socket.IO transport adapter for [Mantle JS](https://github.com/mantlejs/mantle). Bridges Mantle services to Socket.IO clients, enabling real-time bidirectional communication over WebSockets with the same hook pipeline used by REST adapters. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/socketio socket.io ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Transport adapter [Section titled “Transport adapter”](#transport-adapter) `socketio()` is a Mantle plugin that registers a Socket.IO server alongside your existing HTTP transport. Incoming socket events are mapped to service method calls (`find`, `get`, `create`, `update`, `patch`, `remove`) and routed through the full hook pipeline — the same `before`, `after`, and `error` hooks run regardless of whether the call arrives over HTTP or WebSocket. ### Real-time events [Section titled “Real-time events”](#real-time-events) When a service method mutates data (`create`, `update`, `patch`, `remove`), Mantle emits an internal `service:event`; `@mantlejs/socketio` listens for it and can re-emit it to clients as `{path} {event}` (e.g. `"messages created"`). Delivery is **opt-in** — it only reaches connections targeted by a channel publisher (see “Channels” below). Configuring the transport alone does not broadcast anything. ### Provider [Section titled “Provider”](#provider) Calls arriving via Socket.IO set `params.provider = "socket.io"` in `HookContext`. Hooks can inspect this to apply transport-specific behavior (e.g. skip authentication for internal calls). ### Channels [Section titled “Channels”](#channels) Channels are named sets of socket connections. They control which clients receive a real-time event when a service mutates data. Broadcasting is **opt-in**: until you register a publisher on a service (or globally via `app.publish()`), no events are sent to any client. When a publisher is registered, the transport calls it after each mutating method (`create`, `update`, `patch`, `remove`) and broadcasts the result to every connection returned by the publisher. Each socket gets a **connection object** — a plain `Record` that persists for the lifetime of that socket. It is passed to channel publishers and available as `params.connection` inside hooks, making it a natural place to stash per-socket state (e.g. the authenticated user after login). *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { socketio } from "@mantlejs/socketio"; const app = mantle().configure(express()).configure(socketio()); app.listen(3030); ``` **Client (browser or Node)** ```typescript import { io } from "socket.io-client"; const socket = io("http://localhost:3030"); // Call a service method socket.emit("find", "messages", {}, (error: unknown, result: unknown) => { console.log(result); }); // Listen for real-time events socket.on("messages created", (message: unknown) => { console.log("New message:", message); }); ``` > The `messages created` event above only fires once a publisher is registered for the `messages` service — see [Channels quick start](#channels-quick-start) below. Without one, mutations still happen but nothing is broadcast to any client. *** ## Channels quick start [Section titled “Channels quick start”](#channels-quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { socketio } from "@mantlejs/socketio"; const app = mantle().configure(express()).configure(socketio()); // 1. On connection, join every socket to the "everyone" channel app.on("connection", (connection) => { app.channel("everyone").join(connection); }); // 2. Broadcast all service events to the "everyone" channel app.publish(() => app.channel("everyone")); app.listen(3030); ``` **User-scoped channels** — join each socket to a per-user channel and broadcast only to that user: ```typescript app.on("connection", (connection) => { // Store the user on the connection object so publishers can read it const userId = (connection["user"] as { id: number } | undefined)?.id; if (userId) { app.channel(`user/${userId}`).join(connection); } }); app.service("messages").publish((data, ctx) => { const userId = (ctx.params.user as { id: number } | undefined)?.id; return userId ? app.channel(`user/${userId}`) : null; }); ``` **Filtered channels** — send an event only to connections that pass a predicate: ```typescript app.service("messages").publish((data) => app.channel("everyone").filter( (_data, connection) => (connection["role"] as string) === "admin", ), ); ``` **Multiple channels** — return an array to broadcast to the union of all connections (each socket receives the event at most once): ```typescript app.service("notifications").publish((data, ctx) => [ app.channel("admins"), app.channel(`user/${(ctx.params.user as { id: number }).id}`), ]); ``` *** ## API [Section titled “API”](#api) ### `socketio(options?)` [Section titled “socketio(options?)”](#socketiooptions) Returns a `MantlePlugin`. Call via `app.configure(socketio(options))`. Must be configured after an HTTP transport (`express()`, `koa()`, or `http()`) — the plugin attaches to the Node server the transport emits via the `"http:server"` app event when `listen()` is called. ```typescript app.configure( socketio({ path: "/socket.io", timeout: 30000, cors: true, }), ); ``` #### `SocketioOptions` [Section titled “SocketioOptions”](#socketiooptions-1) | Field | Type | Default | Description | | --------------- | ------------------------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `path` | `string` | `"/socket.io"` | URL path the Socket.IO server listens on | | `timeout` | `number` | `20000` | Ping timeout in ms before closing idle connections (engine.io’s built-in default; only overridden when set) | | `cors` | `boolean \| CorsOptions` | disabled | CORS on the socket.io handshake. **Set this whenever the client’s `url` is a different origin than the page** — e.g. a frontend dev server and an API dev server on different ports. `true` reflects `Origin` and allows the CRUD verbs; pass a `CorsOptions` (same shape as `@mantlejs/express`’s `cors` option) to restrict it. Socket.IO attaches its own request listener ahead of the HTTP transport’s middleware, so the transport’s own `cors` option (`express({ cors: true })` and friends) never runs for `/socket.io/*` — this is the only way to allow a cross-origin socket connection. | | `serverOptions` | `Partial` | — | Additional Socket.IO `Server` constructor options. An explicit `serverOptions.cors` overrides the `cors` option above. | *** ### `app.channel(name)` [Section titled “app.channel(name)”](#appchannelname) Returns a `MantleChannel` for the given name. Accepts a single name or an array of names. Channels are created lazily — calling `app.channel("foo")` twice returns the same underlying channel. ```typescript // Single channel const ch = app.channel("admins"); // Combined channel — deduplicates connections automatically const ch = app.channel(["admins", "moderators"]); ``` Requires `socketio()` to be configured. Throws `GeneralError` if called before the plugin is registered. *** ### `app.publish(publisher)` [Section titled “app.publish(publisher)”](#apppublishpublisher) Registers a **global publisher** — called for every service that does not have its own per-service publisher. Returns `app` for chaining. ```typescript app.publish((data, ctx) => { // ctx: { app, path, params } return app.channel("everyone"); }); ``` Return `null`, `undefined`, or `void` to suppress broadcasting for a specific event. *** ### `app.service(path).publish(publisher)` [Section titled “app.service(path).publish(publisher)”](#appservicepathpublishpublisher) Registers a **per-service publisher**. Takes precedence over the global publisher for that service. Returns the service handle for chaining. ```typescript app.service("messages").publish((data, ctx) => { const userId = (ctx.params.user as { id: number } | undefined)?.id; return userId ? app.channel(`user/${userId}`) : null; }); ``` *** ### `params.rooms` — per-call broadcast targeting [Section titled “params.rooms — per-call broadcast targeting”](#paramsrooms--per-call-broadcast-targeting) A before hook (or an internal caller) can set `params.rooms` to a channel name or array of names. When set, the mutation’s event is broadcast **only to those channels**, bypassing any configured publisher. No publisher is required — `rooms` is its own opt-in. ```typescript app.service("messages").hooks({ before: { create: [ async (ctx) => { ctx.params.rooms = [`conversation/${(ctx.data as { conversationId: string }).conversationId}`]; return ctx; }, ], }, }); ``` *** ### `app.channel(name).join(connection)` / `.leave(connection)` [Section titled “app.channel(name).join(connection) / .leave(connection)”](#appchannelnamejoinconnection--leaveconnection) Adds or removes a connection from the channel. Both methods return `this` for chaining. Joining a connection that is already in the channel is a no-op. ```typescript app.on("connection", (connection) => { app.channel("everyone").join(connection); }); app.on("disconnect", (connection) => { // Connections are removed from all channels automatically on disconnect — // you only need leave() for manual mid-session departures. app.channel("vip").leave(connection); }); ``` *** ### `app.channel(name).filter(fn)` [Section titled “app.channel(name).filter(fn)”](#appchannelnamefilterfn) Returns a new `MantleChannel` that wraps the original but only delivers events to connections for which `fn(data, connection)` returns `true`. Does not mutate the original channel. ```typescript app.service("alerts").publish(() => app.channel("everyone").filter( (data, connection) => (connection["role"] as string) !== "guest", ), ); ``` Filters compose: calling `.filter()` on an already-filtered channel ANDs the predicates. *** ### `connection` / `disconnect` events [Section titled “connection / disconnect events”](#connection--disconnect-events) ```typescript app.on("connection", (connection: Record) => { // Fired when a new socket connects. `connection` is the per-socket state object. // This is where you join the socket to its initial channels. }); app.on("disconnect", (connection: Record) => { // Fired when a socket disconnects. // The socket is already removed from all channels automatically. }); ``` The `connection` object is the same reference passed to publishers via the channel’s `connections` array, to filter predicates as the second argument, and to hooks as `params.connection`. *** ## Types [Section titled “Types”](#types) ```typescript import type { SocketioOptions } from "@mantlejs/socketio"; import type { MantleChannel, ChannelPublisher, PublishContext } from "@mantlejs/mantle"; ``` | Type | Description | | --------------------- | --------------------------------------------------------------------------------- | | `SocketioOptions` | Options passed to `socketio()` | | `MantleChannel` | Named set of socket connections; supports `join`, `leave`, and `filter` | | `ChannelPublisher` | `(data, ctx: PublishContext) => MantleChannel \| MantleChannel[] \| null \| void` | | `PublishContext` | `{ app, path, params }` — context passed to every publisher call | *** ## Development [Section titled “Development”](#development) ```bash npx nx build socketio # compile npx nx test socketio # run tests npx nx lint socketio # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build socketio ``` First publish (scoped packages require `--access public`): ```bash cd packages/socketio npm publish --access public ``` Subsequent releases — bump `version` in `packages/socketio/package.json`, then: ```bash cd packages/socketio npm publish ``` # @mantlejs/storage > File upload plugin for Mantle JS. Parses multipart/form-data requests via busboy, writes files to local disk (or a custom adapter), validates MIME types and file sizes, and attaches uploaded file metadata to context.data through a before hook. File upload plugin for [Mantle JS](https://github.com/mantlejs/mantle). Parses `multipart/form-data` requests via [busboy](https://github.com/mscdex/busboy), writes files to local disk (or a custom adapter), validates MIME types and file sizes, and attaches uploaded file metadata to `context.data` through a `before` hook. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/storage busboy ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### Plugin + hook model [Section titled “Plugin + hook model”](#plugin--hook-model) `upload()` is a Mantle plugin that stores upload configuration on the app. It does **not** parse uploads by itself — parsing happens in the `handleUpload()` **before hook**, which you attach to whichever service methods accept files. ### Storage adapters [Section titled “Storage adapters”](#storage-adapters) The built-in `diskStorage()` adapter writes files to a local directory. The `StorageAdapter` interface covers the full file lifecycle — `store()`, `retrieve()`, `delete()`, and an optional `getSignedUrl()` — so cloud adapters can be plugged in via the `storage` option. See [`@mantlejs/storage-s3`](/packages/storage-s3/) and [`@mantlejs/storage-gcs`](/packages/storage-gcs/) for cloud-backed implementations. ### How files reach the hook [Section titled “How files reach the hook”](#how-files-reach-the-hook) `@mantlejs/express` and `@mantlejs/koa` both put the raw Node `IncomingMessage` at `params.request`. `handleUpload()` reads from that to parse the multipart body. This means the hook only runs in HTTP contexts — internal service calls with no `provider` receive no upload processing (and no errors). **`@mantlejs/http` is not currently supported.** Its router never sets `params.request`, so `handleUpload()` treats every request on that transport the same as an internal call — uploads silently pass through (or throw `BadRequest("Upload field '' is required")` if `required: true`), indistinguishable from a genuine internal call. If you need file uploads, configure `@mantlejs/express` or `@mantlejs/koa`. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { upload, diskStorage, handleUpload } from "@mantlejs/storage"; const app = mantle() .configure(express()) .configure( upload({ maxFileSize: 5 * 1024 * 1024, // 5 MB allowedMimeTypes: ["image/jpeg", "image/png"], storage: diskStorage({ destination: "./uploads" }), }), ); app.use("photos", new PhotoService(new PhotoRepository(app))); app.service("photos").hooks({ before: { create: [handleUpload("photo", { required: true })], }, }); app.listen(3030); ``` **Upload a file** ```http POST /photos Content-Type: multipart/form-data; boundary=----boundary ------boundary Content-Disposition: form-data; name="photo"; filename="avatar.jpg" Content-Type: image/jpeg ------boundary-- ``` The service’s `create` method receives `context.data.photo` as an `UploadedFile` object: ```json { "fieldname": "photo", "originalname": "avatar.jpg", "mimetype": "image/jpeg", "size": 42317, "path": "./uploads/1718900000000-avatar.jpg", "key": "1718900000000-avatar.jpg" } ``` *** ## API [Section titled “API”](#api) ### `upload(config?)` [Section titled “upload(config?)”](#uploadconfig) Returns a `MantlePlugin`. Call via `app.configure(upload(config))`. ```typescript app.configure( upload({ maxFileSize: 10 * 1024 * 1024, // optional — default 10 MB allowedMimeTypes: ["image/jpeg"], // optional — default [] (all types allowed) storage: diskStorage({ destination: "./uploads" }), // optional — default local disk at ./uploads }), ); ``` Side effects: * Stores the `UploadEngine` at `app.get("upload")` #### `UploadConfig` [Section titled “UploadConfig”](#uploadconfig-1) | Field | Type | Default | Description | | ------------------ | ---------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | `maxFileSize` | `number` | `10485760` (10 MB) | Maximum file size in bytes. Files that exceed this limit cause `handleUpload()` to throw `BadRequest`. | | `allowedMimeTypes` | `string[]` | `[]` | Allowed MIME types. An empty array means all types are accepted. | | `storage` | `StorageAdapter` | `diskStorage({ destination: "./uploads" })` | Where files are written. | *** ### `diskStorage(config)` [Section titled “diskStorage(config)”](#diskstorageconfig) Built-in storage adapter that writes uploaded files to a local directory. ```typescript import { diskStorage } from "@mantlejs/storage"; const storage = diskStorage({ destination: "./uploads", // Optional: custom filename. Receives the file info; returns the filename string. // Default: `${Date.now()}-${originalname}` filename: (info) => `${info.fieldname}-${Date.now()}.${info.originalname.split(".").pop()}`, }); ``` * Creates `destination` (including parent directories) if it does not exist. * Returns the absolute path of the saved file in `UploadedFile.path`, and the filename (relative to `destination`) in `UploadedFile.key`. * `retrieve(key)` and `delete(key)` take that same `key` value and resolve it against `destination`; both reject with `BadRequest` if the resolved path would escape `destination` (e.g. a `key` containing `../`), and `delete()` rejects with `NotFound` if the file does not exist. * `getSignedUrl` is intentionally omitted — there is no direct-download concept for local disk. #### `DiskStorageConfig` [Section titled “DiskStorageConfig”](#diskstorageconfig-1) | Field | Type | Default | Description | | ------------- | ---------------------------------- | ------------------------------- | ---------------------------------------- | | `destination` | `string` | — | Directory to write files into (required) | | `filename` | `(info: UploadFileInfo) => string` | `${Date.now()}-${originalname}` | Filename transform | *** ### Retrieving and deleting stored files [Section titled “Retrieving and deleting stored files”](#retrieving-and-deleting-stored-files) Every `StorageAdapter` supports reading back and removing a previously stored file via the `key` returned from `store()`: ```typescript const engine = app.get("upload"); const stream = await engine.storage.retrieve(uploadedFile.key); // Readable await engine.storage.delete(uploadedFile.key); if (engine.storage.getSignedUrl) { const url = await engine.storage.getSignedUrl(uploadedFile.key, { expiresIn: 300 }); } ``` `getSignedUrl` is optional on the interface — disk storage does not implement it, so always feature-detect before calling it. #### `GetSignedUrlOptions` [Section titled “GetSignedUrlOptions”](#getsignedurloptions) | Field | Type | Default | Description | | ----------- | -------- | ----------------------------------------- | ------------------------------- | | `expiresIn` | `number` | adapter-specific (S3/GCS default to 900s) | URL validity window, in seconds | *** ### `handleUpload(field, options?)` [Section titled “handleUpload(field, options?)”](#handleuploadfield-options) A `before` hook factory. Parses the multipart body, validates the file, stores it via the configured adapter, writes an `UploadedFile` to `context.data[field]`, and merges any other ordinary (non-file) form fields sent alongside it into `context.data` too — e.g. a hidden `articleId` field on the same upload form, so the record a file belongs to can travel with it in one request. ```typescript app.service("photos").hooks({ before: { create: [handleUpload("photo", { required: true })], patch: [handleUpload("photo")], // optional — silently skips if absent }, }); ``` ```http POST /photos Content-Type: multipart/form-data; boundary=----boundary ------boundary Content-Disposition: form-data; name="albumId" 42 ------boundary Content-Disposition: form-data; name="photo"; filename="avatar.jpg" Content-Type: image/jpeg ------boundary-- ``` `create` receives `context.data.albumId === "42"` (a string — same as a URL-encoded form or query string; coerce it yourself, e.g. `Number(context.data.albumId)`) alongside `context.data.photo`. #### Behaviour [Section titled “Behaviour”](#behaviour) | Condition | Result | | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | `params.request` is absent (internal call, or an unsupported transport) and `required: false` | Returns context unchanged | | `params.request` is absent (internal call, or an unsupported transport) and `required: true` | Throws `BadRequest` | | File field not present in form and `required: false` | Merges any other form fields into `context.data`; the target field stays absent | | File field not present in form and `required: true` | Throws `BadRequest` | | MIME type not in `allowedMimeTypes` | Throws `BadRequest` | | File exceeds `maxFileSize` | Throws `BadRequest` | | File valid | Writes `UploadedFile` to `context.data[field]` and merges other form fields into `context.data` | Unrelated *file* fields are discarded — when multiple files share the same field name, or a file is sent under a different field name than the one `handleUpload()` was called with, only the targeted field’s file is captured. Ordinary text fields are always captured, regardless of where they fall relative to the file part in the multipart body. #### `HandleUploadOptions` [Section titled “HandleUploadOptions”](#handleuploadoptions) | Field | Type | Default | Description | | ---------- | --------- | ------- | ---------------------------------------- | | `required` | `boolean` | `false` | Whether the upload field must be present | *** ### `UploadedFile` [Section titled “UploadedFile”](#uploadedfile) The shape of an uploaded file after `handleUpload()` processes it. ```typescript interface UploadedFile { fieldname: string; // the form field name (e.g. "photo") originalname: string; // original filename from the client mimetype: string; // MIME type (e.g. "image/jpeg") size: number; // file size in bytes path: string; // resolved path on disk (local) or display URL (cloud adapter) key: string; // the adapter-native key — pass this back to retrieve()/delete()/getSignedUrl() } ``` *** ## Writing a custom storage adapter [Section titled “Writing a custom storage adapter”](#writing-a-custom-storage-adapter) Implement `StorageAdapter` to redirect uploads to cloud storage, a database, or any other destination: ```typescript import type { GetSignedUrlOptions, StorageAdapter, UploadFileInfo, UploadedFile } from "@mantlejs/storage"; import type { Readable } from "node:stream"; const customStorage: StorageAdapter = { async store(stream: Readable, info: UploadFileInfo): Promise { const key = `uploads/${Date.now()}-${info.originalname}`; // stream to the backend ... return { fieldname: info.fieldname, originalname: info.originalname, mimetype: info.mimetype, size: bytesUploaded, path: `https://my-bucket.example.com/${key}`, key, }; }, async retrieve(key: string): Promise { // return a Readable for the stored object }, async delete(key: string): Promise { // remove the stored object }, // optional — omit entirely if the backend has no direct-download concept async getSignedUrl(key: string, options?: GetSignedUrlOptions): Promise { // return a time-limited download URL }, }; app.configure(upload({ storage: customStorage })); ``` `@mantlejs/storage-s3` and `@mantlejs/storage-gcs` are reference implementations of this interface backed by AWS S3 and Google Cloud Storage respectively. *** ## Types [Section titled “Types”](#types) ```typescript import type { UploadConfig, UploadedFile, UploadFileInfo, StorageAdapter, DiskStorageConfig, HandleUploadOptions, GetSignedUrlOptions, UploadEngine, } from "@mantlejs/storage"; ``` | Type | Description | | --------------------- | ---------------------------------------------------------------------------------------------- | | `UploadConfig` | Options passed to `upload()` | | `UploadedFile` | Metadata returned after a file is stored | | `UploadFileInfo` | Subset of file info available before storage (fieldname, originalname, mimetype) | | `StorageAdapter` | Interface for custom storage backends (`store`, `retrieve`, `delete`, optional `getSignedUrl`) | | `DiskStorageConfig` | Options for `diskStorage()` | | `HandleUploadOptions` | Options for `handleUpload()` | | `GetSignedUrlOptions` | Options for `StorageAdapter.getSignedUrl()` | | `UploadEngine` | The `{ maxFileSize, allowedMimeTypes, storage }` object stored at `app.get("upload")` | *** ## Error reference [Section titled “Error reference”](#error-reference) | Error | Code | When thrown | | ------------ | ---- | ----------------------------------------------------------------------------------------- | | `BadRequest` | 400 | File field required but absent | | `BadRequest` | 400 | MIME type not in `allowedMimeTypes` | | `BadRequest` | 400 | File exceeds `maxFileSize` | | `BadRequest` | 400 | `diskStorage()` `retrieve`/`delete` called with a key that resolves outside `destination` | | `NotFound` | 404 | `diskStorage()` `delete()` called with a key that does not exist | *** ## Development [Section titled “Development”](#development) ```bash npx nx build storage # compile npx nx test storage # run tests npx nx lint storage # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build storage ``` First publish (scoped packages require `--access public`): ```bash cd packages/storage npm publish --access public ``` Subsequent releases — bump `version` in `packages/storage/package.json`, then: ```bash cd packages/storage npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/storage npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/storage --registry http://localhost:4873 ``` # @mantlejs/storage-gcs > Google Cloud Storage adapter for @mantlejs/storage. Uploads files to a GCS bucket using the official @google-cloud/storage SDK, streaming directly without buffering into memory. Google Cloud Storage adapter for [`@mantlejs/storage`](/packages/storage/). Uploads files to a GCS bucket using the official `@google-cloud/storage` SDK, streaming directly without buffering into memory. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/storage-gcs @google-cloud/storage ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### StorageAdapter [Section titled “StorageAdapter”](#storageadapter) `@mantlejs/storage` delegates file persistence to a `StorageAdapter`. This package provides `gcsStorage()` — a factory that returns an adapter wired to a GCS bucket. Pass it as the `storage` option when calling `upload()`. ### Streaming upload [Section titled “Streaming upload”](#streaming-upload) The adapter pipes the incoming `Readable` stream directly into a GCS write stream via `file.createWriteStream()`. No temporary files or in-memory buffering — memory usage stays flat regardless of file size. ### Read, delete, and signed URLs [Section titled “Read, delete, and signed URLs”](#read-delete-and-signed-urls) Beyond `store()`, the adapter implements the rest of the `StorageAdapter` interface: `retrieve(key)` returns `file.createReadStream()`, `delete(key)` removes the object (mapping a 404 to `NotFound`), and `getSignedUrl(key, options?)` returns a v4 signed URL via `file.getSignedUrl()`. All three take the exact `key` value returned on `UploadedFile.key` from `store()`. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { upload } from "@mantlejs/storage"; import { gcsStorage } from "@mantlejs/storage-gcs"; const app = mantle() .configure(express()) .configure( upload({ storage: gcsStorage({ bucket: process.env.GCS_BUCKET!, keyPrefix: "uploads/", }), maxFileSize: 10 * 1024 * 1024, // 10 MB allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"], }), ); ``` **Use in a service hook:** ```typescript import { handleUpload } from "@mantlejs/storage"; app.service("avatars").hooks({ before: { create: [handleUpload("file")], }, }); ``` **Custom key (timestamp + original name):** ```typescript gcsStorage({ bucket: "my-bucket", key: (info) => `media/${Date.now()}-${info.originalname}`, }); ``` **Explicit service-account key file:** ```typescript gcsStorage({ bucket: "my-bucket", projectId: "my-gcp-project", keyFilename: "/path/to/service-account-key.json", }); ``` **Public objects (HTTPS URL in `UploadedFile.path`):** ```typescript gcsStorage({ bucket: "my-public-bucket", public: true, }); ``` **Private objects (default, `gs://` URI in `UploadedFile.path`):** ```typescript gcsStorage({ bucket: "my-private-bucket", // public: false (default) }); ``` **Retrieve, delete, and generate a signed URL:** ```typescript const storage = gcsStorage({ bucket: "my-bucket" }); const uploaded = await storage.store(stream, info); const readStream = await storage.retrieve(uploaded.key); const url = await storage.getSignedUrl(uploaded.key, { expiresIn: 300 }); // 5 minutes await storage.delete(uploaded.key); ``` *** ## API [Section titled “API”](#api) ### `gcsStorage(config)` [Section titled “gcsStorage(config)”](#gcsstorageconfig) Returns a `GcsStorageAdapter` that implements the `StorageAdapter` interface from `@mantlejs/storage`. ```typescript import { gcsStorage } from "@mantlejs/storage-gcs"; const storage = gcsStorage({ bucket: "my-bucket", }); ``` #### `GcsStorageConfig` [Section titled “GcsStorageConfig”](#gcsstorageconfig-1) | Field | Type | Default | Description | | ------------- | ---------------------------------- | ------------------------------- | ------------------------------------------------------------------ | | `bucket` | `string` | — | GCS bucket name (required) | | `projectId` | `string` | ADC default | GCP project ID | | `keyFilename` | `string` | Application Default Credentials | Path to a service-account key JSON file | | `keyPrefix` | `string` | `""` | Prefix prepended to every generated object name, e.g. `"uploads/"` | | `public` | `boolean` | `false` | When `true`, makes the object public-read and returns an HTTPS URL | | `key` | `(file: UploadFileInfo) => string` | `-` | Fully override the object name from file metadata | `UploadedFile.path` after upload: * `public: true` → `https://storage.googleapis.com/{bucket}/{key}` * `public: false` → `gs://{bucket}/{key}` (use for signed URL generation or server-side access) `UploadedFile.key` is the resolved GCS object name — pass it back into `retrieve()`, `delete()`, or `getSignedUrl()`. *** ### `GcsStorageAdapter#retrieve(key)` [Section titled “GcsStorageAdapter#retrieve(key)”](#gcsstorageadapterretrievekey) Returns the object’s `Readable` via `file.createReadStream()`. ### `GcsStorageAdapter#delete(key)` [Section titled “GcsStorageAdapter#delete(key)”](#gcsstorageadapterdeletekey) Removes the object via `file.delete()`. Throws `NotFound` if the underlying GCS API responds with a 404. ### `GcsStorageAdapter#getSignedUrl(key, options?)` [Section titled “GcsStorageAdapter#getSignedUrl(key, options?)”](#gcsstorageadaptergetsignedurlkey-options) Returns a v4 signed read URL via `file.getSignedUrl()`. | Field | Type | Default | Description | | ----------- | -------- | ------------------ | ------------------------------- | | `expiresIn` | `number` | `900` (15 minutes) | URL validity window, in seconds | *** ## Types [Section titled “Types”](#types) ```typescript import type { GcsStorageConfig } from "@mantlejs/storage-gcs"; import { GcsStorageAdapter, gcsStorage } from "@mantlejs/storage-gcs"; ``` | Export | Kind | Description | | ------------------- | -------- | ----------------------------------------------- | | `gcsStorage` | function | Factory — returns a `GcsStorageAdapter` | | `GcsStorageAdapter` | class | The adapter class (implements `StorageAdapter`) | | `GcsStorageConfig` | type | Configuration passed to `gcsStorage()` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build storage-gcs # compile npx nx test storage-gcs # run tests npx nx lint storage-gcs # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build storage-gcs ``` First publish (scoped packages require `--access public`): ```bash cd packages/storage-gcs npm publish --access public ``` Subsequent releases — bump `version` in `packages/storage-gcs/package.json`, then: ```bash cd packages/storage-gcs npm publish ``` # @mantlejs/storage-s3 > AWS S3 storage adapter for @mantlejs/storage. Uploads files to an S3 bucket using the official AWS SDK v3 with multipart support for files of any size. AWS S3 storage adapter for [`@mantlejs/storage`](/packages/storage/). Uploads files to an S3 bucket using the official AWS SDK v3 with multipart support for files of any size. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/storage-s3 @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### StorageAdapter [Section titled “StorageAdapter”](#storageadapter) `@mantlejs/storage` delegates file persistence to a `StorageAdapter`. This package provides `s3Storage()` — a factory that returns an adapter wired to an S3 bucket. Pass it as the `storage` option when calling `upload()`. ### Multipart upload [Section titled “Multipart upload”](#multipart-upload) The adapter uses `@aws-sdk/lib-storage` `Upload`, which automatically handles multipart uploads for large files and single-part uploads for smaller ones. The incoming `Readable` stream is piped directly — no buffering into memory. ### Read, delete, and signed URLs [Section titled “Read, delete, and signed URLs”](#read-delete-and-signed-urls) Beyond `store()`, the adapter implements the rest of the `StorageAdapter` interface: `retrieve(key)` streams an object back via `GetObjectCommand`, `delete(key)` removes it via `DeleteObjectCommand`, and `getSignedUrl(key, options?)` returns a time-limited download URL via `@aws-sdk/s3-request-presigner`. All three take the exact `key` value returned on `UploadedFile.key` from `store()`. *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { upload } from "@mantlejs/storage"; import { s3Storage } from "@mantlejs/storage-s3"; const app = mantle() .configure(express()) .configure( upload({ storage: s3Storage({ bucket: process.env.S3_BUCKET!, region: process.env.AWS_REGION!, keyPrefix: "uploads/", }), maxFileSize: 10 * 1024 * 1024, // 10 MB allowedMimeTypes: ["image/jpeg", "image/png", "image/webp"], }), ); ``` **Use in a service hook:** ```typescript import { handleUpload } from "@mantlejs/storage"; app.service("avatars").hooks({ before: { create: [handleUpload("file")], }, }); ``` **Custom key (timestamp + original name):** ```typescript s3Storage({ bucket: "my-bucket", region: "us-east-1", key: (info) => `media/${Date.now()}-${info.originalname}`, }); ``` **Explicit credentials (CI / non-default credential chain):** ```typescript s3Storage({ bucket: "my-bucket", region: "us-east-1", credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, }, }); ``` **Public-read objects:** ```typescript s3Storage({ bucket: "my-public-bucket", region: "us-east-1", acl: "public-read", }); ``` **Retrieve, delete, and generate a signed URL:** ```typescript const storage = s3Storage({ bucket: "my-bucket", region: "us-east-1" }); const uploaded = await storage.store(stream, info); const readStream = await storage.retrieve(uploaded.key); const url = await storage.getSignedUrl(uploaded.key, { expiresIn: 300 }); // 5 minutes await storage.delete(uploaded.key); ``` *** ## API [Section titled “API”](#api) ### `s3Storage(config)` [Section titled “s3Storage(config)”](#s3storageconfig) Returns an `S3StorageAdapter` that implements the `StorageAdapter` interface from `@mantlejs/storage`. ```typescript import { s3Storage } from "@mantlejs/storage-s3"; const storage = s3Storage({ bucket: "my-bucket", region: "us-east-1", }); ``` #### `S3StorageConfig` [Section titled “S3StorageConfig”](#s3storageconfig-1) | Field | Type | Default | Description | | ------------- | ---------------------------------- | -------------------------------- | ---------------------------------------------------------- | | `bucket` | `string` | — | S3 bucket name (required) | | `region` | `string` | — | AWS region, e.g. `"us-east-1"` (required) | | `credentials` | `{ accessKeyId, secretAccessKey }` | AWS SDK default credential chain | Explicit AWS credentials | | `keyPrefix` | `string` | `""` | Prefix prepended to every generated key, e.g. `"uploads/"` | | `acl` | `"private" \| "public-read"` | — | Canned ACL applied to each upload | | `key` | `(file: UploadFileInfo) => string` | `-` | Fully override the object key from file metadata | `UploadedFile.path` after upload: `https://{bucket}.s3.{region}.amazonaws.com/{key}`. `UploadedFile.key` is the resolved S3 object key — pass it back into `retrieve()`, `delete()`, or `getSignedUrl()`. *** ### `S3StorageAdapter#retrieve(key)` [Section titled “S3StorageAdapter#retrieve(key)”](#s3storageadapterretrievekey) Fetches the object via `GetObjectCommand` and returns its body as a `Readable`. Throws `NotFound` if the object does not exist. ### `S3StorageAdapter#delete(key)` [Section titled “S3StorageAdapter#delete(key)”](#s3storageadapterdeletekey) Removes the object via `DeleteObjectCommand`. S3 deletes are idempotent — deleting a key that doesn’t exist does not throw. ### `S3StorageAdapter#getSignedUrl(key, options?)` [Section titled “S3StorageAdapter#getSignedUrl(key, options?)”](#s3storageadaptergetsignedurlkey-options) Returns a presigned GET URL via `@aws-sdk/s3-request-presigner`. | Field | Type | Default | Description | | ----------- | -------- | ------------------ | ------------------------------- | | `expiresIn` | `number` | `900` (15 minutes) | URL validity window, in seconds | *** ## Types [Section titled “Types”](#types) ```typescript import type { S3StorageConfig } from "@mantlejs/storage-s3"; import { S3StorageAdapter, s3Storage } from "@mantlejs/storage-s3"; ``` | Export | Kind | Description | | ------------------ | -------- | ----------------------------------------------- | | `s3Storage` | function | Factory — returns an `S3StorageAdapter` | | `S3StorageAdapter` | class | The adapter class (implements `StorageAdapter`) | | `S3StorageConfig` | type | Configuration passed to `s3Storage()` | *** ## Development [Section titled “Development”](#development) ```bash npx nx build storage-s3 # compile npx nx test storage-s3 # run tests npx nx lint storage-s3 # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build storage-s3 ``` First publish (scoped packages require `--access public`): ```bash cd packages/storage-s3 npm publish --access public ``` Subsequent releases — bump `version` in `packages/storage-s3/package.json`, then: ```bash cd packages/storage-s3 npm publish ``` # @mantlejs/supabase > Supabase adapter for Mantle JS. Provides a supabase() plugin, an abstract SupabaseRepository base class backed by Supabase PostgREST, a supabaseAdapter() for cross-instance event sync via Supabase Realtime Broadcast, and optional Postgres Changes subscriptions that translate direct DB mutations into Mantle service events. Supabase adapter for [Mantle JS](https://github.com/mantlejs/mantle). Provides a `supabase()` plugin, an abstract `SupabaseRepository` base class backed by Supabase PostgREST, a `supabaseAdapter()` for cross-instance event sync via Supabase Realtime Broadcast, and optional Postgres Changes subscriptions that translate direct DB mutations into Mantle service events. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/supabase @supabase/supabase-js ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### The `supabase()` plugin [Section titled “The supabase() plugin”](#the-supabase-plugin) `supabase(config)` is a Mantle plugin. It creates a `SupabaseClient` and stores it on the application at `app.get("supabase")`. Call it once during app configuration before registering any repositories. ### `SupabaseRepository` [Section titled “SupabaseRepository\”](#supabaserepositoryt) `SupabaseRepository` is an abstract base class that implements all eight `Repository` methods against a Supabase-hosted PostgreSQL table via the Supabase PostgREST API. Extend it, set `tableName`, and optionally override `primaryKey` and `timestamps`. ### `supabaseAdapter()` — Realtime Broadcast sync transport [Section titled “supabaseAdapter() — Realtime Broadcast sync transport”](#supabaseadapter--realtime-broadcast-sync-transport) `supabaseAdapter()` returns a `SyncAdapter` compatible with `@mantlejs/sync`. It replaces Redis with Supabase Realtime Broadcast channels so teams already using Supabase get horizontal scaling with zero additional infrastructure. ### `listenToChanges` — Postgres Changes subscription [Section titled “listenToChanges — Postgres Changes subscription”](#listentochanges--postgres-changes-subscription) When a `SupabaseRepository` subclass sets `readonly listenToChanges = true`, it opens a Supabase Realtime Postgres Changes subscription. Direct database mutations from PostgREST, Supabase Studio, migrations, or any other source are automatically translated to Mantle `service:event` emissions and fan out through the app event bus. This works alongside `@mantlejs/sync` — both can be active simultaneously. ### QueryParams support [Section titled “QueryParams support”](#queryparams-support) All standard Mantle `QueryParams` operators are translated to PostgREST filter calls: | Operator | PostgREST equivalent | | ---------------------------- | ------------------------------------------------------------- | | `{ field: value }` | `.eq(field, value)` | | `{ field: null }` | `.is(field, null)` | | `$lt`, `$lte`, `$gt`, `$gte` | `.lt()`, `.lte()`, `.gt()`, `.gte()` | | `$ne` | `.neq()` / `.not(field, "is", null)` for null | | `$in` | `.in(field, [])` | | `$nin` | `.not(field, "in", "(…)")` | | `$like`, `$ilike` | `.like()`, `.ilike()` | | `$notlike` | `.not(field, "like", pattern)` | | `$contains` | `.contains(field, value)` — PostgREST `cs` (`@>` containment) | | `$or` | `.or(filter)` | | `$and` | chained calls | #### Nested paths and `$contains` [Section titled “Nested paths and $contains”](#nested-paths-and-contains) Dot-path field names address JSON columns and are translated to PostgREST arrow syntax: `"metadata.owner.name"` becomes `metadata->owner->>name` when compared against a string (text comparison) and `metadata->owner->name` otherwise (jsonb comparison). `$contains` uses jsonb `@>` semantics: an array operand requires the field array to contain every element, a scalar operand is treated as a single element, and an object operand requires the field object to be a recursive superset. Scalar operands are wrapped in an array before being passed to `.contains()`. `$contains` is not supported inside `$or` — move it to the top level of the where clause (top-level conditions are ANDed). ```typescript await repo.findAll({ where: { "metadata.owner.name": "alice", // metadata->owner->>name = 'alice' "metadata.level": { $gt: 4 }, // metadata->level > 4 (jsonb) tags: { $contains: ["red", "blue"] }, // tags @> '["red","blue"]' metadata: { $contains: { owner: { name: "alice" } } }, }, }); ``` *** ## Quick start [Section titled “Quick start”](#quick-start) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { supabase, SupabaseRepository } from "@mantlejs/supabase"; // 1. Configure the plugin const app = mantle() .configure(express()) .configure( supabase({ url: process.env.SUPABASE_URL!, key: process.env.SUPABASE_KEY!, }), ); // 2. Define your entity interface User { id: string; name: string; email: string; created_at?: string; updated_at?: string; } // 3. Create a repository class UserRepository extends SupabaseRepository { readonly tableName = "users"; } // 4. Wire it up app.use("/users", new UserService(new UserRepository(app))); app.listen(3030); ``` ### Custom queries [Section titled “Custom queries”](#custom-queries) Access the raw PostgREST query builder via `this.db` inside your repository: ```typescript class UserRepository extends SupabaseRepository { readonly tableName = "users"; async findByEmail(email: string): Promise { const { data, error } = await this.db.select("*").eq("email", email).maybeSingle(); if (error) throw this.wrapError(error); return data ?? null; } } ``` *** ## API [Section titled “API”](#api) ### `supabase(config)` [Section titled “supabase(config)”](#supabaseconfig) Returns a `MantlePlugin`. Call via `app.configure(supabase(config))`. ```typescript app.configure( supabase({ url: process.env.SUPABASE_URL!, // required — Supabase project URL key: process.env.SUPABASE_KEY!, // required — anon or service_role key options: { // optional — SupabaseClientOptions auth: { autoRefreshToken: false }, }, }), ); ``` Side effects: * Stores the `SupabaseClient` at `app.get("supabase")` #### `SupabaseConfig` [Section titled “SupabaseConfig”](#supabaseconfig-1) | Field | Type | Default | Description | | --------- | ----------------------- | ------- | ------------------------------------------------------------------------------------ | | `url` | `string` | — | Supabase project URL (required) | | `key` | `string` | — | Supabase API key — `anon` for client-side, `service_role` for server-side (required) | | `options` | `SupabaseClientOptions` | — | Additional Supabase client options (optional) | *** ### `SupabaseRepository` [Section titled “SupabaseRepository\”](#supabaserepositoryt-d) Abstract base class. Extend with a concrete `tableName`. ```typescript abstract class SupabaseRepository> implements Repository { abstract readonly tableName: string; readonly primaryKey: string; // default: "id" readonly timestamps: boolean; // default: true } ``` #### Instance properties [Section titled “Instance properties”](#instance-properties) | Property | Type | Default | Description | | ------------ | --------- | ------- | ------------------------------------------------------- | | `tableName` | `string` | — | Supabase table name (abstract — must be set) | | `primaryKey` | `string` | `"id"` | Primary key column name | | `timestamps` | `boolean` | `true` | Auto-write `created_at` / `updated_at` ISO-8601 strings | #### `Repository` methods [Section titled “Repository\ methods”](#repositoryt-methods) | Method | Description | | ---------------------- | ------------------------------------------------------------------------ | | `findAll(params?)` | Fetch all rows matching `QueryParams` (where, sort, limit, skip, select) | | `findById(id)` | Fetch a single row by primary key; returns `null` if not found | | `save(data)` | Insert a new row and return it | | `saveAll(data[])` | Insert multiple rows and return them | | `updateById(id, data)` | Replace all non-key columns; throws `NotFound` if missing | | `patchById(id, data)` | Merge supplied fields only; throws `NotFound` if missing | | `deleteById(id)` | Delete a row and return it; throws `NotFound` if missing | | `count(params?)` | Count rows matching optional `where` clause | #### `protected db` [Section titled “protected db”](#protected-db) Returns a PostgREST `PostgrestQueryBuilder` scoped to `tableName`. Use it in subclass custom queries. #### `listenToChanges` [Section titled “listenToChanges”](#listentochanges) Set `readonly listenToChanges = true` in a subclass to subscribe to Postgres Changes for that table. Requires Realtime to be enabled for the table in your Supabase project settings. ```typescript class PostRepository extends SupabaseRepository { readonly tableName = "posts"; readonly listenToChanges = true; // subscribe to WAL-based DB events } ``` Changes are emitted as `service:event` on the app event bus: | Postgres event | Mantle event | | -------------- | ------------ | | `INSERT` | `created` | | `UPDATE` | `patched` | | `DELETE` | `removed` | ##### External events carry `{ external: true }` [Section titled “External events carry { external: true }”](#external-events-carry--external-true-) These re-emissions did **not** pass through the hook pipeline — no before/after hooks ran, no validation, no `params.user`. So their params argument is `{ external: true }` (typed on `ServiceParams`), letting event consumers (socket.io broadcasts, `@mantlejs/sync`, custom listeners) distinguish them from hook-pipeline events and, for example, skip authorization-derived filtering that assumes hook context. ##### Why `UPDATE` maps to `patched` — never `updated` [Section titled “Why UPDATE maps to patched — never updated”](#why-update-maps-to-patched--never-updated) In Mantle, `updated` means “a caller replaced the full record via `update()`” while `patched` means “some fields changed”. A WAL-level `UPDATE` only tells us the row’s new state — the intent (full replace vs. partial mutation) is unknowable from outside the service layer. `patched` is the honest, conservative claim: consumers reacting to `patched` must already tolerate partial change, whereas mislabeling a partial write as `updated` would let them assume every field was intentionally set. #### `protected wrapError(err)` [Section titled “protected wrapError(err)”](#protected-wraperrorerr) Maps Supabase / PostgreSQL error codes to typed `MantleError` subclasses. Call it in custom query methods to keep error handling consistent. *** ### `supabaseAdapter(options?)` [Section titled “supabaseAdapter(options?)”](#supabaseadapteroptions) Returns a `SyncAdapter` for use with `@mantlejs/sync`. Credentials fall back to `SUPABASE_URL` and `SUPABASE_SERVICE_KEY` (or `SUPABASE_KEY`) environment variables. ```typescript import { sync } from "@mantlejs/sync"; import { supabase, supabaseAdapter } from "@mantlejs/supabase"; const app = mantle() .configure(express()) .configure(supabase({ url: process.env.SUPABASE_URL!, key: process.env.SUPABASE_SERVICE_KEY! })) .configure(socketio()) .configure(sync({ adapter: supabaseAdapter() })); ``` #### `SupabaseAdapterOptions` [Section titled “SupabaseAdapterOptions”](#supabaseadapteroptions-1) | Field | Type | Default | Description | | ----- | -------- | ------------------------------ | -------------------- | | `url` | `string` | `SUPABASE_URL` env var | Supabase project URL | | `key` | `string` | `SUPABASE_SERVICE_KEY` env var | Service role key | *** ## Types [Section titled “Types”](#types) ```typescript import type { SupabaseConfig, SupabaseAdapterOptions, SyncAdapter, SyncMessage } from "@mantlejs/supabase"; import { SupabaseRepository, supabaseAdapter } from "@mantlejs/supabase"; ``` | Export | Kind | Description | | ------------------------ | -------------- | --------------------------------------------- | | `SupabaseConfig` | interface | Options passed to `supabase()` | | `SupabaseRepository` | abstract class | Base repository class to extend | | `SupabaseAdapterOptions` | interface | Options passed to `supabaseAdapter()` | | `SyncAdapter` | interface | Pluggable transport interface for sync | | `SyncMessage` | interface | Cross-instance sync message shape | | `supabaseAdapter` | function | Factory that returns a Supabase `SyncAdapter` | *** ## Error mapping [Section titled “Error mapping”](#error-mapping) All errors are converted to typed `MantleError` subclasses: | PostgreSQL / PostgREST code | Error thrown | Condition | | --------------------------- | -------------------- | ---------------------------------------- | | `PGRST116` | `NotFound` (404) | Zero rows returned by a `.single()` call | | `23505` | `Conflict` (409) | Unique constraint violation | | `23503`, `23514`, `23502` | `BadRequest` (400) | FK, check, or NOT NULL violation | | `42501`, `28000`, `28P01` | `Forbidden` (403) | Insufficient privilege | | anything else | `GeneralError` (500) | Unexpected error | *** ## Development [Section titled “Development”](#development) ```bash npx nx build supabase # compile npx nx test supabase # run tests npx nx lint supabase # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build supabase ``` First publish (scoped packages require `--access public`): ```bash cd packages/supabase npm publish --access public ``` Subsequent releases — bump `version` in `packages/supabase/package.json`, then: ```bash cd packages/supabase npm publish ``` ### Testing locally with Verdaccio [Section titled “Testing locally with Verdaccio”](#testing-locally-with-verdaccio) ```bash # Terminal 1 — start the local registry npx nx run @mantle/source:local-registry # Terminal 2 — publish to it cd packages/supabase npm publish --registry http://localhost:4873 # Install from it in another project npm install @mantlejs/supabase --registry http://localhost:4873 ``` # @mantlejs/sync > Cross-instance event synchronisation for Mantle JS. Intercepts service:event emissions, publishes them to a shared message broker, and re-emits received messages from other instances — enabling @mantlejs/socketio to fan out real-time events across every connected process. Cross-instance event synchronisation for [Mantle JS](https://github.com/mantlejs/mantle). Intercepts `service:event` emissions, publishes them to a shared message broker, and re-emits received messages from other instances — enabling `@mantlejs/socketio` to fan out real-time events across every connected process. *** ## Installation [Section titled “Installation”](#installation) ```bash npm install @mantlejs/sync ``` For Redis (ioredis is an optional peer dependency): ```bash npm install @mantlejs/sync ioredis ``` *** ## Concepts [Section titled “Concepts”](#concepts) ### How it works [Section titled “How it works”](#how-it-works) `sync()` listens for Mantle’s internal `service:event` bus event (emitted after every `create`, `update`, `patch`, or `remove`) and publishes a `SyncMessage` to a shared pub/sub channel. Every other instance subscribed to that channel re-emits the message onto its own local event bus — where `@mantlejs/socketio` picks it up and fans it out to connected WebSocket clients. Each instance generates a random `instanceId` (UUID) at startup. Messages received from the broker that carry the local `instanceId` as `originId` are dropped, preventing double-delivery to the originating process’s clients. Broker failures (publish errors, subscribe failures) are non-fatal: they are logged via `app.get('logger')` if a logger is configured, and execution continues. ### Delivery semantics: at-most-once [Section titled “Delivery semantics: at-most-once”](#delivery-semantics-at-most-once) Events are delivered **at most once** — there is no persistence, acknowledgment, or replay. A message is lost when: * the broker connection is down at publish time (the error is logged and execution continues); * an instance is restarting, or subscribes after the message was published (pub/sub has no backlog); * a WebSocket client is disconnected or reconnecting when its instance fans the event out. Design consequence: **clients must not treat the event stream as a source of truth.** Treat events as cache-invalidation hints and refetch on reconnect. `@mantlejs/client` emits a `reconnect` event for exactly this, and `@mantlejs/react`’s `MantleProvider` calls `queryClient.invalidateQueries()` on it, bounding the staleness a missed event can cause. If you need guaranteed delivery, put a real queue (or an outbox table) behind your service instead of relying on `service:event` fan-out. ### Adapters [Section titled “Adapters”](#adapters) | Adapter | Source | Transport | | ------------------- | -------------------- | ------------------------------------------------------------------------------- | | `redisAdapter()` | `@mantlejs/sync` | Redis pub/sub (ioredis, two connections) — also compatible with DragonflyDB | | `supabaseAdapter()` | `@mantlejs/supabase` | Supabase Realtime Broadcast — zero additional infrastructure for Supabase users | Custom adapters can be plugged in by implementing the `SyncAdapter` interface. *** ## Quick Start [Section titled “Quick Start”](#quick-start) ### Redis [Section titled “Redis”](#redis) ```typescript import { mantle } from "@mantlejs/mantle"; import { express } from "@mantlejs/express"; import { socketio } from "@mantlejs/socketio"; import { sync, redisAdapter } from "@mantlejs/sync"; const app = mantle() .configure(express()) .configure(socketio()) .configure(sync({ adapter: redisAdapter({ url: process.env.REDIS_URL }) })); app.listen(3030); ``` ### Supabase Realtime [Section titled “Supabase Realtime”](#supabase-realtime) ```typescript import { sync } from "@mantlejs/sync"; import { supabase, supabaseAdapter } from "@mantlejs/supabase"; app .configure(supabase({ url: process.env.SUPABASE_URL!, key: process.env.SUPABASE_KEY! })) .configure(sync({ adapter: supabaseAdapter() })); ``` *** ## API [Section titled “API”](#api) ### `sync(options)` [Section titled “sync(options)”](#syncoptions) Returns a Mantle plugin that wires the sync adapter to the application event bus. ```typescript function sync(options: SyncOptions): MantlePlugin; ``` #### Options [Section titled “Options”](#options) | Option | Type | Default | Description | | --------- | ------------- | --------------- | ------------------------------------------------------------------- | | `adapter` | `SyncAdapter` | — | **Required.** The pub/sub transport adapter. | | `channel` | `string` | `"mantle:sync"` | Shared pub/sub channel name. Must be the same across all instances. | *** ### `redisAdapter(options?)` [Section titled “redisAdapter(options?)”](#redisadapteroptions) Creates a Redis pub/sub adapter using ioredis with two dedicated connections (one for publishing, one for subscribing — required by the Redis protocol). ```typescript function redisAdapter(options?: RedisAdapterOptions): SyncAdapter; ``` #### Options [Section titled “Options”](#options-1) | Option | Type | Default | Description | | ---------- | --------- | ------------- | ----------------------------------------------------------------------------------------- | | `url` | `string` | — | Full Redis URL (e.g. `"redis://user:pass@host:6379/0"`). Takes precedence over host/port. | | `host` | `string` | `"127.0.0.1"` | Redis hostname. Ignored when `url` is set. | | `port` | `number` | `6379` | Redis port. Ignored when `url` is set. | | `password` | `string` | — | AUTH password. | | `db` | `number` | `0` | Redis database index. | | `tls` | `boolean` | `false` | Enable TLS. | *** ## Types [Section titled “Types”](#types) ```typescript import type { SyncMessage, SyncMessageParams, SyncAdapter, SyncOptions, RedisAdapterOptions } from "@mantlejs/sync"; import { sync, redisAdapter } from "@mantlejs/sync"; ``` ### `SyncMessage` [Section titled “SyncMessage”](#syncmessage) The message envelope published and received by adapters. ```typescript interface SyncMessage { originId: string; // UUID of the originating instance path: string; // service path (e.g. "users") event: string; // event name (e.g. "created") result: unknown; // the service method result params: SyncMessageParams; } ``` ### `SyncMessageParams` [Section titled “SyncMessageParams”](#syncmessageparams) The whitelisted subset of `ServiceParams` that actually crosses the broker — **not** the full params object from the originating call: ```typescript interface SyncMessageParams { provider?: string; query?: Record; user?: { id: string | number }; } ``` `sync()` reduces every outgoing (and incoming) message to this shape before it touches the adapter. It never carries `headers` (live credentials), the full `user` record, or the non-serializable `connection` object — so request auth tokens and full user documents can never leak across the pub/sub channel, even if a custom adapter logs or persists raw messages. ### `SyncAdapter` [Section titled “SyncAdapter”](#syncadapter) Implement this interface to create a custom pub/sub adapter. ```typescript interface SyncAdapter { publish(channel: string, message: SyncMessage): Promise; subscribe(channel: string, handler: (message: SyncMessage) => void): Promise; close(): Promise; } ``` *** ## Development [Section titled “Development”](#development) ```bash npx nx build sync # compile npx nx test sync # run tests npx nx lint sync # lint ``` *** ## Publishing [Section titled “Publishing”](#publishing) Build before publishing: ```bash npx nx build sync ``` First publish (scoped packages require `--access public`): ```bash cd packages/sync npm publish --access public ``` Subsequent releases — bump `version` in `packages/sync/package.json`, then: ```bash cd packages/sync npm publish ``` # Documentation ## Packages [Section titled “Packages”](#packages) * [@mantlejs/audit](/api/mantlejs/audit/) * [@mantlejs/auth](/api/mantlejs/auth/) * [@mantlejs/auth-apple](/api/mantlejs/auth-apple/) * [@mantlejs/auth-facebook](/api/mantlejs/auth-facebook/) * [@mantlejs/auth-github](/api/mantlejs/auth-github/) * [@mantlejs/auth-google](/api/mantlejs/auth-google/) * [@mantlejs/auth-linkedin](/api/mantlejs/auth-linkedin/) * [@mantlejs/auth-local](/api/mantlejs/auth-local/) * [@mantlejs/auth-microsoft](/api/mantlejs/auth-microsoft/) * [@mantlejs/auth-oauth](/api/mantlejs/auth-oauth/) * [@mantlejs/auth-redis](/api/mantlejs/auth-redis/) * [@mantlejs/auth-twitter](/api/mantlejs/auth-twitter/) * [@mantlejs/cli](/api/mantlejs/cli/) * [@mantlejs/client](/api/mantlejs/client/) * [@mantlejs/config](/api/mantlejs/config/) * [@mantlejs/dynamodb](/api/mantlejs/dynamodb/) * [@mantlejs/embeddings](/api/mantlejs/embeddings/) * [@mantlejs/express](/api/mantlejs/express/) * [@mantlejs/http](/api/mantlejs/http/) * [@mantlejs/knex](/api/mantlejs/knex/) * [@mantlejs/koa](/api/mantlejs/koa/) * [@mantlejs/logger](/api/mantlejs/logger/) * [@mantlejs/mantle](/api/mantlejs/mantle/) * [@mantlejs/mcp](/api/mantlejs/mcp/) * [@mantlejs/mcp-code](/api/mantlejs/mcp-code/) * [@mantlejs/memory](/api/mantlejs/memory/) * [@mantlejs/mongodb](/api/mantlejs/mongodb/) * [@mantlejs/neo4j](/api/mantlejs/neo4j/) * [@mantlejs/openapi](/api/mantlejs/openapi/) * [@mantlejs/pinecone](/api/mantlejs/pinecone/) * [@mantlejs/qdrant](/api/mantlejs/qdrant/) * [@mantlejs/react](/api/mantlejs/react/) * [@mantlejs/schema](/api/mantlejs/schema/) * [@mantlejs/socketio](/api/mantlejs/socketio/) * [@mantlejs/storage](/api/mantlejs/storage/) * [@mantlejs/storage-gcs](/api/mantlejs/storage-gcs/) * [@mantlejs/storage-s3](/api/mantlejs/storage-s3/) * [@mantlejs/supabase](/api/mantlejs/supabase/) * [@mantlejs/sync](/api/mantlejs/sync/) * [create-mantlejs](/api/create-mantlejs/) # create-mantlejs ## Variables [Section titled “Variables”](#variables) * [newProject](/api/create-mantlejs/variables/newproject/) ## References [Section titled “References”](#references) ### Auth [Section titled “Auth”](#auth) Renames and re-exports [newProject](/api/create-mantlejs/variables/newproject/) *** ### Database [Section titled “Database”](#database) Renames and re-exports [newProject](/api/create-mantlejs/variables/newproject/) *** ### NewProjectOptions [Section titled “NewProjectOptions”](#newprojectoptions) Renames and re-exports [newProject](/api/create-mantlejs/variables/newproject/) *** ### PackageManager [Section titled “PackageManager”](#packagemanager) Renames and re-exports [newProject](/api/create-mantlejs/variables/newproject/) *** ### Transport [Section titled “Transport”](#transport) Renames and re-exports [newProject](/api/create-mantlejs/variables/newproject/) # newProject > **newProject**: `any` # @mantlejs/audit ## Interfaces [Section titled “Interfaces”](#interfaces) * [AuditLogOptions](/api/mantlejs/audit/interfaces/auditlogoptions/) * [AuditRecord](/api/mantlejs/audit/interfaces/auditrecord/) ## Functions [Section titled “Functions”](#functions) * [auditLog](/api/mantlejs/audit/functions/auditlog/) # auditLog > **auditLog**(`options`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [audit.ts:75](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L75) Returns a hook recording `{ principal, agentId?, path, method, params, resultSummary, timestamp }` to `options.sink` on every call. Register the *same* returned function in `after.all` and `error.all` — it fires exactly once per call, on whichever phase actually reaches it (a successful call never reaches `error.all`; a thrown call never reaches `after.all`). ```typescript app.service("documents").hooks({ after: { all: [auditLog({ sink: auditRepository })] }, error: { all: [auditLog({ sink: auditRepository })] }, }); ``` A sink write failure is caught and never rethrown — see `AuditLogOptions.onSinkError`. ## Parameters [Section titled “Parameters”](#parameters) ### options [Section titled “options”](#options) [`AuditLogOptions`](/api/mantlejs/audit/interfaces/auditlogoptions/) ## Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) # AuditLogOptions Defined in: [audit.ts:49](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L49) ## Properties [Section titled “Properties”](#properties) ### onSinkError? [Section titled “onSinkError?”](#onsinkerror) > `optional` **onSinkError?**: (`error`, `record`, `ctx`) => `void` Defined in: [audit.ts:57](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L57) Called when the sink write itself throws. The primary operation always succeeds regardless — a sink outage must never take down the service it’s auditing. Defaults to logging via `app.get("logger")` when present (see `@mantlejs/logger`), otherwise silent. #### Parameters [Section titled “Parameters”](#parameters) ##### error [Section titled “error”](#error) `unknown` ##### record [Section titled “record”](#record) [`AuditRecord`](/api/mantlejs/audit/interfaces/auditrecord/) ##### ctx [Section titled “ctx”](#ctx) [`HookContext`](/api/mantlejs/mantle/interfaces/hookcontext/) #### Returns [Section titled “Returns”](#returns) `void` *** ### sink [Section titled “sink”](#sink) > **sink**: [`Repository`](/api/mantlejs/mantle/interfaces/repository/)<[`AuditRecord`](/api/mantlejs/audit/interfaces/auditrecord/)> Defined in: [audit.ts:51](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L51) Where audit records are written. Any `Repository` the deployment already has — Postgres, Mongo, memory, whatever. # AuditRecord Defined in: [audit.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L17) One audit entry: who did what, as which identity, with what result. Recorded by `auditLog()` to whichever `Repository` the deployment configures as the sink — audit records are themselves a normal Mantle-shaped record, not a bespoke storage concept. ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `unknown` Index signature so `AuditRecord` satisfies `Repository>` (e.g. `MemoryRepository`). ## Properties [Section titled “Properties”](#properties) ### agentId? [Section titled “agentId?”](#agentid) > `optional` **agentId?**: `string` Defined in: [audit.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L21) Set only when the call was authorized via an agent token (`HookContext.agent`, see `@mantlejs/auth`). *** ### agentScope? [Section titled “agentScope?”](#agentscope) > `optional` **agentScope?**: [`CapabilityScope`](/api/mantlejs/mantle/type-aliases/capabilityscope/) Defined in: [audit.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L23) The agent token’s capability scope. Present only alongside `agentId`. *** ### delegatingUserId? [Section titled “delegatingUserId?”](#delegatinguserid) > `optional` **delegatingUserId?**: `string` Defined in: [audit.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L25) The user who minted the agent token. Present only alongside `agentId`. *** ### mcp? [Section titled “mcp?”](#mcp) > `optional` **mcp?**: `Record`<`string`, `unknown`> Defined in: [audit.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L39) `ctx.params.mcp` — origin metadata for an MCP-originated call. Under `@mantlejs/mcp-code`’s code mode this is `{ mode: "code", executionId, scriptHash, script? }`, so every service call one agent script made shares an `executionId` (“what did this script do” is one `find()`). *** ### method [Section titled “method”](#method) > **method**: `string` Defined in: [audit.ts:27](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L27) *** ### params? [Section titled “params?”](#params) > `optional` **params?**: `Record`<`string`, `unknown`> Defined in: [audit.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L33) The caller’s query — `ctx.params.query` (filters, pagination, sort, select). Deliberately never `ctx.params.headers` or the raw `ctx.params.user` object: those may carry bearer tokens or other sensitive claims, and identity already has its own field (`principal`). *** ### path [Section titled “path”](#path) > **path**: `string` Defined in: [audit.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L26) *** ### principal? [Section titled “principal?”](#principal) > `optional` **principal?**: `unknown` Defined in: [audit.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L19) `ctx.params.user` — undefined for an unauthenticated or internal call. *** ### resultSummary [Section titled “resultSummary”](#resultsummary) > **resultSummary**: `string` Defined in: [audit.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L42) A short, non-exhaustive description of the outcome — never the full result payload. *** ### status [Section titled “status”](#status) > **status**: `"success"` | `"error"` Defined in: [audit.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L40) *** ### timestamp [Section titled “timestamp”](#timestamp) > **timestamp**: `string` Defined in: [audit.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/audit/src/lib/audit.ts#L44) ISO 8601 timestamp of when the entry was recorded. # @mantlejs/auth ## Interfaces [Section titled “Interfaces”](#interfaces) * [AgentTokenOptions](/api/mantlejs/auth/interfaces/agenttokenoptions/) * [AgentTokenStore](/api/mantlejs/auth/interfaces/agenttokenstore/) * [AuthConfig](/api/mantlejs/auth/interfaces/authconfig/) * [AuthEngine](/api/mantlejs/auth/interfaces/authengine/) * [AuthenticateOptions](/api/mantlejs/auth/interfaces/authenticateoptions/) * [AuthResult](/api/mantlejs/auth/interfaces/authresult/) * [AuthStrategy](/api/mantlejs/auth/interfaces/authstrategy/) * [IssuedAgentToken](/api/mantlejs/auth/interfaces/issuedagenttoken/) * [JwtPayload](/api/mantlejs/auth/interfaces/jwtpayload/) * [RefreshTokenStore](/api/mantlejs/auth/interfaces/refreshtokenstore/) * [TokenPair](/api/mantlejs/auth/interfaces/tokenpair/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [AgentPrincipal](/api/mantlejs/auth/type-aliases/agentprincipal/) ## Functions [Section titled “Functions”](#functions) * [auth](/api/mantlejs/auth/functions/auth/) * [authenticate](/api/mantlejs/auth/functions/authenticate/) * [authorizeAgent](/api/mantlejs/auth/functions/authorizeagent/) * [memoryAgentTokenStore](/api/mantlejs/auth/functions/memoryagenttokenstore/) * [memoryRefreshTokenStore](/api/mantlejs/auth/functions/memoryrefreshtokenstore/) * [sanitizeUser](/api/mantlejs/auth/functions/sanitizeuser/) # @mantlejs/auth-apple ## Interfaces [Section titled “Interfaces”](#interfaces) * [AppleStrategyConfig](/api/mantlejs/auth-apple/interfaces/applestrategyconfig/) ## Functions [Section titled “Functions”](#functions) * [appleStrategy](/api/mantlejs/auth-apple/functions/applestrategy/) # appleStrategy > **appleStrategy**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [apple-strategy.ts:106](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-apple/src/lib/apple-strategy.ts#L106) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) [`AppleStrategyConfig`](/api/mantlejs/auth-apple/interfaces/applestrategyconfig/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # AppleStrategyConfig Defined in: [apple-strategy.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-apple/src/lib/apple-strategy.ts#L15) ## Extends [Section titled “Extends”](#extends) * `Omit`<[`newProject`](/api/create-mantlejs/variables/newproject/), `"clientSecret"`> ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `OAuthPluginConfig` > \[`key`: `number`]: `OAuthPluginConfig` > \[`key`: `symbol`]: `OAuthPluginConfig` ## Properties [Section titled “Properties”](#properties) ### keyId [Section titled “keyId”](#keyid) > **keyId**: `string` Defined in: [apple-strategy.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-apple/src/lib/apple-strategy.ts#L19) Key ID of the Sign in with Apple private key. *** ### privateKey [Section titled “privateKey”](#privatekey) > **privateKey**: `string` Defined in: [apple-strategy.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-apple/src/lib/apple-strategy.ts#L21) PKCS#8 PEM contents of the .p8 private key downloaded from Apple. *** ### teamId [Section titled “teamId”](#teamid) > **teamId**: `string` Defined in: [apple-strategy.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-apple/src/lib/apple-strategy.ts#L17) Apple Developer Team ID (10 characters, from the membership page). # @mantlejs/auth-facebook ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [FacebookStrategyConfig](/api/mantlejs/auth-facebook/type-aliases/facebookstrategyconfig/) ## Functions [Section titled “Functions”](#functions) * [facebookStrategy](/api/mantlejs/auth-facebook/functions/facebookstrategy/) # facebookStrategy > **facebookStrategy**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [facebook-strategy.ts:66](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-facebook/src/lib/facebook-strategy.ts#L66) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) `OAuthPluginConfig` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # FacebookStrategyConfig > **FacebookStrategyConfig** = [`newProject`](/api/create-mantlejs/variables/newproject/) Defined in: [facebook-strategy.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-facebook/src/lib/facebook-strategy.ts#L16) # @mantlejs/auth-github ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [GitHubStrategyConfig](/api/mantlejs/auth-github/type-aliases/githubstrategyconfig/) ## Functions [Section titled “Functions”](#functions) * [githubStrategy](/api/mantlejs/auth-github/functions/githubstrategy/) # githubStrategy > **githubStrategy**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [github-strategy.ts:67](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-github/src/lib/github-strategy.ts#L67) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) `OAuthPluginConfig` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # GitHubStrategyConfig > **GitHubStrategyConfig** = [`newProject`](/api/create-mantlejs/variables/newproject/) Defined in: [github-strategy.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-github/src/lib/github-strategy.ts#L16) # @mantlejs/auth-google ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [GoogleStrategyConfig](/api/mantlejs/auth-google/type-aliases/googlestrategyconfig/) ## Functions [Section titled “Functions”](#functions) * [googleStrategy](/api/mantlejs/auth-google/functions/googlestrategy/) # googleStrategy > **googleStrategy**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [google-strategy.ts:57](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-google/src/lib/google-strategy.ts#L57) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) `OAuthPluginConfig` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # GoogleStrategyConfig > **GoogleStrategyConfig** = [`newProject`](/api/create-mantlejs/variables/newproject/) Defined in: [google-strategy.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-google/src/lib/google-strategy.ts#L10) # @mantlejs/auth-linkedin ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [LinkedInStrategyConfig](/api/mantlejs/auth-linkedin/type-aliases/linkedinstrategyconfig/) ## Functions [Section titled “Functions”](#functions) * [linkedinStrategy](/api/mantlejs/auth-linkedin/functions/linkedinstrategy/) # linkedinStrategy > **linkedinStrategy**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [linkedin-strategy.ts:65](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-linkedin/src/lib/linkedin-strategy.ts#L65) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) `OAuthPluginConfig` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # LinkedInStrategyConfig > **LinkedInStrategyConfig** = [`newProject`](/api/create-mantlejs/variables/newproject/) Defined in: [linkedin-strategy.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-linkedin/src/lib/linkedin-strategy.ts#L16) # @mantlejs/auth-local ## Interfaces [Section titled “Interfaces”](#interfaces) * [LocalStrategyConfig](/api/mantlejs/auth-local/interfaces/localstrategyconfig/) ## Functions [Section titled “Functions”](#functions) * [hashPassword](/api/mantlejs/auth-local/functions/hashpassword/) * [localStrategy](/api/mantlejs/auth-local/functions/localstrategy/) # hashPassword > **hashPassword**(`field?`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [hash-password.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-local/src/lib/hash-password.ts#L4) ## Parameters [Section titled “Parameters”](#parameters) ### field? [Section titled “field?”](#field) `string` = `"password"` ## Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) # localStrategy > **localStrategy**(`config?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [auth-local.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-local/src/lib/auth-local.ts#L14) ## Parameters [Section titled “Parameters”](#parameters) ### config? [Section titled “config?”](#config) [`LocalStrategyConfig`](/api/mantlejs/auth-local/interfaces/localstrategyconfig/) = `{}` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # LocalStrategyConfig Defined in: [auth-local.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-local/src/lib/auth-local.ts#L6) ## Properties [Section titled “Properties”](#properties) ### entityService? [Section titled “entityService?”](#entityservice) > `optional` **entityService?**: `string` Defined in: [auth-local.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-local/src/lib/auth-local.ts#L9) *** ### passwordField? [Section titled “passwordField?”](#passwordfield) > `optional` **passwordField?**: `string` Defined in: [auth-local.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-local/src/lib/auth-local.ts#L8) *** ### usernameField? [Section titled “usernameField?”](#usernamefield) > `optional` **usernameField?**: `string` Defined in: [auth-local.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-local/src/lib/auth-local.ts#L7) # @mantlejs/auth-microsoft ## Interfaces [Section titled “Interfaces”](#interfaces) * [MicrosoftStrategyConfig](/api/mantlejs/auth-microsoft/interfaces/microsoftstrategyconfig/) ## Functions [Section titled “Functions”](#functions) * [microsoftStrategy](/api/mantlejs/auth-microsoft/functions/microsoftstrategy/) # microsoftStrategy > **microsoftStrategy**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [microsoft-strategy.ts:76](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-microsoft/src/lib/microsoft-strategy.ts#L76) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) [`MicrosoftStrategyConfig`](/api/mantlejs/auth-microsoft/interfaces/microsoftstrategyconfig/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # MicrosoftStrategyConfig Defined in: [microsoft-strategy.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-microsoft/src/lib/microsoft-strategy.ts#L16) ## Extends [Section titled “Extends”](#extends) * [`newProject`](/api/create-mantlejs/variables/newproject/) ## Properties [Section titled “Properties”](#properties) ### tenant? [Section titled “tenant?”](#tenant) > `optional` **tenant?**: `string` Defined in: [microsoft-strategy.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-microsoft/src/lib/microsoft-strategy.ts#L18) Entra tenant: “common” (default), “organizations”, “consumers”, or a tenant ID. # @mantlejs/auth-oauth ## Interfaces [Section titled “Interfaces”](#interfaces) * [AuthUrlParams](/api/mantlejs/auth-oauth/interfaces/authurlparams/) * [CallbackExtras](/api/mantlejs/auth-oauth/interfaces/callbackextras/) * [CodeExchangeParams](/api/mantlejs/auth-oauth/interfaces/codeexchangeparams/) * [OAuthPluginConfig](/api/mantlejs/auth-oauth/interfaces/oauthpluginconfig/) * [OAuthProfile](/api/mantlejs/auth-oauth/interfaces/oauthprofile/) * [OAuthProvider](/api/mantlejs/auth-oauth/interfaces/oauthprovider/) * [OAuthStateData](/api/mantlejs/auth-oauth/interfaces/oauthstatedata/) * [OAuthStateStore](/api/mantlejs/auth-oauth/interfaces/oauthstatestore/) ## Functions [Section titled “Functions”](#functions) * [createOAuthPlugin](/api/mantlejs/auth-oauth/functions/createoauthplugin/) * [createStateStore](/api/mantlejs/auth-oauth/functions/createstatestore/) # createOAuthPlugin > **createOAuthPlugin**(`providerKey`, `provider`, `config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [create-oauth-plugin.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/create-oauth-plugin.ts#L9) ## Parameters [Section titled “Parameters”](#parameters) ### providerKey [Section titled “providerKey”](#providerkey) `string` ### provider [Section titled “provider”](#provider) [`OAuthProvider`](/api/mantlejs/auth-oauth/interfaces/oauthprovider/) ### config [Section titled “config”](#config) [`OAuthPluginConfig`](/api/mantlejs/auth-oauth/interfaces/oauthpluginconfig/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # createStateStore > **createStateStore**(`ttlMs?`): [`OAuthStateStore`](/api/mantlejs/auth-oauth/interfaces/oauthstatestore/) Defined in: [state-store.ts:32](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/state-store.ts#L32) ## Parameters [Section titled “Parameters”](#parameters) ### ttlMs? [Section titled “ttlMs?”](#ttlms) `number` = `DEFAULT_TTL_MS` ## Returns [Section titled “Returns”](#returns) [`OAuthStateStore`](/api/mantlejs/auth-oauth/interfaces/oauthstatestore/) # AuthUrlParams Defined in: [types.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L9) ## Properties [Section titled “Properties”](#properties) ### clientId [Section titled “clientId”](#clientid) > **clientId**: `string` Defined in: [types.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L10) *** ### codeVerifier? [Section titled “codeVerifier?”](#codeverifier) > `optional` **codeVerifier?**: `string` Defined in: [types.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L18) PKCE code verifier, present when the provider declares `usePkce`. The provider derives the S256 code challenge from it (Arctic does this internally in `createAuthorizationURL`). *** ### redirectUri [Section titled “redirectUri”](#redirecturi) > **redirectUri**: `string` Defined in: [types.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L11) *** ### scope [Section titled “scope”](#scope) > **scope**: `string`\[] Defined in: [types.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L12) *** ### state [Section titled “state”](#state) > **state**: `string` Defined in: [types.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L13) # CallbackExtras Defined in: [types.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L30) Raw callback payload fields a provider may need beyond code/state (e.g. Apple’s `user` JSON). ## Properties [Section titled “Properties”](#properties) ### body? [Section titled “body?”](#body) > `optional` **body?**: `Record`<`string`, `unknown`> Defined in: [types.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L31) # CodeExchangeParams Defined in: [types.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L21) ## Properties [Section titled “Properties”](#properties) ### clientId [Section titled “clientId”](#clientid) > **clientId**: `string` Defined in: [types.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L23) *** ### clientSecret [Section titled “clientSecret”](#clientsecret) > **clientSecret**: `string` Defined in: [types.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L24) *** ### code [Section titled “code”](#code) > **code**: `string` Defined in: [types.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L22) *** ### codeVerifier? [Section titled “codeVerifier?”](#codeverifier) > `optional` **codeVerifier?**: `string` Defined in: [types.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L26) *** ### redirectUri [Section titled “redirectUri”](#redirecturi) > **redirectUri**: `string` Defined in: [types.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L25) # OAuthPluginConfig Defined in: [types.ts:48](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L48) ## Properties [Section titled “Properties”](#properties) ### callbackUrl? [Section titled “callbackUrl?”](#callbackurl) > `optional` **callbackUrl?**: `string` Defined in: [types.ts:52](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L52) Path registered as the OAuth callback route. Default: `/auth/{providerKey}/callback` *** ### clientId [Section titled “clientId”](#clientid) > **clientId**: `string` Defined in: [types.ts:49](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L49) *** ### clientSecret [Section titled “clientSecret”](#clientsecret) > **clientSecret**: `string` Defined in: [types.ts:50](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L50) *** ### entity? [Section titled “entity?”](#entity) > `optional` **entity?**: `string` Defined in: [types.ts:55](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L55) Service used to find or create users. Default: `'users'` *** ### entityIdField? [Section titled “entityIdField?”](#entityidfield) > `optional` **entityIdField?**: `string` Defined in: [types.ts:57](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L57) Field matched against the provider’s user ID. Default: `'{providerKey}Id'` *** ### redirectUrl? [Section titled “redirectUrl?”](#redirecturl) > `optional` **redirectUrl?**: `string` Defined in: [types.ts:74](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L74) Frontend URL to redirect to once the OAuth dance completes, for browser-based flows where `/auth/{providerKey}` is opened via full-page navigation (an `
`, not `fetch`) and a frontend page needs to pick up the resulting session. When set: * success redirects to `${redirectUrl}#accessToken=...&refreshToken=...` * failure redirects to `${redirectUrl}#error=...` Tokens go in the URL fragment, never the query string, so they aren’t sent to the server or logged. Omit to get the original behavior: a JSON `{ accessToken, refreshToken, user }` body on success, or a thrown error passed to `next()` on failure. *** ### scope? [Section titled “scope?”](#scope) > `optional` **scope?**: `string`\[] Defined in: [types.ts:53](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L53) *** ### stateStore? [Section titled “stateStore?”](#statestore) > `optional` **stateStore?**: [`OAuthStateStore`](/api/mantlejs/auth-oauth/interfaces/oauthstatestore/) Defined in: [types.ts:63](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L63) Store for pending OAuth state (CSRF token + PKCE verifier). Defaults to an in-process in-memory store — multi-instance deployments (e.g. Cloud Run) must inject a shared implementation (Redis or similar) so the callback can land on any instance. # OAuthProfile Defined in: [types.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L3) ## Properties [Section titled “Properties”](#properties) ### email? [Section titled “email?”](#email) > `optional` **email?**: `string` Defined in: [types.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L5) *** ### id [Section titled “id”](#id) > **id**: `string` Defined in: [types.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L4) *** ### name? [Section titled “name?”](#name) > `optional` **name?**: `string` Defined in: [types.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L6) # OAuthProvider Defined in: [types.ts:34](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L34) ## Properties [Section titled “Properties”](#properties) ### callbackMethod? [Section titled “callbackMethod?”](#callbackmethod) > `optional` **callbackMethod?**: `"GET"` | `"POST"` Defined in: [types.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L42) HTTP method of the provider’s callback. Default “GET”. “POST” providers (Apple’s `response_mode=form_post`) receive code/state (and any provider-specific fields) in the form-encoded body instead of the query string. *** ### defaultScope [Section titled “defaultScope”](#defaultscope) > **defaultScope**: `string`\[] Defined in: [types.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L36) *** ### usePkce [Section titled “usePkce”](#usepkce) > **usePkce**: `boolean` Defined in: [types.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L35) ## Methods [Section titled “Methods”](#methods) ### buildAuthUrl() [Section titled “buildAuthUrl()”](#buildauthurl) > **buildAuthUrl**(`params`): `string` Defined in: [types.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L43) #### Parameters [Section titled “Parameters”](#parameters) ##### params [Section titled “params”](#params) [`AuthUrlParams`](/api/mantlejs/auth-oauth/interfaces/authurlparams/) #### Returns [Section titled “Returns”](#returns) `string` *** ### exchangeCode() [Section titled “exchangeCode()”](#exchangecode) > **exchangeCode**(`params`): `Promise`<`string`> Defined in: [types.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L44) #### Parameters [Section titled “Parameters”](#parameters-1) ##### params [Section titled “params”](#params-1) [`CodeExchangeParams`](/api/mantlejs/auth-oauth/interfaces/codeexchangeparams/) #### Returns [Section titled “Returns”](#returns-1) `Promise`<`string`> *** ### fetchProfile() [Section titled “fetchProfile()”](#fetchprofile) > **fetchProfile**(`accessToken`, `extras?`): `Promise`<[`OAuthProfile`](/api/mantlejs/auth-oauth/interfaces/oauthprofile/)> Defined in: [types.ts:45](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/types.ts#L45) #### Parameters [Section titled “Parameters”](#parameters-2) ##### accessToken [Section titled “accessToken”](#accesstoken) `string` ##### extras? [Section titled “extras?”](#extras) [`CallbackExtras`](/api/mantlejs/auth-oauth/interfaces/callbackextras/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<[`OAuthProfile`](/api/mantlejs/auth-oauth/interfaces/oauthprofile/)> # OAuthStateData Defined in: [state-store.ts:1](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/state-store.ts#L1) ## Properties [Section titled “Properties”](#properties) ### codeVerifier? [Section titled “codeVerifier?”](#codeverifier) > `optional` **codeVerifier?**: `string` Defined in: [state-store.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/state-store.ts#L2) *** ### expiresAt [Section titled “expiresAt”](#expiresat) > **expiresAt**: `number` Defined in: [state-store.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/state-store.ts#L3) # OAuthStateStore Defined in: [state-store.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/state-store.ts#L11) Storage for pending OAuth authorization state. Methods are sync-or-async so a shared store (e.g. Redis via `@mantlejs/auth-redis`) can be injected without an interface change. ## Methods [Section titled “Methods”](#methods) ### cleanup() [Section titled “cleanup()”](#cleanup) > **cleanup**(): `void` | `Promise`<`void`> Defined in: [state-store.ts:27](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/state-store.ts#L27) Prune expired entries. May be a no-op where the backend expires keys itself. #### Returns [Section titled “Returns”](#returns) `void` | `Promise`<`void`> *** ### consume() [Section titled “consume()”](#consume) > **consume**(`state`): [`OAuthStateData`](/api/mantlejs/auth-oauth/interfaces/oauthstatedata/) | `Promise`<[`OAuthStateData`](/api/mantlejs/auth-oauth/interfaces/oauthstatedata/) | `undefined`> | `undefined` Defined in: [state-store.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/state-store.ts#L25) Atomically read and remove a pending state entry — the callback handler’s single-use check. Must not be implementable as a separate `get()` followed by `delete()`: two concurrent callback requests for the same `state` (a double-fired network request, or a replayed callback URL) would otherwise both pass the pending-state check before either deletes it, letting both proceed to exchange the same authorization code. Implementations backed by a real datastore should use its native atomic fetch-and-delete (e.g. Redis `GETDEL`, which `@mantlejs/auth-redis`’s `redisStateStore` uses); the in-memory default here is atomic by construction since it never awaits between the read and the delete. #### Parameters [Section titled “Parameters”](#parameters) ##### state [Section titled “state”](#state) `string` #### Returns [Section titled “Returns”](#returns-1) [`OAuthStateData`](/api/mantlejs/auth-oauth/interfaces/oauthstatedata/) | `Promise`<[`OAuthStateData`](/api/mantlejs/auth-oauth/interfaces/oauthstatedata/) | `undefined`> | `undefined` *** ### delete() [Section titled “delete()”](#delete) > **delete**(`state`): `void` | `Promise`<`void`> Defined in: [state-store.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/state-store.ts#L14) #### Parameters [Section titled “Parameters”](#parameters-1) ##### state [Section titled “state”](#state-1) `string` #### Returns [Section titled “Returns”](#returns-2) `void` | `Promise`<`void`> *** ### get() [Section titled “get()”](#get) > **get**(`state`): [`OAuthStateData`](/api/mantlejs/auth-oauth/interfaces/oauthstatedata/) | `Promise`<[`OAuthStateData`](/api/mantlejs/auth-oauth/interfaces/oauthstatedata/) | `undefined`> | `undefined` Defined in: [state-store.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/state-store.ts#L13) #### Parameters [Section titled “Parameters”](#parameters-2) ##### state [Section titled “state”](#state-2) `string` #### Returns [Section titled “Returns”](#returns-3) [`OAuthStateData`](/api/mantlejs/auth-oauth/interfaces/oauthstatedata/) | `Promise`<[`OAuthStateData`](/api/mantlejs/auth-oauth/interfaces/oauthstatedata/) | `undefined`> | `undefined` *** ### set() [Section titled “set()”](#set) > **set**(`state`, `data`): `void` | `Promise`<`void`> Defined in: [state-store.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-oauth/src/lib/state-store.ts#L12) #### Parameters [Section titled “Parameters”](#parameters-3) ##### state [Section titled “state”](#state-3) `string` ##### data [Section titled “data”](#data) `Omit`<[`OAuthStateData`](/api/mantlejs/auth-oauth/interfaces/oauthstatedata/), `"expiresAt"`> #### Returns [Section titled “Returns”](#returns-4) `void` | `Promise`<`void`> # @mantlejs/auth-redis ## Interfaces [Section titled “Interfaces”](#interfaces) * [RedisClientLike](/api/mantlejs/auth-redis/interfaces/redisclientlike/) * [RedisRefreshTokenStoreOptions](/api/mantlejs/auth-redis/interfaces/redisrefreshtokenstoreoptions/) * [RedisStateStoreOptions](/api/mantlejs/auth-redis/interfaces/redisstatestoreoptions/) ## Functions [Section titled “Functions”](#functions) * [redisRefreshTokenStore](/api/mantlejs/auth-redis/functions/redisrefreshtokenstore/) * [redisStateStore](/api/mantlejs/auth-redis/functions/redisstatestore/) # redisRefreshTokenStore > **redisRefreshTokenStore**(`client`, `options?`): `RefreshTokenStore` Defined in: [redis-refresh-token-store.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-refresh-token-store.ts#L25) Redis-backed [RefreshTokenStore](/api/create-mantlejs/variables/newproject/) for multi-instance deployments. Layout: each `jti` is a string key holding its `sub` with a TTL matching the token’s `exp` (`SET … EX`), and each subject has a set of its outstanding `jti`s so `revokeAll` can find them. `consume` uses `GETDEL`, so concurrent consumers of the same token — the rotation-theft race — resolve atomically: exactly one caller sees `true`. The per-subject set is re-armed to the newest token’s TTL on every `add`. Under a fixed `refreshExpiresIn` the newest token always expires last, so the set outlives all its members; stale members left by key expiry are harmless (`consume` and `revokeAll` treat a missing `jti` key as gone). ## Parameters [Section titled “Parameters”](#parameters) ### client [Section titled “client”](#client) [`RedisClientLike`](/api/mantlejs/auth-redis/interfaces/redisclientlike/) ### options? [Section titled “options?”](#options) [`RedisRefreshTokenStoreOptions`](/api/mantlejs/auth-redis/interfaces/redisrefreshtokenstoreoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) `RefreshTokenStore` # redisStateStore > **redisStateStore**(`client`, `options?`): `OAuthStateStore` Defined in: [redis-state-store.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-state-store.ts#L20) Redis-backed [OAuthStateStore](/api/create-mantlejs/variables/newproject/) for multi-instance deployments, where the callback request may land on a different instance than the one that started the flow. Entries are written with `SET … EX`, so Redis expires them itself and `cleanup()` is a no-op. ## Parameters [Section titled “Parameters”](#parameters) ### client [Section titled “client”](#client) [`RedisClientLike`](/api/mantlejs/auth-redis/interfaces/redisclientlike/) ### options? [Section titled “options?”](#options) [`RedisStateStoreOptions`](/api/mantlejs/auth-redis/interfaces/redisstatestoreoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) `OAuthStateStore` # RedisClientLike Defined in: [redis-client.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-client.ts#L7) The subset of Redis commands the stores use, shaped to match `ioredis`. Typing structurally keeps `ioredis` out of this package’s runtime dependencies — any client with these signatures works, including `ioredis-mock` in tests. ## Methods [Section titled “Methods”](#methods) ### del() [Section titled “del()”](#del) > **del**(…`keys`): `Promise`<`number`> Defined in: [redis-client.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-client.ts#L12) #### Parameters [Section titled “Parameters”](#parameters) ##### keys [Section titled “keys”](#keys) …`string`\[] #### Returns [Section titled “Returns”](#returns) `Promise`<`number`> *** ### expire() [Section titled “expire()”](#expire) > **expire**(`key`, `seconds`): `Promise`<`number`> Defined in: [redis-client.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-client.ts#L16) #### Parameters [Section titled “Parameters”](#parameters-1) ##### key [Section titled “key”](#key) `string` ##### seconds [Section titled “seconds”](#seconds) `number` #### Returns [Section titled “Returns”](#returns-1) `Promise`<`number`> *** ### get() [Section titled “get()”](#get) > **get**(`key`): `Promise`<`string` | `null`> Defined in: [redis-client.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-client.ts#L9) #### Parameters [Section titled “Parameters”](#parameters-2) ##### key [Section titled “key”](#key-1) `string` #### Returns [Section titled “Returns”](#returns-2) `Promise`<`string` | `null`> *** ### getdel() [Section titled “getdel()”](#getdel) > **getdel**(`key`): `Promise`<`string` | `null`> Defined in: [redis-client.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-client.ts#L11) Atomic get-and-delete (Redis ≥ 6.2) — the consume-once primitive. #### Parameters [Section titled “Parameters”](#parameters-3) ##### key [Section titled “key”](#key-2) `string` #### Returns [Section titled “Returns”](#returns-3) `Promise`<`string` | `null`> *** ### sadd() [Section titled “sadd()”](#sadd) > **sadd**(`key`, …`members`): `Promise`<`number`> Defined in: [redis-client.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-client.ts#L13) #### Parameters [Section titled “Parameters”](#parameters-4) ##### key [Section titled “key”](#key-3) `string` ##### members [Section titled “members”](#members) …`string`\[] #### Returns [Section titled “Returns”](#returns-4) `Promise`<`number`> *** ### set() [Section titled “set()”](#set) > **set**(`key`, `value`, `expiryMode`, `seconds`): `Promise`<`unknown`> Defined in: [redis-client.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-client.ts#L8) #### Parameters [Section titled “Parameters”](#parameters-5) ##### key [Section titled “key”](#key-4) `string` ##### value [Section titled “value”](#value) `string` ##### expiryMode [Section titled “expiryMode”](#expirymode) `"EX"` ##### seconds [Section titled “seconds”](#seconds-1) `number` #### Returns [Section titled “Returns”](#returns-5) `Promise`<`unknown`> *** ### smembers() [Section titled “smembers()”](#smembers) > **smembers**(`key`): `Promise`<`string`\[]> Defined in: [redis-client.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-client.ts#L15) #### Parameters [Section titled “Parameters”](#parameters-6) ##### key [Section titled “key”](#key-5) `string` #### Returns [Section titled “Returns”](#returns-6) `Promise`<`string`\[]> *** ### srem() [Section titled “srem()”](#srem) > **srem**(`key`, …`members`): `Promise`<`number`> Defined in: [redis-client.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-client.ts#L14) #### Parameters [Section titled “Parameters”](#parameters-7) ##### key [Section titled “key”](#key-6) `string` ##### members [Section titled “members”](#members-1) …`string`\[] #### Returns [Section titled “Returns”](#returns-7) `Promise`<`number`> # RedisRefreshTokenStoreOptions Defined in: [redis-refresh-token-store.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-refresh-token-store.ts#L6) ## Properties [Section titled “Properties”](#properties) ### prefix? [Section titled “prefix?”](#prefix) > `optional` **prefix?**: `string` Defined in: [redis-refresh-token-store.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-refresh-token-store.ts#L8) Key prefix. #### Default [Section titled “Default”](#default) ```ts "mantle:auth:refresh:" ``` # RedisStateStoreOptions Defined in: [redis-state-store.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-state-store.ts#L7) ## Properties [Section titled “Properties”](#properties) ### prefix? [Section titled “prefix?”](#prefix) > `optional` **prefix?**: `string` Defined in: [redis-state-store.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-state-store.ts#L11) Key prefix. #### Default [Section titled “Default”](#default) ```ts "mantle:oauth:state:" ``` *** ### ttlMs? [Section titled “ttlMs?”](#ttlms) > `optional` **ttlMs?**: `number` Defined in: [redis-state-store.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-redis/src/lib/redis-state-store.ts#L9) How long a pending authorization state stays valid. #### Default [Section titled “Default”](#default-1) ```ts 600_000 (10 minutes) ``` # @mantlejs/auth-twitter ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [TwitterStrategyConfig](/api/mantlejs/auth-twitter/type-aliases/twitterstrategyconfig/) ## Functions [Section titled “Functions”](#functions) * [twitterStrategy](/api/mantlejs/auth-twitter/functions/twitterstrategy/) # twitterStrategy > **twitterStrategy**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [twitter-strategy.ts:65](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-twitter/src/lib/twitter-strategy.ts#L65) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) `OAuthPluginConfig` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # TwitterStrategyConfig > **TwitterStrategyConfig** = [`newProject`](/api/create-mantlejs/variables/newproject/) Defined in: [twitter-strategy.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth-twitter/src/lib/twitter-strategy.ts#L10) # auth > **auth**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [auth.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/auth.ts#L19) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) [`AuthConfig`](/api/mantlejs/auth/interfaces/authconfig/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # authenticate > **authenticate**(`strategy`, `options?`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [authenticate.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/authenticate.ts#L15) ## Parameters [Section titled “Parameters”](#parameters) ### strategy [Section titled “strategy”](#strategy) `string` ### options? [Section titled “options?”](#options) [`AuthenticateOptions`](/api/mantlejs/auth/interfaces/authenticateoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) # authorizeAgent > **authorizeAgent**(): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [agent.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/agent.ts#L19) A `before` hook authorizing an agent-token call against the token’s capability scope. Deny-by-default (reuses `@mantlejs/mantle`’s `matchesCapabilityScope`, the same matcher `@mantlejs/mcp`’s expose map is built on) — a call whose `path`+`method` isn’t covered by the token’s scope throws `Forbidden`. Opt-in per route: this is the *only* hook that accepts an agent token. `authenticate("jwt")` rejects a `type: "agent"` payload outright, so a route with no `authorizeAgent()` hook attached can never be reached with an agent token, no matter what scope it carries. Skips (passes through) for internal calls — `params.provider` undefined — matching `authenticate("jwt")`’s own convention. ## Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) # memoryAgentTokenStore > **memoryAgentTokenStore**(): [`AgentTokenStore`](/api/mantlejs/auth/interfaces/agenttokenstore/) Defined in: [agent-token-store.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/agent-token-store.ts#L9) In-memory `AgentTokenStore`. Tracks issued agent-token ids so `revokeAgentToken()` can revoke a token before its JWT’s natural expiry — the same problem `memoryRefreshTokenStore` solves for refresh tokens. Multi-instance deployments must inject a shared implementation (e.g. a Redis-backed store); this default only knows about tokens issued by the same process. ## Returns [Section titled “Returns”](#returns) [`AgentTokenStore`](/api/mantlejs/auth/interfaces/agenttokenstore/) # memoryRefreshTokenStore > **memoryRefreshTokenStore**(): [`RefreshTokenStore`](/api/mantlejs/auth/interfaces/refreshtokenstore/) Defined in: [refresh-token-store.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/refresh-token-store.ts#L16) In-memory [RefreshTokenStore](/api/mantlejs/auth/interfaces/refreshtokenstore/). Suitable for a single process only — multi-instance deployments (Cloud Run) must inject a shared store via `AuthConfig.refreshTokenStore`. Expired entries are pruned opportunistically on `add` so the map cannot grow unbounded under normal issuance traffic. ## Returns [Section titled “Returns”](#returns) [`RefreshTokenStore`](/api/mantlejs/auth/interfaces/refreshtokenstore/) # sanitizeUser > **sanitizeUser**(`fields?`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [sanitize-user.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/sanitize-user.ts#L14) Strip sensitive fields from a result before it’s serialized back to a client. Gated on `context.params.provider`, the same “no provider = internal/trusted call” convention `authenticate()` uses — an unconditional strip would also scrub results returned from internal calls, breaking `@mantlejs/auth-local`’s credential lookup (it reads the stored password hash off `app.service("users").find(...)` to verify against, and that call shares this same service’s `after` hooks). ## Parameters [Section titled “Parameters”](#parameters) ### fields? [Section titled “fields?”](#fields) `string`\[] = `DEFAULT_SENSITIVE_FIELDS` ## Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) # AgentTokenOptions Defined in: [types.ts:69](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L69) ## Properties [Section titled “Properties”](#properties) ### expiresIn? [Section titled “expiresIn?”](#expiresin) > `optional` **expiresIn?**: `string` | `number` Defined in: [types.ts:71](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L71) Agent-token lifetime. Strings use ms format (`"15m"`, `"1h"`); numbers are seconds. #### Default [Section titled “Default”](#default) ```ts "15m" ``` # AgentTokenStore Defined in: [types.ts:53](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L53) Tracks issued agent-token ids so they can be revoked before their JWT’s natural expiry — the same problem `RefreshTokenStore` solves for refresh tokens, applied to short-lived, capability-scoped agent tokens instead. Methods are sync-or-async so a Redis-backed store can be injected without an interface change. ## Methods [Section titled “Methods”](#methods) ### add() [Section titled “add()”](#add) > **add**(`id`, `expiresAt`): `void` | `Promise`<`void`> Defined in: [types.ts:55](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L55) Record an issued agent token. `expiresAt` is the JWT `exp` in epoch seconds. #### Parameters [Section titled “Parameters”](#parameters) ##### id [Section titled “id”](#id) `string` ##### expiresAt [Section titled “expiresAt”](#expiresat) `number` #### Returns [Section titled “Returns”](#returns) `void` | `Promise`<`void`> *** ### isValid() [Section titled “isValid()”](#isvalid) > **isValid**(`id`): `boolean` | `Promise`<`boolean`> Defined in: [types.ts:57](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L57) True unless `id` was never issued on this store, or has since been revoked/reaped. #### Parameters [Section titled “Parameters”](#parameters-1) ##### id [Section titled “id”](#id-1) `string` #### Returns [Section titled “Returns”](#returns-1) `boolean` | `Promise`<`boolean`> *** ### revoke() [Section titled “revoke()”](#revoke) > **revoke**(`id`): `void` | `Promise`<`void`> Defined in: [types.ts:59](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L59) Revoke one agent token immediately, independent of its JWT expiry. #### Parameters [Section titled “Parameters”](#parameters-2) ##### id [Section titled “id”](#id-2) `string` #### Returns [Section titled “Returns”](#returns-2) `void` | `Promise`<`void`> # AuthConfig Defined in: [types.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L3) ## Properties [Section titled “Properties”](#properties) ### agentTokenStore? [Section titled “agentTokenStore?”](#agenttokenstore) > `optional` **agentTokenStore?**: [`AgentTokenStore`](/api/mantlejs/auth/interfaces/agenttokenstore/) Defined in: [types.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L21) Storage for outstanding agent-token ids, enabling `revokeAgentToken()` to revoke a token before its JWT expiry. Defaults to an in-memory store; multi-instance deployments must inject a shared implementation (see D-6, same rationale as `refreshTokenStore`). *** ### algorithms? [Section titled “algorithms?”](#algorithms) > `optional` **algorithms?**: `string`\[] Defined in: [types.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L5) *** ### audience? [Section titled “audience?”](#audience) > `optional` **audience?**: `string` | `string`\[] Defined in: [types.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L8) *** ### expiresIn? [Section titled “expiresIn?”](#expiresin) > `optional` **expiresIn?**: `string` | `number` Defined in: [types.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L6) *** ### issuer? [Section titled “issuer?”](#issuer) > `optional` **issuer?**: `string` Defined in: [types.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L7) *** ### refreshExpiresIn? [Section titled “refreshExpiresIn?”](#refreshexpiresin) > `optional` **refreshExpiresIn?**: `string` | `number` Defined in: [types.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L10) Refresh-token TTL. #### Default [Section titled “Default”](#default) ```ts "30d" ``` *** ### refreshTokenStore? [Section titled “refreshTokenStore?”](#refreshtokenstore) > `optional` **refreshTokenStore?**: [`RefreshTokenStore`](/api/mantlejs/auth/interfaces/refreshtokenstore/) Defined in: [types.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L15) Storage for outstanding refresh-token ids. Defaults to an in-memory store; multi-instance deployments must inject a shared implementation (see D-6). *** ### secret [Section titled “secret”](#secret) > **secret**: `string` Defined in: [types.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L4) # AuthEngine Defined in: [types.ts:102](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L102) ## Properties [Section titled “Properties”](#properties) ### config [Section titled “config”](#config) > `readonly` **config**: [`AuthConfig`](/api/mantlejs/auth/interfaces/authconfig/) Defined in: [types.ts:103](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L103) ## Methods [Section titled “Methods”](#methods) ### authenticate() [Section titled “authenticate()”](#authenticate) > **authenticate**(`strategyName`, `data`, `params`): `Promise`<[`AuthResult`](/api/mantlejs/auth/interfaces/authresult/)> Defined in: [types.ts:115](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L115) #### Parameters [Section titled “Parameters”](#parameters) ##### strategyName [Section titled “strategyName”](#strategyname) `string` ##### data [Section titled “data”](#data) `Record`<`string`, `unknown`> ##### params [Section titled “params”](#params) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns) `Promise`<[`AuthResult`](/api/mantlejs/auth/interfaces/authresult/)> *** ### createJwt() [Section titled “createJwt()”](#createjwt) > **createJwt**(`payload`, `options?`): `string` Defined in: [types.ts:104](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L104) #### Parameters [Section titled “Parameters”](#parameters-1) ##### payload [Section titled “payload”](#payload) [`JwtPayload`](/api/mantlejs/auth/interfaces/jwtpayload/) ##### options? [Section titled “options?”](#options) ###### expiresIn? [Section titled “expiresIn?”](#expiresin) `string` | `number` #### Returns [Section titled “Returns”](#returns-1) `string` *** ### createTokenPair() [Section titled “createTokenPair()”](#createtokenpair) > **createTokenPair**(`sub`, `accessExtra?`): `Promise`<[`TokenPair`](/api/mantlejs/auth/interfaces/tokenpair/)> Defined in: [types.ts:113](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L113) Issue an access + refresh token pair for a subject. The refresh token carries `{ sub, type: "refresh", jti }`, is signed with `refreshExpiresIn`, and its `jti` is recorded in the RefreshTokenStore before the pair is returned. All strategies must issue refresh tokens through this helper so `jti` bookkeeping stays uniform. #### Parameters [Section titled “Parameters”](#parameters-2) ##### sub [Section titled “sub”](#sub) `string` ##### accessExtra? [Section titled “accessExtra?”](#accessextra) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-2) `Promise`<[`TokenPair`](/api/mantlejs/auth/interfaces/tokenpair/)> *** ### isAgentTokenValid() [Section titled “isAgentTokenValid()”](#isagenttokenvalid) > **isAgentTokenValid**(`id`): `Promise`<`boolean`> Defined in: [types.ts:130](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L130) True unless the agent token id was revoked, or was never issued on this engine’s store. #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id) `string` #### Returns [Section titled “Returns”](#returns-3) `Promise`<`boolean`> *** ### issueAgentToken() [Section titled “issueAgentToken()”](#issueagenttoken) > **issueAgentToken**(`scope`, `delegatingUserId`, `options?`): `Promise`<[`IssuedAgentToken`](/api/mantlejs/auth/interfaces/issuedagenttoken/)> Defined in: [types.ts:122](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L122) Mint a short-lived, capability-scoped agent token delegated from `delegatingUserId`. The token is a JWT carrying `{ sub: , type: "agent", scope, delegatingUserId }` — only `authorizeAgent()` accepts it, never `authenticate("jwt")`. Recorded in the `AgentTokenStore` so it can be revoked before its natural expiry via `revokeAgentToken`. #### Parameters [Section titled “Parameters”](#parameters-4) ##### scope [Section titled “scope”](#scope) [`CapabilityScope`](/api/mantlejs/mantle/type-aliases/capabilityscope/) ##### delegatingUserId [Section titled “delegatingUserId”](#delegatinguserid) `string` ##### options? [Section titled “options?”](#options-1) [`AgentTokenOptions`](/api/mantlejs/auth/interfaces/agenttokenoptions/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<[`IssuedAgentToken`](/api/mantlejs/auth/interfaces/issuedagenttoken/)> *** ### registerStrategy() [Section titled “registerStrategy()”](#registerstrategy) > **registerStrategy**(`strategy`): `void` Defined in: [types.ts:114](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L114) #### Parameters [Section titled “Parameters”](#parameters-5) ##### strategy [Section titled “strategy”](#strategy) [`AuthStrategy`](/api/mantlejs/auth/interfaces/authstrategy/) #### Returns [Section titled “Returns”](#returns-5) `void` *** ### revokeAgentToken() [Section titled “revokeAgentToken()”](#revokeagenttoken) > **revokeAgentToken**(`id`): `Promise`<`void`> Defined in: [types.ts:128](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L128) Revoke one agent token immediately, independent of its JWT expiry. #### Parameters [Section titled “Parameters”](#parameters-6) ##### id [Section titled “id”](#id-1) `string` #### Returns [Section titled “Returns”](#returns-6) `Promise`<`void`> *** ### verifyJwt() [Section titled “verifyJwt()”](#verifyjwt) > **verifyJwt**(`token`): [`JwtPayload`](/api/mantlejs/auth/interfaces/jwtpayload/) Defined in: [types.ts:105](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L105) #### Parameters [Section titled “Parameters”](#parameters-7) ##### token [Section titled “token”](#token) `string` #### Returns [Section titled “Returns”](#returns-7) [`JwtPayload`](/api/mantlejs/auth/interfaces/jwtpayload/) # AuthenticateOptions Defined in: [authenticate.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/authenticate.ts#L6) ## Properties [Section titled “Properties”](#properties) ### entity? [Section titled “entity?”](#entity) > `optional` **entity?**: `string` Defined in: [authenticate.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/authenticate.ts#L12) Service path to resolve the authenticated user record from. When set, `params.user` becomes `app.service(entity).get(payload.sub)` (internal call, no provider) and the raw JWT payload moves to `params.authPayload`. When omitted, `params.user` is the raw payload. # AuthResult Defined in: [types.ts:92](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L92) ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `unknown` ## Properties [Section titled “Properties”](#properties) ### accessToken [Section titled “accessToken”](#accesstoken) > **accessToken**: `string` Defined in: [types.ts:93](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L93) # AuthStrategy Defined in: [types.ts:97](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L97) ## Properties [Section titled “Properties”](#properties) ### name [Section titled “name”](#name) > `readonly` **name**: `string` Defined in: [types.ts:98](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L98) ## Methods [Section titled “Methods”](#methods) ### authenticate() [Section titled “authenticate()”](#authenticate) > **authenticate**(`data`, `params`): `Promise`<[`AuthResult`](/api/mantlejs/auth/interfaces/authresult/)> Defined in: [types.ts:99](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L99) #### Parameters [Section titled “Parameters”](#parameters) ##### data [Section titled “data”](#data) `Record`<`string`, `unknown`> ##### params [Section titled “params”](#params) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns) `Promise`<[`AuthResult`](/api/mantlejs/auth/interfaces/authresult/)> # IssuedAgentToken Defined in: [types.ts:74](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L74) ## Properties [Section titled “Properties”](#properties) ### accessToken [Section titled “accessToken”](#accesstoken) > **accessToken**: `string` Defined in: [types.ts:75](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L75) *** ### expiresAt [Section titled “expiresAt”](#expiresat) > **expiresAt**: `number` Defined in: [types.ts:79](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L79) Epoch seconds the token expires at. *** ### id [Section titled “id”](#id) > **id**: `string` Defined in: [types.ts:77](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L77) The agent’s id — pass to `revokeAgentToken()` to revoke this token early. # JwtPayload Defined in: [types.ts:82](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L82) ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `unknown` ## Properties [Section titled “Properties”](#properties) ### aud? [Section titled “aud?”](#aud) > `optional` **aud?**: `string` | `string`\[] Defined in: [types.ts:87](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L87) *** ### exp? [Section titled “exp?”](#exp) > `optional` **exp?**: `number` Defined in: [types.ts:85](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L85) *** ### iat? [Section titled “iat?”](#iat) > `optional` **iat?**: `number` Defined in: [types.ts:84](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L84) *** ### iss? [Section titled “iss?”](#iss) > `optional` **iss?**: `string` Defined in: [types.ts:86](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L86) *** ### jti? [Section titled “jti?”](#jti) > `optional` **jti?**: `string` Defined in: [types.ts:88](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L88) *** ### sub? [Section titled “sub?”](#sub) > `optional` **sub?**: `string` Defined in: [types.ts:83](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L83) # RefreshTokenStore Defined in: [types.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L29) Tracks issued refresh tokens by `jti` so rotation and revocation work. Methods are sync-or-async so a Redis-backed store can be injected without an interface change. ## Methods [Section titled “Methods”](#methods) ### add() [Section titled “add()”](#add) > **add**(`jti`, `sub`, `expiresAt`): `void` | `Promise`<`void`> Defined in: [types.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L31) Record an issued refresh token. `expiresAt` is the JWT `exp` in epoch seconds. #### Parameters [Section titled “Parameters”](#parameters) ##### jti [Section titled “jti”](#jti) `string` ##### sub [Section titled “sub”](#sub) `string` ##### expiresAt [Section titled “expiresAt”](#expiresat) `number` #### Returns [Section titled “Returns”](#returns) `void` | `Promise`<`void`> *** ### consume() [Section titled “consume()”](#consume) > **consume**(`jti`): `boolean` | `Promise`<`boolean`> Defined in: [types.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L37) Atomically remove `jti`, returning whether it was present. A `false` return for a token whose JWT still verifies means the token was already used — treat it as theft and revoke the family. #### Parameters [Section titled “Parameters”](#parameters-1) ##### jti [Section titled “jti”](#jti-1) `string` #### Returns [Section titled “Returns”](#returns-1) `boolean` | `Promise`<`boolean`> *** ### revokeAll() [Section titled “revokeAll()”](#revokeall) > **revokeAll**(`sub`): `void` | `Promise`<`void`> Defined in: [types.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L39) Revoke every outstanding refresh token for a subject. #### Parameters [Section titled “Parameters”](#parameters-2) ##### sub [Section titled “sub”](#sub-1) `string` #### Returns [Section titled “Returns”](#returns-2) `void` | `Promise`<`void`> # TokenPair Defined in: [types.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L42) ## Properties [Section titled “Properties”](#properties) ### accessToken [Section titled “accessToken”](#accesstoken) > **accessToken**: `string` Defined in: [types.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L43) *** ### refreshToken [Section titled “refreshToken”](#refreshtoken) > **refreshToken**: `string` Defined in: [types.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L44) # AgentPrincipal > **AgentPrincipal** = [`AgentContext`](/api/mantlejs/mantle/interfaces/agentcontext/) Defined in: [types.ts:67](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/auth/src/lib/types.ts#L67) The decoded shape of an agent token’s payload, and of `HookContext.agent` once `authorizeAgent()` accepts it. `id` is the agent’s own id (the token’s `sub`/store key) — distinct from `delegatingUserId`, the user who minted the token. # @mantlejs/cli ## Interfaces [Section titled “Interfaces”](#interfaces) * [AddOptions](/api/mantlejs/cli/interfaces/addoptions/) * [AuthenticationGeneratorOptions](/api/mantlejs/cli/interfaces/authenticationgeneratoroptions/) * [GenerateOptions](/api/mantlejs/cli/interfaces/generateoptions/) * [MigrationGeneratorOptions](/api/mantlejs/cli/interfaces/migrationgeneratoroptions/) * [NewProjectOptions](/api/mantlejs/cli/interfaces/newprojectoptions/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [Auth](/api/mantlejs/cli/type-aliases/auth/) * [Database](/api/mantlejs/cli/type-aliases/database/) * [GeneratorName](/api/mantlejs/cli/type-aliases/generatorname/) * [PackageManager](/api/mantlejs/cli/type-aliases/packagemanager/) * [Transport](/api/mantlejs/cli/type-aliases/transport/) ## Functions [Section titled “Functions”](#functions) * [addPackage](/api/mantlejs/cli/functions/addpackage/) * [newProject](/api/mantlejs/cli/functions/newproject/) # addPackage > **addPackage**(`packageName`, `options?`): `Promise`<`void`> Defined in: [add.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/add.ts#L30) ## Parameters [Section titled “Parameters”](#parameters) ### packageName [Section titled “packageName”](#packagename) `string` ### options? [Section titled “options?”](#options) [`AddOptions`](/api/mantlejs/cli/interfaces/addoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) `Promise`<`void`> # newProject > **newProject**(`projectName`, `rawOptions`): `Promise`<`void`> Defined in: [new.ts:38](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L38) ## Parameters [Section titled “Parameters”](#parameters) ### projectName [Section titled “projectName”](#projectname) `string` ### rawOptions [Section titled “rawOptions”](#rawoptions) [`NewProjectOptions`](/api/mantlejs/cli/interfaces/newprojectoptions/) ## Returns [Section titled “Returns”](#returns) `Promise`<`void`> # AddOptions Defined in: [add.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/add.ts#L7) ## Properties [Section titled “Properties”](#properties) ### cwd? [Section titled “cwd?”](#cwd) > `optional` **cwd?**: `string` Defined in: [add.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/add.ts#L8) # AuthenticationGeneratorOptions Defined in: [generators/authentication.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/generators/authentication.ts#L5) ## Properties [Section titled “Properties”](#properties) ### cwd? [Section titled “cwd?”](#cwd) > `optional` **cwd?**: `string` Defined in: [generators/authentication.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/generators/authentication.ts#L6) # GenerateOptions Defined in: [generate.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/generate.ts#L17) ## Properties [Section titled “Properties”](#properties) ### cwd? [Section titled “cwd?”](#cwd) > `optional` **cwd?**: `string` Defined in: [generate.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/generate.ts#L19) *** ### directory? [Section titled “directory?”](#directory) > `optional` **directory?**: `string` Defined in: [generate.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/generate.ts#L18) # MigrationGeneratorOptions Defined in: [generators/migration.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/generators/migration.ts#L5) ## Properties [Section titled “Properties”](#properties) ### cwd? [Section titled “cwd?”](#cwd) > `optional` **cwd?**: `string` Defined in: [generators/migration.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/generators/migration.ts#L6) # NewProjectOptions Defined in: [new.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L23) ## Properties [Section titled “Properties”](#properties) ### auth? [Section titled “auth?”](#auth) > `optional` **auth?**: [`Auth`](/api/mantlejs/cli/type-aliases/auth/) Defined in: [new.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L26) *** ### cors? [Section titled “cors?”](#cors) > `optional` **cors?**: `boolean` Defined in: [new.ts:28](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L28) Enable CORS on the transport (`@mantlejs/express`’s `cors` option). #### Default [Section titled “Default”](#default) ```ts false ``` *** ### cwd? [Section titled “cwd?”](#cwd) > `optional` **cwd?**: `string` Defined in: [new.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L33) *** ### database? [Section titled “database?”](#database) > `optional` **database?**: [`Database`](/api/mantlejs/cli/type-aliases/database/) Defined in: [new.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L25) *** ### packageManager? [Section titled “packageManager?”](#packagemanager) > `optional` **packageManager?**: [`PackageManager`](/api/mantlejs/cli/type-aliases/packagemanager/) Defined in: [new.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L31) *** ### redis? [Section titled “redis?”](#redis) > `optional` **redis?**: `boolean` Defined in: [new.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L30) Wire `@mantlejs/auth-redis` state/refresh-token stores. Ignored when `auth` is `"none"`. #### Default [Section titled “Default”](#default-1) ```ts false ``` *** ### skipInstall? [Section titled “skipInstall?”](#skipinstall) > `optional` **skipInstall?**: `boolean` Defined in: [new.ts:32](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L32) *** ### transport? [Section titled “transport?”](#transport) > `optional` **transport?**: `"express"` Defined in: [new.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L24) # Auth > **Auth** = `"local"` | `"google"` | `"github"` | `"facebook"` | `"apple"` | `"microsoft"` | `"linkedin"` | `"twitter"` | `"none"` Defined in: [new.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L10) # Database > **Database** = `"pg"` | `"sqlite"` | `"mongodb"` | `"none"` Defined in: [new.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L9) # GeneratorName > **GeneratorName** = `"service"` | `"hook"` | `"repository"` | `"authentication"` | `"migration"` Defined in: [generate.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/generate.ts#L7) # PackageManager > **PackageManager** = `"npm"` | `"yarn"` | `"pnpm"` Defined in: [new.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L11) # Transport > **Transport** = `"express"` Defined in: [new.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/cli/src/lib/new.ts#L8) # @mantlejs/client ## Classes [Section titled “Classes”](#classes) * [BatchScheduler](/api/mantlejs/client/classes/batchscheduler/) * [MantleClient](/api/mantlejs/client/classes/mantleclient/) * [MantleClientError](/api/mantlejs/client/classes/mantleclienterror/) * [ServiceClient](/api/mantlejs/client/classes/serviceclient/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [AuthCredentials](/api/mantlejs/client/interfaces/authcredentials/) * [AuthResult](/api/mantlejs/client/interfaces/authresult/) * [BatchCall](/api/mantlejs/client/interfaces/batchcall/) * [BatchOptions](/api/mantlejs/client/interfaces/batchoptions/) * [BatchRestDispatcher](/api/mantlejs/client/interfaces/batchrestdispatcher/) * [BatchResult](/api/mantlejs/client/interfaces/batchresult/) * [ClientOptions](/api/mantlejs/client/interfaces/clientoptions/) * [ClientParams](/api/mantlejs/client/interfaces/clientparams/) * [MantleClientErrorFields](/api/mantlejs/client/interfaces/mantleclienterrorfields/) * [Paginated](/api/mantlejs/client/interfaces/paginated/) * [RestDispatcher](/api/mantlejs/client/interfaces/restdispatcher/) * [SimilarQuery](/api/mantlejs/client/interfaces/similarquery/) * [SocketLike](/api/mantlejs/client/interfaces/socketlike/) * [SocketOptions](/api/mantlejs/client/interfaces/socketoptions/) * [TokenStorage](/api/mantlejs/client/interfaces/tokenstorage/) * [UploadOptions](/api/mantlejs/client/interfaces/uploadoptions/) * [UploadProgress](/api/mantlejs/client/interfaces/uploadprogress/) * [UploadTransportOptions](/api/mantlejs/client/interfaces/uploadtransportoptions/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [ClientEvent](/api/mantlejs/client/type-aliases/clientevent/) * [Id](/api/mantlejs/client/type-aliases/id/) * [ServiceEvent](/api/mantlejs/client/type-aliases/serviceevent/) * [SocketFactory](/api/mantlejs/client/type-aliases/socketfactory/) ## Functions [Section titled “Functions”](#functions) * [defaultStorage](/api/mantlejs/client/functions/defaultstorage/) * [errorFromResponse](/api/mantlejs/client/functions/errorfromresponse/) * [mantle](/api/mantlejs/client/functions/mantle/) * [memoryStorage](/api/mantlejs/client/functions/memorystorage/) * [serializeQuery](/api/mantlejs/client/functions/serializequery/) # BatchScheduler Defined in: [batch-scheduler.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/batch-scheduler.ts#L25) Coalesces service calls made within the same window (default: same microtask tick) into a single `POST /batch` request. Each caller’s promise settles independently from its own `BatchResult` entry. Queues longer than `maxSize` split into multiple requests instead of erroring client-side. ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new BatchScheduler**(`rest`, `options?`, `refresh?`): `BatchScheduler` Defined in: [batch-scheduler.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/batch-scheduler.ts#L31) #### Parameters [Section titled “Parameters”](#parameters) ##### rest [Section titled “rest”](#rest) [`BatchRestDispatcher`](/api/mantlejs/client/interfaces/batchrestdispatcher/) ##### options? [Section titled “options?”](#options) ###### maxSize? [Section titled “maxSize?”](#maxsize) `number` ###### windowMs? [Section titled “windowMs?”](#windowms) `number` ##### refresh? [Section titled “refresh?”](#refresh) () => `Promise`<`boolean`> Token refresh — entries failing with a per-entry 401 are retried once after a successful refresh. #### Returns [Section titled “Returns”](#returns) `BatchScheduler` ## Methods [Section titled “Methods”](#methods) ### enqueue() [Section titled “enqueue()”](#enqueue) > **enqueue**(`call`): `Promise`<`unknown`> Defined in: [batch-scheduler.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/batch-scheduler.ts#L41) #### Parameters [Section titled “Parameters”](#parameters-1) ##### call [Section titled “call”](#call) [`BatchCall`](/api/mantlejs/client/interfaces/batchcall/) #### Returns [Section titled “Returns”](#returns-1) `Promise`<`unknown`> # MantleClient Defined in: [client.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L26) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new MantleClient**(`options`): `MantleClient` Defined in: [client.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L41) #### Parameters [Section titled “Parameters”](#parameters) ##### options [Section titled “options”](#options) [`ClientOptions`](/api/mantlejs/client/interfaces/clientoptions/) #### Returns [Section titled “Returns”](#returns) `MantleClient` ## Accessors [Section titled “Accessors”](#accessors) ### url [Section titled “url”](#url) #### Get Signature [Section titled “Get Signature”](#get-signature) > **get** **url**(): `string` Defined in: [client.ts:104](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L104) The server’s base URL, trailing slashes removed — handy for building links to non-service routes. ##### Returns [Section titled “Returns”](#returns-1) `string` ## Methods [Section titled “Methods”](#methods) ### authenticate() [Section titled “authenticate()”](#authenticate) > **authenticate**(`credentials`): `Promise`<[`AuthResult`](/api/mantlejs/client/interfaces/authresult/)> Defined in: [client.ts:67](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L67) #### Parameters [Section titled “Parameters”](#parameters-1) ##### credentials [Section titled “credentials”](#credentials) [`AuthCredentials`](/api/mantlejs/client/interfaces/authcredentials/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<[`AuthResult`](/api/mantlejs/client/interfaces/authresult/)> *** ### getAccessToken() [Section titled “getAccessToken()”](#getaccesstoken) > **getAccessToken**(): `string` | `undefined` Defined in: [client.ts:108](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L108) #### Returns [Section titled “Returns”](#returns-3) `string` | `undefined` *** ### isAuthenticated() [Section titled “isAuthenticated()”](#isauthenticated) > **isAuthenticated**(): `Promise`<`boolean`> Defined in: [client.ts:120](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L120) Whether a persisted session exists, hydrating the in-memory access token from storage first if nothing has triggered that yet. Use this on app startup instead of `getAccessToken()` — that one is synchronous and returns `undefined` until hydration has happened (normally the side effect of an authenticated REST call), even when a valid token is sitting in storage from a previous session. A UI that checks `getAccessToken()` on first render will show its logged-out state on every page load/refresh regardless of a persisted session. #### Returns [Section titled “Returns”](#returns-4) `Promise`<`boolean`> *** ### logout() [Section titled “logout()”](#logout) > **logout**(): `Promise`<`void`> Defined in: [client.ts:92](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L92) #### Returns [Section titled “Returns”](#returns-5) `Promise`<`void`> *** ### off() [Section titled “off()”](#off) > **off**(`event`, `handler`): `this` Defined in: [client.ts:129](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L129) #### Parameters [Section titled “Parameters”](#parameters-2) ##### event [Section titled “event”](#event) [`ClientEvent`](/api/mantlejs/client/type-aliases/clientevent/) ##### handler [Section titled “handler”](#handler) () => `void` #### Returns [Section titled “Returns”](#returns-6) `this` *** ### on() [Section titled “on()”](#on) > **on**(`event`, `handler`): `this` Defined in: [client.ts:124](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L124) #### Parameters [Section titled “Parameters”](#parameters-3) ##### event [Section titled “event”](#event-1) [`ClientEvent`](/api/mantlejs/client/type-aliases/clientevent/) ##### handler [Section titled “handler”](#handler-1) () => `void` #### Returns [Section titled “Returns”](#returns-7) `this` *** ### request() [Section titled “request()”](#request) > **request**<`R`>(`method`, `path`, `data`, `params?`): `Promise`<`R`> Defined in: [client.ts:135](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L135) REST dispatch used by every `ServiceClient` method: bearer auth, one refresh-retry on 401, typed errors. #### Type Parameters [Section titled “Type Parameters”](#type-parameters) ##### R [Section titled “R”](#r) `R` #### Parameters [Section titled “Parameters”](#parameters-4) ##### method [Section titled “method”](#method) `string` ##### path [Section titled “path”](#path) `string` ##### data [Section titled “data”](#data) `unknown` ##### params? [Section titled “params?”](#params) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) #### Returns [Section titled “Returns”](#returns-8) `Promise`<`R`> *** ### service() [Section titled “service()”](#service) > **service**<`T`>(`path`): [`ServiceClient`](/api/mantlejs/client/classes/serviceclient/)<`T`> Defined in: [client.ts:57](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L57) #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### T [Section titled “T”](#t) `T` = `unknown` #### Parameters [Section titled “Parameters”](#parameters-5) ##### path [Section titled “path”](#path-1) `string` #### Returns [Section titled “Returns”](#returns-9) [`ServiceClient`](/api/mantlejs/client/classes/serviceclient/)<`T`> *** ### setTokens() [Section titled “setTokens()”](#settokens) > **setTokens**(`tokens`): `Promise`<`void`> Defined in: [client.ts:86](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L86) Hydrate the client from a token pair obtained outside `authenticate()` — e.g. an OAuth provider’s redirect-back callback landing tokens in the URL fragment. Stores them and emits `'authenticated'`, same as a successful `authenticate()` call. #### Parameters [Section titled “Parameters”](#parameters-6) ##### tokens [Section titled “tokens”](#tokens) ###### accessToken [Section titled “accessToken”](#accesstoken) `string` ###### refreshToken? [Section titled “refreshToken?”](#refreshtoken) `string` #### Returns [Section titled “Returns”](#returns-10) `Promise`<`void`> *** ### upload() [Section titled “upload()”](#upload) > **upload**<`R`>(`method`, `path`, `body`, `options?`): `Promise`<`R`> Defined in: [client.ts:152](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L152) `multipart/form-data` dispatch used by `ServiceClient.upload()`: bearer auth, one refresh-retry on 401, typed errors — the same guarantees as `request()`. The body is rebuilt for the retry, since a consumed `FormData` stream can’t be replayed by every transport. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-2) ##### R [Section titled “R”](#r-1) `R` #### Parameters [Section titled “Parameters”](#parameters-7) ##### method [Section titled “method”](#method-1) `string` ##### path [Section titled “path”](#path-2) `string` ##### body [Section titled “body”](#body) () => `FormData` ##### options? [Section titled “options?”](#options-1) [`UploadTransportOptions`](/api/mantlejs/client/interfaces/uploadtransportoptions/) = `{}` #### Returns [Section titled “Returns”](#returns-11) `Promise`<`R`> # MantleClientError Defined in: [errors.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L26) Client-side counterpart of the server’s `MantleError` JSON format. `name` mirrors the server error class (`"NotFound"`, `"BadRequest"`, …), `code` the HTTP status. `hint`, when present, is the server’s actionable guidance on how to make the request succeed. ## Extends [Section titled “Extends”](#extends) * `Error` ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new MantleClientError**(`message`, `code`, `name`, `fields?`): `MantleClientError` Defined in: [errors.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L33) #### Parameters [Section titled “Parameters”](#parameters) ##### message [Section titled “message”](#message) `string` ##### code [Section titled “code”](#code) `number` ##### name [Section titled “name”](#name) `string` ##### fields? [Section titled “fields?”](#fields) [`MantleClientErrorFields`](/api/mantlejs/client/interfaces/mantleclienterrorfields/) = `{}` #### Returns [Section titled “Returns”](#returns) `MantleClientError` #### Overrides [Section titled “Overrides”](#overrides) `Error.constructor` ## Properties [Section titled “Properties”](#properties) ### className? [Section titled “className?”](#classname) > `readonly` `optional` **className?**: `string` Defined in: [errors.ts:28](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L28) *** ### code [Section titled “code”](#code-1) > `readonly` **code**: `number` Defined in: [errors.ts:27](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L27) *** ### data? [Section titled “data?”](#data) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L29) *** ### errors? [Section titled “errors?”](#errors) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L30) *** ### hint? [Section titled “hint?”](#hint) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L31) # ServiceClient Defined in: [service-client.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L35) Client-side counterpart of the server’s `Service` — the same six method names, dispatched as REST calls, plus the `similar()` vector-search convention and real-time `on()`/`off()` service events over socket.io. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` = `unknown` ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new ServiceClient**<`T`>(`path`, `rest`, `sockets?`, `scheduler?`): `ServiceClient`<`T`> Defined in: [service-client.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L36) #### Parameters [Section titled “Parameters”](#parameters) ##### path [Section titled “path”](#path) `string` ##### rest [Section titled “rest”](#rest) [`RestDispatcher`](/api/mantlejs/client/interfaces/restdispatcher/) ##### sockets? [Section titled “sockets?”](#sockets) `SocketManager` ##### scheduler? [Section titled “scheduler?”](#scheduler) [`BatchScheduler`](/api/mantlejs/client/classes/batchscheduler/) #### Returns [Section titled “Returns”](#returns) `ServiceClient`<`T`> ## Accessors [Section titled “Accessors”](#accessors) ### realtime [Section titled “realtime”](#realtime) #### Get Signature [Section titled “Get Signature”](#get-signature) > **get** **realtime**(): `boolean` Defined in: [service-client.ts:118](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L118) True when the client was created with the `socket` option — `on()`/`off()` are available. ##### Returns [Section titled “Returns”](#returns-1) `boolean` ## Methods [Section titled “Methods”](#methods) ### create() [Section titled “create()”](#create) > **create**(`data`, `params?`): `Promise`<`T`> Defined in: [service-client.ts:53](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L53) #### Parameters [Section titled “Parameters”](#parameters-1) ##### data [Section titled “data”](#data) `Partial`<`T`> ##### params? [Section titled “params?”](#params) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<`T`> *** ### find() [Section titled “find()”](#find) > **find**(`params?`): `Promise`<`T`\[] | [`Paginated`](/api/mantlejs/client/interfaces/paginated/)<`T`>> Defined in: [service-client.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L43) #### Parameters [Section titled “Parameters”](#parameters-2) ##### params? [Section titled “params?”](#params-1) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`\[] | [`Paginated`](/api/mantlejs/client/interfaces/paginated/)<`T`>> *** ### get() [Section titled “get()”](#get) > **get**(`id`, `params?`): `Promise`<`T`> Defined in: [service-client.ts:48](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L48) #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/client/type-aliases/id/) ##### params? [Section titled “params?”](#params-2) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T`> *** ### off() [Section titled “off()”](#off) > **off**(`event`, `handler`): `this` Defined in: [service-client.ts:127](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L127) #### Parameters [Section titled “Parameters”](#parameters-4) ##### event [Section titled “event”](#event) [`ServiceEvent`](/api/mantlejs/client/type-aliases/serviceevent/) ##### handler [Section titled “handler”](#handler) (`data`) => `void` #### Returns [Section titled “Returns”](#returns-5) `this` *** ### on() [Section titled “on()”](#on) > **on**(`event`, `handler`): `this` Defined in: [service-client.ts:122](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L122) #### Parameters [Section titled “Parameters”](#parameters-5) ##### event [Section titled “event”](#event-1) [`ServiceEvent`](/api/mantlejs/client/type-aliases/serviceevent/) ##### handler [Section titled “handler”](#handler-1) (`data`) => `void` #### Returns [Section titled “Returns”](#returns-6) `this` *** ### patch() [Section titled “patch()”](#patch) > **patch**(`id`, `data`, `params?`): `Promise`<`T`> Defined in: [service-client.ts:63](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L63) #### Parameters [Section titled “Parameters”](#parameters-6) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/client/type-aliases/id/) ##### data [Section titled “data”](#data-1) `Partial`<`T`> ##### params? [Section titled “params?”](#params-3) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T`> *** ### remove() [Section titled “remove()”](#remove) > **remove**(`id`, `params?`): `Promise`<`T`> Defined in: [service-client.ts:68](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L68) #### Parameters [Section titled “Parameters”](#parameters-7) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/client/type-aliases/id/) ##### params? [Section titled “params?”](#params-4) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T`> *** ### similar() [Section titled “similar()”](#similar) > **similar**(`data`, `params?`): `Promise`<`T` & `object`\[]> Defined in: [service-client.ts:113](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L113) Vector-search convention (custom methods dispatch as POST /:service/:method). The service must register `similar` in its `methods` — see `VectorRepositoryService`. #### Parameters [Section titled “Parameters”](#parameters-8) ##### data [Section titled “data”](#data-2) [`SimilarQuery`](/api/mantlejs/client/interfaces/similarquery/) ##### params? [Section titled “params?”](#params-5) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T` & `object`\[]> *** ### update() [Section titled “update()”](#update) > **update**(`id`, `data`, `params?`): `Promise`<`T`> Defined in: [service-client.ts:58](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L58) #### Parameters [Section titled “Parameters”](#parameters-9) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/client/type-aliases/id/) ##### data [Section titled “data”](#data-3) `Partial`<`T`> ##### params? [Section titled “params?”](#params-6) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) #### Returns [Section titled “Returns”](#returns-10) `Promise`<`T`> *** ### upload() [Section titled “upload()”](#upload) > **upload**(`file`, `options?`): `Promise`<`T`> Defined in: [service-client.ts:94](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L94) Upload a file as `multipart/form-data` to a service whose `create` (or, with `options.id`, `patch`) runs `@mantlejs/storage`’s `handleUpload()`. Ordinary `fields` go before the file part, which `handleUpload()` merges into `context.data`. Never batched — `POST /batch` is JSON-only. #### Parameters [Section titled “Parameters”](#parameters-10) ##### file [Section titled “file”](#file) `Blob` ##### options? [Section titled “options?”](#options) [`UploadOptions`](/api/mantlejs/client/interfaces/uploadoptions/) = `{}` #### Returns [Section titled “Returns”](#returns-11) `Promise`<`T`> # defaultStorage > **defaultStorage**(): [`TokenStorage`](/api/mantlejs/client/interfaces/tokenstorage/) Defined in: [storage.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/storage.ts#L18) `localStorage` when available (browser), otherwise [memoryStorage](/api/mantlejs/client/functions/memorystorage/). ## Returns [Section titled “Returns”](#returns) [`TokenStorage`](/api/mantlejs/client/interfaces/tokenstorage/) # errorFromResponse > **errorFromResponse**(`response`): `Promise`<[`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/)> Defined in: [errors.ts:50](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L50) Deserialize a non-2xx `Response` into a `MantleClientError`. Falls back to the HTTP status alone when the body is not Mantle’s error JSON (e.g. a gateway error page). ## Parameters [Section titled “Parameters”](#parameters) ### response [Section titled “response”](#response) `Response` ## Returns [Section titled “Returns”](#returns) `Promise`<[`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/)> # mantle > **mantle**(`options`): [`MantleClient`](/api/mantlejs/client/classes/mantleclient/) Defined in: [client.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L22) Create a Mantle client. Throws `TypeError` when `url` is missing. ## Parameters [Section titled “Parameters”](#parameters) ### options [Section titled “options”](#options) [`ClientOptions`](/api/mantlejs/client/interfaces/clientoptions/) ## Returns [Section titled “Returns”](#returns) [`MantleClient`](/api/mantlejs/client/classes/mantleclient/) # memoryStorage > **memoryStorage**(): [`TokenStorage`](/api/mantlejs/client/interfaces/tokenstorage/) Defined in: [storage.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/storage.ts#L4) In-memory `TokenStorage` — the default outside the browser. ## Returns [Section titled “Returns”](#returns) [`TokenStorage`](/api/mantlejs/client/interfaces/tokenstorage/) # serializeQuery > **serializeQuery**(`query`): `string` Defined in: [serialize-query.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/serialize-query.ts#L15) Serialize a nested query object into the bracket-notation query string that every Mantle HTTP transport parses back into the identical object via `parseQueryString` in `@mantlejs/mantle` (Express’s `qs` produces the same shape): { age: { $gt: 21 } } → “age\[$gt]=21” { $or: \[{ role: “admin” }] } → “$or\[0]\[role]=admin” { tags: \[“a”, “b”] } → “tags\[0]=a\&tags\[1]=b” Values arrive server-side as strings — type coercion is the server schema’s concern. `undefined` values are dropped; `null` serializes as an empty string (matching `qs`), so `IS NULL` queries need server-side coercion. ## Parameters [Section titled “Parameters”](#parameters) ### query [Section titled “query”](#query) `Record`<`string`, `unknown`> ## Returns [Section titled “Returns”](#returns) `string` # AuthCredentials Defined in: [types.ts:81](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L81) ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `unknown` ## Properties [Section titled “Properties”](#properties) ### strategy [Section titled “strategy”](#strategy) > **strategy**: `string` Defined in: [types.ts:82](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L82) # AuthResult Defined in: [types.ts:86](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L86) ## Properties [Section titled “Properties”](#properties) ### accessToken [Section titled “accessToken”](#accesstoken) > **accessToken**: `string` Defined in: [types.ts:87](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L87) *** ### refreshToken? [Section titled “refreshToken?”](#refreshtoken) > `optional` **refreshToken?**: `string` Defined in: [types.ts:88](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L88) *** ### user? [Section titled “user?”](#user) > `optional` **user?**: `unknown` Defined in: [types.ts:89](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L89) # BatchCall Defined in: [types.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L35) One call in a coalesced `POST /batch` request — mirrors the server’s `BatchCall`. ## Properties [Section titled “Properties”](#properties) ### data? [Section titled “data?”](#data) > `optional` **data?**: `unknown` Defined in: [types.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L39) *** ### id? [Section titled “id?”](#id) > `optional` **id?**: [`Id`](/api/mantlejs/client/type-aliases/id/) Defined in: [types.ts:38](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L38) *** ### method [Section titled “method”](#method) > **method**: `"find"` | `"get"` | `"create"` | `"update"` | `"patch"` | `"remove"` Defined in: [types.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L37) *** ### params? [Section titled “params?”](#params) > `optional` **params?**: `object` Defined in: [types.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L40) #### query? [Section titled “query?”](#query) > `optional` **query?**: `Record`<`string`, `unknown`> *** ### service [Section titled “service”](#service) > **service**: `string` Defined in: [types.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L36) # BatchOptions Defined in: [types.ts:50](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L50) ## Properties [Section titled “Properties”](#properties) ### maxSize? [Section titled “maxSize?”](#maxsize) > `optional` **maxSize?**: `number` Defined in: [types.ts:54](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L54) Maximum calls per `POST /batch` — longer queues split into multiple requests. Match the server’s max batch size. #### Default [Section titled “Default”](#default) ```ts 25 ``` *** ### windowMs? [Section titled “windowMs?”](#windowms) > `optional` **windowMs?**: `number` Defined in: [types.ts:52](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L52) Coalescing window in milliseconds. `0` (default) flushes on the next microtask tick. # BatchRestDispatcher Defined in: [batch-scheduler.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/batch-scheduler.ts#L5) Subset of `MantleClient` used to send the coalesced `POST /batch` request. ## Methods [Section titled “Methods”](#methods) ### request() [Section titled “request()”](#request) > **request**<`R`>(`method`, `path`, `data`): `Promise`<`R`> Defined in: [batch-scheduler.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/batch-scheduler.ts#L6) #### Type Parameters [Section titled “Type Parameters”](#type-parameters) ##### R [Section titled “R”](#r) `R` #### Parameters [Section titled “Parameters”](#parameters) ##### method [Section titled “method”](#method) `string` ##### path [Section titled “path”](#path) `string` ##### data [Section titled “data”](#data) `unknown` #### Returns [Section titled “Returns”](#returns) `Promise`<`R`> # BatchResult Defined in: [types.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L44) Outcome of one `BatchCall`, as returned by the server’s batch endpoint. ## Properties [Section titled “Properties”](#properties) ### error? [Section titled “error?”](#error) > `optional` **error?**: `object` Defined in: [types.ts:47](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L47) #### code? [Section titled “code?”](#code) > `optional` **code?**: `number` #### message? [Section titled “message?”](#message) > `optional` **message?**: `string` #### name? [Section titled “name?”](#name) > `optional` **name?**: `string` *** ### result? [Section titled “result?”](#result) > `optional` **result?**: `unknown` Defined in: [types.ts:46](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L46) *** ### status [Section titled “status”](#status) > **status**: `"success"` | `"error"` Defined in: [types.ts:45](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L45) # ClientOptions Defined in: [types.ts:57](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L57) ## Properties [Section titled “Properties”](#properties) ### batch? [Section titled “batch?”](#batch) > `optional` **batch?**: `boolean` | [`BatchOptions`](/api/mantlejs/client/interfaces/batchoptions/) Defined in: [types.ts:71](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L71) Coalesce service calls made within the same window into one `POST /batch` request (the server’s batch endpoint must be enabled — it is by default). Calls carrying per-request `headers` bypass coalescing. #### Default [Section titled “Default”](#default) ```ts false ``` *** ### headers? [Section titled “headers?”](#headers) > `optional` **headers?**: `Record`<`string`, `string`> Defined in: [types.ts:65](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L65) Default headers appended to every REST request. *** ### socket? [Section titled “socket?”](#socket) > `optional` **socket?**: [`SocketOptions`](/api/mantlejs/client/interfaces/socketoptions/) Defined in: [types.ts:63](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L63) Socket.io connection options. Omit to disable real-time features. *** ### storage? [Section titled “storage?”](#storage) > `optional` **storage?**: [`TokenStorage`](/api/mantlejs/client/interfaces/tokenstorage/) Defined in: [types.ts:61](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L61) Token storage. Default: `localStorage` when available, otherwise in-memory. *** ### url [Section titled “url”](#url) > **url**: `string` Defined in: [types.ts:59](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L59) Base URL of the Mantle server, e.g. `"http://localhost:3030"`. Required. # ClientParams Defined in: [types.ts:74](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L74) ## Properties [Section titled “Properties”](#properties) ### headers? [Section titled “headers?”](#headers) > `optional` **headers?**: `Record`<`string`, `string`> Defined in: [types.ts:78](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L78) Per-request header overrides. *** ### query? [Section titled “query?”](#query) > `optional` **query?**: `Record`<`string`, `unknown`> Defined in: [types.ts:76](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L76) Query parameters serialized into the URL for find/get. # MantleClientErrorFields Defined in: [errors.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L13) ## Properties [Section titled “Properties”](#properties) ### className? [Section titled “className?”](#classname) > `optional` **className?**: `string` Defined in: [errors.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L14) *** ### data? [Section titled “data?”](#data) > `optional` **data?**: `unknown` Defined in: [errors.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L15) *** ### errors? [Section titled “errors?”](#errors) > `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L16) *** ### hint? [Section titled “hint?”](#hint) > `optional` **hint?**: `string` Defined in: [errors.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/errors.ts#L17) # Paginated Defined in: [types.ts:92](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L92) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Properties [Section titled “Properties”](#properties) ### data [Section titled “data”](#data) > **data**: `T`\[] Defined in: [types.ts:96](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L96) *** ### limit [Section titled “limit”](#limit) > **limit**: `number` Defined in: [types.ts:94](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L94) *** ### skip [Section titled “skip”](#skip) > **skip**: `number` Defined in: [types.ts:95](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L95) *** ### total [Section titled “total”](#total) > **total**: `number` Defined in: [types.ts:93](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L93) # RestDispatcher Defined in: [service-client.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L16) REST transport provided by `MantleClient` — auth, refresh-retry, and error handling live there. ## Methods [Section titled “Methods”](#methods) ### request() [Section titled “request()”](#request) > **request**<`R`>(`method`, `path`, `data`, `params?`): `Promise`<`R`> Defined in: [service-client.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L17) #### Type Parameters [Section titled “Type Parameters”](#type-parameters) ##### R [Section titled “R”](#r) `R` #### Parameters [Section titled “Parameters”](#parameters) ##### method [Section titled “method”](#method) `string` ##### path [Section titled “path”](#path) `string` ##### data [Section titled “data”](#data) `unknown` ##### params? [Section titled “params?”](#params) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) #### Returns [Section titled “Returns”](#returns) `Promise`<`R`> *** ### upload() [Section titled “upload()”](#upload) > **upload**<`R`>(`method`, `path`, `body`, `options?`): `Promise`<`R`> Defined in: [service-client.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/service-client.ts#L18) #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### R [Section titled “R”](#r-1) `R` #### Parameters [Section titled “Parameters”](#parameters-1) ##### method [Section titled “method”](#method-1) `string` ##### path [Section titled “path”](#path-1) `string` ##### body [Section titled “body”](#body) () => `FormData` ##### options? [Section titled “options?”](#options) ###### headers? [Section titled “headers?”](#headers) `Record`<`string`, `string`> ###### onProgress? [Section titled “onProgress?”](#onprogress) (`progress`) => `void` ###### signal? [Section titled “signal?”](#signal) `AbortSignal` #### Returns [Section titled “Returns”](#returns-1) `Promise`<`R`> # SimilarQuery Defined in: [types.ts:104](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L104) Payload for the `similar()` vector-search convention (see the mantle README). ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `unknown` ## Properties [Section titled “Properties”](#properties) ### topK? [Section titled “topK?”](#topk) > `optional` **topK?**: `number` Defined in: [types.ts:106](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L106) *** ### vector [Section titled “vector”](#vector) > **vector**: `number`\[] Defined in: [types.ts:105](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L105) *** ### where? [Section titled “where?”](#where) > `optional` **where?**: `Record`<`string`, `unknown`> Defined in: [types.ts:107](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L107) # SocketLike Defined in: [types.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L18) A connected socket.io-client socket, typed structurally so the client has no hard dependency on `socket.io-client`. ## Methods [Section titled “Methods”](#methods) ### off() [Section titled “off()”](#off) > **off**(`event`, `handler`): `unknown` Defined in: [types.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L20) #### Parameters [Section titled “Parameters”](#parameters) ##### event [Section titled “event”](#event) `string` ##### handler [Section titled “handler”](#handler) (…`args`) => `void` #### Returns [Section titled “Returns”](#returns) `unknown` *** ### on() [Section titled “on()”](#on) > **on**(`event`, `handler`): `unknown` Defined in: [types.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L19) #### Parameters [Section titled “Parameters”](#parameters-1) ##### event [Section titled “event”](#event-1) `string` ##### handler [Section titled “handler”](#handler-1) (…`args`) => `void` #### Returns [Section titled “Returns”](#returns-1) `unknown` # SocketOptions Defined in: [types.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L30) socket.io-client connection options, passed through to `io(url, options)`. `io` overrides the socket factory itself — inject a stub in tests, or supply a pre-bundled `io` when dynamic import of the optional peer is undesirable. ## Extends [Section titled “Extends”](#extends) * `Record`<`string`, `unknown`> ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `unknown` ## Properties [Section titled “Properties”](#properties) ### io? [Section titled “io?”](#io) > `optional` **io?**: [`SocketFactory`](/api/mantlejs/client/type-aliases/socketfactory/) Defined in: [types.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L31) # TokenStorage Defined in: [types.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L8) Pluggable token persistence. Sync-or-async so `localStorage` (browser), a plain in-memory Map (Node.js), and async stores (React Native AsyncStorage) all satisfy the interface without wrappers. ## Methods [Section titled “Methods”](#methods) ### getItem() [Section titled “getItem()”](#getitem) > **getItem**(`key`): `string` | `Promise`<`string` | `null`> | `null` Defined in: [types.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L9) #### Parameters [Section titled “Parameters”](#parameters) ##### key [Section titled “key”](#key) `string` #### Returns [Section titled “Returns”](#returns) `string` | `Promise`<`string` | `null`> | `null` *** ### removeItem() [Section titled “removeItem()”](#removeitem) > **removeItem**(`key`): `void` | `Promise`<`void`> Defined in: [types.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L11) #### Parameters [Section titled “Parameters”](#parameters-1) ##### key [Section titled “key”](#key-1) `string` #### Returns [Section titled “Returns”](#returns-1) `void` | `Promise`<`void`> *** ### setItem() [Section titled “setItem()”](#setitem) > **setItem**(`key`, `value`): `void` | `Promise`<`void`> Defined in: [types.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L10) #### Parameters [Section titled “Parameters”](#parameters-2) ##### key [Section titled “key”](#key-2) `string` ##### value [Section titled “value”](#value) `string` #### Returns [Section titled “Returns”](#returns-2) `void` | `Promise`<`void`> # UploadOptions Defined in: [types.ts:121](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L121) Options for `ServiceClient.upload()` — a `multipart/form-data` request for `@mantlejs/storage`’s `handleUpload()`. ## Properties [Section titled “Properties”](#properties) ### field? [Section titled “field?”](#field) > `optional` **field?**: `string` Defined in: [types.ts:125](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L125) Multipart field name `handleUpload(field)` reads. #### Default [Section titled “Default”](#default) ```ts "file" ``` *** ### fields? [Section titled “fields?”](#fields) > `optional` **fields?**: `Record`<`string`, `string`> Defined in: [types.ts:127](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L127) Ordinary form fields sent before the file part (merged into `context.data` by `handleUpload()`). *** ### filename? [Section titled “filename?”](#filename) > `optional` **filename?**: `string` Defined in: [types.ts:129](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L129) Filename sent with the file part. Default: the `File`’s own name, or `"blob"`. *** ### headers? [Section titled “headers?”](#headers) > `optional` **headers?**: `Record`<`string`, `string`> Defined in: [types.ts:139](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L139) Per-request header overrides. *** ### id? [Section titled “id?”](#id) > `optional` **id?**: [`Id`](/api/mantlejs/client/type-aliases/id/) Defined in: [types.ts:123](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L123) Upload into an existing record: `PATCH /:service/:id` instead of `POST /:service`. *** ### onProgress? [Section titled “onProgress?”](#onprogress) > `optional` **onProgress?**: (`progress`) => `void` Defined in: [types.ts:135](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L135) Upload progress. When set and `XMLHttpRequest` exists (browsers, React Native), the request goes over XHR — `fetch` can’t report upload progress. Otherwise `fetch` is used and this is never called. #### Parameters [Section titled “Parameters”](#parameters) ##### progress [Section titled “progress”](#progress) [`UploadProgress`](/api/mantlejs/client/interfaces/uploadprogress/) #### Returns [Section titled “Returns”](#returns) `void` *** ### signal? [Section titled “signal?”](#signal) > `optional` **signal?**: `AbortSignal` Defined in: [types.ts:137](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L137) Abort the upload; rejects with the signal’s reason (an `AbortError` by default). # UploadProgress Defined in: [types.ts:112](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L112) Progress of an in-flight `upload()`. ## Properties [Section titled “Properties”](#properties) ### loaded [Section titled “loaded”](#loaded) > **loaded**: `number` Defined in: [types.ts:113](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L113) *** ### percent? [Section titled “percent?”](#percent) > `optional` **percent?**: `number` Defined in: [types.ts:117](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L117) 0–100, when `total` is known. *** ### total? [Section titled “total?”](#total) > `optional` **total?**: `number` Defined in: [types.ts:115](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L115) Total request size in bytes, when the transport reports it. # UploadTransportOptions Defined in: [client.ts:267](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L267) Transport-level upload options (`ServiceClient.upload()` resolves fields/filename into the body). ## Properties [Section titled “Properties”](#properties) ### headers? [Section titled “headers?”](#headers) > `optional` **headers?**: `Record`<`string`, `string`> Defined in: [client.ts:268](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L268) *** ### onProgress? [Section titled “onProgress?”](#onprogress) > `optional` **onProgress?**: (`progress`) => `void` Defined in: [client.ts:269](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L269) #### Parameters [Section titled “Parameters”](#parameters) ##### progress [Section titled “progress”](#progress) [`UploadProgress`](/api/mantlejs/client/interfaces/uploadprogress/) #### Returns [Section titled “Returns”](#returns) `void` *** ### signal? [Section titled “signal?”](#signal) > `optional` **signal?**: `AbortSignal` Defined in: [client.ts:270](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/client.ts#L270) # ClientEvent > **ClientEvent** = `"authenticated"` | `"logout"` | `"reconnect"` Defined in: [types.ts:101](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L101) # Id > **Id** = `string` | `number` Defined in: [types.ts:1](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L1) # ServiceEvent > **ServiceEvent** = `"created"` | `"updated"` | `"patched"` | `"removed"` Defined in: [types.ts:99](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L99) # SocketFactory > **SocketFactory** = (`url`, `options`) => [`SocketLike`](/api/mantlejs/client/interfaces/socketlike/) Defined in: [types.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/client/src/lib/types.ts#L23) ## Parameters [Section titled “Parameters”](#parameters) ### url [Section titled “url”](#url) `string` ### options [Section titled “options”](#options) `Record`<`string`, `unknown`> ## Returns [Section titled “Returns”](#returns) [`SocketLike`](/api/mantlejs/client/interfaces/socketlike/) # @mantlejs/config ## Interfaces [Section titled “Interfaces”](#interfaces) * [ConfigOptions](/api/mantlejs/config/interfaces/configoptions/) ## Functions [Section titled “Functions”](#functions) * [config](/api/mantlejs/config/functions/config/) # config > **config**(`options?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [config.ts:85](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/config/src/lib/config.ts#L85) ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`ConfigOptions`](/api/mantlejs/config/interfaces/configoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # ConfigOptions Defined in: [config.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/config/src/lib/config.ts#L8) ## Properties [Section titled “Properties”](#properties) ### directory? [Section titled “directory?”](#directory) > `optional` **directory?**: `string` Defined in: [config.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/config/src/lib/config.ts#L10) Directory containing config files. Default: process.cwd() + ‘/config’ *** ### envPrefix? [Section titled “envPrefix?”](#envprefix) > `optional` **envPrefix?**: `string` Defined in: [config.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/config/src/lib/config.ts#L16) Prefix for env var overrides. Default: ‘MANTLE\_’ *** ### envVar? [Section titled “envVar?”](#envvar) > `optional` **envVar?**: `string` Defined in: [config.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/config/src/lib/config.ts#L14) Env var that selects the environment overlay. Default: ‘NODE_ENV’ *** ### schema? [Section titled “schema?”](#schema) > `optional` **schema?**: `TSchema` Defined in: [config.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/config/src/lib/config.ts#L12) TypeBox schema — validates merged config at startup, throws GeneralError on failure # @mantlejs/dynamodb ## Classes [Section titled “Classes”](#classes) * [DynamoDbRepository](/api/mantlejs/dynamodb/classes/dynamodbrepository/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [DynamoDbConfig](/api/mantlejs/dynamodb/interfaces/dynamodbconfig/) * [DynamoQueryParams](/api/mantlejs/dynamodb/interfaces/dynamoqueryparams/) * [FilterExpression](/api/mantlejs/dynamodb/interfaces/filterexpression/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [WhereClause](/api/mantlejs/dynamodb/type-aliases/whereclause/) ## Variables [Section titled “Variables”](#variables) * [DYNAMODB_OPERATORS](/api/mantlejs/dynamodb/variables/dynamodb_operators/) ## Functions [Section titled “Functions”](#functions) * [buildKeyCondition](/api/mantlejs/dynamodb/functions/buildkeycondition/) * [dynamodb](/api/mantlejs/dynamodb/functions/dynamodb/) * [dynamodbify](/api/mantlejs/dynamodb/functions/dynamodbify/) * [toConditionMap](/api/mantlejs/dynamodb/functions/toconditionmap/) # DynamoDbRepository Defined in: [dynamodb/src/lib/dynamodb-repository.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L30) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ### D [Section titled “D”](#d) `D` = `Partial`<`T`> ## Implements [Section titled “Implements”](#implements) * [`Repository`](/api/mantlejs/mantle/interfaces/repository/)<`T`, `D`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new DynamoDbRepository**<`T`, `D`>(`app`): `DynamoDbRepository`<`T`, `D`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:80](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L80) #### Parameters [Section titled “Parameters”](#parameters) ##### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) #### Returns [Section titled “Returns”](#returns) `DynamoDbRepository`<`T`, `D`> ## Properties [Section titled “Properties”](#properties) ### \_txItems? [Section titled “\_txItems?”](#_txitems) > `protected` `optional` **\_txItems?**: `TransactWriteItem`\[] Defined in: [dynamodb/src/lib/dynamodb-repository.ts:78](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L78) Buffered write operations when running inside `withTransaction()`. *** ### client [Section titled “client”](#client) > `protected` `readonly` **client**: `DynamoDBClient` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L33) *** ### createdAtField [Section titled “createdAtField”](#createdatfield) > `readonly` **createdAtField**: `string` = `"createdAt"` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:59](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L59) Attribute written for auto-managed creation timestamps. #### Default [Section titled “Default”](#default) ```ts "createdAt" ``` *** ### fieldMap [Section titled “fieldMap”](#fieldmap) > `readonly` **fieldMap**: `Record`<`string`, `string`> = `{}` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:68](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L68) Entity-field-to-attribute overrides, for a table whose attribute names don’t match the entity (e.g. a brownfield table using snake_case). Applies to every field, including `partitionKey`/`sortKey` — they are ordinary attributes in DynamoDB. #### Default [Section titled “Default”](#default-1) ```ts {} ``` *** ### ~~lastKey?~~ [Section titled “lastKey?”](#lastkey) > `optional` **lastKey?**: `Record`<`string`, `AttributeValue`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:75](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L75) The `LastEvaluatedKey` from the most recent paginated `findAll()` call. Use as `_startKey` on the next call. Deprecated Use `findPage()` instead — its cursor travels in the returned page, so concurrent calls on one instance cannot corrupt each other. Will be removed in the next minor release. *** ### partitionKey [Section titled “partitionKey”](#partitionkey) > `readonly` **partitionKey**: `string` = `"id"` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L42) The partition key attribute name for the table. #### Default [Section titled “Default”](#default-2) ```ts "id" ``` *** ### sortKey? [Section titled “sortKey?”](#sortkey) > `readonly` `optional` **sortKey?**: `string` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:49](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L49) Optional sort key attribute name. When set, `findById` expects a composite key object `{ pk: partitionValue, sk: sortValue }` and Query is used instead of GetItem. *** ### tableName [Section titled “tableName”](#tablename) > `abstract` `readonly` **tableName**: `string` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L36) DynamoDB table name. *** ### timestamps [Section titled “timestamps”](#timestamps) > `readonly` **timestamps**: `boolean` = `true` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:56](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L56) When true, `save` / `saveAll` / `updateById` / `patchById` automatically write `createdAt` and `updatedAt` ISO-8601 timestamps. #### Default [Section titled “Default”](#default-3) ```ts true ``` *** ### updatedAtField [Section titled “updatedAtField”](#updatedatfield) > `readonly` **updatedAtField**: `string` = `"updatedAt"` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:61](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L61) Attribute written for auto-managed update timestamps. #### Default [Section titled “Default”](#default-4) ```ts "updatedAt" ``` ## Methods [Section titled “Methods”](#methods) ### buildKey() [Section titled “buildKey()”](#buildkey) > `protected` **buildKey**(`id`): `Record`<`string`, `AttributeValue`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:125](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L125) Build a consistent key map for GetItem / DeleteItem / UpdateItem. #### Parameters [Section titled “Parameters”](#parameters-1) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `AttributeValue`> *** ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:663](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L663) #### Parameters [Section titled “Parameters”](#parameters-2) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<`number`> #### Implementation of [Section titled “Implementation of”](#implementation-of) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`count`](/api/mantlejs/mantle/interfaces/repository/#count) *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:624](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L624) #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-1) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`deleteById`](/api/mantlejs/mantle/interfaces/repository/#deletebyid) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [dynamodb/src/lib/dynamodb-repository.ts:139](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L139) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-4) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`describe`](/api/mantlejs/mantle/interfaces/repository/#describe) *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:154](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L154) #### Parameters [Section titled “Parameters”](#parameters-4) ##### params? [Section titled “params?”](#params-1) [`DynamoQueryParams`](/api/mantlejs/dynamodb/interfaces/dynamoqueryparams/) #### Returns [Section titled “Returns”](#returns-5) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findAll`](/api/mantlejs/mantle/interfaces/repository/#findall) *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:409](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L409) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T` | `null`> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findById`](/api/mantlejs/mantle/interfaces/repository/#findbyid) *** ### findPage() [Section titled “findPage()”](#findpage) > **findPage**(`params?`): `Promise`<[`CursorPage`](/api/mantlejs/mantle/interfaces/cursorpage/)<`T`>> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:181](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L181) Fetch one page of results using native DynamoDB cursor pagination. The returned `cursor` is the `LastEvaluatedKey` encoded as base64 JSON — pass it back as `params.cursor` for the next page. Stateless: unlike `lastKey`, nothing is stored on the repository instance. Uses Query when a sort key is defined and the where clause pins the partition key, otherwise Scan. `skip` and `sort` are rejected — offsets and cross-page ordering don’t compose with cursors. #### Parameters [Section titled “Parameters”](#parameters-6) ##### params? [Section titled “params?”](#params-2) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) & `object` #### Returns [Section titled “Returns”](#returns-7) `Promise`<[`CursorPage`](/api/mantlejs/mantle/interfaces/cursorpage/)<`T`>> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findPage`](/api/mantlejs/mantle/interfaces/repository/#findpage) *** ### fromItem() [Section titled “fromItem()”](#fromitem) > `protected` **fromItem**(`item`): `T` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:119](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L119) Unmarshall a DynamoDB attribute map to a plain object. #### Parameters [Section titled “Parameters”](#parameters-7) ##### item [Section titled “item”](#item) `Record`<`string`, `AttributeValue`> #### Returns [Section titled “Returns”](#returns-8) `T` *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:553](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L553) #### Parameters [Section titled “Parameters”](#parameters-8) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data) `D` #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`patchById`](/api/mantlejs/mantle/interfaces/repository/#patchbyid) *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:423](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L423) #### Parameters [Section titled “Parameters”](#parameters-9) ##### data [Section titled “data”](#data-1) `D` #### Returns [Section titled “Returns”](#returns-10) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-7) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`save`](/api/mantlejs/mantle/interfaces/repository/#save) *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:444](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L444) #### Parameters [Section titled “Parameters”](#parameters-10) ##### data [Section titled “data”](#data-2) `D`\[] #### Returns [Section titled “Returns”](#returns-11) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-8) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`saveAll`](/api/mantlejs/mantle/interfaces/repository/#saveall) *** ### toEntityField() [Section titled “toEntityField()”](#toentityfield) > `protected` **toEntityField**(`field`): `string` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:103](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L103) Translates an attribute name back to its entity field name — the inverse of `toField`. #### Parameters [Section titled “Parameters”](#parameters-11) ##### field [Section titled “field”](#field) `string` #### Returns [Section titled “Returns”](#returns-12) `string` *** ### toField() [Section titled “toField()”](#tofield) > `protected` **toField**(`field`): `string` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:98](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L98) Translates an entity field name to its attribute name (fieldMap override, else identity). #### Parameters [Section titled “Parameters”](#parameters-12) ##### field [Section titled “field”](#field-1) `string` #### Returns [Section titled “Returns”](#returns-13) `string` *** ### toItem() [Section titled “toItem()”](#toitem) > `protected` **toItem**(`data`): `Record`<`string`, `AttributeValue`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:109](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L109) Marshall a plain object to DynamoDB attribute values, omitting undefined/null keys. #### Parameters [Section titled “Parameters”](#parameters-13) ##### data [Section titled “data”](#data-3) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-14) `Record`<`string`, `AttributeValue`> *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:483](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L483) #### Parameters [Section titled “Parameters”](#parameters-14) ##### id [Section titled “id”](#id-4) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-4) `D` #### Returns [Section titled “Returns”](#returns-15) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-9) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`updateById`](/api/mantlejs/mantle/interfaces/repository/#updatebyid) *** ### withTransaction() [Section titled “withTransaction()”](#withtransaction) > **withTransaction**<`R`>(`callback`): `Promise`<`R`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:713](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L713) Execute a set of repository write operations atomically via DynamoDB TransactWriteItems. Supports up to 100 write operations per transaction. Note: in transaction mode, mutation methods (save, updateById, patchById, deleteById) return the input data immediately rather than the persisted item, since TransactWriteItems does not support ReturnValues. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### R [Section titled “R”](#r) `R` #### Parameters [Section titled “Parameters”](#parameters-15) ##### callback [Section titled “callback”](#callback) (`repo`) => `Promise`<`R`> #### Returns [Section titled “Returns”](#returns-16) `Promise`<`R`> *** ### wrapError() [Section titled “wrapError()”](#wraperror) > `protected` **wrapError**(`err`): `Error` Defined in: [dynamodb/src/lib/dynamodb-repository.ts:743](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L743) #### Parameters [Section titled “Parameters”](#parameters-16) ##### err [Section titled “err”](#err) `unknown` #### Returns [Section titled “Returns”](#returns-17) `Error` # buildKeyCondition > **buildKeyCondition**(`partitionKey`, `sortKey`, `where`, `toField?`): `object` Defined in: [dynamodb/src/lib/dynamodbify.ts:238](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodbify.ts#L238) Build a KeyConditionExpression from a simple where clause restricted to the partition key (and optionally the sort key). Used in Query operations. ## Parameters [Section titled “Parameters”](#parameters) ### partitionKey [Section titled “partitionKey”](#partitionkey) `string` ### sortKey [Section titled “sortKey”](#sortkey) `string` | `undefined` ### where [Section titled “where”](#where) [`WhereClause`](/api/mantlejs/dynamodb/type-aliases/whereclause/) ### toField? [Section titled “toField?”](#tofield) (`field`) => `string` ## Returns [Section titled “Returns”](#returns) `object` ### filterCondition? [Section titled “filterCondition?”](#filtercondition) > `optional` **filterCondition?**: `string` ### keyCondition [Section titled “keyCondition”](#keycondition) > **keyCondition**: `string` ### names [Section titled “names”](#names) > **names**: `Record`<`string`, `string`> ### values [Section titled “values”](#values) > **values**: `Record`<`string`, `AttributeValue`> # dynamodb > **dynamodb**(`config?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [dynamodb/src/lib/dynamodb.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb.ts#L19) Mantle plugin that creates a DynamoDB client and stores it on the application. ## Parameters [Section titled “Parameters”](#parameters) ### config? [Section titled “config?”](#config) [`DynamoDbConfig`](/api/mantlejs/dynamodb/interfaces/dynamodbconfig/) = `{}` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) ## Example [Section titled “Example”](#example) ```ts const app = mantle().configure(dynamodb({ region: "us-east-1" })); ``` # dynamodbify > **dynamodbify**(`where`, `toField?`): [`FilterExpression`](/api/mantlejs/dynamodb/interfaces/filterexpression/) Defined in: [dynamodb/src/lib/dynamodbify.ts:66](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodbify.ts#L66) Translates a Mantle `QueryParams.where` clause into a DynamoDB FilterExpression (suitable for Scan and Query operations). Supported operators: Comparison : $lt, $lte, $gt, $gte Equality : field = value, $ne Null check : field: null → attribute_not_exists / null check Inclusion : $in, $nin Logical : $or, $and Pattern : $begins (begins_with) Contains : $contains Unsupported operators (including `$like`) throw `BadRequest`. ## Parameters [Section titled “Parameters”](#parameters) ### where [Section titled “where”](#where) [`WhereClause`](/api/mantlejs/dynamodb/type-aliases/whereclause/) ### toField? [Section titled “toField?”](#tofield) (`field`) => `string` ## Returns [Section titled “Returns”](#returns) [`FilterExpression`](/api/mantlejs/dynamodb/interfaces/filterexpression/) # toConditionMap > **toConditionMap**(`where`): `Record`<`string`, `Condition`> Defined in: [dynamodb/src/lib/dynamodbify.ts:291](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodbify.ts#L291) **`Internal`** Convert a simple equality where clause to the legacy `Condition` map used by the AWS SDK v3’s older API shapes. ## Parameters [Section titled “Parameters”](#parameters) ### where [Section titled “where”](#where) [`WhereClause`](/api/mantlejs/dynamodb/type-aliases/whereclause/) ## Returns [Section titled “Returns”](#returns) `Record`<`string`, `Condition`> # DynamoDbConfig Defined in: [dynamodb/src/lib/dynamodb.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb.ts#L4) ## Properties [Section titled “Properties”](#properties) ### clientConfig? [Section titled “clientConfig?”](#clientconfig) > `optional` **clientConfig?**: `DynamoDBClientConfig` Defined in: [dynamodb/src/lib/dynamodb.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb.ts#L8) Full DynamoDB client config — endpoint, credentials, etc. Takes precedence over `region`. *** ### region? [Section titled “region?”](#region) > `optional` **region?**: `string` Defined in: [dynamodb/src/lib/dynamodb.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb.ts#L6) AWS region (e.g. “us-east-1”). Falls back to AWS_REGION env var when omitted. # DynamoQueryParams Defined in: [dynamodb/src/lib/dynamodb-repository.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L21) ## Extends [Section titled “Extends”](#extends) * [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) ## Properties [Section titled “Properties”](#properties) ### ~~\_startKey?~~ [Section titled “\_startKey?”](#_startkey) > `optional` **\_startKey?**: `Record`<`string`, `AttributeValue`> Defined in: [dynamodb/src/lib/dynamodb-repository.ts:27](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodb-repository.ts#L27) Cursor for the next page. Pass the `lastKey` from the previous `findAll()` call. Deprecated Use `findPage()` with its string `cursor` instead — `lastKey`/`_startKey` share mutable state on the repository instance. Will be removed in the next minor release. *** ### limit? [Section titled “limit?”](#limit) > `optional` **limit?**: `number` Defined in: mantle/dist/lib/types.d.ts:53 #### Inherited from [Section titled “Inherited from”](#inherited-from) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/).[`limit`](/api/mantlejs/mantle/interfaces/queryparams/#limit) *** ### select? [Section titled “select?”](#select) > `optional` **select?**: `string`\[] Defined in: mantle/dist/lib/types.d.ts:56 #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/).[`select`](/api/mantlejs/mantle/interfaces/queryparams/#select) *** ### skip? [Section titled “skip?”](#skip) > `optional` **skip?**: `number` Defined in: mantle/dist/lib/types.d.ts:54 #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/).[`skip`](/api/mantlejs/mantle/interfaces/queryparams/#skip) *** ### sort? [Section titled “sort?”](#sort) > `optional` **sort?**: `Record`<`string`, `"asc"` | `"desc"`> Defined in: mantle/dist/lib/types.d.ts:55 #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/).[`sort`](/api/mantlejs/mantle/interfaces/queryparams/#sort) *** ### where? [Section titled “where?”](#where) > `optional` **where?**: `Record`<`string`, `unknown`> Defined in: mantle/dist/lib/types.d.ts:52 Filter clause. Keys are field names — adapters backed by nested documents (memory, supabase, mongodb) also accept dot-path keys like `"metadata.tags"`. Values are literals (equality), `null` (IS NULL), or operator objects: `$lt`/`$lte`/`$gt`/`$gte`, `$ne`, `$in`/`$nin`, `$like`/`$notlike`/`$ilike`, `$contains` (array/JSON containment, jsonb `@>` semantics), plus top-level `$or`/`$and`. Each adapter enforces its supported subset via `assertOperators` — see `describe().operators`. #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/).[`where`](/api/mantlejs/mantle/interfaces/queryparams/#where) # FilterExpression Defined in: [dynamodb/src/lib/dynamodbify.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodbify.ts#L35) ## Properties [Section titled “Properties”](#properties) ### expression [Section titled “expression”](#expression) > **expression**: `string` Defined in: [dynamodb/src/lib/dynamodbify.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodbify.ts#L37) The filter expression string, e.g. “#n0 = :v0 AND #n1 > :v1” *** ### names [Section titled “names”](#names) > **names**: `Record`<`string`, `string`> Defined in: [dynamodb/src/lib/dynamodbify.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodbify.ts#L39) ExpressionAttributeNames — aliases for reserved words / dots in field names *** ### values [Section titled “values”](#values) > **values**: `Record`<`string`, `AttributeValue`> Defined in: [dynamodb/src/lib/dynamodbify.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodbify.ts#L41) ExpressionAttributeValues — typed AttributeValue map # WhereClause > **WhereClause** = `Record`<`string`, `WhereValue`> Defined in: [dynamodb/src/lib/dynamodbify.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodbify.ts#L7) # DYNAMODB_OPERATORS > `const` **DYNAMODB_OPERATORS**: `ReadonlySet`<`string`> Defined in: [dynamodb/src/lib/dynamodbify.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/dynamodb/src/lib/dynamodbify.ts#L21) All query operators supported by the DynamoDB adapter. `$like` is deliberately absent: DynamoDB has no wildcard matching, and remapping it to `contains()` would silently change its semantics. # @mantlejs/embeddings ## Interfaces [Section titled “Interfaces”](#interfaces) * [EmbeddingProvider](/api/mantlejs/embeddings/interfaces/embeddingprovider/) * [EmbedOptions](/api/mantlejs/embeddings/interfaces/embedoptions/) ## Functions [Section titled “Functions”](#functions) * [embed](/api/mantlejs/embeddings/functions/embed/) # embed > **embed**<`T`>(`options`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/)<`T`> Defined in: [embed.ts:60](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/embeddings/src/lib/embed.ts#L60) Returns a hook that embeds the just-written record’s text and upserts it into `options.vectors` — the auto-embed-on-write hook. Attach to `after.create`/`after.update`/`after.patch` (never `after.find`/`after.get`/`after.remove` — there is no freshly-written record to embed there): ```typescript app.service("articles").hooks({ after: { create: [embed({ vectors: articlesVectorRepo, provider: openaiEmbeddings(), field: ["title", "body"] })], update: [embed({ vectors: articlesVectorRepo, provider: openaiEmbeddings(), field: ["title", "body"] })], patch: [embed({ vectors: articlesVectorRepo, provider: openaiEmbeddings(), field: ["title", "body"] })], }, }); ``` **Idempotent by construction:** `upsertVector(id, vector, data)` is an upsert keyed on the source record’s id — re-running this hook for the same id (a retry, or a second edit) replaces the vector at that id rather than creating a duplicate, on every adapter (`@mantlejs/pinecone`, `@mantlejs/qdrant`, `@mantlejs/mongodb`’s `MongoVectorRepository`, `@mantlejs/knex`’s `KnexVectorRepository`). No extra bookkeeping is needed for that guarantee — it comes from the adapter contract, not from this hook. **Non-fatal on failure:** an embedding-provider error or a vector-upsert error is caught here and never rethrown — the primary write this hook runs after has already succeeded, and a degraded/unavailable embedding provider must never turn that into a failed request. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ## Parameters [Section titled “Parameters”](#parameters) ### options [Section titled “options”](#options) [`EmbedOptions`](/api/mantlejs/embeddings/interfaces/embedoptions/)<`T`> ## Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/)<`T`> # EmbeddingProvider Defined in: [embed.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/embeddings/src/lib/embed.ts#L8) A pluggable embedding provider — the thing `embed()` calls to turn source text into a vector. `@mantlejs/embeddings` never bundles a specific provider (OpenAI, Cohere, a local model, …); apps supply their own implementation, matching every vector adapter’s own model-agnostic design. ## Properties [Section titled “Properties”](#properties) ### dimensions? [Section titled “dimensions?”](#dimensions) > `optional` **dimensions?**: `number` Defined in: [embed.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/embeddings/src/lib/embed.ts#L10) Embedding dimensionality — informational, useful for validating a target index/collection. ## Methods [Section titled “Methods”](#methods) ### embed() [Section titled “embed()”](#embed) > **embed**(`text`): `Promise`<`number`\[]> Defined in: [embed.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/embeddings/src/lib/embed.ts#L11) #### Parameters [Section titled “Parameters”](#parameters) ##### text [Section titled “text”](#text) `string` #### Returns [Section titled “Returns”](#returns) `Promise`<`number`\[]> # EmbedOptions Defined in: [embed.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/embeddings/src/lib/embed.ts#L14) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ## Properties [Section titled “Properties”](#properties) ### field [Section titled “field”](#field) > **field**: keyof `T` | keyof `T`\[] | ((`record`) => `string`) Defined in: [embed.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/embeddings/src/lib/embed.ts#L24) Source text to embed, derived from the just-written record (`ctx.result`): a single field name, a list of field names (joined with `"\n"`), or a function for full control (e.g. combining fields with custom formatting, or pulling in related data). *** ### onError? [Section titled “onError?”](#onerror) > `optional` **onError?**: (`error`, `record`, `ctx`) => `void` Defined in: [embed.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/embeddings/src/lib/embed.ts#L31) Called when extracting the text, calling the provider, or upserting the vector throws. The primary write is never rolled back or blocked regardless — see the “Cross-adapter write consistency” pattern in the root README. Defaults to logging via `app.get("logger")` when configured, otherwise silent. #### Parameters [Section titled “Parameters”](#parameters) ##### error [Section titled “error”](#error) `unknown` ##### record [Section titled “record”](#record) `T` ##### ctx [Section titled “ctx”](#ctx) [`HookContext`](/api/mantlejs/mantle/interfaces/hookcontext/)<`T`> #### Returns [Section titled “Returns”](#returns) `void` *** ### provider [Section titled “provider”](#provider) > **provider**: [`EmbeddingProvider`](/api/mantlejs/embeddings/interfaces/embeddingprovider/) Defined in: [embed.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/embeddings/src/lib/embed.ts#L18) The embedding provider called with the extracted source text. *** ### vectors [Section titled “vectors”](#vectors) > **vectors**: [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/)<`T`> Defined in: [embed.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/embeddings/src/lib/embed.ts#L16) The vector adapter to upsert the computed embedding into. # @mantlejs/express ## Interfaces [Section titled “Interfaces”](#interfaces) * [ExpressOptions](/api/mantlejs/express/interfaces/expressoptions/) ## Functions [Section titled “Functions”](#functions) * [errorHandler](/api/mantlejs/express/functions/errorhandler/) * [express](/api/mantlejs/express/functions/express/) # errorHandler > **errorHandler**(): (`err`, `_req`, `res`, `_next`) => `void` Defined in: [error-handler.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/express/src/lib/error-handler.ts#L4) ## Returns [Section titled “Returns”](#returns) (`err`, `_req`, `res`, `_next`) => `void` # express > **express**(`existingApp?`, `options?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [express.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/express/src/lib/express.ts#L37) ## Parameters [Section titled “Parameters”](#parameters) ### existingApp? [Section titled “existingApp?”](#existingapp) `Application` ### options? [Section titled “options?”](#options) [`ExpressOptions`](/api/mantlejs/express/interfaces/expressoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # ExpressOptions Defined in: [express.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/express/src/lib/express.ts#L17) ## Properties [Section titled “Properties”](#properties) ### batch? [Section titled “batch?”](#batch) > `optional` **batch?**: `boolean` | { `maxSize?`: `number`; `path?`: `string`; } Defined in: [express.ts:28](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/express/src/lib/express.ts#L28) Mount a batch endpoint (default `POST /batch`) dispatching a `BatchCall[]` body through `app.batch()` — every call runs its service’s full hook pipeline. On by default; `false` disables it, an object overrides the route path or max batch size (default 25). *** ### cors? [Section titled “cors?”](#cors) > `optional` **cors?**: `boolean` | [`CorsOptions`](/api/mantlejs/mantle/interfaces/corsoptions/) Defined in: [express.ts:34](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/express/src/lib/express.ts#L34) Enable CORS via the `cors` npm package. `true` resolves to permissive defaults (reflects `Origin`, allows the CRUD verbs, no credentials); pass a `CorsOptions` object to customize origin/methods/headers/credentials. Disabled (no CORS headers) by default. *** ### introspection? [Section titled “introspection?”](#introspection) > `optional` **introspection?**: `boolean` | { `path?`: `string`; } Defined in: [express.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/express/src/lib/express.ts#L22) Mount an introspection endpoint (default `GET /_services`) returning a `ServiceDescriptor[]` for every registered service. Off by default. # @mantlejs/http ## Interfaces [Section titled “Interfaces”](#interfaces) * [HttpOptions](/api/mantlejs/http/interfaces/httpoptions/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [FetchHandler](/api/mantlejs/http/type-aliases/fetchhandler/) * [NodeHttpHandler](/api/mantlejs/http/type-aliases/nodehttphandler/) ## Functions [Section titled “Functions”](#functions) * [http](/api/mantlejs/http/functions/http/) * [toErrorResponse](/api/mantlejs/http/functions/toerrorresponse/) # http > **http**(`options?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [http.ts:164](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/http/src/lib/http.ts#L164) ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`HttpOptions`](/api/mantlejs/http/interfaces/httpoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # toErrorResponse > **toErrorResponse**(`err`): `object` Defined in: [error-handler.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/http/src/lib/error-handler.ts#L3) ## Parameters [Section titled “Parameters”](#parameters) ### err [Section titled “err”](#err) `unknown` ## Returns [Section titled “Returns”](#returns) `object` ### body [Section titled “body”](#body) > **body**: `Record`<`string`, `unknown`> ### status [Section titled “status”](#status) > **status**: `number` # HttpOptions Defined in: [http.ts:143](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/http/src/lib/http.ts#L143) ## Properties [Section titled “Properties”](#properties) ### batch? [Section titled “batch?”](#batch) > `optional` **batch?**: `boolean` | { `maxSize?`: `number`; `path?`: `string`; } Defined in: [http.ts:154](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/http/src/lib/http.ts#L154) Mount a batch endpoint (default `POST /batch`) dispatching a `BatchCall[]` body through `app.batch()` — every call runs its service’s full hook pipeline. On by default; `false` disables it, an object overrides the route path or max batch size (default 25). *** ### cors? [Section titled “cors?”](#cors) > `optional` **cors?**: `boolean` | [`CorsOptions`](/api/mantlejs/mantle/interfaces/corsoptions/) Defined in: [http.ts:161](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/http/src/lib/http.ts#L161) Enable CORS via hand-rolled header logic (no framework to delegate to). `true` resolves to permissive defaults (reflects `Origin`, allows the CRUD verbs, no credentials); pass a `CorsOptions` object to customize origin/methods/headers/credentials. `OPTIONS` preflight requests short-circuit with a 204 before reaching the router. Disabled by default. *** ### introspection? [Section titled “introspection?”](#introspection) > `optional` **introspection?**: `boolean` | { `path?`: `string`; } Defined in: [http.ts:148](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/http/src/lib/http.ts#L148) Mount an introspection endpoint (default `GET /_services`) returning a `ServiceDescriptor[]` for every registered service. Off by default. # FetchHandler > **FetchHandler** = (`request`) => `Promise`<`Response`> Defined in: [http.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/http/src/lib/http.ts#L30) ## Parameters [Section titled “Parameters”](#parameters) ### request [Section titled “request”](#request) `Request` ## Returns [Section titled “Returns”](#returns) `Promise`<`Response`> # NodeHttpHandler > **NodeHttpHandler** = (`req`, `res`) => `void` Defined in: [http.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/http/src/lib/http.ts#L29) ## Parameters [Section titled “Parameters”](#parameters) ### req [Section titled “req”](#req) `IncomingMessage` ### res [Section titled “res”](#res) `ServerResponse` ## Returns [Section titled “Returns”](#returns) `void` # @mantlejs/knex ## Classes [Section titled “Classes”](#classes) * [KnexRepository](/api/mantlejs/knex/classes/knexrepository/) * [KnexVectorRepository](/api/mantlejs/knex/classes/knexvectorrepository/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [KnexConfig](/api/mantlejs/knex/interfaces/knexconfig/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [DistanceOperator](/api/mantlejs/knex/type-aliases/distanceoperator/) * [WhereClause](/api/mantlejs/knex/type-aliases/whereclause/) ## Variables [Section titled “Variables”](#variables) * [KNEX_OPERATORS](/api/mantlejs/knex/variables/knex_operators/) ## Functions [Section titled “Functions”](#functions) * [capabilitiesForClient](/api/mantlejs/knex/functions/capabilitiesforclient/) * [knex](/api/mantlejs/knex/functions/knex/) * [knexify](/api/mantlejs/knex/functions/knexify/) * [mapWhereFields](/api/mantlejs/knex/functions/mapwherefields/) # KnexRepository Defined in: [knex-repository.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L18) ## Extended by [Section titled “Extended by”](#extended-by) * [`KnexVectorRepository`](/api/mantlejs/knex/classes/knexvectorrepository/) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ### D [Section titled “D”](#d) `D` = `Partial`<`T`> ## Implements [Section titled “Implements”](#implements) * [`Repository`](/api/mantlejs/mantle/interfaces/repository/)<`T`, `D`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new KnexRepository**<`T`, `D`>(`app`): `KnexRepository`<`T`, `D`> Defined in: [knex-repository.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L42) #### Parameters [Section titled “Parameters”](#parameters) ##### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) #### Returns [Section titled “Returns”](#returns) `KnexRepository`<`T`, `D`> ## Properties [Section titled “Properties”](#properties) ### \_trx [Section titled “\_trx”](#_trx) > `protected` **\_trx**: `Transaction`<`any`, `any`\[]> | `null` = `null` Defined in: [knex-repository.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L20) *** ### columnCase [Section titled “columnCase”](#columncase) > `readonly` **columnCase**: `"camelCase"` | `"snake_case"` = `"camelCase"` Defined in: [knex-repository.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L35) Naming convention used to derive column names from entity field names. “camelCase” (default) uses entity field names verbatim as column names. “snake_case” converts every field automatically (userId -> user_id) so the entity/type can stay camelCase while the table uses snake_case columns. *** ### createdAtField [Section titled “createdAtField”](#createdatfield) > `readonly` **createdAtField**: `string` = `"createdAt"` Defined in: [knex-repository.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L26) Entity-side field written for auto-managed creation timestamps. #### Default [Section titled “Default”](#default) ```ts "createdAt" ``` *** ### fieldMap [Section titled “fieldMap”](#fieldmap) > `readonly` **fieldMap**: `Record`<`string`, `string`> = `{}` Defined in: [knex-repository.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L40) Explicit entity-field-to-column overrides, applied before `columnCase` conversion. Use this for individual columns that don’t follow the table’s general convention. *** ### idField [Section titled “idField”](#idfield) > `readonly` **idField**: `string` = `"id"` Defined in: [knex-repository.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L23) *** ### knex [Section titled “knex”](#knex) > `protected` `readonly` **knex**: `Knex` Defined in: [knex-repository.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L19) *** ### tableName [Section titled “tableName”](#tablename) > `abstract` `readonly` **tableName**: `string` Defined in: [knex-repository.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L22) *** ### timestamps [Section titled “timestamps”](#timestamps) > `readonly` **timestamps**: `boolean` = `true` Defined in: [knex-repository.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L24) *** ### updatedAtField [Section titled “updatedAtField”](#updatedatfield) > `readonly` **updatedAtField**: `string` = `"updatedAt"` Defined in: [knex-repository.ts:28](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L28) Entity-side field written for auto-managed update timestamps. #### Default [Section titled “Default”](#default-1) ```ts "updatedAt" ``` ## Accessors [Section titled “Accessors”](#accessors) ### db [Section titled “db”](#db) #### Get Signature [Section titled “Get Signature”](#get-signature) > **get** **db**(): `QueryBuilder` Defined in: [knex-repository.ts:47](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L47) Raw query builder for this table, respecting any active transaction. ##### Returns [Section titled “Returns”](#returns-1) `QueryBuilder` *** ### qb [Section titled “qb”](#qb) #### Get Signature [Section titled “Get Signature”](#get-signature-1) > **get** `protected` **qb**(): `Knex`<`any`, `any`\[]> | `Transaction`<`any`, `any`\[]> Defined in: [knex-repository.ts:79](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L79) ##### Returns [Section titled “Returns”](#returns-2) `Knex`<`any`, `any`\[]> | `Transaction`<`any`, `any`\[]> ## Methods [Section titled “Methods”](#methods) ### buildQuery() [Section titled “buildQuery()”](#buildquery) > `protected` **buildQuery**(`params?`): `QueryBuilder` Defined in: [knex-repository.ts:94](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L94) #### Parameters [Section titled “Parameters”](#parameters-1) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-3) `QueryBuilder` *** ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [knex-repository.ts:297](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L297) #### Parameters [Section titled “Parameters”](#parameters-2) ##### params? [Section titled “params?”](#params-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`number`> #### Implementation of [Section titled “Implementation of”](#implementation-of) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`count`](/api/mantlejs/mantle/interfaces/repository/#count) *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [knex-repository.ts:272](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L272) #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-5) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-1) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`deleteById`](/api/mantlejs/mantle/interfaces/repository/#deletebyid) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [knex-repository.ts:51](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L51) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-6) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`describe`](/api/mantlejs/mantle/interfaces/repository/#describe) *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [knex-repository.ts:152](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L152) #### Parameters [Section titled “Parameters”](#parameters-4) ##### params? [Section titled “params?”](#params-2) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findAll`](/api/mantlejs/mantle/interfaces/repository/#findall) *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [knex-repository.ts:164](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L164) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T` | `null`> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findById`](/api/mantlejs/mantle/interfaces/repository/#findbyid) *** ### mapDataToColumns() [Section titled “mapDataToColumns()”](#mapdatatocolumns) > `protected` **mapDataToColumns**(`data`): `Record`<`string`, `unknown`> Defined in: [knex-repository.ts:130](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L130) Renames the keys of a data payload from entity field names to column names. #### Parameters [Section titled “Parameters”](#parameters-6) ##### data [Section titled “data”](#data) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-9) `Record`<`string`, `unknown`> *** ### mapRowToEntity() [Section titled “mapRowToEntity()”](#maprowtoentity) > `protected` **mapRowToEntity**(`row`): `T` Defined in: [knex-repository.ts:135](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L135) Renames the keys of a returned row from column names to entity field names. #### Parameters [Section titled “Parameters”](#parameters-7) ##### row [Section titled “row”](#row) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-10) `T` *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [knex-repository.ts:243](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L243) #### Parameters [Section titled “Parameters”](#parameters-8) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-1) `D` #### Returns [Section titled “Returns”](#returns-11) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`patchById`](/api/mantlejs/mantle/interfaces/repository/#patchbyid) *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [knex-repository.ts:175](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L175) #### Parameters [Section titled “Parameters”](#parameters-9) ##### data [Section titled “data”](#data-2) `D` #### Returns [Section titled “Returns”](#returns-12) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`save`](/api/mantlejs/mantle/interfaces/repository/#save) *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [knex-repository.ts:192](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L192) #### Parameters [Section titled “Parameters”](#parameters-10) ##### data [Section titled “data”](#data-3) `D`\[] #### Returns [Section titled “Returns”](#returns-13) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-7) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`saveAll`](/api/mantlejs/mantle/interfaces/repository/#saveall) *** ### toColumn() [Section titled “toColumn()”](#tocolumn) > `protected` **toColumn**(`field`): `string` Defined in: [knex-repository.ts:117](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L117) Translates an entity field name to its column name (fieldMap override, then columnCase convention). #### Parameters [Section titled “Parameters”](#parameters-11) ##### field [Section titled “field”](#field) `string` #### Returns [Section titled “Returns”](#returns-14) `string` *** ### toEntityField() [Section titled “toEntityField()”](#toentityfield) > `protected` **toEntityField**(`column`): `string` Defined in: [knex-repository.ts:123](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L123) Translates a column name back to its entity field name — the inverse of `toColumn`. #### Parameters [Section titled “Parameters”](#parameters-12) ##### column [Section titled “column”](#column) `string` #### Returns [Section titled “Returns”](#returns-15) `string` *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [knex-repository.ts:217](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L217) #### Parameters [Section titled “Parameters”](#parameters-13) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-4) `D` #### Returns [Section titled “Returns”](#returns-16) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-8) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`updateById`](/api/mantlejs/mantle/interfaces/repository/#updatebyid) *** ### withTransaction() [Section titled “withTransaction()”](#withtransaction) > **withTransaction**<`R`>(`callback`): `Promise`<`R`> Defined in: [knex-repository.ts:69](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L69) Run a set of repository operations inside a single database transaction. The callback receives a transaction-scoped copy of this repository. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### R [Section titled “R”](#r) `R` #### Parameters [Section titled “Parameters”](#parameters-14) ##### callback [Section titled “callback”](#callback) (`repo`) => `Promise`<`R`> #### Returns [Section titled “Returns”](#returns-17) `Promise`<`R`> *** ### wrapError() [Section titled “wrapError()”](#wraperror) > `protected` **wrapError**(`err`): `Error` Defined in: [knex-repository.ts:313](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L313) #### Parameters [Section titled “Parameters”](#parameters-15) ##### err [Section titled “err”](#err) `unknown` #### Returns [Section titled “Returns”](#returns-18) `Error` # KnexVectorRepository Defined in: [knex-vector-repository.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-vector-repository.ts#L13) Extends KnexRepository with pgvector support for PostgreSQL. Subclasses must be connected to a `pg` client — vector methods throw GeneralError on other databases. ## Extends [Section titled “Extends”](#extends) * [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/)<`T`, `D`> ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ### D [Section titled “D”](#d) `D` = `Partial`<`T`> ## Implements [Section titled “Implements”](#implements) * [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/)<`T`, `D`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new KnexVectorRepository**<`T`, `D`>(`app`): `KnexVectorRepository`<`T`, `D`> Defined in: [knex-repository.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L42) #### Parameters [Section titled “Parameters”](#parameters) ##### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) #### Returns [Section titled “Returns”](#returns) `KnexVectorRepository`<`T`, `D`> #### Inherited from [Section titled “Inherited from”](#inherited-from) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`constructor`](/api/mantlejs/knex/classes/knexrepository/#constructor) ## Properties [Section titled “Properties”](#properties) ### \_trx [Section titled “\_trx”](#_trx) > `protected` **\_trx**: `Transaction`<`any`, `any`\[]> | `null` = `null` Defined in: [knex-repository.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L20) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`_trx`](/api/mantlejs/knex/classes/knexrepository/#_trx) *** ### columnCase [Section titled “columnCase”](#columncase) > `readonly` **columnCase**: `"camelCase"` | `"snake_case"` = `"camelCase"` Defined in: [knex-repository.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L35) Naming convention used to derive column names from entity field names. “camelCase” (default) uses entity field names verbatim as column names. “snake_case” converts every field automatically (userId -> user_id) so the entity/type can stay camelCase while the table uses snake_case columns. #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`columnCase`](/api/mantlejs/knex/classes/knexrepository/#columncase) *** ### createdAtField [Section titled “createdAtField”](#createdatfield) > `readonly` **createdAtField**: `string` = `"createdAt"` Defined in: [knex-repository.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L26) Entity-side field written for auto-managed creation timestamps. #### Default [Section titled “Default”](#default) ```ts "createdAt" ``` #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`createdAtField`](/api/mantlejs/knex/classes/knexrepository/#createdatfield) *** ### distanceOperator [Section titled “distanceOperator”](#distanceoperator) > `readonly` **distanceOperator**: [`DistanceOperator`](/api/mantlejs/knex/type-aliases/distanceoperator/) = `"<=>"` Defined in: [knex-vector-repository.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-vector-repository.ts#L18) *** ### fieldMap [Section titled “fieldMap”](#fieldmap) > `readonly` **fieldMap**: `Record`<`string`, `string`> = `{}` Defined in: [knex-repository.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L40) Explicit entity-field-to-column overrides, applied before `columnCase` conversion. Use this for individual columns that don’t follow the table’s general convention. #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`fieldMap`](/api/mantlejs/knex/classes/knexrepository/#fieldmap) *** ### idField [Section titled “idField”](#idfield) > `readonly` **idField**: `string` = `"id"` Defined in: [knex-repository.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L23) #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`idField`](/api/mantlejs/knex/classes/knexrepository/#idfield) *** ### knex [Section titled “knex”](#knex) > `protected` `readonly` **knex**: `Knex` Defined in: [knex-repository.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L19) #### Inherited from [Section titled “Inherited from”](#inherited-from-6) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`knex`](/api/mantlejs/knex/classes/knexrepository/#knex) *** ### tableName [Section titled “tableName”](#tablename) > `abstract` `readonly` **tableName**: `string` Defined in: [knex-repository.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L22) #### Inherited from [Section titled “Inherited from”](#inherited-from-7) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`tableName`](/api/mantlejs/knex/classes/knexrepository/#tablename) *** ### timestamps [Section titled “timestamps”](#timestamps) > `readonly` **timestamps**: `boolean` = `true` Defined in: [knex-repository.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L24) #### Inherited from [Section titled “Inherited from”](#inherited-from-8) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`timestamps`](/api/mantlejs/knex/classes/knexrepository/#timestamps) *** ### updatedAtField [Section titled “updatedAtField”](#updatedatfield) > `readonly` **updatedAtField**: `string` = `"updatedAt"` Defined in: [knex-repository.ts:28](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L28) Entity-side field written for auto-managed update timestamps. #### Default [Section titled “Default”](#default-1) ```ts "updatedAt" ``` #### Inherited from [Section titled “Inherited from”](#inherited-from-9) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`updatedAtField`](/api/mantlejs/knex/classes/knexrepository/#updatedatfield) *** ### vectorColumn [Section titled “vectorColumn”](#vectorcolumn) > `readonly` **vectorColumn**: `string` = `"embedding"` Defined in: [knex-vector-repository.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-vector-repository.ts#L17) ## Accessors [Section titled “Accessors”](#accessors) ### db [Section titled “db”](#db) #### Get Signature [Section titled “Get Signature”](#get-signature) > **get** **db**(): `QueryBuilder` Defined in: [knex-repository.ts:47](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L47) Raw query builder for this table, respecting any active transaction. ##### Returns [Section titled “Returns”](#returns-1) `QueryBuilder` #### Inherited from [Section titled “Inherited from”](#inherited-from-10) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`db`](/api/mantlejs/knex/classes/knexrepository/#db) *** ### qb [Section titled “qb”](#qb) #### Get Signature [Section titled “Get Signature”](#get-signature-1) > **get** `protected` **qb**(): `Knex`<`any`, `any`\[]> | `Transaction`<`any`, `any`\[]> Defined in: [knex-repository.ts:79](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L79) ##### Returns [Section titled “Returns”](#returns-2) `Knex`<`any`, `any`\[]> | `Transaction`<`any`, `any`\[]> #### Inherited from [Section titled “Inherited from”](#inherited-from-11) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`qb`](/api/mantlejs/knex/classes/knexrepository/#qb) ## Methods [Section titled “Methods”](#methods) ### buildQuery() [Section titled “buildQuery()”](#buildquery) > `protected` **buildQuery**(`params?`): `QueryBuilder` Defined in: [knex-repository.ts:94](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L94) #### Parameters [Section titled “Parameters”](#parameters-1) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-3) `QueryBuilder` #### Inherited from [Section titled “Inherited from”](#inherited-from-12) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`buildQuery`](/api/mantlejs/knex/classes/knexrepository/#buildquery) *** ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [knex-repository.ts:297](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L297) #### Parameters [Section titled “Parameters”](#parameters-2) ##### params? [Section titled “params?”](#params-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`number`> #### Implementation of [Section titled “Implementation of”](#implementation-of) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`count`](/api/mantlejs/mantle/interfaces/vectorrepository/#count) #### Inherited from [Section titled “Inherited from”](#inherited-from-13) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`count`](/api/mantlejs/knex/classes/knexrepository/#count) *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [knex-repository.ts:272](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L272) #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-5) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-1) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`deleteById`](/api/mantlejs/mantle/interfaces/vectorrepository/#deletebyid) #### Inherited from [Section titled “Inherited from”](#inherited-from-14) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`deleteById`](/api/mantlejs/knex/classes/knexrepository/#deletebyid) *** ### deleteVector() [Section titled “deleteVector()”](#deletevector) > **deleteVector**(`id`): `Promise`<`T`> Defined in: [knex-vector-repository.ts:119](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-vector-repository.ts#L119) Delete a record and its associated vector by id. #### Parameters [Section titled “Parameters”](#parameters-4) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`deleteVector`](/api/mantlejs/mantle/interfaces/vectorrepository/#deletevector) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [knex-repository.ts:51](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L51) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-7) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`describe`](/api/mantlejs/mantle/interfaces/vectorrepository/#describe) #### Inherited from [Section titled “Inherited from”](#inherited-from-15) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`describe`](/api/mantlejs/knex/classes/knexrepository/#describe) *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [knex-repository.ts:152](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L152) #### Parameters [Section titled “Parameters”](#parameters-5) ##### params? [Section titled “params?”](#params-2) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findAll`](/api/mantlejs/mantle/interfaces/vectorrepository/#findall) #### Inherited from [Section titled “Inherited from”](#inherited-from-16) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`findAll`](/api/mantlejs/knex/classes/knexrepository/#findall) *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [knex-repository.ts:164](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L164) #### Parameters [Section titled “Parameters”](#parameters-6) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T` | `null`> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findById`](/api/mantlejs/mantle/interfaces/vectorrepository/#findbyid) #### Inherited from [Section titled “Inherited from”](#inherited-from-17) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`findById`](/api/mantlejs/knex/classes/knexrepository/#findbyid) *** ### findSimilar() [Section titled “findSimilar()”](#findsimilar) > **findSimilar**(`vector`, `topK`, `params?`): `Promise`<`T` & `object`\[]> Defined in: [knex-vector-repository.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-vector-repository.ts#L37) Find the top-K records most similar to the given embedding vector. Results include a synthetic `_score` column with the computed pgvector distance — LOWER is more similar (it is a distance, not a similarity). Generates: SELECT \*, $1::vector AS \_score FROM ORDER BY $1::vector LIMIT $2 #### Parameters [Section titled “Parameters”](#parameters-7) ##### vector [Section titled “vector”](#vector) `number`\[] ##### topK [Section titled “topK”](#topk) `number` ##### params? [Section titled “params?”](#params-3) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-10) `Promise`<`T` & `object`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findSimilar`](/api/mantlejs/mantle/interfaces/vectorrepository/#findsimilar) *** ### mapDataToColumns() [Section titled “mapDataToColumns()”](#mapdatatocolumns) > `protected` **mapDataToColumns**(`data`): `Record`<`string`, `unknown`> Defined in: [knex-repository.ts:130](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L130) Renames the keys of a data payload from entity field names to column names. #### Parameters [Section titled “Parameters”](#parameters-8) ##### data [Section titled “data”](#data) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-11) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-18) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`mapDataToColumns`](/api/mantlejs/knex/classes/knexrepository/#mapdatatocolumns) *** ### mapRowToEntity() [Section titled “mapRowToEntity()”](#maprowtoentity) > `protected` **mapRowToEntity**(`row`): `T` Defined in: [knex-repository.ts:135](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L135) Renames the keys of a returned row from column names to entity field names. #### Parameters [Section titled “Parameters”](#parameters-9) ##### row [Section titled “row”](#row) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-12) `T` #### Inherited from [Section titled “Inherited from”](#inherited-from-19) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`mapRowToEntity`](/api/mantlejs/knex/classes/knexrepository/#maprowtoentity) *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [knex-repository.ts:243](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L243) #### Parameters [Section titled “Parameters”](#parameters-10) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-1) `D` #### Returns [Section titled “Returns”](#returns-13) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-7) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`patchById`](/api/mantlejs/mantle/interfaces/vectorrepository/#patchbyid) #### Inherited from [Section titled “Inherited from”](#inherited-from-20) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`patchById`](/api/mantlejs/knex/classes/knexrepository/#patchbyid) *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [knex-repository.ts:175](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L175) #### Parameters [Section titled “Parameters”](#parameters-11) ##### data [Section titled “data”](#data-2) `D` #### Returns [Section titled “Returns”](#returns-14) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-8) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`save`](/api/mantlejs/mantle/interfaces/vectorrepository/#save) #### Inherited from [Section titled “Inherited from”](#inherited-from-21) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`save`](/api/mantlejs/knex/classes/knexrepository/#save) *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [knex-repository.ts:192](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L192) #### Parameters [Section titled “Parameters”](#parameters-12) ##### data [Section titled “data”](#data-3) `D`\[] #### Returns [Section titled “Returns”](#returns-15) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-9) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`saveAll`](/api/mantlejs/mantle/interfaces/vectorrepository/#saveall) #### Inherited from [Section titled “Inherited from”](#inherited-from-22) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`saveAll`](/api/mantlejs/knex/classes/knexrepository/#saveall) *** ### toColumn() [Section titled “toColumn()”](#tocolumn) > `protected` **toColumn**(`field`): `string` Defined in: [knex-repository.ts:117](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L117) Translates an entity field name to its column name (fieldMap override, then columnCase convention). #### Parameters [Section titled “Parameters”](#parameters-13) ##### field [Section titled “field”](#field) `string` #### Returns [Section titled “Returns”](#returns-16) `string` #### Inherited from [Section titled “Inherited from”](#inherited-from-23) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`toColumn`](/api/mantlejs/knex/classes/knexrepository/#tocolumn) *** ### toEntityField() [Section titled “toEntityField()”](#toentityfield) > `protected` **toEntityField**(`column`): `string` Defined in: [knex-repository.ts:123](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L123) Translates a column name back to its entity field name — the inverse of `toColumn`. #### Parameters [Section titled “Parameters”](#parameters-14) ##### column [Section titled “column”](#column) `string` #### Returns [Section titled “Returns”](#returns-17) `string` #### Inherited from [Section titled “Inherited from”](#inherited-from-24) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`toEntityField`](/api/mantlejs/knex/classes/knexrepository/#toentityfield) *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [knex-repository.ts:217](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L217) #### Parameters [Section titled “Parameters”](#parameters-15) ##### id [Section titled “id”](#id-4) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-4) `D` #### Returns [Section titled “Returns”](#returns-18) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-10) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`updateById`](/api/mantlejs/mantle/interfaces/vectorrepository/#updatebyid) #### Inherited from [Section titled “Inherited from”](#inherited-from-25) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`updateById`](/api/mantlejs/knex/classes/knexrepository/#updatebyid) *** ### upsertVector() [Section titled “upsertVector()”](#upsertvector) > **upsertVector**(`id`, `vector`, `data`): `Promise`<`T`> Defined in: [knex-vector-repository.ts:82](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-vector-repository.ts#L82) Upsert a record with its embedding vector: updates the vector and data columns on an existing row (but not createdAt), or inserts a new one. Deliberately an UPDATE followed by a conditional INSERT rather than a single `INSERT ... ON CONFLICT DO UPDATE` — Postgres validates NOT NULL constraints on the full candidate insert tuple before checking for a conflict, so a single-statement upsert that only lists the id/vector/timestamp columns fails on any other NOT NULL column (e.g. a required `title`) even when the conflicting row already has one. Not atomic: a concurrent upsertVector on the same id between the UPDATE and the INSERT could race to insert twice. #### Parameters [Section titled “Parameters”](#parameters-16) ##### id [Section titled “id”](#id-5) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### vector [Section titled “vector”](#vector-1) `number`\[] ##### data [Section titled “data”](#data-5) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns-19) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-11) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`upsertVector`](/api/mantlejs/mantle/interfaces/vectorrepository/#upsertvector) *** ### withTransaction() [Section titled “withTransaction()”](#withtransaction) > **withTransaction**<`R`>(`callback`): `Promise`<`R`> Defined in: [knex-repository.ts:69](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L69) Run a set of repository operations inside a single database transaction. The callback receives a transaction-scoped copy of this repository. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### R [Section titled “R”](#r) `R` #### Parameters [Section titled “Parameters”](#parameters-17) ##### callback [Section titled “callback”](#callback) (`repo`) => `Promise`<`R`> #### Returns [Section titled “Returns”](#returns-20) `Promise`<`R`> #### Inherited from [Section titled “Inherited from”](#inherited-from-26) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`withTransaction`](/api/mantlejs/knex/classes/knexrepository/#withtransaction) *** ### wrapError() [Section titled “wrapError()”](#wraperror) > `protected` **wrapError**(`err`): `Error` Defined in: [knex-repository.ts:313](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-repository.ts#L313) #### Parameters [Section titled “Parameters”](#parameters-18) ##### err [Section titled “err”](#err) `unknown` #### Returns [Section titled “Returns”](#returns-21) `Error` #### Inherited from [Section titled “Inherited from”](#inherited-from-27) [`KnexRepository`](/api/mantlejs/knex/classes/knexrepository/).[`wrapError`](/api/mantlejs/knex/classes/knexrepository/#wraperror) | | | - | # capabilitiesForClient > **capabilitiesForClient**(`client`): `object` Defined in: [knexify.ts:71](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knexify.ts#L71) The per-client capability breakdown this adapter’s `describe()` reports — computed from the actual connected client string rather than a single static constant, so `describe()` can’t drift from what the translator actually does (the bug this refactor fixes: `describe()` used to advertise `$contains` unconditionally even though only PostgreSQL could execute it). Takes a plain client string (not a `Knex.QueryBuilder`) so callers can feed it either the raw `Knex` instance’s `client.config.client` (what `KnexRepository.describe()` does, matching the same client-detection convention its `supportsReturning` getter already uses) or a query builder’s. ## Parameters [Section titled “Parameters”](#parameters) ### client [Section titled “client”](#client) `string` ## Returns [Section titled “Returns”](#returns) `object` ### nestedPaths [Section titled “nestedPaths”](#nestedpaths) > **nestedPaths**: `boolean` ### operators [Section titled “operators”](#operators) > **operators**: `string`\[] # knex > **knex**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [knex.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex.ts#L13) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) [`KnexConfig`](/api/mantlejs/knex/interfaces/knexconfig/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # knexify > **knexify**(`builder`, `where`): `QueryBuilder` Defined in: [knexify.ts:95](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knexify.ts#L95) Translates a structured where clause (with query operators) into Knex query builder calls. Supported operators: Comparison : $lt, $lte, $gt, $gte Equality : $ne, $in, $nin Logical : $or, $and Pattern : $like, $notlike, $ilike (all clients — Knex compiles LIKE/ILIKE per dialect) Containment: $contains — jsonb `@>` (PostgreSQL) or `JSON_CONTAINS` (MySQL) only Null : field: null → IS NULL field: { $ne: null } → IS NOT NULL Dot-path field names (`"metadata.owner.name"`) address nested JSON fields on PostgreSQL, MySQL, SQLite, and MSSQL, for equality, $lt/$lte/$gt/$gte/$ne/$like/$notlike (`$ilike` on PostgreSQL only — it isn’t standard SQL, unlike the others). `$in`/`$nin`/null-checks on a dot-path field, and `$contains` combined with a dot-path field on a client outside `CONTAINS_FAMILIES`, throw a clear `BadRequest` rather than emitting wrong or broken SQL. ## Parameters [Section titled “Parameters”](#parameters) ### builder [Section titled “builder”](#builder) `QueryBuilder` ### where [Section titled “where”](#where) [`WhereClause`](/api/mantlejs/knex/type-aliases/whereclause/) ## Returns [Section titled “Returns”](#returns) `QueryBuilder` # mapWhereFields > **mapWhereFields**(`where`, `toColumn`): [`WhereClause`](/api/mantlejs/knex/type-aliases/whereclause/) Defined in: [knexify.ts:127](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knexify.ts#L127) Renames the field keys of a where clause via `toColumn`, recursing into `$or`/`$and` branches and leaving operator keys ($lt, $ne, …) and values untouched. For a dot-path field (`"metadata.ownerName"`), only the *root* segment — the actual SQL column — goes through `toColumn`. The remaining segments are JSON key names inside the stored document, not SQL identifiers, so `fieldMap`/`columnCase` (e.g. `snake_case`) must never touch them: they describe the table’s column-naming convention, not the shape of whatever JSON happens to be stored in one jsonb/JSON column. ## Parameters [Section titled “Parameters”](#parameters) ### where [Section titled “where”](#where) [`WhereClause`](/api/mantlejs/knex/type-aliases/whereclause/) ### toColumn [Section titled “toColumn”](#tocolumn) (`field`) => `string` ## Returns [Section titled “Returns”](#returns) [`WhereClause`](/api/mantlejs/knex/type-aliases/whereclause/) # KnexConfig Defined in: [knex.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex.ts#L5) ## Properties [Section titled “Properties”](#properties) ### client [Section titled “client”](#client) > **client**: `string` Defined in: [knex.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex.ts#L7) Knex client identifier: ‘pg’, ‘mysql2’, ‘sqlite3’, ‘mssql’, ‘oracledb’, etc. *** ### connection [Section titled “connection”](#connection) > **connection**: `string` | `StaticConnectionConfig` | `ConnectionConfigProvider` | `undefined` Defined in: [knex.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex.ts#L8) *** ### pool? [Section titled “pool?”](#pool) > `optional` **pool?**: `object` Defined in: [knex.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex.ts#L9) #### max? [Section titled “max?”](#max) > `optional` **max?**: `number` #### min? [Section titled “min?”](#min) > `optional` **min?**: `number` *** ### searchPath? [Section titled “searchPath?”](#searchpath) > `optional` **searchPath?**: `string` | `string`\[] Defined in: [knex.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex.ts#L10) # DistanceOperator > **DistanceOperator** = `"<=>"` | `"<#>"` | `"<->"` Defined in: [knex-vector-repository.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knex-vector-repository.ts#L7) # WhereClause > **WhereClause** = `Record`<`string`, `WhereValue`> Defined in: [knexify.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knexify.ts#L6) # KNEX_OPERATORS > `const` **KNEX_OPERATORS**: `ReadonlySet`<`string`> Defined in: [knexify.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/knex/src/lib/knexify.ts#L22) All query operators the Knex adapter’s translator understands. This is a superset across every supported client — the connected client decides which of these actually execute; `$contains` and dot-path field names throw a clear, client-naming `BadRequest` when the connected client doesn’t support them, rather than silently producing wrong or broken SQL. See `clientCapabilities()` for the per-client breakdown this adapter’s `describe()` reports. # @mantlejs/koa ## Interfaces [Section titled “Interfaces”](#interfaces) * [KoaOptions](/api/mantlejs/koa/interfaces/koaoptions/) ## Functions [Section titled “Functions”](#functions) * [errorHandler](/api/mantlejs/koa/functions/errorhandler/) * [koa](/api/mantlejs/koa/functions/koa/) # errorHandler > **errorHandler**(`ctx`, `next`): `Promise`<`void`> Defined in: [error-handler.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/koa/src/lib/error-handler.ts#L4) ## Parameters [Section titled “Parameters”](#parameters) ### ctx [Section titled “ctx”](#ctx) `Context` ### next [Section titled “next”](#next) `Next` ## Returns [Section titled “Returns”](#returns) `Promise`<`void`> # koa > **koa**(`options?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [koa.ts:107](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/koa/src/lib/koa.ts#L107) ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`KoaOptions`](/api/mantlejs/koa/interfaces/koaoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # KoaOptions Defined in: [koa.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/koa/src/lib/koa.ts#L29) ## Properties [Section titled “Properties”](#properties) ### app? [Section titled “app?”](#app) > `optional` **app?**: `__module` Defined in: [koa.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/koa/src/lib/koa.ts#L31) Existing Koa application instance. When omitted, a new one is created. *** ### batch? [Section titled “batch?”](#batch) > `optional` **batch?**: `boolean` | { `maxSize?`: `number`; `path?`: `string`; } Defined in: [koa.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/koa/src/lib/koa.ts#L42) Mount a batch endpoint (default `POST /batch`) dispatching a `BatchCall[]` body through `app.batch()` — every call runs its service’s full hook pipeline. On by default; `false` disables it, an object overrides the route path or max batch size (default 25). *** ### cors? [Section titled “cors?”](#cors) > `optional` **cors?**: `boolean` | [`CorsOptions`](/api/mantlejs/mantle/interfaces/corsoptions/) Defined in: [koa.ts:48](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/koa/src/lib/koa.ts#L48) Enable CORS via `@koa/cors`. `true` resolves to permissive defaults (reflects `Origin`, allows the CRUD verbs, no credentials); pass a `CorsOptions` object to customize origin/methods/headers/credentials. Disabled (no CORS headers) by default. *** ### introspection? [Section titled “introspection?”](#introspection) > `optional` **introspection?**: `boolean` | { `path?`: `string`; } Defined in: [koa.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/koa/src/lib/koa.ts#L36) Mount an introspection endpoint (default `GET /_services`) returning a `ServiceDescriptor[]` for every registered service. Off by default. # @mantlejs/logger ## Interfaces [Section titled “Interfaces”](#interfaces) * [CreateLoggerOptions](/api/mantlejs/logger/interfaces/createloggeroptions/) * [LogErrorOptions](/api/mantlejs/logger/interfaces/logerroroptions/) * [LogLayerLike](/api/mantlejs/logger/interfaces/loglayerlike/) * [LogRequestOptions](/api/mantlejs/logger/interfaces/logrequestoptions/) * [PinoLike](/api/mantlejs/logger/interfaces/pinolike/) ## Variables [Section titled “Variables”](#variables) * [SENSITIVE_PATHS](/api/mantlejs/logger/variables/sensitive_paths/) ## Functions [Section titled “Functions”](#functions) * [createLogger](/api/mantlejs/logger/functions/createlogger/) * [logError](/api/mantlejs/logger/functions/logerror/) * [logger](/api/mantlejs/logger/functions/logger/) * [loglayerAdapter](/api/mantlejs/logger/functions/loglayeradapter/) * [logRequest](/api/mantlejs/logger/functions/logrequest/) * [pinoAdapter](/api/mantlejs/logger/functions/pinoadapter/) * [redactPaths](/api/mantlejs/logger/functions/redactpaths/) # createLogger > **createLogger**(`options?`): `Promise`<[`Logger`](/api/mantlejs/mantle/interfaces/logger/)> Defined in: [create-logger.ts:55](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/create-logger.ts#L55) Builds a Logger backed by pino, with production-ready defaults for level, redaction, and GCP severity mapping. ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`CreateLoggerOptions`](/api/mantlejs/logger/interfaces/createloggeroptions/) = `{}` ## Returns [Section titled “Returns”](#returns) `Promise`<[`Logger`](/api/mantlejs/mantle/interfaces/logger/)> # logError > **logError**(`options?`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [log-error.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/log-error.ts#L13) Error hook. Logs structured error details from ctx.error. ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`LogErrorOptions`](/api/mantlejs/logger/interfaces/logerroroptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) # logger > **logger**(`adapter`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [logger.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/logger.ts#L4) Registers a Logger adapter on the application via app.set(‘logger’, adapter). ## Parameters [Section titled “Parameters”](#parameters) ### adapter [Section titled “adapter”](#adapter) [`Logger`](/api/mantlejs/mantle/interfaces/logger/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # loglayerAdapter > **loglayerAdapter**(`log`): [`Logger`](/api/mantlejs/mantle/interfaces/logger/) Defined in: [loglayer-adapter.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/loglayer-adapter.ts#L23) Wraps a LogLayer instance to satisfy the Mantle Logger interface. Merges the current RequestContext into `withMetadata()` on every call. `child(bindings)` closes over merged bindings rather than using LogLayer’s own `child()` (whose semantics differ) — bindings are folded into every `withMetadata` call. ## Parameters [Section titled “Parameters”](#parameters) ### log [Section titled “log”](#log) [`LogLayerLike`](/api/mantlejs/logger/interfaces/loglayerlike/) ## Returns [Section titled “Returns”](#returns) [`Logger`](/api/mantlejs/mantle/interfaces/logger/) # logRequest > **logRequest**(`options?`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [log-request.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/log-request.ts#L19) Returns a hook that logs service call duration. Register the same returned function in before.all, after.all, AND error.all. The first invocation (before phase) records the start time; the second (after or error phase) emits the log record with elapsed duration and status. ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`LogRequestOptions`](/api/mantlejs/logger/interfaces/logrequestoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) # pinoAdapter > **pinoAdapter**(`pino`): [`Logger`](/api/mantlejs/mantle/interfaces/logger/) Defined in: [pino-adapter.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/pino-adapter.ts#L18) Wraps a pino logger to satisfy the Mantle Logger interface. Pino uses (obj, msg) argument order; the Logger interface uses (msg, obj). Automatically merges the current RequestContext (correlationId, etc.) into every log record. ## Parameters [Section titled “Parameters”](#parameters) ### pino [Section titled “pino”](#pino) [`PinoLike`](/api/mantlejs/logger/interfaces/pinolike/) ## Returns [Section titled “Returns”](#returns) [`Logger`](/api/mantlejs/mantle/interfaces/logger/) # redactPaths > **redactPaths**(`value`, `paths?`): `unknown` Defined in: [redact.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/redact.ts#L36) Deep-clones `value`, replacing any field matched by `paths` with `"[Redacted]"`. Path syntax is a small subset of pino’s `redact.paths`: a bare key (`"password"`) matches only at the top level; a `"*.key"` entry matches `key` at any depth. ## Parameters [Section titled “Parameters”](#parameters) ### value [Section titled “value”](#value) `unknown` ### paths? [Section titled “paths?”](#paths) `string`\[] = `SENSITIVE_PATHS` ## Returns [Section titled “Returns”](#returns) `unknown` # CreateLoggerOptions Defined in: [create-logger.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/create-logger.ts#L7) ## Properties [Section titled “Properties”](#properties) ### gcp? [Section titled “gcp?”](#gcp) > `optional` **gcp?**: `boolean` Defined in: [create-logger.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/create-logger.ts#L15) Google Cloud structured logging: level -> `severity` labels, message key “message”. Default: false. *** ### level? [Section titled “level?”](#level) > `optional` **level?**: `"debug"` | `"info"` | `"warn"` | `"error"` Defined in: [create-logger.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/create-logger.ts#L9) Default: “info” when NODE_ENV === “production”, else “debug”. *** ### pino? [Section titled “pino?”](#pino) > `optional` **pino?**: `Record`<`string`, `unknown`> Defined in: [create-logger.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/create-logger.ts#L17) Extra pino options merged last (escape hatch). *** ### pretty? [Section titled “pretty?”](#pretty) > `optional` **pretty?**: `boolean` Defined in: [create-logger.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/create-logger.ts#L13) Pretty-print via pino-pretty when available. Default: false. Ignored in production. *** ### redact? [Section titled “redact?”](#redact) > `optional` **redact?**: `string`\[] Defined in: [create-logger.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/create-logger.ts#L11) Pino redact paths. Default: SENSITIVE_PATHS. Pass \[] to disable. # LogErrorOptions Defined in: [log-error.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/log-error.ts#L5) ## Properties [Section titled “Properties”](#properties) ### includeStack? [Section titled “includeStack?”](#includestack) > `optional` **includeStack?**: `boolean` Defined in: [log-error.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/log-error.ts#L9) Include stack trace. Default: true when NODE_ENV !== ‘production’ *** ### levelByCode? [Section titled “levelByCode?”](#levelbycode) > `optional` **levelByCode?**: `boolean` Defined in: [log-error.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/log-error.ts#L7) Log 4xx errors as ‘warn’, 5xx errors as ‘error’. Default: true # LogLayerLike Defined in: [loglayer-adapter.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/loglayer-adapter.ts#L5) Duck-type of a LogLayer instance — no dependency on the `loglayer` package. ## Methods [Section titled “Methods”](#methods) ### child()? [Section titled “child()?”](#child) > `optional` **child**(): `unknown` Defined in: [loglayer-adapter.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/loglayer-adapter.ts#L12) #### Returns [Section titled “Returns”](#returns) `unknown` *** ### withMetadata() [Section titled “withMetadata()”](#withmetadata) > **withMetadata**(`meta`): `object` Defined in: [loglayer-adapter.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/loglayer-adapter.ts#L6) #### Parameters [Section titled “Parameters”](#parameters) ##### meta [Section titled “meta”](#meta) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-1) `object` ##### debug() [Section titled “debug()”](#debug) > **debug**(`msg`): `void` ###### Parameters [Section titled “Parameters”](#parameters-1) ###### msg [Section titled “msg”](#msg) `string` ###### Returns [Section titled “Returns”](#returns-2) `void` ##### error() [Section titled “error()”](#error) > **error**(`msg`): `void` ###### Parameters [Section titled “Parameters”](#parameters-2) ###### msg [Section titled “msg”](#msg-1) `string` ###### Returns [Section titled “Returns”](#returns-3) `void` ##### info() [Section titled “info()”](#info) > **info**(`msg`): `void` ###### Parameters [Section titled “Parameters”](#parameters-3) ###### msg [Section titled “msg”](#msg-2) `string` ###### Returns [Section titled “Returns”](#returns-4) `void` ##### warn() [Section titled “warn()”](#warn) > **warn**(`msg`): `void` ###### Parameters [Section titled “Parameters”](#parameters-4) ###### msg [Section titled “msg”](#msg-3) `string` ###### Returns [Section titled “Returns”](#returns-5) `void` # LogRequestOptions Defined in: [log-request.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/log-request.ts#L4) ## Properties [Section titled “Properties”](#properties) ### includeParams? [Section titled “includeParams?”](#includeparams) > `optional` **includeParams?**: `boolean` Defined in: [log-request.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/log-request.ts#L8) Include params in the log record. Default: false — may contain sensitive data *** ### level? [Section titled “level?”](#level) > `optional` **level?**: `"debug"` | `"info"` Defined in: [log-request.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/log-request.ts#L6) Log level for successful calls. Default: ‘debug’ *** ### redactParams? [Section titled “redactParams?”](#redactparams) > `optional` **redactParams?**: `string`\[] Defined in: [log-request.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/log-request.ts#L10) Paths redacted from `params` when includeParams is true. Default: SENSITIVE_PATHS. # PinoLike Defined in: [pino-adapter.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/pino-adapter.ts#L5) Minimal duck-type for a pino logger. Accepts pino.Logger or any compatible object. ## Methods [Section titled “Methods”](#methods) ### child()? [Section titled “child()?”](#child) > `optional` **child**(`bindings`): `PinoLike` Defined in: [pino-adapter.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/pino-adapter.ts#L10) #### Parameters [Section titled “Parameters”](#parameters) ##### bindings [Section titled “bindings”](#bindings) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns) `PinoLike` *** ### debug() [Section titled “debug()”](#debug) > **debug**(`obj`, `msg`): `void` Defined in: [pino-adapter.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/pino-adapter.ts#L6) #### Parameters [Section titled “Parameters”](#parameters-1) ##### obj [Section titled “obj”](#obj) `object` ##### msg [Section titled “msg”](#msg) `string` #### Returns [Section titled “Returns”](#returns-1) `void` *** ### error() [Section titled “error()”](#error) > **error**(`obj`, `msg`): `void` Defined in: [pino-adapter.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/pino-adapter.ts#L9) #### Parameters [Section titled “Parameters”](#parameters-2) ##### obj [Section titled “obj”](#obj-1) `object` ##### msg [Section titled “msg”](#msg-1) `string` #### Returns [Section titled “Returns”](#returns-2) `void` *** ### info() [Section titled “info()”](#info) > **info**(`obj`, `msg`): `void` Defined in: [pino-adapter.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/pino-adapter.ts#L7) #### Parameters [Section titled “Parameters”](#parameters-3) ##### obj [Section titled “obj”](#obj-2) `object` ##### msg [Section titled “msg”](#msg-2) `string` #### Returns [Section titled “Returns”](#returns-3) `void` *** ### warn() [Section titled “warn()”](#warn) > **warn**(`obj`, `msg`): `void` Defined in: [pino-adapter.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/pino-adapter.ts#L8) #### Parameters [Section titled “Parameters”](#parameters-4) ##### obj [Section titled “obj”](#obj-3) `object` ##### msg [Section titled “msg”](#msg-3) `string` #### Returns [Section titled “Returns”](#returns-4) `void` # SENSITIVE_PATHS > `const` **SENSITIVE_PATHS**: `string`\[] Defined in: [redact.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/logger/src/lib/redact.ts#L5) Default sensitive-field paths redacted by `createLogger` (pino-level) and by `logRequest`/`logError` (own-code walk, for adapters that aren’t pino). # @mantlejs/mantle ## Classes [Section titled “Classes”](#classes) * [BadRequest](/api/mantlejs/mantle/classes/badrequest/) * [Conflict](/api/mantlejs/mantle/classes/conflict/) * [Forbidden](/api/mantlejs/mantle/classes/forbidden/) * [GeneralError](/api/mantlejs/mantle/classes/generalerror/) * [MantleError](/api/mantlejs/mantle/classes/mantleerror/) * [MethodNotAllowed](/api/mantlejs/mantle/classes/methodnotallowed/) * [NotAuthenticated](/api/mantlejs/mantle/classes/notauthenticated/) * [NotFound](/api/mantlejs/mantle/classes/notfound/) * [NotImplemented](/api/mantlejs/mantle/classes/notimplemented/) * [RepositoryService](/api/mantlejs/mantle/classes/repositoryservice/) * [TooManyRequests](/api/mantlejs/mantle/classes/toomanyrequests/) * [Unavailable](/api/mantlejs/mantle/classes/unavailable/) * [Unprocessable](/api/mantlejs/mantle/classes/unprocessable/) * [VectorRepositoryService](/api/mantlejs/mantle/classes/vectorrepositoryservice/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [AgentContext](/api/mantlejs/mantle/interfaces/agentcontext/) * [BatchCall](/api/mantlejs/mantle/interfaces/batchcall/) * [BatchDispatchOptions](/api/mantlejs/mantle/interfaces/batchdispatchoptions/) * [BatchResult](/api/mantlejs/mantle/interfaces/batchresult/) * [CorsOptions](/api/mantlejs/mantle/interfaces/corsoptions/) * [CursorPage](/api/mantlejs/mantle/interfaces/cursorpage/) * [GraphRepository](/api/mantlejs/mantle/interfaces/graphrepository/) * [HookConfig](/api/mantlejs/mantle/interfaces/hookconfig/) * [HookContext](/api/mantlejs/mantle/interfaces/hookcontext/) * [HttpRequestLike](/api/mantlejs/mantle/interfaces/httprequestlike/) * [HttpResponseLike](/api/mantlejs/mantle/interfaces/httpresponselike/) * [HttpRouterLike](/api/mantlejs/mantle/interfaces/httprouterlike/) * [Logger](/api/mantlejs/mantle/interfaces/logger/) * [MantleApplication](/api/mantlejs/mantle/interfaces/mantleapplication/) * [MantleChannel](/api/mantlejs/mantle/interfaces/mantlechannel/) * [MantleOptions](/api/mantlejs/mantle/interfaces/mantleoptions/) * [NestedQueryCase](/api/mantlejs/mantle/interfaces/nestedquerycase/) * [NestedQueryRecord](/api/mantlejs/mantle/interfaces/nestedqueryrecord/) * [Paginated](/api/mantlejs/mantle/interfaces/paginated/) * [PublishContext](/api/mantlejs/mantle/interfaces/publishcontext/) * [QueryParams](/api/mantlejs/mantle/interfaces/queryparams/) * [Repository](/api/mantlejs/mantle/interfaces/repository/) * [RepositoryCapabilities](/api/mantlejs/mantle/interfaces/repositorycapabilities/) * [RepositoryServiceOptions](/api/mantlejs/mantle/interfaces/repositoryserviceoptions/) * [RequestContext](/api/mantlejs/mantle/interfaces/requestcontext/) * [Service](/api/mantlejs/mantle/interfaces/service/) * [ServiceDescriptor](/api/mantlejs/mantle/interfaces/servicedescriptor/) * [ServiceHandle](/api/mantlejs/mantle/interfaces/servicehandle/) * [ServiceOptions](/api/mantlejs/mantle/interfaces/serviceoptions/) * [ServiceParams](/api/mantlejs/mantle/interfaces/serviceparams/) * [SimilarData](/api/mantlejs/mantle/interfaces/similardata/) * [VectorRepository](/api/mantlejs/mantle/interfaces/vectorrepository/) * [VectorRepositoryServiceOptions](/api/mantlejs/mantle/interfaces/vectorrepositoryserviceoptions/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [CapabilityScope](/api/mantlejs/mantle/type-aliases/capabilityscope/) * [ChannelPublisher](/api/mantlejs/mantle/type-aliases/channelpublisher/) * [HookFunction](/api/mantlejs/mantle/type-aliases/hookfunction/) * [HttpRouteHandler](/api/mantlejs/mantle/type-aliases/httproutehandler/) * [Id](/api/mantlejs/mantle/type-aliases/id/) * [MantlePlugin](/api/mantlejs/mantle/type-aliases/mantleplugin/) * [MethodHookMap](/api/mantlejs/mantle/type-aliases/methodhookmap/) ## Variables [Section titled “Variables”](#variables) * [CORS_DEFAULT_METHODS](/api/mantlejs/mantle/variables/cors_default_methods/) * [DEFAULT_MAX_BATCH_SIZE](/api/mantlejs/mantle/variables/default_max_batch_size/) * [NESTED_QUERY_CASES](/api/mantlejs/mantle/variables/nested_query_cases/) * [NESTED_QUERY_RECORDS](/api/mantlejs/mantle/variables/nested_query_records/) ## Functions [Section titled “Functions”](#functions) * [assertOperators](/api/mantlejs/mantle/functions/assertoperators/) * [getContext](/api/mantlejs/mantle/functions/getcontext/) * [mantle](/api/mantlejs/mantle/functions/mantle/) * [matchesCapabilityScope](/api/mantlejs/mantle/functions/matchescapabilityscope/) * [parseQueryString](/api/mantlejs/mantle/functions/parsequerystring/) * [resolveCorsOrigin](/api/mantlejs/mantle/functions/resolvecorsorigin/) * [withContext](/api/mantlejs/mantle/functions/withcontext/) # BadRequest Defined in: [errors.ts:34](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L34) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new BadRequest**(`message?`, `data?`, `errors?`, `hint?`): `BadRequest` Defined in: [errors.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L35) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Bad Request"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `BadRequest` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # Conflict Defined in: [errors.ts:64](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L64) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new Conflict**(`message?`, `data?`, `errors?`, `hint?`): `Conflict` Defined in: [errors.ts:65](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L65) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Conflict"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `Conflict` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # Forbidden Defined in: [errors.ts:46](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L46) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new Forbidden**(`message?`, `data?`, `errors?`, `hint?`): `Forbidden` Defined in: [errors.ts:47](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L47) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Forbidden"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `Forbidden` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # GeneralError Defined in: [errors.ts:82](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L82) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new GeneralError**(`message?`, `data?`, `errors?`, `hint?`): `GeneralError` Defined in: [errors.ts:83](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L83) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"General Error"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `GeneralError` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # MantleError Defined in: [errors.ts:1](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L1) ## Extends [Section titled “Extends”](#extends) * `Error` ## Extended by [Section titled “Extended by”](#extended-by) * [`BadRequest`](/api/mantlejs/mantle/classes/badrequest/) * [`NotAuthenticated`](/api/mantlejs/mantle/classes/notauthenticated/) * [`Forbidden`](/api/mantlejs/mantle/classes/forbidden/) * [`NotFound`](/api/mantlejs/mantle/classes/notfound/) * [`MethodNotAllowed`](/api/mantlejs/mantle/classes/methodnotallowed/) * [`Conflict`](/api/mantlejs/mantle/classes/conflict/) * [`Unprocessable`](/api/mantlejs/mantle/classes/unprocessable/) * [`TooManyRequests`](/api/mantlejs/mantle/classes/toomanyrequests/) * [`GeneralError`](/api/mantlejs/mantle/classes/generalerror/) * [`NotImplemented`](/api/mantlejs/mantle/classes/notimplemented/) * [`Unavailable`](/api/mantlejs/mantle/classes/unavailable/) * [`CodeExecutorFault`](/api/mantlejs/mcp-code/classes/codeexecutorfault/) * [`CodeLimitExceeded`](/api/mantlejs/mcp-code/classes/codelimitexceeded/) * [`CodeOutputTooLarge`](/api/mantlejs/mcp-code/classes/codeoutputtoolarge/) * [`CodeScriptError`](/api/mantlejs/mcp-code/classes/codescripterror/) * [`CodeTimeout`](/api/mantlejs/mcp-code/classes/codetimeout/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new MantleError**(`message`, `code`, `className`, `data?`, `errors?`, `hint?`): `MantleError` Defined in: [errors.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L9) #### Parameters [Section titled “Parameters”](#parameters) ##### message [Section titled “message”](#message) `string` ##### code [Section titled “code”](#code) `number` ##### className [Section titled “className”](#classname) `string` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `MantleError` #### Overrides [Section titled “Overrides”](#overrides) `Error.constructor` ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname-1) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) *** ### code [Section titled “code”](#code-1) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> # MethodNotAllowed Defined in: [errors.ts:58](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L58) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new MethodNotAllowed**(`message?`, `data?`, `errors?`, `hint?`): `MethodNotAllowed` Defined in: [errors.ts:59](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L59) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Method Not Allowed"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `MethodNotAllowed` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # NotAuthenticated Defined in: [errors.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L40) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new NotAuthenticated**(`message?`, `data?`, `errors?`, `hint?`): `NotAuthenticated` Defined in: [errors.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L41) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Not Authenticated"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `NotAuthenticated` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # NotFound Defined in: [errors.ts:52](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L52) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new NotFound**(`message?`, `data?`, `errors?`, `hint?`): `NotFound` Defined in: [errors.ts:53](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L53) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Not Found"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `NotFound` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # NotImplemented Defined in: [errors.ts:88](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L88) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new NotImplemented**(`message?`, `data?`, `errors?`, `hint?`): `NotImplemented` Defined in: [errors.ts:89](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L89) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Not Implemented"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `NotImplemented` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # RepositoryService Defined in: [repository-service.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L44) A concrete `Service` over any `Repository` — the framework-owned bridge from `ServiceParams.query` (raw strings from HTTP) to `QueryParams`. Query conventions (see the Phase 4 TDD, section 14): * Reserved keys inside `query`: `$limit`, `$skip`, `$sort`, `$select`; everything else is `where`. * `find()` ALWAYS returns `Paginated` — `total` via `repository.count({ where })`. * With `options.fields`, unknown where/sort/select fields throw `BadRequest`. * With `options.schema`, string where-values are coerced to the field’s declared type; without one they pass through unchanged. * `update`/`patch`/`remove` propagate the repository’s `NotFound` untouched. For a service backed by more than one repository (e.g. writing an activity-log entry alongside the primary record), compose them directly behind a hand-written `Service` instead — see “Services with multiple repositories” in the root README. ## Extended by [Section titled “Extended by”](#extended-by) * [`VectorRepositoryService`](/api/mantlejs/mantle/classes/vectorrepositoryservice/) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ### D [Section titled “D”](#d) `D` = `Partial`<`T`> ## Implements [Section titled “Implements”](#implements) * [`Service`](/api/mantlejs/mantle/interfaces/service/)<`T`, `D`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new RepositoryService**<`T`, `D`>(`repository`, `options?`): `RepositoryService`<`T`, `D`> Defined in: [repository-service.ts:45](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L45) #### Parameters [Section titled “Parameters”](#parameters) ##### repository [Section titled “repository”](#repository) [`Repository`](/api/mantlejs/mantle/interfaces/repository/)<`T`, `D`> ##### options? [Section titled “options?”](#options) [`RepositoryServiceOptions`](/api/mantlejs/mantle/interfaces/repositoryserviceoptions/) = `{}` #### Returns [Section titled “Returns”](#returns) `RepositoryService`<`T`, `D`> ## Properties [Section titled “Properties”](#properties) ### options [Section titled “options”](#options-1) > `protected` `readonly` **options**: [`RepositoryServiceOptions`](/api/mantlejs/mantle/interfaces/repositoryserviceoptions/) = `{}` Defined in: [repository-service.ts:47](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L47) *** ### repository [Section titled “repository”](#repository-1) > `protected` `readonly` **repository**: [`Repository`](/api/mantlejs/mantle/interfaces/repository/)<`T`, `D`> Defined in: [repository-service.ts:46](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L46) ## Methods [Section titled “Methods”](#methods) ### assertFields() [Section titled “assertFields()”](#assertfields) > `protected` **assertFields**(`where`): `void` Defined in: [repository-service.ts:179](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L179) #### Parameters [Section titled “Parameters”](#parameters-1) ##### where [Section titled “where”](#where) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-1) `void` *** ### create() [Section titled “create()”](#create) #### Call Signature [Section titled “Call Signature”](#call-signature) > **create**(`data`, `_params?`): `Promise`<`T`> Defined in: [repository-service.ts:83](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L83) ##### Parameters [Section titled “Parameters”](#parameters-2) ###### data [Section titled “data”](#data) `D` ###### \_params? [Section titled “\_params?”](#_params) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) ##### Returns [Section titled “Returns”](#returns-2) `Promise`<`T`> ##### Implementation of [Section titled “Implementation of”](#implementation-of) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`create`](/api/mantlejs/mantle/interfaces/service/#create) #### Call Signature [Section titled “Call Signature”](#call-signature-1) > **create**(`data`, `_params?`): `Promise`<`T`\[]> Defined in: [repository-service.ts:84](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L84) ##### Parameters [Section titled “Parameters”](#parameters-3) ###### data [Section titled “data”](#data-1) `D`\[] ###### \_params? [Section titled “\_params?”](#_params-1) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) ##### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`\[]> ##### Implementation of [Section titled “Implementation of”](#implementation-of-1) `Service.create` *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) | `undefined` Defined in: [repository-service.ts:105](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L105) Surface the underlying repository’s capabilities so `ServiceHandle.describe()` can include them. #### Returns [Section titled “Returns”](#returns-4) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) | `undefined` *** ### find() [Section titled “find()”](#find) > **find**(`params?`): `Promise`<[`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`>> Defined in: [repository-service.ts:50](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L50) #### Parameters [Section titled “Parameters”](#parameters-4) ##### params? [Section titled “params?”](#params) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-5) `Promise`<[`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`>> #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`find`](/api/mantlejs/mantle/interfaces/service/#find) *** ### get() [Section titled “get()”](#get) > **get**(`id`, `_params?`): `Promise`<`T`> Defined in: [repository-service.ts:75](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L75) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### \_params? [Section titled “\_params?”](#_params-2) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`get`](/api/mantlejs/mantle/interfaces/service/#get) *** ### parseQuery() [Section titled “parseQuery()”](#parsequery) > `protected` **parseQuery**(`query`): `ParsedQuery` Defined in: [repository-service.ts:111](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L111) #### Parameters [Section titled “Parameters”](#parameters-6) ##### query [Section titled “query”](#query) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-7) `ParsedQuery` *** ### patch() [Section titled “patch()”](#patch) > **patch**(`id`, `data`, `_params?`): `Promise`<`T`> Defined in: [repository-service.ts:96](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L96) #### Parameters [Section titled “Parameters”](#parameters-7) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-2) `D` ##### \_params? [Section titled “\_params?”](#_params-3) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`patch`](/api/mantlejs/mantle/interfaces/service/#patch) *** ### remove() [Section titled “remove()”](#remove) > **remove**(`id`, `_params?`): `Promise`<`T`> Defined in: [repository-service.ts:100](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L100) #### Parameters [Section titled “Parameters”](#parameters-8) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### \_params? [Section titled “\_params?”](#_params-4) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`remove`](/api/mantlejs/mantle/interfaces/service/#remove) *** ### update() [Section titled “update()”](#update) > **update**(`id`, `data`, `_params?`): `Promise`<`T`> Defined in: [repository-service.ts:92](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L92) #### Parameters [Section titled “Parameters”](#parameters-9) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-3) `D` ##### \_params? [Section titled “\_params?”](#_params-5) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-10) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`update`](/api/mantlejs/mantle/interfaces/service/#update) # TooManyRequests Defined in: [errors.ts:76](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L76) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new TooManyRequests**(`message?`, `data?`, `errors?`, `hint?`): `TooManyRequests` Defined in: [errors.ts:77](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L77) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Too Many Requests"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `TooManyRequests` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # Unavailable Defined in: [errors.ts:94](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L94) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new Unavailable**(`message?`, `data?`, `errors?`, `hint?`): `Unavailable` Defined in: [errors.ts:95](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L95) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Service Unavailable"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `Unavailable` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # Unprocessable Defined in: [errors.ts:70](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L70) ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new Unprocessable**(`message?`, `data?`, `errors?`, `hint?`): `Unprocessable` Defined in: [errors.ts:71](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L71) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Unprocessable Entity"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `Unprocessable` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: [errors.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L3) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: [errors.ts:2](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L2) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: [errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L4) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: [errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L5) #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: [errors.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L7) Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: [errors.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/errors.ts#L20) #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # VectorRepositoryService Defined in: [vector-repository-service.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L36) A `RepositoryService` over a `VectorRepository` that ships the `similar` custom method pre-wired to `repository.findSimilar()`. Register it with the method listed so transports expose it (custom methods dispatch as `POST //similar`): ```typescript app.use("docs", new VectorRepositoryService(new DocRepository(app)), { methods: ["find", "get", "create", "update", "patch", "remove", "similar"], }); ``` Every result carries the adapter’s native match metric as `_score` — see the adapter README for whether higher or lower means “more similar”. ## Extends [Section titled “Extends”](#extends) * [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/)<`T`, `D`> ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ### D [Section titled “D”](#d) `D` = `Partial`<`T`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new VectorRepositoryService**<`T`, `D`>(`repository`, `options?`): `VectorRepositoryService`<`T`, `D`> Defined in: [vector-repository-service.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L40) #### Parameters [Section titled “Parameters”](#parameters) ##### repository [Section titled “repository”](#repository) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/)<`T`, `D`> ##### options? [Section titled “options?”](#options) [`VectorRepositoryServiceOptions`](/api/mantlejs/mantle/interfaces/vectorrepositoryserviceoptions/) = `{}` #### Returns [Section titled “Returns”](#returns) `VectorRepositoryService`<`T`, `D`> #### Overrides [Section titled “Overrides”](#overrides) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`constructor`](/api/mantlejs/mantle/classes/repositoryservice/#constructor) ## Properties [Section titled “Properties”](#properties) ### options [Section titled “options”](#options-1) > `protected` `readonly` **options**: [`VectorRepositoryServiceOptions`](/api/mantlejs/mantle/interfaces/vectorrepositoryserviceoptions/) = `{}` Defined in: [vector-repository-service.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L42) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`options`](/api/mantlejs/mantle/classes/repositoryservice/#options) *** ### repository [Section titled “repository”](#repository-1) > `protected` `readonly` **repository**: [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/)<`T`, `D`> Defined in: [vector-repository-service.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L41) #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`repository`](/api/mantlejs/mantle/classes/repositoryservice/#repository) ## Methods [Section titled “Methods”](#methods) ### assertFields() [Section titled “assertFields()”](#assertfields) > `protected` **assertFields**(`where`): `void` Defined in: [repository-service.ts:179](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L179) #### Parameters [Section titled “Parameters”](#parameters-1) ##### where [Section titled “where”](#where) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-1) `void` #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`assertFields`](/api/mantlejs/mantle/classes/repositoryservice/#assertfields) *** ### create() [Section titled “create()”](#create) #### Call Signature [Section titled “Call Signature”](#call-signature) > **create**(`data`, `_params?`): `Promise`<`T`> Defined in: [repository-service.ts:83](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L83) ##### Parameters [Section titled “Parameters”](#parameters-2) ###### data [Section titled “data”](#data) `D` ###### \_params? [Section titled “\_params?”](#_params) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) ##### Returns [Section titled “Returns”](#returns-2) `Promise`<`T`> ##### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`create`](/api/mantlejs/mantle/classes/repositoryservice/#create) #### Call Signature [Section titled “Call Signature”](#call-signature-1) > **create**(`data`, `_params?`): `Promise`<`T`\[]> Defined in: [repository-service.ts:84](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L84) ##### Parameters [Section titled “Parameters”](#parameters-3) ###### data [Section titled “data”](#data-1) `D`\[] ###### \_params? [Section titled “\_params?”](#_params-1) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) ##### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`\[]> ##### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`create`](/api/mantlejs/mantle/classes/repositoryservice/#create) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) | `undefined` Defined in: [repository-service.ts:105](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L105) Surface the underlying repository’s capabilities so `ServiceHandle.describe()` can include them. #### Returns [Section titled “Returns”](#returns-4) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) | `undefined` #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`describe`](/api/mantlejs/mantle/classes/repositoryservice/#describe) *** ### find() [Section titled “find()”](#find) > **find**(`params?`): `Promise`<[`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`>> Defined in: [repository-service.ts:50](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L50) #### Parameters [Section titled “Parameters”](#parameters-4) ##### params? [Section titled “params?”](#params) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-5) `Promise`<[`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`>> #### Inherited from [Section titled “Inherited from”](#inherited-from-6) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`find`](/api/mantlejs/mantle/classes/repositoryservice/#find) *** ### get() [Section titled “get()”](#get) > **get**(`id`, `_params?`): `Promise`<`T`> Defined in: [repository-service.ts:75](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L75) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### \_params? [Section titled “\_params?”](#_params-2) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-7) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`get`](/api/mantlejs/mantle/classes/repositoryservice/#get) *** ### parseQuery() [Section titled “parseQuery()”](#parsequery) > `protected` **parseQuery**(`query`): `ParsedQuery` Defined in: [repository-service.ts:111](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L111) #### Parameters [Section titled “Parameters”](#parameters-6) ##### query [Section titled “query”](#query) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-7) `ParsedQuery` #### Inherited from [Section titled “Inherited from”](#inherited-from-8) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`parseQuery`](/api/mantlejs/mantle/classes/repositoryservice/#parsequery) *** ### patch() [Section titled “patch()”](#patch) > **patch**(`id`, `data`, `_params?`): `Promise`<`T`> Defined in: [repository-service.ts:96](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L96) #### Parameters [Section titled “Parameters”](#parameters-7) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-2) `D` ##### \_params? [Section titled “\_params?”](#_params-3) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-9) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`patch`](/api/mantlejs/mantle/classes/repositoryservice/#patch) *** ### remove() [Section titled “remove()”](#remove) > **remove**(`id`, `_params?`): `Promise`<`T`> Defined in: [repository-service.ts:100](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L100) #### Parameters [Section titled “Parameters”](#parameters-8) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### \_params? [Section titled “\_params?”](#_params-4) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-10) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`remove`](/api/mantlejs/mantle/classes/repositoryservice/#remove) *** ### similar() [Section titled “similar()”](#similar) > **similar**(`data`, `_params?`): `Promise`<`T` & `object`\[]> Defined in: [vector-repository-service.ts:47](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L47) #### Parameters [Section titled “Parameters”](#parameters-9) ##### data [Section titled “data”](#data-3) [`SimilarData`](/api/mantlejs/mantle/interfaces/similardata/) ##### \_params? [Section titled “\_params?”](#_params-5) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-10) `Promise`<`T` & `object`\[]> *** ### update() [Section titled “update()”](#update) > **update**(`id`, `data`, `_params?`): `Promise`<`T`> Defined in: [repository-service.ts:92](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L92) #### Parameters [Section titled “Parameters”](#parameters-10) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-4) `D` ##### \_params? [Section titled “\_params?”](#_params-6) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-11) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-11) [`RepositoryService`](/api/mantlejs/mantle/classes/repositoryservice/).[`update`](/api/mantlejs/mantle/classes/repositoryservice/#update) # assertOperators > **assertOperators**(`where`, `supported`, `adapterName`): `void` Defined in: [query-operators.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-operators.ts#L12) Validate that every `$`-prefixed operator in a where clause is supported by the adapter translating it. Recurses into `$or`/`$and` arrays and per-field operator objects. Throws `BadRequest` naming the offending operator, the adapter, and the full supported set — for an agent the error text is the documentation. Adapters call this at the top of their where-clause translators so an unsupported operator fails loud instead of silently corrupting results. ## Parameters [Section titled “Parameters”](#parameters) ### where [Section titled “where”](#where) `Record`<`string`, `unknown`> ### supported [Section titled “supported”](#supported) `ReadonlySet`<`string`> ### adapterName [Section titled “adapterName”](#adaptername) `string` ## Returns [Section titled “Returns”](#returns) `void` # getContext > **getContext**(): [`RequestContext`](/api/mantlejs/mantle/interfaces/requestcontext/) | `undefined` Defined in: [context.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/context.ts#L16) Returns the current request context, or undefined when called outside a withContext scope. ## Returns [Section titled “Returns”](#returns) [`RequestContext`](/api/mantlejs/mantle/interfaces/requestcontext/) | `undefined` # mantle > **mantle**(`options?`): [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) Defined in: [mantle.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/mantle.ts#L4) ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`MantleOptions`](/api/mantlejs/mantle/interfaces/mantleoptions/) ## Returns [Section titled “Returns”](#returns) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) # matchesCapabilityScope > **matchesCapabilityScope**(`scope`, `path`, `method`): `boolean` Defined in: [capability-scope.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/capability-scope.ts#L12) Deny-by-default match: `path` must be present in `scope`, and `method` must be within its grant. ## Parameters [Section titled “Parameters”](#parameters) ### scope [Section titled “scope”](#scope) [`CapabilityScope`](/api/mantlejs/mantle/type-aliases/capabilityscope/) ### path [Section titled “path”](#path) `string` ### method [Section titled “method”](#method) `string` ## Returns [Section titled “Returns”](#returns) `boolean` # parseQueryString > **parseQueryString**(`flat`): `Record`<`string`, `unknown`> Defined in: [parse-query-string.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/parse-query-string.ts#L18) Parse flat query-string keys with bracket notation into the nested object shape Express produces via `qs`, so every HTTP transport yields identical `params.query`: { “age\[$gt]”: “21” } → { age: { $gt: “21” } } { “$or\[0]\[role]”: “admin” } → { $or: \[{ role: “admin” }] } { “tags\[]”: \[“a”, “b”] } → { tags: \[“a”, “b”] } { tags: \[“a”, “b”] } (repeated key) → { tags: \[“a”, “b”] } Values stay strings — type coercion is a schema concern, not a transport concern. Keys nested deeper than MAX_DEPTH throw `BadRequest`. ## Parameters [Section titled “Parameters”](#parameters) ### flat [Section titled “flat”](#flat) `Record`<`string`, `string` | `string`\[]> ## Returns [Section titled “Returns”](#returns) `Record`<`string`, `unknown`> # resolveCorsOrigin > **resolveCorsOrigin**(`origin`, `requestOrigin`): `string` | `undefined` Defined in: [cors.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/cors.ts#L12) Resolve `CorsOptions.origin` against a request’s `Origin` header into the concrete value to send back as `Access-Control-Allow-Origin`, or `undefined` when the origin is disallowed (no CORS headers should be sent). Shared by all three HTTP transports so `origin: true` (reflect), a fixed string, an allow-list, and a custom function all behave identically regardless of which transport is mounted. ## Parameters [Section titled “Parameters”](#parameters) ### origin [Section titled “origin”](#origin) `string` | `boolean` | `string`\[] | ((`requestOrigin`) => `string` | `boolean`) | `undefined` ### requestOrigin [Section titled “requestOrigin”](#requestorigin) `string` | `undefined` ## Returns [Section titled “Returns”](#returns) `string` | `undefined` # withContext > **withContext**<`T`>(`context`, `fn`): `T` Defined in: [context.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/context.ts#L11) Runs fn inside a request context. All async operations spawned within fn inherit the context. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Parameters [Section titled “Parameters”](#parameters) ### context [Section titled “context”](#context) [`RequestContext`](/api/mantlejs/mantle/interfaces/requestcontext/) ### fn [Section titled “fn”](#fn) () => `T` ## Returns [Section titled “Returns”](#returns) `T` # AgentContext Defined in: [types.ts:232](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L232) Identity of an agent-originated call — an `AgentPrincipal` (see `@mantlejs/auth`) that passed `authorizeAgent()`’s capability check. `id` is the agent’s own id (distinct from `delegatingUserId`, the user who minted the token). ## Properties [Section titled “Properties”](#properties) ### delegatingUserId [Section titled “delegatingUserId”](#delegatinguserid) > **delegatingUserId**: `string` Defined in: [types.ts:235](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L235) *** ### id [Section titled “id”](#id) > **id**: `string` Defined in: [types.ts:233](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L233) *** ### scope [Section titled “scope”](#scope) > **scope**: [`CapabilityScope`](/api/mantlejs/mantle/type-aliases/capabilityscope/) Defined in: [types.ts:234](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L234) # BatchCall Defined in: [types.ts:285](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L285) One call in a batch, dispatched by `app.batch()` through the full hook pipeline of the target service. ## Properties [Section titled “Properties”](#properties) ### data? [Section titled “data?”](#data) > `optional` **data?**: `unknown` Defined in: [types.ts:290](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L290) *** ### id? [Section titled “id?”](#id) > `optional` **id?**: [`Id`](/api/mantlejs/mantle/type-aliases/id/) Defined in: [types.ts:289](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L289) *** ### method [Section titled “method”](#method) > **method**: `"find"` | `"get"` | `"create"` | `"update"` | `"patch"` | `"remove"` Defined in: [types.ts:288](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L288) *** ### params? [Section titled “params?”](#params) > `optional` **params?**: `object` Defined in: [types.ts:292](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L292) Per-call params. `query` uses the same shape as a parsed REST query string (`$limit`, `$sort`, where fields…). #### query? [Section titled “query?”](#query) > `optional` **query?**: `Record`<`string`, `unknown`> *** ### service [Section titled “service”](#service) > **service**: `string` Defined in: [types.ts:287](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L287) Registered service path, e.g. `"users"`. # BatchDispatchOptions Defined in: [types.ts:303](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L303) ## Properties [Section titled “Properties”](#properties) ### maxSize? [Section titled “maxSize?”](#maxsize) > `optional` **maxSize?**: `number` Defined in: [types.ts:305](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L305) Maximum calls per batch. Requests exceeding it are rejected with `BadRequest` before any call executes. #### Default [Section titled “Default”](#default) ```ts 25 ``` # BatchResult Defined in: [types.ts:296](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L296) Outcome of one `BatchCall`. Returned by `app.batch()` in the same order as the input array. ## Properties [Section titled “Properties”](#properties) ### error? [Section titled “error?”](#error) > `optional` **error?**: `object` Defined in: [types.ts:300](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L300) `MantleError.toJSON()` shape — at minimum `name`, `message`, `code`. #### Index Signature [Section titled “Index Signature”](#index-signature) \[`key`: `string`]: `unknown` #### code [Section titled “code”](#code) > **code**: `number` #### message [Section titled “message”](#message) > **message**: `string` #### name [Section titled “name”](#name) > **name**: `string` *** ### result? [Section titled “result?”](#result) > `optional` **result?**: `unknown` Defined in: [types.ts:298](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L298) *** ### status [Section titled “status”](#status) > **status**: `"success"` | `"error"` Defined in: [types.ts:297](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L297) # CorsOptions Defined in: [types.ts:212](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L212) Normalized CORS option shape shared by `@mantlejs/express`, `@mantlejs/koa`, and `@mantlejs/http` so switching transports doesn’t require relearning CORS configuration. Each transport translates this into its own mechanism — the `cors` package, `@koa/cors`, or hand-rolled headers for `@mantlejs/http`. ## Properties [Section titled “Properties”](#properties) ### allowedHeaders? [Section titled “allowedHeaders?”](#allowedheaders) > `optional` **allowedHeaders?**: `string`\[] Defined in: [types.ts:218](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L218) Allowed request headers for `Access-Control-Allow-Headers`. Default: reflects `Access-Control-Request-Headers`. *** ### credentials? [Section titled “credentials?”](#credentials) > `optional` **credentials?**: `boolean` Defined in: [types.ts:222](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L222) Allow credentials (cookies, `Authorization` header) cross-origin. Default: `false`. *** ### exposedHeaders? [Section titled “exposedHeaders?”](#exposedheaders) > `optional` **exposedHeaders?**: `string`\[] Defined in: [types.ts:220](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L220) Headers exposed to the browser via `Access-Control-Expose-Headers`. *** ### maxAge? [Section titled “maxAge?”](#maxage) > `optional` **maxAge?**: `number` Defined in: [types.ts:224](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L224) How long, in seconds, browsers may cache a preflight response (`Access-Control-Max-Age`). *** ### methods? [Section titled “methods?”](#methods) > `optional` **methods?**: `string`\[] Defined in: [types.ts:216](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L216) Allowed methods for `Access-Control-Allow-Methods`. Default: `["GET", "POST", "PUT", "PATCH", "DELETE"]`. *** ### origin? [Section titled “origin?”](#origin) > `optional` **origin?**: `string` | `boolean` | `string`\[] | ((`requestOrigin`) => `string` | `boolean`) Defined in: [types.ts:214](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L214) Allowed origin(s) for `Access-Control-Allow-Origin`. `true` reflects the request’s `Origin` header. Default: `true`. # CursorPage Defined in: [types.ts:110](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L110) One page of a cursor-paginated result set, returned by `Repository.findPage()`. `cursor` is an opaque adapter-specific token; pass it back as `params.cursor` to fetch the next page. Absent when there are no further pages. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Properties [Section titled “Properties”](#properties) ### cursor? [Section titled “cursor?”](#cursor) > `optional` **cursor?**: `string` Defined in: [types.ts:112](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L112) *** ### data [Section titled “data”](#data) > **data**: `T`\[] Defined in: [types.ts:111](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L111) # GraphRepository Defined in: [types.ts:148](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L148) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ## Methods [Section titled “Methods”](#methods) ### createNode() [Section titled “createNode()”](#createnode) > **createNode**(`data`): `Promise`<`T`> Defined in: [types.ts:150](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L150) Create a node #### Parameters [Section titled “Parameters”](#parameters) ##### data [Section titled “data”](#data) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns) `Promise`<`T`> *** ### createRelationship() [Section titled “createRelationship()”](#createrelationship) > **createRelationship**(`fromId`, `toId`, `type`, `properties?`): `Promise`<`void`> Defined in: [types.ts:156](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L156) Create a directed relationship between two nodes #### Parameters [Section titled “Parameters”](#parameters-1) ##### fromId [Section titled “fromId”](#fromid) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### toId [Section titled “toId”](#toid) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### type [Section titled “type”](#type) `string` ##### properties? [Section titled “properties?”](#properties) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-1) `Promise`<`void`> *** ### deleteNode() [Section titled “deleteNode()”](#deletenode) > **deleteNode**(`id`): `Promise`<`T`> Defined in: [types.ts:160](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L160) Delete a node and all its relationships #### Parameters [Section titled “Parameters”](#parameters-2) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<`T`> *** ### describe()? [Section titled “describe()?”](#describe) > `optional` **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [types.ts:164](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L164) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-3) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. *** ### findNodeById() [Section titled “findNodeById()”](#findnodebyid) > **findNodeById**(`id`): `Promise`<`T` | `null`> Defined in: [types.ts:152](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L152) Find a node by ID #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T` | `null`> *** ### findNodes() [Section titled “findNodes()”](#findnodes) > **findNodes**(`params?`): `Promise`<`T`\[]> Defined in: [types.ts:154](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L154) Find nodes matching properties #### Parameters [Section titled “Parameters”](#parameters-4) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-5) `Promise`<`T`\[]> *** ### raw() [Section titled “raw()”](#raw) > **raw**<`R`>(`query`, `params?`): `Promise`<`R`\[]> Defined in: [types.ts:162](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L162) Execute a raw query in the adapter’s native graph language (Cypher for Neo4j, AQL for ArangoDB, …). #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### R [Section titled “R”](#r) `R` = `T` #### Parameters [Section titled “Parameters”](#parameters-5) ##### query [Section titled “query”](#query) `string` ##### params? [Section titled “params?”](#params-1) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-6) `Promise`<`R`\[]> *** ### traverse() [Section titled “traverse()”](#traverse) > **traverse**(`startId`, `relation`, `depth?`): `Promise`<`T`\[]> Defined in: [types.ts:158](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L158) Traverse relationships from a starting node #### Parameters [Section titled “Parameters”](#parameters-6) ##### startId [Section titled “startId”](#startid) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### relation [Section titled “relation”](#relation) `string` ##### depth? [Section titled “depth?”](#depth) `number` #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T`\[]> # HookConfig Defined in: [types.ts:264](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L264) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` = `unknown` ## Properties [Section titled “Properties”](#properties) ### after? [Section titled “after?”](#after) > `optional` **after?**: [`MethodHookMap`](/api/mantlejs/mantle/type-aliases/methodhookmap/)<`T`> Defined in: [types.ts:266](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L266) *** ### before? [Section titled “before?”](#before) > `optional` **before?**: [`MethodHookMap`](/api/mantlejs/mantle/type-aliases/methodhookmap/)<`T`> Defined in: [types.ts:265](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L265) *** ### error? [Section titled “error?”](#error) > `optional` **error?**: [`MethodHookMap`](/api/mantlejs/mantle/type-aliases/methodhookmap/)<`T`> Defined in: [types.ts:267](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L267) # HookContext Defined in: [types.ts:238](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L238) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` = `unknown` ## Properties [Section titled “Properties”](#properties) ### agent? [Section titled “agent?”](#agent) > `optional` **agent?**: [`AgentContext`](/api/mantlejs/mantle/interfaces/agentcontext/) Defined in: [types.ts:255](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L255) Set by an `authorizeAgent()`-style hook (see `@mantlejs/auth`) when the call was authorized via a capability-scoped agent token rather than a user credential. Additive — never replaces `params.user`, and absent entirely for ordinary user/internal calls. *** ### app [Section titled “app”](#app) > **app**: [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) Defined in: [types.ts:239](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L239) *** ### data? [Section titled “data?”](#data) > `optional` **data?**: `Partial`<`T`> Defined in: [types.ts:245](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L245) *** ### error? [Section titled “error?”](#error) > `optional` **error?**: `Error` Defined in: [types.ts:248](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L248) *** ### id? [Section titled “id?”](#id) > `optional` **id?**: [`Id`](/api/mantlejs/mantle/type-aliases/id/) Defined in: [types.ts:246](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L246) *** ### method [Section titled “method”](#method) > **method**: `string` Defined in: [types.ts:242](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L242) *** ### params [Section titled “params”](#params) > **params**: [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) Defined in: [types.ts:244](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L244) *** ### path [Section titled “path”](#path) > **path**: `string` Defined in: [types.ts:241](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L241) *** ### provider? [Section titled “provider?”](#provider) > `optional` **provider?**: `string` Defined in: [types.ts:243](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L243) *** ### result? [Section titled “result?”](#result) > `optional` **result?**: `T` | `T`\[] | [`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`> Defined in: [types.ts:247](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L247) *** ### service [Section titled “service”](#service) > **service**: `Partial`<[`Service`](/api/mantlejs/mantle/interfaces/service/)<`T`>> Defined in: [types.ts:240](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L240) *** ### statusCode? [Section titled “statusCode?”](#statuscode) > `optional` **statusCode?**: `number` Defined in: [types.ts:249](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L249) # HttpRequestLike Defined in: [types.ts:168](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L168) Minimal express-compatible request passed to `HttpRouterLike` handlers. ## Properties [Section titled “Properties”](#properties) ### body? [Section titled “body?”](#body) > `optional` **body?**: `unknown` Defined in: [types.ts:173](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L173) Parsed request body for POST routes. Express/koa/http all populate it when the request carries one. *** ### headers [Section titled “headers”](#headers) > **headers**: `Record`<`string`, `string` | `string`\[] | `undefined`> Defined in: [types.ts:171](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L171) *** ### protocol [Section titled “protocol”](#protocol) > **protocol**: `string` Defined in: [types.ts:169](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L169) *** ### query [Section titled “query”](#query) > **query**: `Record`<`string`, `unknown`> Defined in: [types.ts:170](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L170) ## Methods [Section titled “Methods”](#methods) ### get() [Section titled “get()”](#get) > **get**(`header`): `string` | `undefined` Defined in: [types.ts:174](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L174) #### Parameters [Section titled “Parameters”](#parameters) ##### header [Section titled “header”](#header) `string` #### Returns [Section titled “Returns”](#returns) `string` | `undefined` # HttpResponseLike Defined in: [types.ts:178](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L178) Minimal express-compatible response passed to `HttpRouterLike` handlers. ## Methods [Section titled “Methods”](#methods) ### json() [Section titled “json()”](#json) > **json**(`body`): `void` Defined in: [types.ts:180](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L180) #### Parameters [Section titled “Parameters”](#parameters) ##### body [Section titled “body”](#body) `unknown` #### Returns [Section titled “Returns”](#returns) `void` *** ### redirect() [Section titled “redirect()”](#redirect) > **redirect**(`url`): `void` Defined in: [types.ts:181](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L181) #### Parameters [Section titled “Parameters”](#parameters-1) ##### url [Section titled “url”](#url) `string` #### Returns [Section titled “Returns”](#returns-1) `void` *** ### send()? [Section titled “send()?”](#send) > `optional` **send**(`body`): `void` Defined in: [types.ts:187](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L187) Send a raw pre-serialized string body (e.g. an HTML page). Strings starting with “<” are served as text/html, otherwise text/plain — mirroring Express’s `res.send()`. Optional: Express provides it natively; the koa/http adapters implement it. #### Parameters [Section titled “Parameters”](#parameters-2) ##### body [Section titled “body”](#body-1) `string` #### Returns [Section titled “Returns”](#returns-2) `void` *** ### status() [Section titled “status()”](#status) > **status**(`code`): `this` Defined in: [types.ts:179](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L179) #### Parameters [Section titled “Parameters”](#parameters-3) ##### code [Section titled “code”](#code) `number` #### Returns [Section titled “Returns”](#returns-3) `this` # HttpRouterLike Defined in: [types.ts:202](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L202) Transport-neutral router contract. Every HTTP transport registers an implementation under `app.set("http:router", …)` so plugins can mount raw routes (e.g. OAuth redirects) without knowing which transport is in play. Transports also set `app.set("http:server", server)` and `app.emit("http:server", server)` when `listen()` creates the underlying Node server. ## Methods [Section titled “Methods”](#methods) ### get() [Section titled “get()”](#get) > **get**(`path`, `handler`): `void` Defined in: [types.ts:203](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L203) #### Parameters [Section titled “Parameters”](#parameters) ##### path [Section titled “path”](#path) `string` ##### handler [Section titled “handler”](#handler) [`HttpRouteHandler`](/api/mantlejs/mantle/type-aliases/httproutehandler/) #### Returns [Section titled “Returns”](#returns) `void` *** ### post() [Section titled “post()”](#post) > **post**(`path`, `handler`): `void` Defined in: [types.ts:204](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L204) #### Parameters [Section titled “Parameters”](#parameters-1) ##### path [Section titled “path”](#path-1) `string` ##### handler [Section titled “handler”](#handler-1) [`HttpRouteHandler`](/api/mantlejs/mantle/type-aliases/httproutehandler/) #### Returns [Section titled “Returns”](#returns-1) `void` # Logger Defined in: [types.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L23) ## Methods [Section titled “Methods”](#methods) ### child()? [Section titled “child()?”](#child) > `optional` **child**(`bindings`): `Logger` Defined in: [types.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L29) Returns a logger with `bindings` merged into every record. Optional — callers must feature-check. #### Parameters [Section titled “Parameters”](#parameters) ##### bindings [Section titled “bindings”](#bindings) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns) `Logger` *** ### debug() [Section titled “debug()”](#debug) > **debug**(`msg`, `context?`): `void` Defined in: [types.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L24) #### Parameters [Section titled “Parameters”](#parameters-1) ##### msg [Section titled “msg”](#msg) `string` ##### context? [Section titled “context?”](#context) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-1) `void` *** ### error() [Section titled “error()”](#error) > **error**(`msg`, `context?`): `void` Defined in: [types.ts:27](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L27) #### Parameters [Section titled “Parameters”](#parameters-2) ##### msg [Section titled “msg”](#msg-1) `string` ##### context? [Section titled “context?”](#context-1) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-2) `void` *** ### info() [Section titled “info()”](#info) > **info**(`msg`, `context?`): `void` Defined in: [types.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L25) #### Parameters [Section titled “Parameters”](#parameters-3) ##### msg [Section titled “msg”](#msg-2) `string` ##### context? [Section titled “context?”](#context-2) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-3) `void` *** ### warn() [Section titled “warn()”](#warn) > **warn**(`msg`, `context?`): `void` Defined in: [types.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L26) #### Parameters [Section titled “Parameters”](#parameters-4) ##### msg [Section titled “msg”](#msg-3) `string` ##### context? [Section titled “context?”](#context-3) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-4) `void` # MantleApplication Defined in: [types.ts:341](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L341) ## Methods [Section titled “Methods”](#methods) ### batch() [Section titled “batch()”](#batch) > **batch**(`calls`, `params?`, `options?`): `Promise`<[`BatchResult`](/api/mantlejs/mantle/interfaces/batchresult/)\[]> Defined in: [types.ts:352](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L352) Dispatch many service calls concurrently (`Promise.allSettled` semantics) and return one `BatchResult` per call, in input order. Each call runs the target service’s full hook pipeline — batch is not a way to bypass authentication or validation. `params` (headers, user, provider — typically the outer HTTP request’s) is inherited by every call and merged with the call’s own `params.query`. One call failing does not fail the batch; there is no cross-call atomicity. #### Parameters [Section titled “Parameters”](#parameters) ##### calls [Section titled “calls”](#calls) [`BatchCall`](/api/mantlejs/mantle/interfaces/batchcall/)\[] ##### params? [Section titled “params?”](#params) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) ##### options? [Section titled “options?”](#options) [`BatchDispatchOptions`](/api/mantlejs/mantle/interfaces/batchdispatchoptions/) #### Returns [Section titled “Returns”](#returns) `Promise`<[`BatchResult`](/api/mantlejs/mantle/interfaces/batchresult/)\[]> *** ### channel() [Section titled “channel()”](#channel) > **channel**(`name`): [`MantleChannel`](/api/mantlejs/mantle/interfaces/mantlechannel/) Defined in: [types.ts:360](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L360) #### Parameters [Section titled “Parameters”](#parameters-1) ##### name [Section titled “name”](#name) `string` | `string`\[] #### Returns [Section titled “Returns”](#returns-1) [`MantleChannel`](/api/mantlejs/mantle/interfaces/mantlechannel/) *** ### configure() [Section titled “configure()”](#configure) > **configure**(`plugin`): `this` Defined in: [types.ts:353](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L353) #### Parameters [Section titled “Parameters”](#parameters-2) ##### plugin [Section titled “plugin”](#plugin) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) #### Returns [Section titled “Returns”](#returns-2) `this` *** ### emit() [Section titled “emit()”](#emit) > **emit**(`event`, …`args`): `void` Defined in: [types.ts:359](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L359) #### Parameters [Section titled “Parameters”](#parameters-3) ##### event [Section titled “event”](#event) `string` ##### args [Section titled “args”](#args) …`unknown`\[] #### Returns [Section titled “Returns”](#returns-3) `void` *** ### get() [Section titled “get()”](#get) > **get**<`T`>(`key`): `T` Defined in: [types.ts:355](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L355) #### Type Parameters [Section titled “Type Parameters”](#type-parameters) ##### T [Section titled “T”](#t) `T` = `unknown` #### Parameters [Section titled “Parameters”](#parameters-4) ##### key [Section titled “key”](#key) `string` #### Returns [Section titled “Returns”](#returns-4) `T` *** ### off() [Section titled “off()”](#off) > **off**(`event`, `listener`): `this` Defined in: [types.ts:358](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L358) #### Parameters [Section titled “Parameters”](#parameters-5) ##### event [Section titled “event”](#event-1) `string` ##### listener [Section titled “listener”](#listener) (…`args`) => `void` #### Returns [Section titled “Returns”](#returns-5) `this` *** ### on() [Section titled “on()”](#on) > **on**(`event`, `listener`): `this` Defined in: [types.ts:357](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L357) #### Parameters [Section titled “Parameters”](#parameters-6) ##### event [Section titled “event”](#event-2) `string` ##### listener [Section titled “listener”](#listener-1) (…`args`) => `void` #### Returns [Section titled “Returns”](#returns-6) `this` *** ### publish() [Section titled “publish()”](#publish) > **publish**<`T`>(`publisher`): `this` Defined in: [types.ts:361](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L361) #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### T [Section titled “T”](#t-1) `T` = `unknown` #### Parameters [Section titled “Parameters”](#parameters-7) ##### publisher [Section titled “publisher”](#publisher) [`ChannelPublisher`](/api/mantlejs/mantle/type-aliases/channelpublisher/)<`T`> #### Returns [Section titled “Returns”](#returns-7) `this` *** ### service() [Section titled “service()”](#service) > **service**<`T`>(`path`): [`ServiceHandle`](/api/mantlejs/mantle/interfaces/servicehandle/)<`T`> Defined in: [types.ts:343](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L343) #### Type Parameters [Section titled “Type Parameters”](#type-parameters-2) ##### T [Section titled “T”](#t-2) `T` = `unknown` #### Parameters [Section titled “Parameters”](#parameters-8) ##### path [Section titled “path”](#path) `string` #### Returns [Section titled “Returns”](#returns-8) [`ServiceHandle`](/api/mantlejs/mantle/interfaces/servicehandle/)<`T`> *** ### set() [Section titled “set()”](#set) > **set**(`key`, `value`): `this` Defined in: [types.ts:354](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L354) #### Parameters [Section titled “Parameters”](#parameters-9) ##### key [Section titled “key”](#key-1) `string` ##### value [Section titled “value”](#value) `unknown` #### Returns [Section titled “Returns”](#returns-9) `this` *** ### teardown() [Section titled “teardown()”](#teardown) > **teardown**(): `Promise`<`void`> Defined in: [types.ts:356](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L356) #### Returns [Section titled “Returns”](#returns-10) `Promise`<`void`> *** ### use() [Section titled “use()”](#use) > **use**<`T`>(`path`, `service`, `options?`): `this` Defined in: [types.ts:342](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L342) #### Type Parameters [Section titled “Type Parameters”](#type-parameters-3) ##### T [Section titled “T”](#t-3) `T` = `unknown` #### Parameters [Section titled “Parameters”](#parameters-10) ##### path [Section titled “path”](#path-1) `string` ##### service [Section titled “service”](#service-1) `Partial`<[`Service`](/api/mantlejs/mantle/interfaces/service/)<`T`>> ##### options? [Section titled “options?”](#options-1) [`ServiceOptions`](/api/mantlejs/mantle/interfaces/serviceoptions/) #### Returns [Section titled “Returns”](#returns-11) `this` # MantleChannel Defined in: [types.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L5) ## Properties [Section titled “Properties”](#properties) ### connections [Section titled “connections”](#connections) > `readonly` **connections**: `Record`<`string`, `unknown`>\[] Defined in: [types.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L6) ## Methods [Section titled “Methods”](#methods) ### filter() [Section titled “filter()”](#filter) > **filter**(`fn`): `MantleChannel` Defined in: [types.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L9) #### Parameters [Section titled “Parameters”](#parameters) ##### fn [Section titled “fn”](#fn) (`data`, `connection`) => `boolean` #### Returns [Section titled “Returns”](#returns) `MantleChannel` *** ### join() [Section titled “join()”](#join) > **join**(`connection`): `this` Defined in: [types.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L7) #### Parameters [Section titled “Parameters”](#parameters-1) ##### connection [Section titled “connection”](#connection) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-1) `this` *** ### leave() [Section titled “leave()”](#leave) > **leave**(`connection`): `this` Defined in: [types.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L8) #### Parameters [Section titled “Parameters”](#parameters-2) ##### connection [Section titled “connection”](#connection-1) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-2) `this` # MantleOptions Defined in: [types.ts:308](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L308) ## Properties [Section titled “Properties”](#properties) ### errorHandler? [Section titled “errorHandler?”](#errorhandler) > `optional` **errorHandler?**: `boolean` Defined in: [types.ts:309](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L309) # NestedQueryCase Defined in: [query-fixtures.ts:52](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L52) ## Properties [Section titled “Properties”](#properties) ### expectedIds [Section titled “expectedIds”](#expectedids) > **expectedIds**: `string`\[] Defined in: [query-fixtures.ts:56](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L56) Ids from `NESTED_QUERY_RECORDS` the where clause must match, in id order. *** ### name [Section titled “name”](#name) > **name**: `string` Defined in: [query-fixtures.ts:53](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L53) *** ### where [Section titled “where”](#where) > **where**: `Record`<`string`, `unknown`> Defined in: [query-fixtures.ts:54](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L54) # NestedQueryRecord Defined in: [query-fixtures.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L20) Shared conformance fixture for nested-path + `$contains` querying (D-7). Adapters that support dot-path field names and the `$contains` operator run these cases in their specs so every implementation agrees on semantics. `@mantlejs/memory` is the executable reference: it seeds `NESTED_QUERY_RECORDS` and asserts each case returns `expectedIds`. Translator-style adapters (e.g. `@mantlejs/supabase`, which is specced against a mocked client) feed each case’s `where` through their translator and assert the emitted backend calls. Semantics (mirrors PostgreSQL jsonb `@>` containment): * Dot-path keys (`"metadata.owner.name"`) address nested fields. * `$contains` with an array operand: the field array contains every element. * `$contains` with a scalar operand: the field array contains that element. * `$contains` with an object operand: the field object is a superset (recursively) of the operand. ## Extends [Section titled “Extends”](#extends) * `Record`<`string`, `unknown`> ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `unknown` ## Properties [Section titled “Properties”](#properties) ### id [Section titled “id”](#id) > **id**: `string` Defined in: [query-fixtures.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L21) *** ### metadata [Section titled “metadata”](#metadata) > **metadata**: `object` Defined in: [query-fixtures.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L24) #### level [Section titled “level”](#level) > **level**: `number` #### owner [Section titled “owner”](#owner) > **owner**: `object` ##### owner.name [Section titled “owner.name”](#ownername) > **name**: `string` #### tags [Section titled “tags”](#tags) > **tags**: `string`\[] *** ### name [Section titled “name”](#name) > **name**: `string` Defined in: [query-fixtures.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L22) *** ### tags [Section titled “tags”](#tags-1) > **tags**: `string`\[] Defined in: [query-fixtures.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L23) # Paginated Defined in: [types.ts:32](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L32) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Properties [Section titled “Properties”](#properties) ### data [Section titled “data”](#data) > **data**: `T`\[] Defined in: [types.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L36) *** ### limit [Section titled “limit”](#limit) > **limit**: `number` Defined in: [types.ts:34](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L34) *** ### skip [Section titled “skip”](#skip) > **skip**: `number` Defined in: [types.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L35) *** ### total [Section titled “total”](#total) > **total**: `number` Defined in: [types.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L33) # PublishContext Defined in: [types.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L12) ## Properties [Section titled “Properties”](#properties) ### app [Section titled “app”](#app) > **app**: [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) Defined in: [types.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L13) *** ### params [Section titled “params”](#params) > **params**: [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) Defined in: [types.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L15) *** ### path [Section titled “path”](#path) > **path**: `string` Defined in: [types.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L14) # QueryParams Defined in: [types.ts:53](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L53) ## Extended by [Section titled “Extended by”](#extended-by) * [`DynamoQueryParams`](/api/mantlejs/dynamodb/interfaces/dynamoqueryparams/) ## Properties [Section titled “Properties”](#properties) ### limit? [Section titled “limit?”](#limit) > `optional` **limit?**: `number` Defined in: [types.ts:64](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L64) *** ### select? [Section titled “select?”](#select) > `optional` **select?**: `string`\[] Defined in: [types.ts:67](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L67) *** ### skip? [Section titled “skip?”](#skip) > `optional` **skip?**: `number` Defined in: [types.ts:65](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L65) *** ### sort? [Section titled “sort?”](#sort) > `optional` **sort?**: `Record`<`string`, `"asc"` | `"desc"`> Defined in: [types.ts:66](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L66) *** ### where? [Section titled “where?”](#where) > `optional` **where?**: `Record`<`string`, `unknown`> Defined in: [types.ts:63](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L63) Filter clause. Keys are field names — adapters backed by nested documents (memory, supabase, mongodb) also accept dot-path keys like `"metadata.tags"`. Values are literals (equality), `null` (IS NULL), or operator objects: `$lt`/`$lte`/`$gt`/`$gte`, `$ne`, `$in`/`$nin`, `$like`/`$notlike`/`$ilike`, `$contains` (array/JSON containment, jsonb `@>` semantics), plus top-level `$or`/`$and`. Each adapter enforces its supported subset via `assertOperators` — see `describe().operators`. # Repository Defined in: [types.ts:115](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L115) ## Extended by [Section titled “Extended by”](#extended-by) * [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ### D [Section titled “D”](#d) `D` = `Partial`<`T`> ## Methods [Section titled “Methods”](#methods) ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [types.ts:123](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L123) #### Parameters [Section titled “Parameters”](#parameters) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns) `Promise`<`number`> *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [types.ts:122](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L122) #### Parameters [Section titled “Parameters”](#parameters-1) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-1) `Promise`<`T`> *** ### describe()? [Section titled “describe()?”](#describe) > `optional` **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [types.ts:132](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L132) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-2) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [types.ts:116](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L116) #### Parameters [Section titled “Parameters”](#parameters-2) ##### params? [Section titled “params?”](#params-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`\[]> *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [types.ts:117](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L117) #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T` | `null`> *** ### findPage()? [Section titled “findPage()?”](#findpage) > `optional` **findPage**(`params?`): `Promise`<[`CursorPage`](/api/mantlejs/mantle/interfaces/cursorpage/)<`T`>> Defined in: [types.ts:130](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L130) Optional cursor pagination. Implemented by adapters whose backend is natively cursored (DynamoDB, Qdrant, Pinecone) — `describe().pagination` reports `"cursor"` or `"both"` when available. Stateless: the cursor lives entirely in the returned page, so concurrent calls on one repository instance never interfere. #### Parameters [Section titled “Parameters”](#parameters-4) ##### params? [Section titled “params?”](#params-2) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) & `object` #### Returns [Section titled “Returns”](#returns-5) `Promise`<[`CursorPage`](/api/mantlejs/mantle/interfaces/cursorpage/)<`T`>> *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [types.ts:121](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L121) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data) `D` #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T`> *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [types.ts:118](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L118) #### Parameters [Section titled “Parameters”](#parameters-6) ##### data [Section titled “data”](#data-1) `D` #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T`> *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [types.ts:119](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L119) #### Parameters [Section titled “Parameters”](#parameters-7) ##### data [Section titled “data”](#data-2) `D`\[] #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T`\[]> *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [types.ts:120](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L120) #### Parameters [Section titled “Parameters”](#parameters-8) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-3) `D` #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T`> # RepositoryCapabilities Defined in: [types.ts:84](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L84) Machine-readable description of what a repository adapter can do. Returned by `Repository.describe()` so tooling (OpenAPI generation, `/_services` introspection, agents) can discover adapter capabilities without trial-and-error queries. ## Properties [Section titled “Properties”](#properties) ### adapter [Section titled “adapter”](#adapter) > **adapter**: `string` Defined in: [types.ts:86](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L86) Package name of the adapter, e.g. “@mantlejs/knex”. *** ### fullTextSearch [Section titled “fullTextSearch”](#fulltextsearch) > **fullTextSearch**: `boolean` Defined in: [types.ts:92](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L92) Whether the adapter exposes native full-text search. *** ### nestedPaths [Section titled “nestedPaths”](#nestedpaths) > **nestedPaths**: `boolean` Defined in: [types.ts:100](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L100) Whether dot-path field names (e.g. `"metadata.owner.name"`) address nested fields for querying. Required (not optional) so the absence of this capability is a discoverable `false`, not a silent omission — a caller (or an agent) can check this before assuming a dot-path query will work, rather than discovering the gap as a runtime error or, worse, a literal-column-name misinterpretation. *** ### operators [Section titled “operators”](#operators) > **operators**: `string`\[] Defined in: [types.ts:88](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L88) Exactly the `$`-operators the adapter’s where-clause translator accepts — the same set its `assertOperators` call enforces. *** ### pagination [Section titled “pagination”](#pagination) > **pagination**: `"offset"` | `"cursor"` | `"both"` Defined in: [types.ts:90](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L90) Pagination styles the adapter supports through `QueryParams`. *** ### scanning? [Section titled “scanning?”](#scanning) > `optional` **scanning?**: (`where`) => `boolean` Defined in: [types.ts:102](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L102) When present, returns true if the given where clause forces a full scan (e.g. DynamoDB Scan instead of Query). #### Parameters [Section titled “Parameters”](#parameters) ##### where [Section titled “where”](#where) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns) `boolean` # RepositoryServiceOptions Defined in: [repository-service.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L4) ## Extended by [Section titled “Extended by”](#extended-by) * [`VectorRepositoryServiceOptions`](/api/mantlejs/mantle/interfaces/vectorrepositoryserviceoptions/) ## Properties [Section titled “Properties”](#properties) ### fields? [Section titled “fields?”](#fields) > `optional` **fields?**: `string`\[] Defined in: [repository-service.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L13) Whitelist of queryable fields. When set, a where/sort/select key outside it throws BadRequest. *** ### paginate? [Section titled “paginate?”](#paginate) > `optional` **paginate?**: `object` Defined in: [repository-service.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L15) Pagination defaults. When set, find() applies `default` and caps `$limit` at `max`. #### default [Section titled “default”](#default) > **default**: `number` #### max [Section titled “max”](#max) > **max**: `number` *** ### schema? [Section titled “schema?”](#schema) > `optional` **schema?**: `object` Defined in: [repository-service.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L11) Duck-typed JSON-Schema-ish object: `{ properties: { field: { type: "number" } } }`. Used only to coerce query-string values to their field types. A TypeBox entity schema satisfies this shape — but this is structural on purpose: @mantlejs/mantle depends on nothing. #### properties? [Section titled “properties?”](#properties-1) > `optional` **properties?**: `Record`<`string`, { `type?`: `string`; }> # RequestContext Defined in: [context.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/context.ts#L3) ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `unknown` ## Properties [Section titled “Properties”](#properties) ### correlationId [Section titled “correlationId”](#correlationid) > **correlationId**: `string` Defined in: [context.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/context.ts#L4) # Service Defined in: [types.ts:70](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L70) ## Extended by [Section titled “Extended by”](#extended-by) * [`ServiceHandle`](/api/mantlejs/mantle/interfaces/servicehandle/) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ### D [Section titled “D”](#d) `D` = `Partial`<`T`> ## Methods [Section titled “Methods”](#methods) ### create() [Section titled “create()”](#create) > **create**(`data`, `params?`): `Promise`<`T`> Defined in: [types.ts:73](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L73) #### Parameters [Section titled “Parameters”](#parameters) ##### data [Section titled “data”](#data) `D` ##### params? [Section titled “params?”](#params) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns) `Promise`<`T`> *** ### find() [Section titled “find()”](#find) > **find**(`params?`): `Promise`<`T`\[] | [`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`>> Defined in: [types.ts:71](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L71) #### Parameters [Section titled “Parameters”](#parameters-1) ##### params? [Section titled “params?”](#params-1) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-1) `Promise`<`T`\[] | [`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`>> *** ### get() [Section titled “get()”](#get) > **get**(`id`, `params?`): `Promise`<`T`> Defined in: [types.ts:72](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L72) #### Parameters [Section titled “Parameters”](#parameters-2) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### params? [Section titled “params?”](#params-2) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<`T`> *** ### patch() [Section titled “patch()”](#patch) > **patch**(`id`, `data`, `params?`): `Promise`<`T`> Defined in: [types.ts:75](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L75) #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-1) `D` ##### params? [Section titled “params?”](#params-3) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`> *** ### remove() [Section titled “remove()”](#remove) > **remove**(`id`, `params?`): `Promise`<`T`> Defined in: [types.ts:76](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L76) #### Parameters [Section titled “Parameters”](#parameters-4) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### params? [Section titled “params?”](#params-4) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T`> *** ### update() [Section titled “update()”](#update) > **update**(`id`, `data`, `params?`): `Promise`<`T`> Defined in: [types.ts:74](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L74) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-2) `D` ##### params? [Section titled “params?”](#params-5) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-5) `Promise`<`T`> # ServiceDescriptor Defined in: [types.ts:318](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L318) Machine-readable description of a registered service, returned by `ServiceHandle.describe()` and served by the transports’ opt-in `GET /_services` introspection endpoint. ## Properties [Section titled “Properties”](#properties) ### authRequired? [Section titled “authRequired?”](#authrequired) > `optional` **authRequired?**: `boolean` Defined in: [types.ts:328](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L328) True when an auth hook (a hook carrying an `authStrategy` property, e.g. `authenticate("jwt")`) is registered in `before.all`. *** ### capabilities? [Section titled “capabilities?”](#capabilities) > `optional` **capabilities?**: [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [types.ts:326](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L326) The underlying repository’s capabilities, when the service exposes them (see `RepositoryService.describe()`). *** ### events [Section titled “events”](#events) > **events**: `string`\[] Defined in: [types.ts:322](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L322) Event names this service emits: the standard created/updated/patched/removed set filtered by registered methods, plus custom `ServiceOptions.events`. *** ### methods [Section titled “methods”](#methods) > **methods**: `string`\[] Defined in: [types.ts:320](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L320) *** ### path [Section titled “path”](#path) > **path**: `string` Defined in: [types.ts:319](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L319) *** ### schema? [Section titled “schema?”](#schema) > `optional` **schema?**: `unknown` Defined in: [types.ts:324](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L324) The `ServiceOptions.schema` stored at registration (JSON Schema, e.g. via TypeBox). # ServiceHandle Defined in: [types.ts:331](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L331) ## Extends [Section titled “Extends”](#extends) * [`Service`](/api/mantlejs/mantle/interfaces/service/)<`T`> ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Properties [Section titled “Properties”](#properties) ### methods [Section titled “methods”](#methods) > `readonly` **methods**: `string`\[] Defined in: [types.ts:336](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L336) *** ### publisher? [Section titled “publisher?”](#publisher) > `readonly` `optional` **publisher?**: [`ChannelPublisher`](/api/mantlejs/mantle/type-aliases/channelpublisher/)<`unknown`> Defined in: [types.ts:338](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L338) *** ### schema? [Section titled “schema?”](#schema) > `readonly` `optional` **schema?**: `unknown` Defined in: [types.ts:335](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L335) ## Methods [Section titled “Methods”](#methods-1) ### create() [Section titled “create()”](#create) > **create**(`data`, `params?`): `Promise`<`T`> Defined in: [types.ts:73](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L73) #### Parameters [Section titled “Parameters”](#parameters) ##### data [Section titled “data”](#data) `D` ##### params? [Section titled “params?”](#params) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`create`](/api/mantlejs/mantle/interfaces/service/#create) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`ServiceDescriptor`](/api/mantlejs/mantle/interfaces/servicedescriptor/) Defined in: [types.ts:334](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L334) #### Returns [Section titled “Returns”](#returns-1) [`ServiceDescriptor`](/api/mantlejs/mantle/interfaces/servicedescriptor/) *** ### dispatch() [Section titled “dispatch()”](#dispatch) > **dispatch**(`method`, `data?`, `id?`, `params?`): `Promise`<`T` | `T`\[] | [`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`>> Defined in: [types.ts:333](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L333) #### Parameters [Section titled “Parameters”](#parameters-1) ##### method [Section titled “method”](#method) `string` ##### data? [Section titled “data?”](#data-1) `Partial`<`T`> ##### id? [Section titled “id?”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### params? [Section titled “params?”](#params-1) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<`T` | `T`\[] | [`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`>> *** ### find() [Section titled “find()”](#find) > **find**(`params?`): `Promise`<`T`\[] | [`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`>> Defined in: [types.ts:71](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L71) #### Parameters [Section titled “Parameters”](#parameters-2) ##### params? [Section titled “params?”](#params-2) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`\[] | [`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`>> #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`find`](/api/mantlejs/mantle/interfaces/service/#find) *** ### get() [Section titled “get()”](#get) > **get**(`id`, `params?`): `Promise`<`T`> Defined in: [types.ts:72](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L72) #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### params? [Section titled “params?”](#params-3) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`get`](/api/mantlejs/mantle/interfaces/service/#get) *** ### hooks() [Section titled “hooks()”](#hooks) > **hooks**(`config`): `this` Defined in: [types.ts:332](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L332) #### Parameters [Section titled “Parameters”](#parameters-4) ##### config [Section titled “config”](#config) [`HookConfig`](/api/mantlejs/mantle/interfaces/hookconfig/)<`T`> #### Returns [Section titled “Returns”](#returns-5) `this` *** ### patch() [Section titled “patch()”](#patch) > **patch**(`id`, `data`, `params?`): `Promise`<`T`> Defined in: [types.ts:75](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L75) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-2) `D` ##### params? [Section titled “params?”](#params-4) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`patch`](/api/mantlejs/mantle/interfaces/service/#patch) *** ### publish() [Section titled “publish()”](#publish) > **publish**(`publisher`): `this` Defined in: [types.ts:337](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L337) #### Parameters [Section titled “Parameters”](#parameters-6) ##### publisher [Section titled “publisher”](#publisher-1) [`ChannelPublisher`](/api/mantlejs/mantle/type-aliases/channelpublisher/)<`T`> #### Returns [Section titled “Returns”](#returns-7) `this` *** ### remove() [Section titled “remove()”](#remove) > **remove**(`id`, `params?`): `Promise`<`T`> Defined in: [types.ts:76](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L76) #### Parameters [Section titled “Parameters”](#parameters-7) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### params? [Section titled “params?”](#params-5) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`remove`](/api/mantlejs/mantle/interfaces/service/#remove) *** ### update() [Section titled “update()”](#update) > **update**(`id`, `data`, `params?`): `Promise`<`T`> Defined in: [types.ts:74](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L74) #### Parameters [Section titled “Parameters”](#parameters-8) ##### id [Section titled “id”](#id-4) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-3) `D` ##### params? [Section titled “params?”](#params-6) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`Service`](/api/mantlejs/mantle/interfaces/service/).[`update`](/api/mantlejs/mantle/interfaces/service/#update) # ServiceOptions Defined in: [types.ts:270](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L270) ## Properties [Section titled “Properties”](#properties) ### events? [Section titled “events?”](#events) > `optional` **events?**: `string`\[] Defined in: [types.ts:276](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L276) Custom method names that emit a `service:event` when dispatched — the event name is the method name. Standard mutation events (created/updated/patched/removed) always emit. *** ### methods? [Section titled “methods?”](#methods) > `optional` **methods?**: `string`\[] Defined in: [types.ts:271](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L271) *** ### schema? [Section titled “schema?”](#schema) > `optional` **schema?**: `unknown` Defined in: [types.ts:278](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L278) TypeBox schema for this service. Stored for tooling introspection — not validated here. # ServiceParams Defined in: [types.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L39) ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `unknown` ## Properties [Section titled “Properties”](#properties) ### connection? [Section titled “connection?”](#connection) > `optional` **connection?**: `Record`<`string`, `unknown`> Defined in: [types.ts:45](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L45) Per-socket connection state. Set by the socket.io transport and persists across calls from the same socket. *** ### external? [Section titled “external?”](#external) > `optional` **external?**: `boolean` Defined in: [types.ts:49](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L49) True on `service:event` emissions that did NOT pass through the hook pipeline — e.g. Supabase change-feed re-emissions of direct DB mutations. *** ### headers? [Section titled “headers?”](#headers) > `optional` **headers?**: `Record`<`string`, `string`> Defined in: [types.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L43) *** ### provider? [Section titled “provider?”](#provider) > `optional` **provider?**: `string` Defined in: [types.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L42) *** ### query? [Section titled “query?”](#query) > `optional` **query?**: `Record`<`string`, `unknown`> Defined in: [types.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L40) *** ### rooms? [Section titled “rooms?”](#rooms) > `optional` **rooms?**: `string` | `string`\[] Defined in: [types.ts:47](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L47) Rooms to broadcast mutation events to. If set by a before hook, the socket.io transport will broadcast only to these rooms instead of all clients. *** ### user? [Section titled “user?”](#user) > `optional` **user?**: `Record`<`string`, `unknown`> Defined in: [types.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L41) # SimilarData Defined in: [vector-repository-service.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L11) Request body for the `similar` custom method. ## Properties [Section titled “Properties”](#properties) ### topK? [Section titled “topK?”](#topk) > `optional` **topK?**: `number` Defined in: [vector-repository-service.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L15) How many neighbours to return. #### Default [Section titled “Default”](#default) 10 (or `options.topK.default`) *** ### vector [Section titled “vector”](#vector) > **vector**: `number`\[] Defined in: [vector-repository-service.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L13) The query embedding. Length must match the repository’s vector space. *** ### where? [Section titled “where?”](#where) > `optional` **where?**: `Record`<`string`, `unknown`> Defined in: [vector-repository-service.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L17) Optional metadata filter, same operator syntax as `find()`’s where. # VectorRepository Defined in: [types.ts:135](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L135) ## Extends [Section titled “Extends”](#extends) * [`Repository`](/api/mantlejs/mantle/interfaces/repository/)<`T`, `D`> ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ### D [Section titled “D”](#d) `D` = `Partial`<`T`> ## Methods [Section titled “Methods”](#methods) ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [types.ts:123](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L123) #### Parameters [Section titled “Parameters”](#parameters) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns) `Promise`<`number`> #### Inherited from [Section titled “Inherited from”](#inherited-from) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`count`](/api/mantlejs/mantle/interfaces/repository/#count) *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [types.ts:122](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L122) #### Parameters [Section titled “Parameters”](#parameters-1) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-1) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`deleteById`](/api/mantlejs/mantle/interfaces/repository/#deletebyid) *** ### deleteVector() [Section titled “deleteVector()”](#deletevector) > **deleteVector**(`id`): `Promise`<`T`> Defined in: [types.ts:145](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L145) Delete a vector and its associated record #### Parameters [Section titled “Parameters”](#parameters-2) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<`T`> *** ### describe()? [Section titled “describe()?”](#describe) > `optional` **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [types.ts:132](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L132) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-3) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`describe`](/api/mantlejs/mantle/interfaces/repository/#describe) *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [types.ts:116](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L116) #### Parameters [Section titled “Parameters”](#parameters-3) ##### params? [Section titled “params?”](#params-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T`\[]> #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findAll`](/api/mantlejs/mantle/interfaces/repository/#findall) *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [types.ts:117](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L117) #### Parameters [Section titled “Parameters”](#parameters-4) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-5) `Promise`<`T` | `null`> #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findById`](/api/mantlejs/mantle/interfaces/repository/#findbyid) *** ### findPage()? [Section titled “findPage()?”](#findpage) > `optional` **findPage**(`params?`): `Promise`<[`CursorPage`](/api/mantlejs/mantle/interfaces/cursorpage/)<`T`>> Defined in: [types.ts:130](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L130) Optional cursor pagination. Implemented by adapters whose backend is natively cursored (DynamoDB, Qdrant, Pinecone) — `describe().pagination` reports `"cursor"` or `"both"` when available. Stateless: the cursor lives entirely in the returned page, so concurrent calls on one repository instance never interfere. #### Parameters [Section titled “Parameters”](#parameters-5) ##### params? [Section titled “params?”](#params-2) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) & `object` #### Returns [Section titled “Returns”](#returns-6) `Promise`<[`CursorPage`](/api/mantlejs/mantle/interfaces/cursorpage/)<`T`>> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findPage`](/api/mantlejs/mantle/interfaces/repository/#findpage) *** ### findSimilar() [Section titled “findSimilar()”](#findsimilar) > **findSimilar**(`vector`, `topK`, `params?`): `Promise`<`T` & `object`\[]> Defined in: [types.ts:141](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L141) Find the top-K records most similar to the given embedding vector. Every result carries the adapter’s native match metric as `_score` — whether a higher or lower value means “more similar” is adapter-specific; see the adapter README. #### Parameters [Section titled “Parameters”](#parameters-6) ##### vector [Section titled “vector”](#vector) `number`\[] ##### topK [Section titled “topK”](#topk) `number` ##### params? [Section titled “params?”](#params-3) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T` & `object`\[]> *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [types.ts:121](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L121) #### Parameters [Section titled “Parameters”](#parameters-7) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data) `D` #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-6) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`patchById`](/api/mantlejs/mantle/interfaces/repository/#patchbyid) *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [types.ts:118](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L118) #### Parameters [Section titled “Parameters”](#parameters-8) ##### data [Section titled “data”](#data-1) `D` #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-7) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`save`](/api/mantlejs/mantle/interfaces/repository/#save) *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [types.ts:119](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L119) #### Parameters [Section titled “Parameters”](#parameters-9) ##### data [Section titled “data”](#data-2) `D`\[] #### Returns [Section titled “Returns”](#returns-10) `Promise`<`T`\[]> #### Inherited from [Section titled “Inherited from”](#inherited-from-8) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`saveAll`](/api/mantlejs/mantle/interfaces/repository/#saveall) *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [types.ts:120](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L120) #### Parameters [Section titled “Parameters”](#parameters-10) ##### id [Section titled “id”](#id-4) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-3) `D` #### Returns [Section titled “Returns”](#returns-11) `Promise`<`T`> #### Inherited from [Section titled “Inherited from”](#inherited-from-9) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`updateById`](/api/mantlejs/mantle/interfaces/repository/#updatebyid) *** ### upsertVector() [Section titled “upsertVector()”](#upsertvector) > **upsertVector**(`id`, `vector`, `data`): `Promise`<`T`> Defined in: [types.ts:143](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L143) Upsert a record with its embedding vector #### Parameters [Section titled “Parameters”](#parameters-11) ##### id [Section titled “id”](#id-5) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### vector [Section titled “vector”](#vector-1) `number`\[] ##### data [Section titled “data”](#data-4) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns-12) `Promise`<`T`> # VectorRepositoryServiceOptions Defined in: [vector-repository-service.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L5) ## Extends [Section titled “Extends”](#extends) * [`RepositoryServiceOptions`](/api/mantlejs/mantle/interfaces/repositoryserviceoptions/) ## Properties [Section titled “Properties”](#properties) ### fields? [Section titled “fields?”](#fields) > `optional` **fields?**: `string`\[] Defined in: [repository-service.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L13) Whitelist of queryable fields. When set, a where/sort/select key outside it throws BadRequest. #### Inherited from [Section titled “Inherited from”](#inherited-from) [`RepositoryServiceOptions`](/api/mantlejs/mantle/interfaces/repositoryserviceoptions/).[`fields`](/api/mantlejs/mantle/interfaces/repositoryserviceoptions/#fields) *** ### paginate? [Section titled “paginate?”](#paginate) > `optional` **paginate?**: `object` Defined in: [repository-service.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L15) Pagination defaults. When set, find() applies `default` and caps `$limit` at `max`. #### default [Section titled “default”](#default) > **default**: `number` #### max [Section titled “max”](#max) > **max**: `number` #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`RepositoryServiceOptions`](/api/mantlejs/mantle/interfaces/repositoryserviceoptions/).[`paginate`](/api/mantlejs/mantle/interfaces/repositoryserviceoptions/#paginate) *** ### schema? [Section titled “schema?”](#schema) > `optional` **schema?**: `object` Defined in: [repository-service.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/repository-service.ts#L11) Duck-typed JSON-Schema-ish object: `{ properties: { field: { type: "number" } } }`. Used only to coerce query-string values to their field types. A TypeBox entity schema satisfies this shape — but this is structural on purpose: @mantlejs/mantle depends on nothing. #### properties? [Section titled “properties?”](#properties-1) > `optional` **properties?**: `Record`<`string`, { `type?`: `string`; }> #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`RepositoryServiceOptions`](/api/mantlejs/mantle/interfaces/repositoryserviceoptions/).[`schema`](/api/mantlejs/mantle/interfaces/repositoryserviceoptions/#schema) *** ### topK? [Section titled “topK?”](#topk) > `optional` **topK?**: `object` Defined in: [vector-repository-service.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/vector-repository-service.ts#L7) Bounds for `similar()`’s topK. When set, a missing topK gets `default` and requests are capped at `max`. #### default [Section titled “default”](#default-1) > **default**: `number` #### max [Section titled “max”](#max-1) > **max**: `number` # CapabilityScope > **CapabilityScope** = `Record`<`string`, `string`\[] | `true`> Defined in: [capability-scope.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/capability-scope.ts#L9) Grant map for one capability-scoped principal: methods allowed per service path, or `true` for every method on that path (a path-level wildcard). A path absent from the map — or present with a method not listed — is denied. This is the same shape `@mantlejs/mcp`’s `services` expose map already uses; `@mantlejs/auth`’s agent tokens (`authorizeAgent()`) reuse it too, so there is one deny-by-default “is this path+method allowed” implementation in the framework, not two that can silently drift apart. # ChannelPublisher > **ChannelPublisher**<`T`> = (`data`, `context`) => [`MantleChannel`](/api/mantlejs/mantle/interfaces/mantlechannel/) | [`MantleChannel`](/api/mantlejs/mantle/interfaces/mantlechannel/)\[] | `null` | `undefined` | `void` Defined in: [types.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L18) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` = `unknown` ## Parameters [Section titled “Parameters”](#parameters) ### data [Section titled “data”](#data) `T` | `T`\[] | [`Paginated`](/api/mantlejs/mantle/interfaces/paginated/)<`T`> ### context [Section titled “context”](#context) [`PublishContext`](/api/mantlejs/mantle/interfaces/publishcontext/) ## Returns [Section titled “Returns”](#returns) [`MantleChannel`](/api/mantlejs/mantle/interfaces/mantlechannel/) | [`MantleChannel`](/api/mantlejs/mantle/interfaces/mantlechannel/)\[] | `null` | `undefined` | `void` # HookFunction > **HookFunction**<`T`> = (`context`) => `Promise`<[`HookContext`](/api/mantlejs/mantle/interfaces/hookcontext/)<`T`>> | [`HookContext`](/api/mantlejs/mantle/interfaces/hookcontext/)<`T`> Defined in: [types.ts:258](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L258) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` = `unknown` ## Parameters [Section titled “Parameters”](#parameters) ### context [Section titled “context”](#context) [`HookContext`](/api/mantlejs/mantle/interfaces/hookcontext/)<`T`> ## Returns [Section titled “Returns”](#returns) `Promise`<[`HookContext`](/api/mantlejs/mantle/interfaces/hookcontext/)<`T`>> | [`HookContext`](/api/mantlejs/mantle/interfaces/hookcontext/)<`T`> # HttpRouteHandler > **HttpRouteHandler** = (`req`, `res`, `next`) => `void` | `Promise`<`void`> Defined in: [types.ts:190](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L190) ## Parameters [Section titled “Parameters”](#parameters) ### req [Section titled “req”](#req) [`HttpRequestLike`](/api/mantlejs/mantle/interfaces/httprequestlike/) ### res [Section titled “res”](#res) [`HttpResponseLike`](/api/mantlejs/mantle/interfaces/httpresponselike/) ### next [Section titled “next”](#next) (`err?`) => `void` ## Returns [Section titled “Returns”](#returns) `void` | `Promise`<`void`> # Id > **Id** = `string` | `number` Defined in: [types.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L3) # MantlePlugin > **MantlePlugin** = (`app`) => `void` | `Promise`<`void`> Defined in: [types.ts:312](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L312) ## Parameters [Section titled “Parameters”](#parameters) ### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) ## Returns [Section titled “Returns”](#returns) `void` | `Promise`<`void`> # MethodHookMap > **MethodHookMap**<`T`> = { \[method in keyof Service\ | “all”]?: HookFunction\\[] } Defined in: [types.ts:260](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L260) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` # CORS_DEFAULT_METHODS > `const` **CORS_DEFAULT_METHODS**: `string`\[] Defined in: [cors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/cors.ts#L4) Default `Access-Control-Allow-Methods` list for `cors: true` — the CRUD verbs Mantle service routes use. # DEFAULT_MAX_BATCH_SIZE > `const` **DEFAULT_MAX_BATCH_SIZE**: `25` = `25` Defined in: [types.ts:282](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/types.ts#L282) Default maximum number of calls per batch request. Transports can override via their `batch` option. # NESTED_QUERY_CASES > `const` **NESTED_QUERY_CASES**: [`NestedQueryCase`](/api/mantlejs/mantle/interfaces/nestedquerycase/)\[] Defined in: [query-fixtures.ts:59](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L59) # NESTED_QUERY_RECORDS > `const` **NESTED_QUERY_RECORDS**: [`NestedQueryRecord`](/api/mantlejs/mantle/interfaces/nestedqueryrecord/)\[] Defined in: [query-fixtures.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mantle/src/lib/query-fixtures.ts#L31) # @mantlejs/mcp ## Interfaces [Section titled “Interfaces”](#interfaces) * [EventRecord](/api/mantlejs/mcp/interfaces/eventrecord/) * [McpCodeModeInput](/api/mantlejs/mcp/interfaces/mcpcodemodeinput/) * [McpCodeModeProvider](/api/mantlejs/mcp/interfaces/mcpcodemodeprovider/) * [McpCodeModeSurface](/api/mantlejs/mcp/interfaces/mcpcodemodesurface/) * [McpExposedService](/api/mantlejs/mcp/interfaces/mcpexposedservice/) * [McpMethodSchema](/api/mantlejs/mcp/interfaces/mcpmethodschema/) * [McpOptions](/api/mantlejs/mcp/interfaces/mcpoptions/) * [McpPromptDefinition](/api/mantlejs/mcp/interfaces/mcppromptdefinition/) * [McpPromptMessage](/api/mantlejs/mcp/interfaces/mcppromptmessage/) * [McpQueryOptions](/api/mantlejs/mcp/interfaces/mcpqueryoptions/) * [McpResourceDefinition](/api/mantlejs/mcp/interfaces/mcpresourcedefinition/) * [McpToolContext](/api/mantlejs/mcp/interfaces/mcptoolcontext/) * [McpToolDefinition](/api/mantlejs/mcp/interfaces/mcptooldefinition/) * [StartMcpOptions](/api/mantlejs/mcp/interfaces/startmcpoptions/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [McpMethodRunner](/api/mantlejs/mcp/type-aliases/mcpmethodrunner/) * [McpMode](/api/mantlejs/mcp/type-aliases/mcpmode/) * [McpServerFactory](/api/mantlejs/mcp/type-aliases/mcpserverfactory/) ## Functions [Section titled “Functions”](#functions) * [buildQuerySchema](/api/mantlejs/mcp/functions/buildqueryschema/) * [createServiceMethodRunner](/api/mantlejs/mcp/functions/createservicemethodrunner/) * [describeServiceMethod](/api/mantlejs/mcp/functions/describeservicemethod/) * [mcp](/api/mantlejs/mcp/functions/mcp/) * [startMcp](/api/mantlejs/mcp/functions/startmcp/) * [toolName](/api/mantlejs/mcp/functions/toolname/) # @mantlejs/mcp-code ## Classes [Section titled “Classes”](#classes) * [CodeExecutorFault](/api/mantlejs/mcp-code/classes/codeexecutorfault/) * [CodeLimitExceeded](/api/mantlejs/mcp-code/classes/codelimitexceeded/) * [CodeOutputTooLarge](/api/mantlejs/mcp-code/classes/codeoutputtoolarge/) * [CodeScriptError](/api/mantlejs/mcp-code/classes/codescripterror/) * [CodeTimeout](/api/mantlejs/mcp-code/classes/codetimeout/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [BridgeCall](/api/mantlejs/mcp-code/interfaces/bridgecall/) * [CodeBridge](/api/mantlejs/mcp-code/interfaces/codebridge/) * [CodeExecutionReport](/api/mantlejs/mcp-code/interfaces/codeexecutionreport/) * [CodeExecutor](/api/mantlejs/mcp-code/interfaces/codeexecutor/) * [CodeLimits](/api/mantlejs/mcp-code/interfaces/codelimits/) * [CodeModeCallMetadata](/api/mantlejs/mcp-code/interfaces/codemodecallmetadata/) * [CodeModeOptions](/api/mantlejs/mcp-code/interfaces/codemodeoptions/) * [ConformanceBridge](/api/mantlejs/mcp-code/interfaces/conformancebridge/) * [ConformanceCase](/api/mantlejs/mcp-code/interfaces/conformancecase/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [CodeExecutionResult](/api/mantlejs/mcp-code/type-aliases/codeexecutionresult/) ## Variables [Section titled “Variables”](#variables) * [API_RESOURCE_URI](/api/mantlejs/mcp-code/variables/api_resource_uri/) * [CODE_EXECUTOR_CONFORMANCE_CASES](/api/mantlejs/mcp-code/variables/code_executor_conformance_cases/) * [DEFAULT_CODE_LIMITS](/api/mantlejs/mcp-code/variables/default_code_limits/) * [MIN_WASM_MEMORY_BYTES](/api/mantlejs/mcp-code/variables/min_wasm_memory_bytes/) ## Functions [Section titled “Functions”](#functions) * [codeMode](/api/mantlejs/mcp-code/functions/codemode/) * [conformanceBridge](/api/mantlejs/mcp-code/functions/conformancebridge/) * [quickJsExecutor](/api/mantlejs/mcp-code/functions/quickjsexecutor/) # CodeExecutorFault Defined in: [mcp-code/src/lib/errors.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/errors.ts#L40) The executor itself failed — the sandbox engine threw on the host (e.g. a native stack overflow from a `stackBytes` above the safe ceiling). The sandbox instance is discarded; the host process is unaffected. ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new CodeExecutorFault**(`message?`, `data?`, `errors?`, `hint?`): `CodeExecutorFault` Defined in: [mcp-code/src/lib/errors.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/errors.ts#L41) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Code executor fault"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `CodeExecutorFault` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: mantle/dist/lib/errors.d.ts:3 #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: mantle/dist/lib/errors.d.ts:2 #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: mantle/dist/lib/errors.d.ts:4 #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: mantle/dist/lib/errors.d.ts:5 #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: mantle/dist/lib/errors.d.ts:7 Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: mantle/dist/lib/errors.d.ts:9 #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # CodeLimitExceeded Defined in: [mcp-code/src/lib/errors.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/errors.ts#L11) A `CodeLimits` cap other than time or output size was hit — bridged-call count, guest memory, or stack depth. ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new CodeLimitExceeded**(`message?`, `data?`, `errors?`, `hint?`): `CodeLimitExceeded` Defined in: [mcp-code/src/lib/errors.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/errors.ts#L12) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Code execution limit exceeded"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `CodeLimitExceeded` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: mantle/dist/lib/errors.d.ts:3 #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: mantle/dist/lib/errors.d.ts:2 #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: mantle/dist/lib/errors.d.ts:4 #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: mantle/dist/lib/errors.d.ts:5 #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: mantle/dist/lib/errors.d.ts:7 Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: mantle/dist/lib/errors.d.ts:9 #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # CodeOutputTooLarge Defined in: [mcp-code/src/lib/errors.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/errors.ts#L18) The script’s JSON-serialized return value exceeded `CodeLimits.maxOutputBytes`. ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new CodeOutputTooLarge**(`message?`, `data?`, `errors?`, `hint?`): `CodeOutputTooLarge` Defined in: [mcp-code/src/lib/errors.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/errors.ts#L19) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Code execution result is too large"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `CodeOutputTooLarge` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: mantle/dist/lib/errors.d.ts:3 #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: mantle/dist/lib/errors.d.ts:2 #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: mantle/dist/lib/errors.d.ts:4 #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: mantle/dist/lib/errors.d.ts:5 #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: mantle/dist/lib/errors.d.ts:7 Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: mantle/dist/lib/errors.d.ts:9 #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # CodeScriptError Defined in: [mcp-code/src/lib/errors.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/errors.ts#L29) The script itself failed — a syntax error, or an uncaught error that isn’t a Mantle service error (e.g. a `TypeError` from calling a method the API doesn’t have). Uncaught *service* errors (`Forbidden`, `NotFound`, …) surface as their own class instead. ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new CodeScriptError**(`message?`, `data?`, `errors?`, `hint?`): `CodeScriptError` Defined in: [mcp-code/src/lib/errors.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/errors.ts#L30) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Code execution failed"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `CodeScriptError` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: mantle/dist/lib/errors.d.ts:3 #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: mantle/dist/lib/errors.d.ts:2 #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: mantle/dist/lib/errors.d.ts:4 #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: mantle/dist/lib/errors.d.ts:5 #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: mantle/dist/lib/errors.d.ts:7 Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: mantle/dist/lib/errors.d.ts:9 #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # CodeTimeout Defined in: [mcp-code/src/lib/errors.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/errors.ts#L4) The script ran past `CodeLimits.timeoutMs` — a CPU loop (interrupt handler) or a bridged call that never settled (host timer). ## Extends [Section titled “Extends”](#extends) * [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new CodeTimeout**(`message?`, `data?`, `errors?`, `hint?`): `CodeTimeout` Defined in: [mcp-code/src/lib/errors.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/errors.ts#L5) #### Parameters [Section titled “Parameters”](#parameters) ##### message? [Section titled “message?”](#message) `string` = `"Code execution timed out"` ##### data? [Section titled “data?”](#data) `unknown` ##### errors? [Section titled “errors?”](#errors) `unknown`\[] ##### hint? [Section titled “hint?”](#hint) `string` #### Returns [Section titled “Returns”](#returns) `CodeTimeout` #### Overrides [Section titled “Overrides”](#overrides) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`constructor`](/api/mantlejs/mantle/classes/mantleerror/#constructor) ## Properties [Section titled “Properties”](#properties) ### className [Section titled “className”](#classname) > `readonly` **className**: `string` Defined in: mantle/dist/lib/errors.d.ts:3 #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`className`](/api/mantlejs/mantle/classes/mantleerror/#classname) *** ### code [Section titled “code”](#code) > `readonly` **code**: `number` Defined in: mantle/dist/lib/errors.d.ts:2 #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`code`](/api/mantlejs/mantle/classes/mantleerror/#code) *** ### data? [Section titled “data?”](#data-1) > `readonly` `optional` **data?**: `unknown` Defined in: mantle/dist/lib/errors.d.ts:4 #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`data`](/api/mantlejs/mantle/classes/mantleerror/#data) *** ### errors? [Section titled “errors?”](#errors-1) > `readonly` `optional` **errors?**: `unknown`\[] Defined in: mantle/dist/lib/errors.d.ts:5 #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`errors`](/api/mantlejs/mantle/classes/mantleerror/#errors) *** ### hint? [Section titled “hint?”](#hint-1) > `readonly` `optional` **hint?**: `string` Defined in: mantle/dist/lib/errors.d.ts:7 Actionable guidance for the caller — what to change so the request succeeds. Serialized in toJSON(). #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`hint`](/api/mantlejs/mantle/classes/mantleerror/#hint) ## Methods [Section titled “Methods”](#methods) ### toJSON() [Section titled “toJSON()”](#tojson) > **toJSON**(): `Record`<`string`, `unknown`> Defined in: mantle/dist/lib/errors.d.ts:9 #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MantleError`](/api/mantlejs/mantle/classes/mantleerror/).[`toJSON`](/api/mantlejs/mantle/classes/mantleerror/#tojson) # codeMode > **codeMode**(`options?`): `McpCodeModeProvider` Defined in: [mcp-code/src/lib/code-mode.ts:83](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L83) MCP code mode: instead of one tool per service method, the agent gets a typed TypeScript API of the exposed services plus two tools — `search_api` (load declarations piece by piece) and `execute` (run a script against the API in a sandbox) — and the `mantle://code/api.d.ts` resource. Pass the result as `mcp({ codeMode: codeMode() })`. Every `mantle..()` call inside a script dispatches through the exact runner tool mode uses (`createServiceMethodRunner` from `@mantlejs/mcp`) with the session’s params, `provider: "mcp"`, and `params.mcp` set (see `CodeModeCallMetadata`) — so the service’s full hook pipeline, including `authorizeAgent()` and `@mantlejs/audit`, applies to each call. The expose map is the hard boundary: only exposed methods exist in the sandbox. ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`CodeModeOptions`](/api/mantlejs/mcp-code/interfaces/codemodeoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) `McpCodeModeProvider` # conformanceBridge > **conformanceBridge**(): [`ConformanceBridge`](/api/mantlejs/mcp-code/interfaces/conformancebridge/) Defined in: [mcp-code/src/lib/conformance.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L23) The fixture bridge every conformance case runs against — one `items` service: `get(id)` → `{ id }`; `find()` → two records after 10 ms; `slow()` → `"slow"` after 100 ms; `fail()` → rejects `NotFound("Item missing", { id: "x" }, …, hint)`; `never()` → never settles. ## Returns [Section titled “Returns”](#returns) [`ConformanceBridge`](/api/mantlejs/mcp-code/interfaces/conformancebridge/) # quickJsExecutor > **quickJsExecutor**(): [`CodeExecutor`](/api/mantlejs/mcp-code/interfaces/codeexecutor/) Defined in: [mcp-code/src/lib/quickjs-executor.ts:58](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/quickjs-executor.ts#L58) The default `CodeExecutor`: QuickJS (sync WASM build) with host calls bridged as guest promises. Every execution gets a **fresh WASM instance with its own bounded `WebAssembly.Memory`** — the only hard memory cap (QuickJS’s own limit is soft), memory is reclaimed after a bomb, and an instance poisoned by a host-side fault is simply discarded. See PRD Decisions #12–13 for the measurements behind each choice. ## Returns [Section titled “Returns”](#returns) [`CodeExecutor`](/api/mantlejs/mcp-code/interfaces/codeexecutor/) # BridgeCall Defined in: [mcp-code/src/lib/conformance.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L5) A recorded bridged call, for cases asserting what reached the host. ## Properties [Section titled “Properties”](#properties) ### args [Section titled “args”](#args) > **args**: `unknown`\[] Defined in: [mcp-code/src/lib/conformance.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L8) *** ### method [Section titled “method”](#method) > **method**: `string` Defined in: [mcp-code/src/lib/conformance.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L7) *** ### path [Section titled “path”](#path) > **path**: `string` Defined in: [mcp-code/src/lib/conformance.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L6) # CodeBridge Defined in: [mcp-code/src/lib/types.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L41) The host side of the sandbox’s `mantle` API. `services` is the API’s shape (path → method names) — the executor builds `mantle[path][method](...args)` inside the sandbox from it and nothing else. `call` receives the JSON-decoded positional arguments and resolves with a JSON-serializable value, or rejects (a `MantleError`’s `toJSON()` shape reaches the script). ## Extended by [Section titled “Extended by”](#extended-by) * [`ConformanceBridge`](/api/mantlejs/mcp-code/interfaces/conformancebridge/) ## Properties [Section titled “Properties”](#properties) ### services [Section titled “services”](#services) > **services**: `Record`<`string`, `string`\[]> Defined in: [mcp-code/src/lib/types.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L42) ## Methods [Section titled “Methods”](#methods) ### call() [Section titled “call()”](#call) > **call**(`path`, `method`, `args`): `Promise`<`unknown`> Defined in: [mcp-code/src/lib/types.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L43) #### Parameters [Section titled “Parameters”](#parameters) ##### path [Section titled “path”](#path) `string` ##### method [Section titled “method”](#method) `string` ##### args [Section titled “args”](#args) `unknown`\[] #### Returns [Section titled “Returns”](#returns) `Promise`<`unknown`> # CodeExecutionReport Defined in: [mcp-code/src/lib/code-mode.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L40) What `execute` returns to the agent on success. ## Properties [Section titled “Properties”](#properties) ### calls [Section titled “calls”](#calls) > **calls**: `number` Defined in: [mcp-code/src/lib/code-mode.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L43) *** ### executionId [Section titled “executionId”](#executionid) > **executionId**: `string` Defined in: [mcp-code/src/lib/code-mode.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L42) *** ### logs [Section titled “logs”](#logs) > **logs**: `string`\[] Defined in: [mcp-code/src/lib/code-mode.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L44) *** ### notes? [Section titled “notes?”](#notes) > `optional` **notes?**: `string`\[] Defined in: [mcp-code/src/lib/code-mode.ts:46](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L46) Notes from bridged calls (e.g. a truncated find page) and the log-truncation notice. *** ### result [Section titled “result”](#result) > **result**: `unknown` Defined in: [mcp-code/src/lib/code-mode.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L41) # CodeExecutor Defined in: [mcp-code/src/lib/types.ts:66](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L66) Runs untrusted JavaScript (the body of an async function — `return` sends the result back, top-level `await` works) against a bridge. Contract, checked by `CODE_EXECUTOR_CONFORMANCE_CASES`: fresh state per execution, JSON-only values across the boundary, no host capabilities beyond `mantle` and `console`, every limit enforced with a typed error, and `execute` never rejects — failures are `{ ok: false, error }`. ## Methods [Section titled “Methods”](#methods) ### execute() [Section titled “execute()”](#execute) > **execute**(`code`, `bridge`, `limits`): `Promise`<[`CodeExecutionResult`](/api/mantlejs/mcp-code/type-aliases/codeexecutionresult/)> Defined in: [mcp-code/src/lib/types.ts:67](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L67) #### Parameters [Section titled “Parameters”](#parameters) ##### code [Section titled “code”](#code) `string` ##### bridge [Section titled “bridge”](#bridge) [`CodeBridge`](/api/mantlejs/mcp-code/interfaces/codebridge/) ##### limits [Section titled “limits”](#limits) [`CodeLimits`](/api/mantlejs/mcp-code/interfaces/codelimits/) #### Returns [Section titled “Returns”](#returns) `Promise`<[`CodeExecutionResult`](/api/mantlejs/mcp-code/type-aliases/codeexecutionresult/)> # CodeLimits Defined in: [mcp-code/src/lib/types.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L8) Per-execution caps. Defaults (`DEFAULT_CODE_LIMITS`) come from the Phase 7 sandbox spike (PRD Decisions #12–13). Every limit fails the execution with a typed `MantleError`; none can take down the host process. ## Properties [Section titled “Properties”](#properties) ### maxCalls [Section titled “maxCalls”](#maxcalls) > **maxCalls**: `number` Defined in: [mcp-code/src/lib/types.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L21) Bridged service calls allowed per execution. #### Default [Section titled “Default”](#default) ```ts 50 ``` *** ### maxOutputBytes [Section titled “maxOutputBytes”](#maxoutputbytes) > **maxOutputBytes**: `number` Defined in: [mcp-code/src/lib/types.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L23) Cap on the JSON-serialized result, and separately on captured log output. #### Default [Section titled “Default”](#default-1) ```ts 64 KiB ``` *** ### memoryBytes [Section titled “memoryBytes”](#memorybytes) > **memoryBytes**: `number` Defined in: [mcp-code/src/lib/types.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L12) QuickJS `setMemoryLimit` — a soft cap (it undercounts some allocations). #### Default [Section titled “Default”](#default-2) ```ts 32 MiB ``` *** ### stackBytes [Section titled “stackBytes”](#stackbytes) > **stackBytes**: `number` Defined in: [mcp-code/src/lib/types.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L19) QuickJS `setMaxStackSize`. QuickJS runs on the host thread’s native stack, so keep this under \~¼ of it (\~256 KiB on Node’s default main-thread stack). #### Default [Section titled “Default”](#default-3) ```ts 192 KiB ``` *** ### timeoutMs [Section titled “timeoutMs”](#timeoutms) > **timeoutMs**: `number` Defined in: [mcp-code/src/lib/types.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L10) Wall-clock deadline: interrupt handler for CPU loops, host timer for a bridged call that never settles. #### Default [Section titled “Default”](#default-4) ```ts 10000 ``` *** ### wasmMemoryBytes [Section titled “wasmMemoryBytes”](#wasmmemorybytes) > **wasmMemoryBytes**: `number` Defined in: [mcp-code/src/lib/types.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L14) Bounded `WebAssembly.Memory` maximum — the hard cap, enforced by the WASM engine. #### Default [Section titled “Default”](#default-5) ```ts 64 MiB ``` # CodeModeCallMetadata Defined in: [mcp-code/src/lib/code-mode.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L30) Per-call metadata code mode adds to `params.mcp` on every bridged service call, readable by any hook (and recorded by `@mantlejs/audit`). All calls from one `execute` share `executionId`. ## Properties [Section titled “Properties”](#properties) ### executionId [Section titled “executionId”](#executionid) > **executionId**: `string` Defined in: [mcp-code/src/lib/code-mode.ts:32](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L32) *** ### mode [Section titled “mode”](#mode) > **mode**: `"code"` Defined in: [mcp-code/src/lib/code-mode.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L31) *** ### script? [Section titled “script?”](#script) > `optional` **script?**: `string` Defined in: [mcp-code/src/lib/code-mode.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L36) The script as submitted — only with `auditScriptSource: true`. *** ### scriptHash [Section titled “scriptHash”](#scripthash) > **scriptHash**: `string` Defined in: [mcp-code/src/lib/code-mode.ts:34](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L34) SHA-256 (hex) of the script as submitted. # CodeModeOptions Defined in: [mcp-code/src/lib/code-mode.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L13) ## Properties [Section titled “Properties”](#properties) ### auditScriptSource? [Section titled “auditScriptSource?”](#auditscriptsource) > `optional` **auditScriptSource?**: `boolean` Defined in: [mcp-code/src/lib/code-mode.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L23) Also put the full script source in `params.mcp.script` (and so in every audit record it reaches). Off by default — scripts can embed data — and `params.mcp.scriptHash` (SHA-256) is always present to correlate a record with the script that made it. #### Default [Section titled “Default”](#default) ```ts false ``` *** ### executor? [Section titled “executor?”](#executor) > `optional` **executor?**: [`CodeExecutor`](/api/mantlejs/mcp-code/interfaces/codeexecutor/) Defined in: [mcp-code/src/lib/code-mode.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L15) The sandbox. #### Default [Section titled “Default”](#default-1) ```ts quickJsExecutor() ``` *** ### limits? [Section titled “limits?”](#limits) > `optional` **limits?**: `Partial`<[`CodeLimits`](/api/mantlejs/mcp-code/interfaces/codelimits/)> Defined in: [mcp-code/src/lib/code-mode.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L17) Override any of `DEFAULT_CODE_LIMITS`. # ConformanceBridge Defined in: [mcp-code/src/lib/conformance.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L11) The host side of the sandbox’s `mantle` API. `services` is the API’s shape (path → method names) — the executor builds `mantle[path][method](...args)` inside the sandbox from it and nothing else. `call` receives the JSON-decoded positional arguments and resolves with a JSON-serializable value, or rejects (a `MantleError`’s `toJSON()` shape reaches the script). ## Extends [Section titled “Extends”](#extends) * [`CodeBridge`](/api/mantlejs/mcp-code/interfaces/codebridge/) ## Properties [Section titled “Properties”](#properties) ### log [Section titled “log”](#log) > `readonly` **log**: [`BridgeCall`](/api/mantlejs/mcp-code/interfaces/bridgecall/)\[] Defined in: [mcp-code/src/lib/conformance.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L12) *** ### services [Section titled “services”](#services) > **services**: `Record`<`string`, `string`\[]> Defined in: [mcp-code/src/lib/types.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L42) #### Inherited from [Section titled “Inherited from”](#inherited-from) [`CodeBridge`](/api/mantlejs/mcp-code/interfaces/codebridge/).[`services`](/api/mantlejs/mcp-code/interfaces/codebridge/#services) ## Methods [Section titled “Methods”](#methods) ### call() [Section titled “call()”](#call) > **call**(`path`, `method`, `args`): `Promise`<`unknown`> Defined in: [mcp-code/src/lib/types.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L43) #### Parameters [Section titled “Parameters”](#parameters) ##### path [Section titled “path”](#path) `string` ##### method [Section titled “method”](#method) `string` ##### args [Section titled “args”](#args) `unknown`\[] #### Returns [Section titled “Returns”](#returns) `Promise`<`unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`CodeBridge`](/api/mantlejs/mcp-code/interfaces/codebridge/).[`call`](/api/mantlejs/mcp-code/interfaces/codebridge/#call) # ConformanceCase Defined in: [mcp-code/src/lib/conformance.ts:49](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L49) ## Properties [Section titled “Properties”](#properties) ### before? [Section titled “before?”](#before) > `optional` **before?**: `object` Defined in: [mcp-code/src/lib/conformance.ts:54](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L54) Run first on the same executor (its outcome is ignored) — for fresh-state and recovery cases. #### code [Section titled “code”](#code) > **code**: `string` #### limits? [Section titled “limits?”](#limits) > `optional` **limits?**: `Partial`<[`CodeLimits`](/api/mantlejs/mcp-code/interfaces/codelimits/)> *** ### check? [Section titled “check?”](#check) > `optional` **check?**: (`result`, `bridge`, `elapsedMs`) => `string` | `undefined` Defined in: [mcp-code/src/lib/conformance.ts:60](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L60) Further checks — return a failure description, or `undefined` when the case passes. #### Parameters [Section titled “Parameters”](#parameters) ##### result [Section titled “result”](#result) [`CodeExecutionResult`](/api/mantlejs/mcp-code/type-aliases/codeexecutionresult/) ##### bridge [Section titled “bridge”](#bridge) [`ConformanceBridge`](/api/mantlejs/mcp-code/interfaces/conformancebridge/) ##### elapsedMs [Section titled “elapsedMs”](#elapsedms) `number` #### Returns [Section titled “Returns”](#returns) `string` | `undefined` *** ### code [Section titled “code”](#code-1) > **code**: `string` Defined in: [mcp-code/src/lib/conformance.ts:51](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L51) *** ### concurrency? [Section titled “concurrency?”](#concurrency) > `optional` **concurrency?**: `number` Defined in: [mcp-code/src/lib/conformance.ts:56](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L56) Run this many executions concurrently; every one must meet `expect`. #### Default [Section titled “Default”](#default) ```ts 1 ``` *** ### expect [Section titled “expect”](#expect) > **expect**: { `ok`: `true`; `value?`: `unknown`; } | { `errorName`: `string` | `string`\[]; `ok`: `false`; } Defined in: [mcp-code/src/lib/conformance.ts:58](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L58) `errorName` may list several acceptable typed errors. *** ### limits? [Section titled “limits?”](#limits-1) > `optional` **limits?**: `Partial`<[`CodeLimits`](/api/mantlejs/mcp-code/interfaces/codelimits/)> Defined in: [mcp-code/src/lib/conformance.ts:52](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L52) *** ### name [Section titled “name”](#name) > **name**: `string` Defined in: [mcp-code/src/lib/conformance.ts:50](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L50) # CodeExecutionResult > **CodeExecutionResult** = `CodeExecutionOutcome` & `object` | `CodeExecutionOutcome` & `object` Defined in: [mcp-code/src/lib/types.ts:55](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L55) # API_RESOURCE_URI > `const` **API_RESOURCE_URI**: `"mantle://code/api.d.ts"` = `"mantle://code/api.d.ts"` Defined in: [mcp-code/src/lib/code-mode.ts:49](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/code-mode.ts#L49) # CODE_EXECUTOR_CONFORMANCE_CASES > `const` **CODE_EXECUTOR_CONFORMANCE_CASES**: readonly [`ConformanceCase`](/api/mantlejs/mcp-code/interfaces/conformancecase/)\[] Defined in: [mcp-code/src/lib/conformance.ts:71](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/conformance.ts#L71) Executor conformance suite (PRD spec 4) — data, not tests, so any `CodeExecutor` can run it from its own spec runner (see `quickjs-executor.spec.ts` for the reference loop). Covers the bridge contract, JSON-only values, error shapes, fresh state, concurrency, escape probes, and every limit. Mirrors how `NESTED_QUERY_CASES` is shared across query adapters. # DEFAULT_CODE_LIMITS > `const` **DEFAULT_CODE_LIMITS**: [`CodeLimits`](/api/mantlejs/mcp-code/interfaces/codelimits/) Defined in: [mcp-code/src/lib/types.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/types.ts#L26) # MIN_WASM_MEMORY_BYTES > `const` **MIN_WASM_MEMORY_BYTES**: `number` Defined in: [mcp-code/src/lib/quickjs-executor.ts:38](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp-code/src/lib/quickjs-executor.ts#L38) The QuickJS build’s initial linear memory (16 MiB) — `wasmMemoryBytes` may not go below it. # buildQuerySchema > **buildQuerySchema**(`operators`, `limits?`): `JsonObject` Defined in: [query-schema.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/query-schema.ts#L15) JSON Schema for the `query` argument of generated `find`/`get`/`remove` tools. The shape is the structured `QueryParams` form (`where`/`limit`/`skip`/`sort`/`select`) — friendlier to schema-driven callers than the REST `$`-prefixed convention; the dispatch layer translates. When the service’s repository reports its supported operators via `describe().capabilities.operators`, the per-field operator object is closed over exactly that set, so a caller is never offered an operator the adapter would reject. ## Parameters [Section titled “Parameters”](#parameters) ### operators [Section titled “operators”](#operators) `string`\[] | `undefined` ### limits? [Section titled “limits?”](#limits) #### defaultLimit [Section titled “defaultLimit”](#defaultlimit) `number` #### maxLimit [Section titled “maxLimit”](#maxlimit) `number` ## Returns [Section titled “Returns”](#returns) `JsonObject` # createServiceMethodRunner > **createServiceMethodRunner**(`app`, `descriptor`, `method`, `limits`): [`McpMethodRunner`](/api/mantlejs/mcp/type-aliases/mcpmethodrunner/) Defined in: [tools.ts:344](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/tools.ts#L344) See `McpMethodRunner`. `limits` are the effective find() guardrails (pass `McpCodeModeInput.query`). ## Parameters [Section titled “Parameters”](#parameters) ### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) ### descriptor [Section titled “descriptor”](#descriptor) [`ServiceDescriptor`](/api/mantlejs/mantle/interfaces/servicedescriptor/) ### method [Section titled “method”](#method) `string` ### limits [Section titled “limits”](#limits) `Required`<[`McpQueryOptions`](/api/mantlejs/mcp/interfaces/mcpqueryoptions/)> ## Returns [Section titled “Returns”](#returns) [`McpMethodRunner`](/api/mantlejs/mcp/type-aliases/mcpmethodrunner/) # describeServiceMethod > **describeServiceMethod**(`descriptor`, `method`, `limits`): [`McpMethodSchema`](/api/mantlejs/mcp/interfaces/mcpmethodschema/) Defined in: [tools.ts:237](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/tools.ts#L237) Listing metadata for one service method — see `McpMethodSchema`. `limits` are the effective find() guardrails, advertised on `find` (pass `McpCodeModeInput.query`). ## Parameters [Section titled “Parameters”](#parameters) ### descriptor [Section titled “descriptor”](#descriptor) [`ServiceDescriptor`](/api/mantlejs/mantle/interfaces/servicedescriptor/) ### method [Section titled “method”](#method) `string` ### limits [Section titled “limits”](#limits) `Required`<[`McpQueryOptions`](/api/mantlejs/mcp/interfaces/mcpqueryoptions/)> ## Returns [Section titled “Returns”](#returns) [`McpMethodSchema`](/api/mantlejs/mcp/interfaces/mcpmethodschema/) # mcp > **mcp**(`options`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [mcp.ts:32](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/mcp.ts#L32) Expose registered services as MCP tools. Deny-by-default: only services (and methods) named in `options.services` get tools, and every tool call routes through the service’s full hook pipeline with `params.provider = "mcp"` — authentication, validation, and events apply exactly as they do for REST callers. Configure AFTER the HTTP transport (for `transport: "http"`) and BEFORE registering services. The expose map is resolved when the server is first needed — at `listen()` for HTTP, at `startMcp()` for stdio — and an unknown path or method fails the boot. ## Parameters [Section titled “Parameters”](#parameters) ### options [Section titled “options”](#options) [`McpOptions`](/api/mantlejs/mcp/interfaces/mcpoptions/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # startMcp > **startMcp**(`app`, `options?`): `Promise`<`Server`<{ }, { }, {\[`key`: `string`]: `unknown`; }>> Defined in: [mcp.ts:123](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/mcp.ts#L123) Connect the configured MCP server to stdio. Intended usage is a separate entry point (`mcp.ts`) that builds the app without `listen()` and ends with `await startMcp(app)`. ## Parameters [Section titled “Parameters”](#parameters) ### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) ### options? [Section titled “options?”](#options) [`StartMcpOptions`](/api/mantlejs/mcp/interfaces/startmcpoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) `Promise`<`Server`<{ }, { }, {\[`key`: `string`]: `unknown`; }>> # toolName > **toolName**(`path`, `method`): `string` Defined in: [tools.ts:212](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/tools.ts#L212) “admin/blog-posts” + “find” → “admin_blog_posts_find” ## Parameters [Section titled “Parameters”](#parameters) ### path [Section titled “path”](#path) `string` ### method [Section titled “method”](#method) `string` ## Returns [Section titled “Returns”](#returns) `string` # EventRecord Defined in: [events.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/events.ts#L4) ## Properties [Section titled “Properties”](#properties) ### data [Section titled “data”](#data) > **data**: `unknown` Defined in: [events.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/events.ts#L7) *** ### event [Section titled “event”](#event) > **event**: `string` Defined in: [events.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/events.ts#L5) *** ### path [Section titled “path”](#path) > **path**: `string` Defined in: [events.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/events.ts#L6) *** ### timestamp [Section titled “timestamp”](#timestamp) > **timestamp**: `string` Defined in: [events.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/events.ts#L8) # McpCodeModeInput Defined in: [types.ts:88](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L88) Input to `McpCodeModeProvider.build()`. ## Properties [Section titled “Properties”](#properties) ### app [Section titled “app”](#app) > **app**: [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) Defined in: [types.ts:89](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L89) *** ### query [Section titled “query”](#query) > **query**: `Required`<[`McpQueryOptions`](/api/mantlejs/mcp/interfaces/mcpqueryoptions/)> Defined in: [types.ts:93](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L93) Effective find() guardrails, defaults applied. *** ### services [Section titled “services”](#services) > **services**: [`McpExposedService`](/api/mantlejs/mcp/interfaces/mcpexposedservice/)\[] Defined in: [types.ts:91](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L91) The resolved expose map — `"*"` and `true` already expanded, unknown paths/methods already rejected. # McpCodeModeProvider Defined in: [types.ts:111](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L111) Extension point for code mode (implemented by `@mantlejs/mcp-code`; this package never depends on an implementation). `build()` runs once, when the expose map is resolved — at `listen()` for HTTP, at `startMcp()` for stdio. The surface is therefore shared by every session: per-session behavior (e.g. narrowing the API shown to an agent token) belongs in the returned handlers, which receive the session’s `McpToolContext.params` on every call. ## Methods [Section titled “Methods”](#methods) ### build() [Section titled “build()”](#build) > **build**(`input`): [`McpCodeModeSurface`](/api/mantlejs/mcp/interfaces/mcpcodemodesurface/) Defined in: [types.ts:112](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L112) #### Parameters [Section titled “Parameters”](#parameters) ##### input [Section titled “input”](#input) [`McpCodeModeInput`](/api/mantlejs/mcp/interfaces/mcpcodemodeinput/) #### Returns [Section titled “Returns”](#returns) [`McpCodeModeSurface`](/api/mantlejs/mcp/interfaces/mcpcodemodesurface/) # McpCodeModeSurface Defined in: [types.ts:97](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L97) What a code-mode provider contributes to the server. ## Properties [Section titled “Properties”](#properties) ### resources? [Section titled “resources?”](#resources) > `optional` **resources?**: [`McpResourceDefinition`](/api/mantlejs/mcp/interfaces/mcpresourcedefinition/)\[] Defined in: [types.ts:101](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L101) Same rules as app-authored resources (unique URI, `mantle://events/` reserved). *** ### tools [Section titled “tools”](#tools) > **tools**: [`McpToolDefinition`](/api/mantlejs/mcp/interfaces/mcptooldefinition/)\[] Defined in: [types.ts:99](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L99) Same collision rules as app-authored tools: a name clash with any registered tool throws `BadRequest`. # McpExposedService Defined in: [types.ts:78](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L78) One expose-map entry after resolution — what a code-mode provider builds its API from. ## Properties [Section titled “Properties”](#properties) ### descriptor [Section titled “descriptor”](#descriptor) > **descriptor**: [`ServiceDescriptor`](/api/mantlejs/mantle/interfaces/servicedescriptor/) Defined in: [types.ts:84](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L84) `app.service(path).describe()` — the same metadata the generated tools are built from. *** ### methods [Section titled “methods”](#methods) > **methods**: `string`\[] Defined in: [types.ts:82](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L82) Exposed methods only (the expose map’s list, or every registered method for `true`), custom methods included. *** ### path [Section titled “path”](#path) > **path**: `string` Defined in: [types.ts:80](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L80) Service path, leading slash stripped. # McpMethodSchema Defined in: [tools.ts:38](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/tools.ts#L38) Listing metadata for one exposed service method — tool name, description (including destructive-operation notes and the find-limit clamp), and JSON Schemas. The single source the generated tools are built from; exported so a code-mode provider derives its typed API from exactly the same shapes and the two surfaces cannot drift. ## Properties [Section titled “Properties”](#properties) ### description [Section titled “description”](#description) > **description**: `string` Defined in: [tools.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/tools.ts#L40) *** ### inputSchema [Section titled “inputSchema”](#inputschema) > **inputSchema**: `JsonObject` Defined in: [tools.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/tools.ts#L41) *** ### name [Section titled “name”](#name) > **name**: `string` Defined in: [tools.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/tools.ts#L39) *** ### outputSchema? [Section titled “outputSchema?”](#outputschema) > `optional` **outputSchema?**: `JsonObject` Defined in: [tools.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/tools.ts#L42) # McpOptions Defined in: [types.ts:115](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L115) ## Properties [Section titled “Properties”](#properties) ### codeMode? [Section titled “codeMode?”](#codemode) > `optional` **codeMode?**: [`McpCodeModeProvider`](/api/mantlejs/mcp/interfaces/mcpcodemodeprovider/) Defined in: [types.ts:151](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L151) Code-mode provider, e.g. `codeMode()` from `@mantlejs/mcp-code`. *** ### events? [Section titled “events?”](#events) > `optional` **events?**: `boolean` Defined in: [types.ts:136](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L136) Expose service events (`created`/`updated`/`patched`/`removed` + custom) as MCP resources (`mantle://events/{path}`) — for services in the expose map only. #### Default [Section titled “Default”](#default) ```ts false ``` *** ### mode? [Section titled “mode?”](#mode) > `optional` **mode?**: [`McpMode`](/api/mantlejs/mcp/type-aliases/mcpmode/) Defined in: [types.ts:149](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L149) Tool surface. `"code"`/`"both"` require `codeMode` (`BadRequest` otherwise). #### Default [Section titled “Default”](#default-1) “code” when `codeMode` is set, `"tools"` otherwise *** ### path? [Section titled “path?”](#path) > `optional` **path?**: `string` Defined in: [types.ts:129](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L129) HTTP transport only: mount path. #### Default [Section titled “Default”](#default-2) ```ts "/mcp" ``` *** ### prompts? [Section titled “prompts?”](#prompts) > `optional` **prompts?**: [`McpPromptDefinition`](/api/mantlejs/mcp/interfaces/mcppromptdefinition/)\[] Defined in: [types.ts:142](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L142) App-authored prompt templates. The `prompts` capability is only declared when non-empty. *** ### query? [Section titled “query?”](#query) > `optional` **query?**: [`McpQueryOptions`](/api/mantlejs/mcp/interfaces/mcpqueryoptions/) Defined in: [types.ts:144](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L144) find() result guardrails. #### Default [Section titled “Default”](#default-3) ```ts { defaultLimit: 25, maxLimit: 100 } ``` *** ### resources? [Section titled “resources?”](#resources) > `optional` **resources?**: [`McpResourceDefinition`](/api/mantlejs/mcp/interfaces/mcpresourcedefinition/)\[] Defined in: [types.ts:140](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L140) App-authored read-only resources served alongside the event resources. *** ### serverInfo? [Section titled “serverInfo?”](#serverinfo) > `optional` **serverInfo?**: `object` Defined in: [types.ts:131](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L131) Server identity reported during MCP initialization. Defaults: name `"mantle"`, version `"0.0.0"`. #### name? [Section titled “name?”](#name) > `optional` **name?**: `string` #### version? [Section titled “version?”](#version) > `optional` **version?**: `string` *** ### services [Section titled “services”](#services) > **services**: [`CapabilityScope`](/api/mantlejs/mantle/type-aliases/capabilityscope/) | `"*"` Defined in: [types.ts:125](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L125) Expose map — required, deny-by-default. Maps a registered service path to the methods exposed as tools: an explicit method list, or `true` for every method the service registered in `app.use()`. `"*"` exposes all methods of all services registered after `mcp()` — a deliberate escape hatch for prototyping. An unknown path or method throws `BadRequest` when the server is built (at `listen()`/`startMcp()`). Same `CapabilityScope` shape `@mantlejs/auth`’s `authorizeAgent()` uses for agent-token scopes — one deny-by-default grant shape shared across the framework rather than two independently-typed lookalikes. *** ### tools? [Section titled “tools?”](#tools) > `optional` **tools?**: [`McpToolDefinition`](/api/mantlejs/mcp/interfaces/mcptooldefinition/)\[] Defined in: [types.ts:138](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L138) App-authored tools registered alongside the generated ones. *** ### transport [Section titled “transport”](#transport) > **transport**: `"stdio"` | `"http"` Defined in: [types.ts:127](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L127) `"stdio"` runs a standalone MCP server via `startMcp(app)`; `"http"` mounts the endpoint on the app’s HTTP transport. # McpPromptDefinition Defined in: [types.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L41) App-authored prompt template, surfaced by MCP clients as a user-invokable command. `get` may return a plain string (shorthand for a single user message) or the full `{ description?, messages }` shape. ## Properties [Section titled “Properties”](#properties) ### arguments? [Section titled “arguments?”](#arguments) > `optional` **arguments?**: `object`\[] Defined in: [types.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L44) #### description? [Section titled “description?”](#description) > `optional` **description?**: `string` #### name [Section titled “name”](#name) > **name**: `string` #### required? [Section titled “required?”](#required) > `optional` **required?**: `boolean` *** ### description? [Section titled “description?”](#description-1) > `optional` **description?**: `string` Defined in: [types.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L43) *** ### get [Section titled “get”](#get) > **get**: (`args`, `ctx`) => `Promise`<`string` | { `description?`: `string`; `messages`: [`McpPromptMessage`](/api/mantlejs/mcp/interfaces/mcppromptmessage/)\[]; }> Defined in: [types.ts:45](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L45) #### Parameters [Section titled “Parameters”](#parameters) ##### args [Section titled “args”](#args) `Record`<`string`, `string`> ##### ctx [Section titled “ctx”](#ctx) [`McpToolContext`](/api/mantlejs/mcp/interfaces/mcptoolcontext/) #### Returns [Section titled “Returns”](#returns) `Promise`<`string` | { `description?`: `string`; `messages`: [`McpPromptMessage`](/api/mantlejs/mcp/interfaces/mcppromptmessage/)\[]; }> *** ### name [Section titled “name”](#name-1) > **name**: `string` Defined in: [types.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L42) # McpPromptMessage Defined in: [types.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L31) One message of a prompt template result. ## Properties [Section titled “Properties”](#properties) ### content [Section titled “content”](#content) > **content**: `object` Defined in: [types.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L33) #### text [Section titled “text”](#text) > **text**: `string` #### type [Section titled “type”](#type) > **type**: `"text"` *** ### role [Section titled “role”](#role) > **role**: `"user"` | `"assistant"` Defined in: [types.ts:32](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L32) # McpQueryOptions Defined in: [types.ts:62](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L62) find() guardrails, applied to `params.query` before dispatch and advertised in generated schemas. ## Properties [Section titled “Properties”](#properties) ### defaultLimit? [Section titled “defaultLimit?”](#defaultlimit) > `optional` **defaultLimit?**: `number` Defined in: [types.ts:64](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L64) Limit applied when the caller sends none. #### Default [Section titled “Default”](#default) ```ts 25 ``` *** ### maxLimit? [Section titled “maxLimit?”](#maxlimit) > `optional` **maxLimit?**: `number` Defined in: [types.ts:66](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L66) Hard clamp on the requested limit. #### Default [Section titled “Default”](#default-1) ```ts 100 ``` # McpResourceDefinition Defined in: [types.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L20) App-authored read-only resource — reference content an agent loads as context rather than queries as a tool (schema docs, enum values, usage guides). `read` runs with the session’s params, so inner `dispatch()` calls hit the full hook pipeline. ## Properties [Section titled “Properties”](#properties) ### description? [Section titled “description?”](#description) > `optional` **description?**: `string` Defined in: [types.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L24) *** ### mimeType? [Section titled “mimeType?”](#mimetype) > `optional` **mimeType?**: `string` Defined in: [types.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L26) #### Default [Section titled “Default”](#default) ```ts "text/plain" ``` *** ### name [Section titled “name”](#name) > **name**: `string` Defined in: [types.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L23) *** ### read [Section titled “read”](#read) > **read**: (`ctx`) => `Promise`<`string`> Defined in: [types.ts:27](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L27) #### Parameters [Section titled “Parameters”](#parameters) ##### ctx [Section titled “ctx”](#ctx) [`McpToolContext`](/api/mantlejs/mcp/interfaces/mcptoolcontext/) #### Returns [Section titled “Returns”](#returns) `Promise`<`string`> *** ### uri [Section titled “uri”](#uri) > **uri**: `string` Defined in: [types.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L22) Unique resource URI. The `mantle://events/` namespace is reserved for event resources. # McpToolContext Defined in: [types.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L9) Context handed to app-authored tool handlers. Dispatch inner service calls with `ctx.app.service(path).dispatch(method, data, id, ctx.params)` — every call runs the target service’s full hook pipeline with the MCP session’s identity; custom tools get no privileged path. ## Properties [Section titled “Properties”](#properties) ### app [Section titled “app”](#app) > **app**: [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) Defined in: [types.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L10) *** ### params [Section titled “params”](#params) > **params**: [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) Defined in: [types.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L12) `provider: "mcp"`; `user`/`authenticated` resolved from the session’s bearer token (HTTP transport). # McpToolDefinition Defined in: [types.ts:52](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L52) App-authored (composite/task-level) tool, e.g. chaining two service calls. ## Properties [Section titled “Properties”](#properties) ### description [Section titled “description”](#description) > **description**: `string` Defined in: [types.ts:55](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L55) *** ### handler [Section titled “handler”](#handler) > **handler**: (`args`, `ctx`) => `Promise`<`unknown`> Defined in: [types.ts:58](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L58) #### Parameters [Section titled “Parameters”](#parameters) ##### args [Section titled “args”](#args) `unknown` ##### ctx [Section titled “ctx”](#ctx) [`McpToolContext`](/api/mantlejs/mcp/interfaces/mcptoolcontext/) #### Returns [Section titled “Returns”](#returns) `Promise`<`unknown`> *** ### inputSchema [Section titled “inputSchema”](#inputschema) > **inputSchema**: `unknown` Defined in: [types.ts:57](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L57) JSON Schema for the tool arguments (a TypeBox object schema works as-is). *** ### name [Section titled “name”](#name) > **name**: `string` Defined in: [types.ts:54](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L54) Tool name. Colliding with a generated tool name (or another custom tool) throws `BadRequest`. # StartMcpOptions Defined in: [mcp.ts:109](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/mcp.ts#L109) ## Properties [Section titled “Properties”](#properties) ### params? [Section titled “params?”](#params) > `optional` **params?**: [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) Defined in: [mcp.ts:116](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/mcp.ts#L116) Extra `ServiceParams` merged into every tool call of this stdio session — e.g. `{ headers: { authorization: "Bearer …" } }` so services’ `authenticate("jwt")` hooks pass. stdio carries no HTTP request, so without this the session is anonymous and protected services reject exactly as they would an unauthenticated REST call. # McpMethodRunner > **McpMethodRunner** = (`args`, `params`) => `Promise`<{ `note?`: `string`; `result`: `unknown`; }> Defined in: [tools.ts:338](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/tools.ts#L338) Executes one service method from tool-style arguments (`{ query }`, `{ id, query }`, `{ data }`, `{ id, data }`) through `service.dispatch()` with the given params — find-limit clamping, structured-query → REST-query translation, and id checks included. The generated tools run exactly this; exported so a code-mode bridge dispatches through the identical path. ## Parameters [Section titled “Parameters”](#parameters) ### args [Section titled “args”](#args) `Record`<`string`, `unknown`> ### params [Section titled “params”](#params) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) ## Returns [Section titled “Returns”](#returns) `Promise`<{ `note?`: `string`; `result`: `unknown`; }> # McpMode > **McpMode** = `"tools"` | `"code"` | `"both"` Defined in: [types.ts:75](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/types.ts#L75) Which tool surface the server lists. `"tools"`: one generated tool per exposed method. `"code"`: the generated per-method tools are suppressed and the `codeMode` provider’s tools/resources take their place. `"both"`: the union. App-authored `tools`, `resources`, `prompts`, and event resources are registered in every mode. # McpServerFactory > **McpServerFactory** = (`sessionParams?`) => `Server` Defined in: [mcp.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mcp/src/lib/mcp.ts#L20) Stored under `app.get("mcp:server")` — builds a server bound to one session’s params. ## Parameters [Section titled “Parameters”](#parameters) ### sessionParams? [Section titled “sessionParams?”](#sessionparams) [`ServiceParams`](/api/mantlejs/mantle/interfaces/serviceparams/) ## Returns [Section titled “Returns”](#returns) `Server` # @mantlejs/memory ## Classes [Section titled “Classes”](#classes) * [MemoryRepository](/api/mantlejs/memory/classes/memoryrepository/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [MemoryRepositoryOptions](/api/mantlejs/memory/interfaces/memoryrepositoryoptions/) ## Variables [Section titled “Variables”](#variables) * [MEMORY_OPERATORS](/api/mantlejs/memory/variables/memory_operators/) # MemoryRepository Defined in: [memory-repository.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L43) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ## Implements [Section titled “Implements”](#implements) * [`Repository`](/api/mantlejs/mantle/interfaces/repository/)<`T`, `Partial`<`T`>> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new MemoryRepository**<`T`>(`options?`): `MemoryRepository`<`T`> Defined in: [memory-repository.ts:52](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L52) #### Parameters [Section titled “Parameters”](#parameters) ##### options? [Section titled “options?”](#options) [`MemoryRepositoryOptions`](/api/mantlejs/memory/interfaces/memoryrepositoryoptions/) = `{}` #### Returns [Section titled “Returns”](#returns) `MemoryRepository`<`T`> ## Accessors [Section titled “Accessors”](#accessors) ### store [Section titled “store”](#store) #### Get Signature [Section titled “Get Signature”](#get-signature) > **get** **store**(): `ReadonlyMap`<[`Id`](/api/mantlejs/mantle/type-aliases/id/), `Record`<`string`, `unknown`>> Defined in: [memory-repository.ts:94](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L94) The raw internal store, keyed by id, holding records in storage-key shape (post-fieldMap). ##### Returns [Section titled “Returns”](#returns-1) `ReadonlyMap`<[`Id`](/api/mantlejs/mantle/type-aliases/id/), `Record`<`string`, `unknown`>> ## Methods [Section titled “Methods”](#methods) ### clear() [Section titled “clear()”](#clear) > **clear**(): `this` Defined in: [memory-repository.ts:106](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L106) #### Returns [Section titled “Returns”](#returns-2) `this` *** ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [memory-repository.ts:210](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L210) #### Parameters [Section titled “Parameters”](#parameters-1) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`number`> #### Implementation of [Section titled “Implementation of”](#implementation-of) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`count`](/api/mantlejs/mantle/interfaces/repository/#count) *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [memory-repository.ts:201](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L201) #### Parameters [Section titled “Parameters”](#parameters-2) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-1) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`deleteById`](/api/mantlejs/mantle/interfaces/repository/#deletebyid) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [memory-repository.ts:223](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L223) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-5) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`describe`](/api/mantlejs/mantle/interfaces/repository/#describe) *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [memory-repository.ts:111](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L111) #### Parameters [Section titled “Parameters”](#parameters-3) ##### params? [Section titled “params?”](#params-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findAll`](/api/mantlejs/mantle/interfaces/repository/#findall) *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [memory-repository.ts:139](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L139) #### Parameters [Section titled “Parameters”](#parameters-4) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T` | `null`> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findById`](/api/mantlejs/mantle/interfaces/repository/#findbyid) *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [memory-repository.ts:182](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L182) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`patchById`](/api/mantlejs/mantle/interfaces/repository/#patchbyid) *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [memory-repository.ts:144](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L144) #### Parameters [Section titled “Parameters”](#parameters-6) ##### data [Section titled “data”](#data-1) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`save`](/api/mantlejs/mantle/interfaces/repository/#save) *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [memory-repository.ts:162](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L162) #### Parameters [Section titled “Parameters”](#parameters-7) ##### data [Section titled “data”](#data-2) `Partial`<`T`>\[] #### Returns [Section titled “Returns”](#returns-10) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-7) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`saveAll`](/api/mantlejs/mantle/interfaces/repository/#saveall) *** ### seed() [Section titled “seed()”](#seed) > **seed**(`records`): `this` Defined in: [memory-repository.ts:98](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L98) #### Parameters [Section titled “Parameters”](#parameters-8) ##### records [Section titled “records”](#records) `T`\[] #### Returns [Section titled “Returns”](#returns-11) `this` *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [memory-repository.ts:166](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L166) #### Parameters [Section titled “Parameters”](#parameters-9) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-3) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns-12) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-8) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`updateById`](/api/mantlejs/mantle/interfaces/repository/#updatebyid) # MemoryRepositoryOptions Defined in: [memory-repository.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L21) ## Properties [Section titled “Properties”](#properties) ### autoId? [Section titled “autoId?”](#autoid) > `optional` **autoId?**: `boolean` Defined in: [memory-repository.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L25) Auto-generate string UUIDs for new records. Default: true *** ### createdAtField? [Section titled “createdAtField?”](#createdatfield) > `optional` **createdAtField?**: `string` Defined in: [memory-repository.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L29) Field written for auto-managed creation timestamps. Default: ‘createdAt’ *** ### fieldMap? [Section titled “fieldMap?”](#fieldmap) > `optional` **fieldMap?**: `Record`<`string`, `string`> Defined in: [memory-repository.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L37) Entity-field-to-storage-key overrides, for mimicking a backing store whose field names don’t match the entity (e.g. mirroring a KnexRepository’s `fieldMap` in tests without a real database). Default: {} *** ### idField? [Section titled “idField?”](#idfield) > `optional` **idField?**: `string` Defined in: [memory-repository.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L23) Primary key field name. Default: ‘id’ *** ### timestamps? [Section titled “timestamps?”](#timestamps) > `optional` **timestamps?**: `boolean` Defined in: [memory-repository.ts:27](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L27) Auto-manage createdAt / updatedAt timestamps. Default: true *** ### updatedAtField? [Section titled “updatedAtField?”](#updatedatfield) > `optional` **updatedAtField?**: `string` Defined in: [memory-repository.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L31) Field written for auto-managed update timestamps. Default: ‘updatedAt’ # MEMORY_OPERATORS > `const` **MEMORY_OPERATORS**: `ReadonlySet`<`string`> Defined in: [memory-repository.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/memory/src/lib/memory-repository.ts#L5) All query operators supported by the in-memory adapter — the full Mantle operator set (it’s the test reference). # @mantlejs/mongodb ## Classes [Section titled “Classes”](#classes) * [MongoRepository](/api/mantlejs/mongodb/classes/mongorepository/) * [MongoVectorRepository](/api/mantlejs/mongodb/classes/mongovectorrepository/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [MongoConfig](/api/mantlejs/mongodb/interfaces/mongoconfig/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [WhereClause](/api/mantlejs/mongodb/type-aliases/whereclause/) ## Variables [Section titled “Variables”](#variables) * [MONGO_OPERATORS](/api/mantlejs/mongodb/variables/mongo_operators/) ## Functions [Section titled “Functions”](#functions) * [mongodb](/api/mantlejs/mongodb/functions/mongodb/) * [toMongoFilter](/api/mantlejs/mongodb/functions/tomongofilter/) * [toMongoProjection](/api/mantlejs/mongodb/functions/tomongoprojection/) * [toMongoSort](/api/mantlejs/mongodb/functions/tomongosort/) # MongoRepository Defined in: [mongo-repository.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L21) A Mantle `Repository` implementation backed by a MongoDB collection, using the official `mongodb` driver directly — no Mongoose, consistent with the query-builder-not-ORM approach `@mantlejs/knex` takes for SQL. Mongo’s native `_id` (`ObjectId`) is exposed as `id: string` (hex) at the `Repository` boundary — callers never see a raw `ObjectId`. Dot-path where keys and `$contains` are supported natively; `$like`/`$ilike`/`$notlike` are not — use the raw `collection` escape hatch with `$regex` for pattern matching. ## Extended by [Section titled “Extended by”](#extended-by) * [`MongoVectorRepository`](/api/mantlejs/mongodb/classes/mongovectorrepository/) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> The entity shape. Must extend `Record`. ### D [Section titled “D”](#d) `D` = `Partial`<`T`> The write shape (defaults to `Partial`). ## Implements [Section titled “Implements”](#implements) * [`Repository`](/api/mantlejs/mantle/interfaces/repository/)<`T`, `D`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new MongoRepository**<`T`, `D`>(`app`): `MongoRepository`<`T`, `D`> Defined in: [mongo-repository.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L44) #### Parameters [Section titled “Parameters”](#parameters) ##### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) #### Returns [Section titled “Returns”](#returns) `MongoRepository`<`T`, `D`> ## Properties [Section titled “Properties”](#properties) ### \_session? [Section titled “\_session?”](#_session) > `protected` `optional` **\_session?**: `ClientSession` Defined in: [mongo-repository.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L42) Session bound by `withTransaction()` — every driver call passes it when set. *** ### client [Section titled “client”](#client) > `protected` `readonly` **client**: `MongoClient` Defined in: [mongo-repository.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L22) *** ### collectionName [Section titled “collectionName”](#collectionname) > `abstract` `readonly` **collectionName**: `string` Defined in: [mongo-repository.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L26) The MongoDB collection name to target. *** ### createdAtField [Section titled “createdAtField”](#createdatfield) > `readonly` **createdAtField**: `string` = `"createdAt"` Defined in: [mongo-repository.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L31) Field written for auto-managed creation timestamps. #### Default [Section titled “Default”](#default) ```ts "createdAt" ``` *** ### db [Section titled “db”](#db) > `protected` `readonly` **db**: `Db` Defined in: [mongo-repository.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L23) *** ### fieldMap [Section titled “fieldMap”](#fieldmap) > `readonly` **fieldMap**: `Record`<`string`, `string`> = `{}` Defined in: [mongo-repository.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L39) Entity-field-to-document-field overrides, for a collection whose field names don’t match the entity (e.g. a brownfield collection using snake_case). Does not apply to `id` — Mongo’s id is always the driver’s `_id`. #### Default [Section titled “Default”](#default-1) ```ts {} ``` *** ### timestamps [Section titled “timestamps”](#timestamps) > `readonly` **timestamps**: `boolean` = `true` Defined in: [mongo-repository.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L29) When true, writes maintain `createdAt`/`updatedAt` as BSON `Date` fields. #### Default [Section titled “Default”](#default-2) ```ts true ``` *** ### updatedAtField [Section titled “updatedAtField”](#updatedatfield) > `readonly` **updatedAtField**: `string` = `"updatedAt"` Defined in: [mongo-repository.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L33) Field written for auto-managed update timestamps. #### Default [Section titled “Default”](#default-3) ```ts "updatedAt" ``` ## Accessors [Section titled “Accessors”](#accessors) ### collection [Section titled “collection”](#collection) #### Get Signature [Section titled “Get Signature”](#get-signature) > **get** `protected` **collection**(): `Collection`<`Document`> Defined in: [mongo-repository.ts:50](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L50) Escape hatch — the underlying driver `Collection` for native queries (`$regex`, aggregations, …). ##### Returns [Section titled “Returns”](#returns-1) `Collection`<`Document`> ## Methods [Section titled “Methods”](#methods) ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [mongo-repository.ts:172](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L172) #### Parameters [Section titled “Parameters”](#parameters-1) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<`number`> #### Implementation of [Section titled “Implementation of”](#implementation-of) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`count`](/api/mantlejs/mantle/interfaces/repository/#count) *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [mongo-repository.ts:161](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L161) #### Parameters [Section titled “Parameters”](#parameters-2) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-1) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`deleteById`](/api/mantlejs/mantle/interfaces/repository/#deletebyid) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [mongo-repository.ts:54](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L54) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-4) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`describe`](/api/mantlejs/mantle/interfaces/repository/#describe) *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [mongo-repository.ts:66](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L66) #### Parameters [Section titled “Parameters”](#parameters-3) ##### params? [Section titled “params?”](#params-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-5) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findAll`](/api/mantlejs/mantle/interfaces/repository/#findall) *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [mongo-repository.ts:83](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L83) #### Parameters [Section titled “Parameters”](#parameters-4) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T` | `null`> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findById`](/api/mantlejs/mantle/interfaces/repository/#findbyid) *** ### fromDocument() [Section titled “fromDocument()”](#fromdocument) > `protected` **fromDocument**(`doc`): `T` Defined in: [mongo-repository.ts:238](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L238) Map a driver document to the entity shape: `_id: ObjectId` becomes `id: string`. #### Parameters [Section titled “Parameters”](#parameters-5) ##### doc [Section titled “doc”](#doc) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-7) `T` *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [mongo-repository.ts:129](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L129) #### Parameters [Section titled “Parameters”](#parameters-6) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data) `D` #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`patchById`](/api/mantlejs/mantle/interfaces/repository/#patchbyid) *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [mongo-repository.ts:92](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L92) #### Parameters [Section titled “Parameters”](#parameters-7) ##### data [Section titled “data”](#data-1) `D` #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`save`](/api/mantlejs/mantle/interfaces/repository/#save) *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [mongo-repository.ts:102](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L102) #### Parameters [Section titled “Parameters”](#parameters-8) ##### data [Section titled “data”](#data-2) `D`\[] #### Returns [Section titled “Returns”](#returns-10) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-7) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`saveAll`](/api/mantlejs/mantle/interfaces/repository/#saveall) *** ### sessionOptions() [Section titled “sessionOptions()”](#sessionoptions) > `protected` **sessionOptions**(): `object` Defined in: [mongo-repository.ts:209](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L209) #### Returns [Section titled “Returns”](#returns-11) `object` ##### session? [Section titled “session?”](#session) > `optional` **session?**: `ClientSession` *** ### toDocument() [Section titled “toDocument()”](#todocument) > `protected` **toDocument**(`data`, `op`, `now?`): `Record`<`string`, `unknown`> Defined in: [mongo-repository.ts:245](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L245) Map boundary data to a driver document: `id` becomes `_id`, timestamps applied. #### Parameters [Section titled “Parameters”](#parameters-9) ##### data [Section titled “data”](#data-3) `Record`<`string`, `unknown`> ##### op [Section titled “op”](#op) `"create"` | `"update"` ##### now? [Section titled “now?”](#now) `Date` = `...` #### Returns [Section titled “Returns”](#returns-12) `Record`<`string`, `unknown`> *** ### toEntityField() [Section titled “toEntityField()”](#toentityfield) > `protected` **toEntityField**(`field`): `string` Defined in: [mongo-repository.ts:232](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L232) Translates a document field name back to its entity field name — the inverse of `toField`. #### Parameters [Section titled “Parameters”](#parameters-10) ##### field [Section titled “field”](#field) `string` #### Returns [Section titled “Returns”](#returns-13) `string` *** ### toField() [Section titled “toField()”](#tofield) > `protected` **toField**(`field`): `string` Defined in: [mongo-repository.ts:227](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L227) Translates an entity field name to its document field name (fieldMap override, else identity). #### Parameters [Section titled “Parameters”](#parameters-11) ##### field [Section titled “field”](#field-1) `string` #### Returns [Section titled “Returns”](#returns-14) `string` *** ### toObjectId() [Section titled “toObjectId()”](#toobjectid) > `protected` **toObjectId**(`id`): `ObjectId` Defined in: [mongo-repository.ts:214](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L214) Convert a boundary `Id` to an `ObjectId`, rejecting malformed ids loudly. #### Parameters [Section titled “Parameters”](#parameters-12) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-15) `ObjectId` *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [mongo-repository.ts:113](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L113) #### Parameters [Section titled “Parameters”](#parameters-13) ##### id [Section titled “id”](#id-4) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-4) `D` #### Returns [Section titled “Returns”](#returns-16) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-8) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`updateById`](/api/mantlejs/mantle/interfaces/repository/#updatebyid) *** ### withTransaction() [Section titled “withTransaction()”](#withtransaction) > **withTransaction**<`R`>(`fn`): `Promise`<`R`> Defined in: [mongo-repository.ts:190](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L190) Run a block of repository calls inside one MongoDB transaction. Requires the deployment to be a replica set — true for every Atlas cluster including free-tier M0; standalone servers throw the driver’s native error. The callback receives a session-bound copy of this repository; calls on the original instance are NOT part of the transaction. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### R [Section titled “R”](#r) `R` #### Parameters [Section titled “Parameters”](#parameters-14) ##### fn [Section titled “fn”](#fn) (`txRepo`) => `Promise`<`R`> #### Returns [Section titled “Returns”](#returns-17) `Promise`<`R`> *** ### wrapError() [Section titled “wrapError()”](#wraperror) > `protected` **wrapError**(`err`): `Error` Defined in: [mongo-repository.ts:263](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L263) #### Parameters [Section titled “Parameters”](#parameters-15) ##### err [Section titled “err”](#err) `unknown` #### Returns [Section titled “Returns”](#returns-18) `Error` # MongoVectorRepository Defined in: [mongo-vector-repository.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-vector-repository.ts#L26) A `MongoRepository` that also implements Mantle’s `VectorRepository` via **MongoDB Atlas Vector Search** (`$vectorSearch` aggregation stage). Requirements — this is an Atlas feature, not core MongoDB: * An Atlas cluster (M0 free tier works) running MongoDB 6.0.11+/7.0.2+. * An Atlas Vector Search index named `vectorIndexName` on this collection, covering `vectorField` as `type: "vector"`. Fields referenced in a `findSimilar` where clause must be indexed as `type: "filter"` in the same index. Every `findSimilar` result carries the Atlas similarity score as `_score` — HIGHER is more similar (score ∈ \[0, 1] for cosine/dotProduct). The embedding field itself is stripped from results. Register it with `VectorRepositoryService` to expose `POST //similar` exactly like the Pinecone/Qdrant adapters. ## Extends [Section titled “Extends”](#extends) * [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/)<`T`, `D`> ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ### D [Section titled “D”](#d) `D` = `Partial`<`T`> ## Implements [Section titled “Implements”](#implements) * [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/)<`T`, `D`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new MongoVectorRepository**<`T`, `D`>(`app`): `MongoVectorRepository`<`T`, `D`> Defined in: [mongo-repository.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L44) #### Parameters [Section titled “Parameters”](#parameters) ##### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) #### Returns [Section titled “Returns”](#returns) `MongoVectorRepository`<`T`, `D`> #### Inherited from [Section titled “Inherited from”](#inherited-from) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`constructor`](/api/mantlejs/mongodb/classes/mongorepository/#constructor) ## Properties [Section titled “Properties”](#properties) ### \_session? [Section titled “\_session?”](#_session) > `protected` `optional` **\_session?**: `ClientSession` Defined in: [mongo-repository.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L42) Session bound by `withTransaction()` — every driver call passes it when set. #### Inherited from [Section titled “Inherited from”](#inherited-from-1) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`_session`](/api/mantlejs/mongodb/classes/mongorepository/#_session) *** ### candidateMultiplier [Section titled “candidateMultiplier”](#candidatemultiplier) > `readonly` **candidateMultiplier**: `number` = `10` Defined in: [mongo-vector-repository.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-vector-repository.ts#L41) ANN candidate pool multiplier: `numCandidates = topK * candidateMultiplier` (capped at Atlas’s 10000 limit). Atlas recommends 10–20× for good recall. #### Default [Section titled “Default”](#default) ```ts 10 ``` *** ### client [Section titled “client”](#client) > `protected` `readonly` **client**: `MongoClient` Defined in: [mongo-repository.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L22) #### Inherited from [Section titled “Inherited from”](#inherited-from-2) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`client`](/api/mantlejs/mongodb/classes/mongorepository/#client) *** ### collectionName [Section titled “collectionName”](#collectionname) > `abstract` `readonly` **collectionName**: `string` Defined in: [mongo-repository.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L26) The MongoDB collection name to target. #### Inherited from [Section titled “Inherited from”](#inherited-from-3) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`collectionName`](/api/mantlejs/mongodb/classes/mongorepository/#collectionname) *** ### createdAtField [Section titled “createdAtField”](#createdatfield) > `readonly` **createdAtField**: `string` = `"createdAt"` Defined in: [mongo-repository.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L31) Field written for auto-managed creation timestamps. #### Default [Section titled “Default”](#default-1) ```ts "createdAt" ``` #### Inherited from [Section titled “Inherited from”](#inherited-from-4) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`createdAtField`](/api/mantlejs/mongodb/classes/mongorepository/#createdatfield) *** ### db [Section titled “db”](#db) > `protected` `readonly` **db**: `Db` Defined in: [mongo-repository.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L23) #### Inherited from [Section titled “Inherited from”](#inherited-from-5) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`db`](/api/mantlejs/mongodb/classes/mongorepository/#db) *** ### fieldMap [Section titled “fieldMap”](#fieldmap) > `readonly` **fieldMap**: `Record`<`string`, `string`> = `{}` Defined in: [mongo-repository.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L39) Entity-field-to-document-field overrides, for a collection whose field names don’t match the entity (e.g. a brownfield collection using snake_case). Does not apply to `id` — Mongo’s id is always the driver’s `_id`. #### Default [Section titled “Default”](#default-2) ```ts {} ``` #### Inherited from [Section titled “Inherited from”](#inherited-from-6) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`fieldMap`](/api/mantlejs/mongodb/classes/mongorepository/#fieldmap) *** ### timestamps [Section titled “timestamps”](#timestamps) > `readonly` **timestamps**: `boolean` = `true` Defined in: [mongo-repository.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L29) When true, writes maintain `createdAt`/`updatedAt` as BSON `Date` fields. #### Default [Section titled “Default”](#default-3) ```ts true ``` #### Inherited from [Section titled “Inherited from”](#inherited-from-7) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`timestamps`](/api/mantlejs/mongodb/classes/mongorepository/#timestamps) *** ### updatedAtField [Section titled “updatedAtField”](#updatedatfield) > `readonly` **updatedAtField**: `string` = `"updatedAt"` Defined in: [mongo-repository.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L33) Field written for auto-managed update timestamps. #### Default [Section titled “Default”](#default-4) ```ts "updatedAt" ``` #### Inherited from [Section titled “Inherited from”](#inherited-from-8) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`updatedAtField`](/api/mantlejs/mongodb/classes/mongorepository/#updatedatfield) *** ### vectorField [Section titled “vectorField”](#vectorfield) > `readonly` **vectorField**: `string` = `"embedding"` Defined in: [mongo-vector-repository.ts:34](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-vector-repository.ts#L34) Document field storing the embedding. #### Default [Section titled “Default”](#default-5) ```ts "embedding" ``` *** ### vectorIndexName [Section titled “vectorIndexName”](#vectorindexname) > `readonly` **vectorIndexName**: `string` = `"vector_index"` Defined in: [mongo-vector-repository.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-vector-repository.ts#L31) Name of the Atlas Vector Search index on this collection. #### Default [Section titled “Default”](#default-6) ```ts "vector_index" ``` ## Accessors [Section titled “Accessors”](#accessors) ### collection [Section titled “collection”](#collection) #### Get Signature [Section titled “Get Signature”](#get-signature) > **get** `protected` **collection**(): `Collection`<`Document`> Defined in: [mongo-repository.ts:50](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L50) Escape hatch — the underlying driver `Collection` for native queries (`$regex`, aggregations, …). ##### Returns [Section titled “Returns”](#returns-1) `Collection`<`Document`> #### Inherited from [Section titled “Inherited from”](#inherited-from-9) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`collection`](/api/mantlejs/mongodb/classes/mongorepository/#collection) ## Methods [Section titled “Methods”](#methods) ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [mongo-repository.ts:172](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L172) #### Parameters [Section titled “Parameters”](#parameters-1) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<`number`> #### Implementation of [Section titled “Implementation of”](#implementation-of) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`count`](/api/mantlejs/mantle/interfaces/vectorrepository/#count) #### Inherited from [Section titled “Inherited from”](#inherited-from-10) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`count`](/api/mantlejs/mongodb/classes/mongorepository/#count) *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [mongo-repository.ts:161](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L161) #### Parameters [Section titled “Parameters”](#parameters-2) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-1) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`deleteById`](/api/mantlejs/mantle/interfaces/vectorrepository/#deletebyid) #### Inherited from [Section titled “Inherited from”](#inherited-from-11) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`deleteById`](/api/mantlejs/mongodb/classes/mongorepository/#deletebyid) *** ### deleteVector() [Section titled “deleteVector()”](#deletevector) > **deleteVector**(`id`): `Promise`<`T`> Defined in: [mongo-vector-repository.ts:102](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-vector-repository.ts#L102) Delete a vector and its associated record #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`deleteVector`](/api/mantlejs/mantle/interfaces/vectorrepository/#deletevector) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [mongo-repository.ts:54](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L54) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-5) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`describe`](/api/mantlejs/mantle/interfaces/vectorrepository/#describe) #### Inherited from [Section titled “Inherited from”](#inherited-from-12) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`describe`](/api/mantlejs/mongodb/classes/mongorepository/#describe) *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [mongo-repository.ts:66](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L66) #### Parameters [Section titled “Parameters”](#parameters-4) ##### params? [Section titled “params?”](#params-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findAll`](/api/mantlejs/mantle/interfaces/vectorrepository/#findall) #### Inherited from [Section titled “Inherited from”](#inherited-from-13) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`findAll`](/api/mantlejs/mongodb/classes/mongorepository/#findall) *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [mongo-repository.ts:83](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L83) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T` | `null`> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findById`](/api/mantlejs/mantle/interfaces/vectorrepository/#findbyid) #### Inherited from [Section titled “Inherited from”](#inherited-from-14) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`findById`](/api/mantlejs/mongodb/classes/mongorepository/#findbyid) *** ### findSimilar() [Section titled “findSimilar()”](#findsimilar) > **findSimilar**(`vector`, `topK`, `params?`): `Promise`<`T` & `object`\[]> Defined in: [mongo-vector-repository.ts:46](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-vector-repository.ts#L46) Top-K nearest neighbours via `$vectorSearch`. HIGHER `_score` is more similar. #### Parameters [Section titled “Parameters”](#parameters-6) ##### vector [Section titled “vector”](#vector) `number`\[] ##### topK [Section titled “topK”](#topk) `number` ##### params? [Section titled “params?”](#params-2) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T` & `object`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findSimilar`](/api/mantlejs/mantle/interfaces/vectorrepository/#findsimilar) *** ### fromDocument() [Section titled “fromDocument()”](#fromdocument) > `protected` **fromDocument**(`doc`): `T` Defined in: [mongo-repository.ts:238](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L238) Map a driver document to the entity shape: `_id: ObjectId` becomes `id: string`. #### Parameters [Section titled “Parameters”](#parameters-7) ##### doc [Section titled “doc”](#doc) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-9) `T` #### Inherited from [Section titled “Inherited from”](#inherited-from-15) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`fromDocument`](/api/mantlejs/mongodb/classes/mongorepository/#fromdocument) *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [mongo-repository.ts:129](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L129) #### Parameters [Section titled “Parameters”](#parameters-8) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data) `D` #### Returns [Section titled “Returns”](#returns-10) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-7) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`patchById`](/api/mantlejs/mantle/interfaces/vectorrepository/#patchbyid) #### Inherited from [Section titled “Inherited from”](#inherited-from-16) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`patchById`](/api/mantlejs/mongodb/classes/mongorepository/#patchbyid) *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [mongo-repository.ts:92](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L92) #### Parameters [Section titled “Parameters”](#parameters-9) ##### data [Section titled “data”](#data-1) `D` #### Returns [Section titled “Returns”](#returns-11) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-8) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`save`](/api/mantlejs/mantle/interfaces/vectorrepository/#save) #### Inherited from [Section titled “Inherited from”](#inherited-from-17) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`save`](/api/mantlejs/mongodb/classes/mongorepository/#save) *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [mongo-repository.ts:102](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L102) #### Parameters [Section titled “Parameters”](#parameters-10) ##### data [Section titled “data”](#data-2) `D`\[] #### Returns [Section titled “Returns”](#returns-12) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-9) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`saveAll`](/api/mantlejs/mantle/interfaces/vectorrepository/#saveall) #### Inherited from [Section titled “Inherited from”](#inherited-from-18) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`saveAll`](/api/mantlejs/mongodb/classes/mongorepository/#saveall) *** ### sessionOptions() [Section titled “sessionOptions()”](#sessionoptions) > `protected` **sessionOptions**(): `object` Defined in: [mongo-repository.ts:209](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L209) #### Returns [Section titled “Returns”](#returns-13) `object` ##### session? [Section titled “session?”](#session) > `optional` **session?**: `ClientSession` #### Inherited from [Section titled “Inherited from”](#inherited-from-19) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`sessionOptions`](/api/mantlejs/mongodb/classes/mongorepository/#sessionoptions) *** ### toDocument() [Section titled “toDocument()”](#todocument) > `protected` **toDocument**(`data`, `op`, `now?`): `Record`<`string`, `unknown`> Defined in: [mongo-repository.ts:245](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L245) Map boundary data to a driver document: `id` becomes `_id`, timestamps applied. #### Parameters [Section titled “Parameters”](#parameters-11) ##### data [Section titled “data”](#data-3) `Record`<`string`, `unknown`> ##### op [Section titled “op”](#op) `"create"` | `"update"` ##### now? [Section titled “now?”](#now) `Date` = `...` #### Returns [Section titled “Returns”](#returns-14) `Record`<`string`, `unknown`> #### Inherited from [Section titled “Inherited from”](#inherited-from-20) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`toDocument`](/api/mantlejs/mongodb/classes/mongorepository/#todocument) *** ### toEntityField() [Section titled “toEntityField()”](#toentityfield) > `protected` **toEntityField**(`field`): `string` Defined in: [mongo-repository.ts:232](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L232) Translates a document field name back to its entity field name — the inverse of `toField`. #### Parameters [Section titled “Parameters”](#parameters-12) ##### field [Section titled “field”](#field) `string` #### Returns [Section titled “Returns”](#returns-15) `string` #### Inherited from [Section titled “Inherited from”](#inherited-from-21) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`toEntityField`](/api/mantlejs/mongodb/classes/mongorepository/#toentityfield) *** ### toField() [Section titled “toField()”](#tofield) > `protected` **toField**(`field`): `string` Defined in: [mongo-repository.ts:227](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L227) Translates an entity field name to its document field name (fieldMap override, else identity). #### Parameters [Section titled “Parameters”](#parameters-13) ##### field [Section titled “field”](#field-1) `string` #### Returns [Section titled “Returns”](#returns-16) `string` #### Inherited from [Section titled “Inherited from”](#inherited-from-22) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`toField`](/api/mantlejs/mongodb/classes/mongorepository/#tofield) *** ### toObjectId() [Section titled “toObjectId()”](#toobjectid) > `protected` **toObjectId**(`id`): `ObjectId` Defined in: [mongo-repository.ts:214](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L214) Convert a boundary `Id` to an `ObjectId`, rejecting malformed ids loudly. #### Parameters [Section titled “Parameters”](#parameters-14) ##### id [Section titled “id”](#id-4) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-17) `ObjectId` #### Inherited from [Section titled “Inherited from”](#inherited-from-23) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`toObjectId`](/api/mantlejs/mongodb/classes/mongorepository/#toobjectid) *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [mongo-repository.ts:113](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L113) #### Parameters [Section titled “Parameters”](#parameters-15) ##### id [Section titled “id”](#id-5) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-4) `D` #### Returns [Section titled “Returns”](#returns-18) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-10) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`updateById`](/api/mantlejs/mantle/interfaces/vectorrepository/#updatebyid) #### Inherited from [Section titled “Inherited from”](#inherited-from-24) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`updateById`](/api/mantlejs/mongodb/classes/mongorepository/#updatebyid) *** ### upsertVector() [Section titled “upsertVector()”](#upsertvector) > **upsertVector**(`id`, `vector`, `data`): `Promise`<`T`> Defined in: [mongo-vector-repository.ts:73](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-vector-repository.ts#L73) Upsert a record with its embedding vector #### Parameters [Section titled “Parameters”](#parameters-16) ##### id [Section titled “id”](#id-6) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### vector [Section titled “vector”](#vector-1) `number`\[] ##### data [Section titled “data”](#data-5) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns-19) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-11) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`upsertVector`](/api/mantlejs/mantle/interfaces/vectorrepository/#upsertvector) *** ### withTransaction() [Section titled “withTransaction()”](#withtransaction) > **withTransaction**<`R`>(`fn`): `Promise`<`R`> Defined in: [mongo-repository.ts:190](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L190) Run a block of repository calls inside one MongoDB transaction. Requires the deployment to be a replica set — true for every Atlas cluster including free-tier M0; standalone servers throw the driver’s native error. The callback receives a session-bound copy of this repository; calls on the original instance are NOT part of the transaction. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### R [Section titled “R”](#r) `R` #### Parameters [Section titled “Parameters”](#parameters-17) ##### fn [Section titled “fn”](#fn) (`txRepo`) => `Promise`<`R`> #### Returns [Section titled “Returns”](#returns-20) `Promise`<`R`> #### Inherited from [Section titled “Inherited from”](#inherited-from-25) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`withTransaction`](/api/mantlejs/mongodb/classes/mongorepository/#withtransaction) *** ### wrapError() [Section titled “wrapError()”](#wraperror) > `protected` **wrapError**(`err`): `Error` Defined in: [mongo-repository.ts:263](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-repository.ts#L263) #### Parameters [Section titled “Parameters”](#parameters-18) ##### err [Section titled “err”](#err) `unknown` #### Returns [Section titled “Returns”](#returns-21) `Error` #### Inherited from [Section titled “Inherited from”](#inherited-from-26) [`MongoRepository`](/api/mantlejs/mongodb/classes/mongorepository/).[`wrapError`](/api/mantlejs/mongodb/classes/mongorepository/#wraperror) # mongodb > **mongodb**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [mongodb.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongodb.ts#L26) Mantle plugin that opens one `MongoClient` and stores it on the application — the same “connection lives on `app`, repositories pull it in their constructor” pattern used by `@mantlejs/knex`. The client connects lazily on first operation. Stores `app.set("mongoClient", client)` and `app.set("mongoDb", client.db(dbName))`. `app.teardown()` closes the client. ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) [`MongoConfig`](/api/mantlejs/mongodb/interfaces/mongoconfig/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) ## Example [Section titled “Example”](#example) ```ts const app = mantle().configure(mongodb({ uri: process.env.MONGODB_URI!, dbName: "app" })); ``` # toMongoFilter > **toMongoFilter**(`where`, `toField?`): `Record`<`string`, `unknown`> Defined in: [mongo-filter.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-filter.ts#L40) Translate a Mantle `QueryParams.where` clause into a MongoDB filter document. Most operators (`$lt`/`$lte`/`$gt`/`$gte`/`$ne`/`$in`/`$nin`/`$or`/`$and`) are native MongoDB syntax and pass through unchanged; dot-path keys (`"metadata.owner.name"`) are native too. The two Mantle-specific translations: * `id` keys become `_id`, with 24-hex string values converted to `ObjectId` — the repository boundary only ever deals in string ids. * `$contains` (jsonb `@>` semantics) becomes: scalar operand → field equality (MongoDB equality matches array elements natively), array operand → `$all`, object operand → recursively expanded dot-path conditions. ## Parameters [Section titled “Parameters”](#parameters) ### where [Section titled “where”](#where) [`WhereClause`](/api/mantlejs/mongodb/type-aliases/whereclause/) ### toField? [Section titled “toField?”](#tofield) (`field`) => `string` ## Returns [Section titled “Returns”](#returns) `Record`<`string`, `unknown`> # toMongoProjection > **toMongoProjection**(`select`, `toField?`): `Record`<`string`, `1`> Defined in: [mongo-filter.ts:158](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-filter.ts#L158) Translate a Mantle `QueryParams.select` list to a MongoDB projection (`_id` is always included). ## Parameters [Section titled “Parameters”](#parameters) ### select [Section titled “select”](#select) `string`\[] ### toField? [Section titled “toField?”](#tofield) (`field`) => `string` ## Returns [Section titled “Returns”](#returns) `Record`<`string`, `1`> # toMongoSort > **toMongoSort**(`sort`, `toField?`): `Record`<`string`, `1` | `-1`> Defined in: [mongo-filter.ts:145](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-filter.ts#L145) Translate a Mantle `QueryParams.sort` map to a MongoDB sort document (`id` → `_id`). ## Parameters [Section titled “Parameters”](#parameters) ### sort [Section titled “sort”](#sort) `Record`<`string`, `"asc"` | `"desc"`> ### toField? [Section titled “toField?”](#tofield) (`field`) => `string` ## Returns [Section titled “Returns”](#returns) `Record`<`string`, `1` | `-1`> # MongoConfig Defined in: [mongodb.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongodb.ts#L4) ## Properties [Section titled “Properties”](#properties) ### clientOptions? [Section titled “clientOptions?”](#clientoptions) > `optional` **clientOptions?**: `MongoClientOptions` Defined in: [mongodb.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongodb.ts#L10) Full driver client options — TLS, pool sizing, etc. Passed through untouched. *** ### dbName [Section titled “dbName”](#dbname) > **dbName**: `string` Defined in: [mongodb.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongodb.ts#L8) Database name to operate on. *** ### uri [Section titled “uri”](#uri) > **uri**: `string` Defined in: [mongodb.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongodb.ts#L6) Atlas or self-hosted connection string, e.g. “mongodb+srv://…” # WhereClause > **WhereClause** = `Record`<`string`, `unknown`> Defined in: [mongo-filter.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-filter.ts#L25) # MONGO_OPERATORS > `const` **MONGO_OPERATORS**: readonly \[`"$lt"`, `"$lte"`, `"$gt"`, `"$gte"`, `"$ne"`, `"$in"`, `"$nin"`, `"$or"`, `"$and"`, `"$contains"`] Defined in: [mongo-filter.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/mongodb/src/lib/mongo-filter.ts#L10) Exactly the `$`-operators `toMongoFilter` accepts. Everything else — including the `$like`/`$ilike`/`$notlike` family, which this adapter does not support — is rejected with `BadRequest` via `assertOperators`; use the raw `collection` escape hatch with `$regex` for pattern matching instead. # @mantlejs/neo4j ## Classes [Section titled “Classes”](#classes) * [Neo4jRepository](/api/mantlejs/neo4j/classes/neo4jrepository/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [Neo4jOptions](/api/mantlejs/neo4j/interfaces/neo4joptions/) * [WhereResult](/api/mantlejs/neo4j/interfaces/whereresult/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [WhereClause](/api/mantlejs/neo4j/type-aliases/whereclause/) ## Variables [Section titled “Variables”](#variables) * [NEO4J_OPERATORS](/api/mantlejs/neo4j/variables/neo4j_operators/) ## Functions [Section titled “Functions”](#functions) * [neo4j](/api/mantlejs/neo4j/functions/neo4j/) * [toNeo4jWhere](/api/mantlejs/neo4j/functions/toneo4jwhere/) # Neo4jRepository Defined in: [neo4j-repository.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L16) A Mantle `GraphRepository` implementation backed by Neo4j. Subclasses must declare `readonly label: string` — the Neo4j node label. Each node stores a UUID `id` property plus all entity fields as node properties. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> The entity shape. Must extend `Record`. ## Implements [Section titled “Implements”](#implements) * [`GraphRepository`](/api/mantlejs/mantle/interfaces/graphrepository/)<`T`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new Neo4jRepository**<`T`>(`app`): `Neo4jRepository`<`T`> Defined in: [neo4j-repository.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L39) #### Parameters [Section titled “Parameters”](#parameters) ##### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) #### Returns [Section titled “Returns”](#returns) `Neo4jRepository`<`T`> ## Properties [Section titled “Properties”](#properties) ### createdAtField [Section titled “createdAtField”](#createdatfield) > `readonly` **createdAtField**: `string` = `"createdAt"` Defined in: [neo4j-repository.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L30) Property written for auto-managed creation timestamps. #### Default [Section titled “Default”](#default) ```ts "createdAt" ``` *** ### database [Section titled “database”](#database) > `protected` `readonly` **database**: `string` Defined in: [neo4j-repository.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L18) *** ### driver [Section titled “driver”](#driver) > `protected` `readonly` **driver**: `Driver` Defined in: [neo4j-repository.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L17) *** ### fieldMap [Section titled “fieldMap”](#fieldmap) > `readonly` **fieldMap**: `Record`<`string`, `string`> = `{}` Defined in: [neo4j-repository.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L37) Entity-field-to-node-property overrides, for a label whose property names don’t match the entity (e.g. a brownfield graph using snake_case). #### Default [Section titled “Default”](#default-1) ```ts {} ``` *** ### idField [Section titled “idField”](#idfield) > `readonly` **idField**: `string` = `"id"` Defined in: [neo4j-repository.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L24) The property used as the node identifier. #### Default [Section titled “Default”](#default-2) ```ts "id" ``` *** ### label [Section titled “label”](#label) > `abstract` `readonly` **label**: `string` Defined in: [neo4j-repository.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L21) The Neo4j node label. Override in subclass. *** ### timestamps [Section titled “timestamps”](#timestamps) > `readonly` **timestamps**: `boolean` = `true` Defined in: [neo4j-repository.ts:27](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L27) When true, auto-write `createdAt` / `updatedAt` ISO-8601 timestamps. #### Default [Section titled “Default”](#default-3) ```ts true ``` *** ### updatedAtField [Section titled “updatedAtField”](#updatedatfield) > `readonly` **updatedAtField**: `string` = `"updatedAt"` Defined in: [neo4j-repository.ts:32](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L32) Property written for auto-managed update timestamps. #### Default [Section titled “Default”](#default-4) ```ts "updatedAt" ``` ## Methods [Section titled “Methods”](#methods) ### buildProps() [Section titled “buildProps()”](#buildprops) > `protected` **buildProps**(`data`): `Record`<`string`, `unknown`> Defined in: [neo4j-repository.ts:94](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L94) #### Parameters [Section titled “Parameters”](#parameters-1) ##### data [Section titled “data”](#data) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `unknown`> *** ### createNode() [Section titled “createNode()”](#createnode) > **createNode**(`data`): `Promise`<`T`> Defined in: [neo4j-repository.ts:102](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L102) Create a node #### Parameters [Section titled “Parameters”](#parameters-2) ##### data [Section titled “data”](#data-1) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns-2) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of) [`GraphRepository`](/api/mantlejs/mantle/interfaces/graphrepository/).[`createNode`](/api/mantlejs/mantle/interfaces/graphrepository/#createnode) *** ### createRelationship() [Section titled “createRelationship()”](#createrelationship) > **createRelationship**(`fromId`, `toId`, `type`, `properties?`): `Promise`<`void`> Defined in: [neo4j-repository.ts:182](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L182) Create a directed relationship between two nodes #### Parameters [Section titled “Parameters”](#parameters-3) ##### fromId [Section titled “fromId”](#fromid) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### toId [Section titled “toId”](#toid) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### type [Section titled “type”](#type) `string` ##### properties? [Section titled “properties?”](#properties-1) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-3) `Promise`<`void`> #### Implementation of [Section titled “Implementation of”](#implementation-of-1) [`GraphRepository`](/api/mantlejs/mantle/interfaces/graphrepository/).[`createRelationship`](/api/mantlejs/mantle/interfaces/graphrepository/#createrelationship) *** ### deleteNode() [Section titled “deleteNode()”](#deletenode) > **deleteNode**(`id`): `Promise`<`T`> Defined in: [neo4j-repository.ts:212](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L212) Delete a node and all its relationships #### Parameters [Section titled “Parameters”](#parameters-4) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`GraphRepository`](/api/mantlejs/mantle/interfaces/graphrepository/).[`deleteNode`](/api/mantlejs/mantle/interfaces/graphrepository/#deletenode) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [neo4j-repository.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L44) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-5) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`GraphRepository`](/api/mantlejs/mantle/interfaces/graphrepository/).[`describe`](/api/mantlejs/mantle/interfaces/graphrepository/#describe) *** ### findNodeById() [Section titled “findNodeById()”](#findnodebyid) > **findNodeById**(`id`): `Promise`<`T` | `null`> Defined in: [neo4j-repository.ts:124](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L124) Find a node by ID #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T` | `null`> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`GraphRepository`](/api/mantlejs/mantle/interfaces/graphrepository/).[`findNodeById`](/api/mantlejs/mantle/interfaces/graphrepository/#findnodebyid) *** ### findNodes() [Section titled “findNodes()”](#findnodes) > **findNodes**(`params?`): `Promise`<`T`\[]> Defined in: [neo4j-repository.ts:139](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L139) Find nodes matching properties #### Parameters [Section titled “Parameters”](#parameters-6) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`GraphRepository`](/api/mantlejs/mantle/interfaces/graphrepository/).[`findNodes`](/api/mantlejs/mantle/interfaces/graphrepository/#findnodes) *** ### openSession() [Section titled “openSession()”](#opensession) > `protected` **openSession**(): `Session` Defined in: [neo4j-repository.ts:59](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L59) #### Returns [Section titled “Returns”](#returns-8) `Session` *** ### raw() [Section titled “raw()”](#raw) > **raw**<`R`>(`query`, `params?`): `Promise`<`R`\[]> Defined in: [neo4j-repository.ts:228](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L228) Execute a raw query in the adapter’s native graph language (Cypher for Neo4j, AQL for ArangoDB, …). #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### R [Section titled “R”](#r) `R` = `T` #### Parameters [Section titled “Parameters”](#parameters-7) ##### query [Section titled “query”](#query) `string` ##### params? [Section titled “params?”](#params-1) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-9) `Promise`<`R`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`GraphRepository`](/api/mantlejs/mantle/interfaces/graphrepository/).[`raw`](/api/mantlejs/mantle/interfaces/graphrepository/#raw) *** ### recordToNode() [Section titled “recordToNode()”](#recordtonode) > `protected` **recordToNode**(`record`): `T` Defined in: [neo4j-repository.ts:74](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L74) #### Parameters [Section titled “Parameters”](#parameters-8) ##### record [Section titled “record”](#record) `Record`<`RecordShape`<`PropertyKey`, `any`>, `PropertyKey`, `RecordShape`<`PropertyKey`, `number`>> #### Returns [Section titled “Returns”](#returns-10) `T` *** ### run() [Section titled “run()”](#run) > `protected` **run**<`R`>(`fn`): `Promise`<`R`> Defined in: [neo4j-repository.ts:63](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L63) #### Type Parameters [Section titled “Type Parameters”](#type-parameters-2) ##### R [Section titled “R”](#r-1) `R` #### Parameters [Section titled “Parameters”](#parameters-9) ##### fn [Section titled “fn”](#fn) (`session`) => `Promise`<`R`> #### Returns [Section titled “Returns”](#returns-11) `Promise`<`R`> *** ### toEntity() [Section titled “toEntity()”](#toentity) > `protected` **toEntity**(`props`): `T` Defined in: [neo4j-repository.ts:90](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L90) #### Parameters [Section titled “Parameters”](#parameters-10) ##### props [Section titled “props”](#props) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-12) `T` *** ### toEntityField() [Section titled “toEntityField()”](#toentityfield) > `protected` **toEntityField**(`field`): `string` Defined in: [neo4j-repository.ts:85](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L85) Translates a node property name back to its entity field name — the inverse of `toField`. #### Parameters [Section titled “Parameters”](#parameters-11) ##### field [Section titled “field”](#field) `string` #### Returns [Section titled “Returns”](#returns-13) `string` *** ### toField() [Section titled “toField()”](#tofield) > `protected` **toField**(`field`): `string` Defined in: [neo4j-repository.ts:80](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L80) Translates an entity field name to its node property name (fieldMap override, else identity). #### Parameters [Section titled “Parameters”](#parameters-12) ##### field [Section titled “field”](#field-1) `string` #### Returns [Section titled “Returns”](#returns-14) `string` *** ### traverse() [Section titled “traverse()”](#traverse) > **traverse**(`startId`, `relation`, `depth?`): `Promise`<`T`\[]> Defined in: [neo4j-repository.ts:198](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L198) Traverse relationships from a starting node #### Parameters [Section titled “Parameters”](#parameters-13) ##### startId [Section titled “startId”](#startid) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### relation [Section titled “relation”](#relation) `string` ##### depth? [Section titled “depth?”](#depth) `number` = `1` #### Returns [Section titled “Returns”](#returns-15) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-7) [`GraphRepository`](/api/mantlejs/mantle/interfaces/graphrepository/).[`traverse`](/api/mantlejs/mantle/interfaces/graphrepository/#traverse) *** ### withTransaction() [Section titled “withTransaction()”](#withtransaction) > **withTransaction**<`R`>(`fn`): `Promise`<`R`> Defined in: [neo4j-repository.ts:252](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L252) #### Type Parameters [Section titled “Type Parameters”](#type-parameters-3) ##### R [Section titled “R”](#r-2) `R` #### Parameters [Section titled “Parameters”](#parameters-14) ##### fn [Section titled “fn”](#fn-1) (`repo`) => `Promise`<`R`> #### Returns [Section titled “Returns”](#returns-16) `Promise`<`R`> *** ### wrapError() [Section titled “wrapError()”](#wraperror) > `protected` **wrapError**(`err`): `Error` Defined in: [neo4j-repository.ts:273](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-repository.ts#L273) #### Parameters [Section titled “Parameters”](#parameters-15) ##### err [Section titled “err”](#err) `unknown` #### Returns [Section titled “Returns”](#returns-17) `Error` # neo4j > **neo4j**(`options`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [neo4j.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j.ts#L24) Mantle plugin that opens a Neo4j driver connection and stores it on the application. ## Parameters [Section titled “Parameters”](#parameters) ### options [Section titled “options”](#options) [`Neo4jOptions`](/api/mantlejs/neo4j/interfaces/neo4joptions/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) ## Example [Section titled “Example”](#example) ```ts const app = mantle().configure( neo4j({ uri: process.env.NEO4J_URI, auth: { username: 'neo4j', password: process.env.NEO4J_PASSWORD } }) ); ``` # toNeo4jWhere > **toNeo4jWhere**(`where`, `alias?`, `toField?`): [`WhereResult`](/api/mantlejs/neo4j/interfaces/whereresult/) Defined in: [neo4j-where.ts:66](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-where.ts#L66) Convert a Mantle `QueryParams.where` clause to a Cypher WHERE expression and parameter map. The alias `n` is used for the node variable. All parameter keys are prefixed with `_w_` plus a counter to avoid collisions. Mapping rules: { field: value } → n.field = $p { field: null } → n.field IS NULL { field: \[a, b] } → n.field IN $p { field: { $lt } } → n.field < $p { field: { $lte } } → n.field <= $p { field: { $gt } } → n.field > $p { field: { $gte } } → n.field >= $p { field: { $ne: v } } → n.field <> $p { field: { $ne: null } } → n.field IS NOT NULL { field: { $in: \[…] } } → n.field IN $p { field: { $nin: \[…] } }→ NOT n.field IN $p { field: { $like: ‘x%’ } }→ n.field STARTS WITH / ENDS WITH / CONTAINS $p (heuristic) { field: { $ilike } } → toLower(n.field) CONTAINS toLower($p) { field: { $notlike } } → NOT (n.field CONTAINS $p) { $or: \[…] } → (a OR b OR …) { $and: \[…] } → (a AND b AND …) ## Parameters [Section titled “Parameters”](#parameters) ### where [Section titled “where”](#where) [`WhereClause`](/api/mantlejs/neo4j/type-aliases/whereclause/) ### alias? [Section titled “alias?”](#alias) `string` = `"n"` ### toField? [Section titled “toField?”](#tofield) (`field`) => `string` ## Returns [Section titled “Returns”](#returns) [`WhereResult`](/api/mantlejs/neo4j/interfaces/whereresult/) # Neo4jOptions Defined in: [neo4j.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j.ts#L5) ## Properties [Section titled “Properties”](#properties) ### auth [Section titled “auth”](#auth) > **auth**: `object` Defined in: [neo4j.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j.ts#L9) Authentication credentials #### password [Section titled “password”](#password) > **password**: `string` #### username [Section titled “username”](#username) > **username**: `string` *** ### database? [Section titled “database?”](#database) > `optional` **database?**: `string` Defined in: [neo4j.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j.ts#L11) Neo4j database name. Default: ‘neo4j’ *** ### uri? [Section titled “uri?”](#uri) > `optional` **uri?**: `string` Defined in: [neo4j.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j.ts#L7) Neo4j Bolt URI. Default: ‘bolt://localhost:7687’ # WhereResult Defined in: [neo4j-where.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-where.ts#L36) ## Properties [Section titled “Properties”](#properties) ### clause [Section titled “clause”](#clause) > **clause**: `string` Defined in: [neo4j-where.ts:38](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-where.ts#L38) The Cypher WHERE expression, without the `WHERE` keyword. *** ### params [Section titled “params”](#params) > **params**: `Record`<`string`, `unknown`> Defined in: [neo4j-where.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-where.ts#L39) # WhereClause > **WhereClause** = `Record`<`string`, `WhereValue`> Defined in: [neo4j-where.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-where.ts#L5) # NEO4J_OPERATORS > `const` **NEO4J_OPERATORS**: `ReadonlySet`<`string`> Defined in: [neo4j-where.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/neo4j/src/lib/neo4j-where.ts#L8) All query operators supported by the Neo4j adapter. # @mantlejs/openapi ## Interfaces [Section titled “Interfaces”](#interfaces) * [OpenApiInfo](/api/mantlejs/openapi/interfaces/openapiinfo/) * [OpenApiOptions](/api/mantlejs/openapi/interfaces/openapioptions/) ## Functions [Section titled “Functions”](#functions) * [buildOpenApiDocument](/api/mantlejs/openapi/functions/buildopenapidocument/) * [openapi](/api/mantlejs/openapi/functions/openapi/) # buildOpenApiDocument > **buildOpenApiDocument**(`descriptors`, `info?`): `JsonObject` Defined in: [document.ts:49](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/openapi/src/lib/document.ts#L49) Assemble an OpenAPI 3.1 document from the descriptors returned by `ServiceHandle.describe()`. Services without a schema get a generic `object` schema — a missing schema never skips a service or errors. ## Parameters [Section titled “Parameters”](#parameters) ### descriptors [Section titled “descriptors”](#descriptors) [`ServiceDescriptor`](/api/mantlejs/mantle/interfaces/servicedescriptor/)\[] ### info? [Section titled “info?”](#info) [`OpenApiInfo`](/api/mantlejs/openapi/interfaces/openapiinfo/) = `{}` ## Returns [Section titled “Returns”](#returns) `JsonObject` # openapi > **openapi**(`options?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [openapi.ts:32](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/openapi/src/lib/openapi.ts#L32) OpenAPI 3.1 generation plugin. Walks every service registered after it via `ServiceHandle.describe()` and serves the assembled document at `specPath`. Configure it AFTER the HTTP transport (it mounts routes through the transport-neutral `"http:router"`) and BEFORE registering services: ```ts const app = mantle() .configure(express()) .configure(openapi({ docsPath: "/docs" })); app.use("users", new UserService(), { schema: userSchema }); ``` The document is rebuilt per request, so hooks registered later (e.g. `authenticate("jwt")` marking a service as requiring `bearerAuth`) are picked up. ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`OpenApiOptions`](/api/mantlejs/openapi/interfaces/openapioptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # OpenApiInfo Defined in: [document.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/openapi/src/lib/document.ts#L3) ## Properties [Section titled “Properties”](#properties) ### description? [Section titled “description?”](#description) > `optional` **description?**: `string` Defined in: [document.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/openapi/src/lib/document.ts#L8) *** ### title? [Section titled “title?”](#title) > `optional` **title?**: `string` Defined in: [document.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/openapi/src/lib/document.ts#L5) Document title. #### Default [Section titled “Default”](#default) ```ts "Mantle API" ``` *** ### version? [Section titled “version?”](#version) > `optional` **version?**: `string` Defined in: [document.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/openapi/src/lib/document.ts#L7) API version string. #### Default [Section titled “Default”](#default-1) ```ts "0.0.0" ``` # OpenApiOptions Defined in: [openapi.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/openapi/src/lib/openapi.ts#L6) ## Properties [Section titled “Properties”](#properties) ### docsPath? [Section titled “docsPath?”](#docspath) > `optional` **docsPath?**: `string` Defined in: [openapi.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/openapi/src/lib/openapi.ts#L10) When set, serves a Swagger UI page at this route. Off by default. *** ### info? [Section titled “info?”](#info) > `optional` **info?**: [`OpenApiInfo`](/api/mantlejs/openapi/interfaces/openapiinfo/) Defined in: [openapi.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/openapi/src/lib/openapi.ts#L12) Document metadata (`info` object). *** ### specPath? [Section titled “specPath?”](#specpath) > `optional` **specPath?**: `string` Defined in: [openapi.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/openapi/src/lib/openapi.ts#L8) Route the OpenAPI 3.1 JSON document is served from. #### Default [Section titled “Default”](#default) ```ts "/openapi.json" ``` # @mantlejs/pinecone ## Classes [Section titled “Classes”](#classes) * [PineconeRepository](/api/mantlejs/pinecone/classes/pineconerepository/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [PineconeConfig](/api/mantlejs/pinecone/type-aliases/pineconeconfig/) * [WhereClause](/api/mantlejs/pinecone/type-aliases/whereclause/) ## Variables [Section titled “Variables”](#variables) * [PINECONE_OPERATORS](/api/mantlejs/pinecone/variables/pinecone_operators/) ## Functions [Section titled “Functions”](#functions) * [pinecone](/api/mantlejs/pinecone/functions/pinecone/) * [toPineconeFilter](/api/mantlejs/pinecone/functions/topineconefilter/) # PineconeRepository Defined in: [pinecone-repository.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L22) A Mantle `VectorRepository` implementation backed by a Pinecone vector index. Records are stored as Pinecone vectors. The `idField` value is the Pinecone record ID; all other fields are stored in the vector metadata. Call `upsertVector` to attach a real embedding — `save` / `saveAll` insert a zero vector placeholder that is later replaced. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> The entity shape. Must extend `Record`. ### D [Section titled “D”](#d) `D` = `Partial`<`T`> The write shape (defaults to `Partial`). ## Implements [Section titled “Implements”](#implements) * [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/)<`T`, `D`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new PineconeRepository**<`T`, `D`>(`app`): `PineconeRepository`<`T`, `D`> Defined in: [pinecone-repository.ts:51](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L51) #### Parameters [Section titled “Parameters”](#parameters) ##### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) #### Returns [Section titled “Returns”](#returns) `PineconeRepository`<`T`, `D`> ## Properties [Section titled “Properties”](#properties) ### client [Section titled “client”](#client) > `protected` `readonly` **client**: `Pinecone` Defined in: [pinecone-repository.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L25) *** ### createdAtField [Section titled “createdAtField”](#createdatfield) > `readonly` **createdAtField**: `string` = `"createdAt"` Defined in: [pinecone-repository.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L39) Metadata key written for auto-managed creation timestamps. #### Default [Section titled “Default”](#default) ```ts "createdAt" ``` *** ### fieldMap [Section titled “fieldMap”](#fieldmap) > `readonly` **fieldMap**: `Record`<`string`, `string`> = `{}` Defined in: [pinecone-repository.ts:47](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L47) Entity-field-to-metadata-key overrides, for an index whose metadata keys don’t match the entity (e.g. a brownfield index using snake_case). Does not apply to `idField` — the record id is never stored in metadata. #### Default [Section titled “Default”](#default-1) ```ts {} ``` *** ### idField [Section titled “idField”](#idfield) > `readonly` **idField**: `string` = `"id"` Defined in: [pinecone-repository.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L35) The metadata key that holds the record id. #### Default [Section titled “Default”](#default-2) ```ts "id" ``` *** ### indexName [Section titled “indexName”](#indexname) > `abstract` `readonly` **indexName**: `string` Defined in: [pinecone-repository.ts:28](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L28) The Pinecone index name to target. *** ### namespace [Section titled “namespace”](#namespace) > `abstract` `readonly` **namespace**: `string` Defined in: [pinecone-repository.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L30) The namespace within the index. *** ### timestamps [Section titled “timestamps”](#timestamps) > `readonly` **timestamps**: `boolean` = `true` Defined in: [pinecone-repository.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L37) When true, `save` / `saveAll` / `updateById` / `patchById` write ISO-8601 timestamps. #### Default [Section titled “Default”](#default-3) ```ts true ``` *** ### updatedAtField [Section titled “updatedAtField”](#updatedatfield) > `readonly` **updatedAtField**: `string` = `"updatedAt"` Defined in: [pinecone-repository.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L41) Metadata key written for auto-managed update timestamps. #### Default [Section titled “Default”](#default-4) ```ts "updatedAt" ``` *** ### vectorDimension [Section titled “vectorDimension”](#vectordimension) > `abstract` `readonly` **vectorDimension**: `number` Defined in: [pinecone-repository.ts:32](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L32) The number of dimensions in the index — required for zero-vector placeholders. ## Accessors [Section titled “Accessors”](#accessors) ### index [Section titled “index”](#index) #### Get Signature [Section titled “Get Signature”](#get-signature) > **get** `protected` **index**(): `Index` Defined in: [pinecone-repository.ts:56](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L56) Lazily resolved namespace-scoped Pinecone Index instance. ##### Returns [Section titled “Returns”](#returns-1) `Index` ## Methods [Section titled “Methods”](#methods) ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [pinecone-repository.ts:292](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L292) #### Parameters [Section titled “Parameters”](#parameters-1) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<`number`> #### Implementation of [Section titled “Implementation of”](#implementation-of) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`count`](/api/mantlejs/mantle/interfaces/vectorrepository/#count) *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [pinecone-repository.ts:280](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L280) #### Parameters [Section titled “Parameters”](#parameters-2) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-1) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`deleteById`](/api/mantlejs/mantle/interfaces/vectorrepository/#deletebyid) *** ### deleteVector() [Section titled “deleteVector()”](#deletevector) > **deleteVector**(`id`): `Promise`<`T`> Defined in: [pinecone-repository.ts:108](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L108) Delete a vector and its associated record #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`deleteVector`](/api/mantlejs/mantle/interfaces/vectorrepository/#deletevector) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [pinecone-repository.ts:63](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L63) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-5) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`describe`](/api/mantlejs/mantle/interfaces/vectorrepository/#describe) *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [pinecone-repository.ts:114](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L114) #### Parameters [Section titled “Parameters”](#parameters-4) ##### params? [Section titled “params?”](#params-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findAll`](/api/mantlejs/mantle/interfaces/vectorrepository/#findall) *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [pinecone-repository.ts:202](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L202) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T` | `null`> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findById`](/api/mantlejs/mantle/interfaces/vectorrepository/#findbyid) *** ### findPage() [Section titled “findPage()”](#findpage) > **findPage**(`params?`): `Promise`<[`CursorPage`](/api/mantlejs/mantle/interfaces/cursorpage/)<`T`>> Defined in: [pinecone-repository.ts:163](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L163) Fetch one page of records using Pinecone’s native list pagination. The returned `cursor` is Pinecone’s opaque `paginationToken`; pass it back as `params.cursor` for the next page. `where`, `skip`, and `sort` are rejected — Pinecone’s list API enumerates IDs only and cannot filter or order. #### Parameters [Section titled “Parameters”](#parameters-6) ##### params? [Section titled “params?”](#params-2) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) & `object` #### Returns [Section titled “Returns”](#returns-8) `Promise`<[`CursorPage`](/api/mantlejs/mantle/interfaces/cursorpage/)<`T`>> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findPage`](/api/mantlejs/mantle/interfaces/vectorrepository/#findpage) *** ### findSimilar() [Section titled “findSimilar()”](#findsimilar) > **findSimilar**(`vector`, `topK`, `params?`): `Promise`<`T` & `object`\[]> Defined in: [pinecone-repository.ts:79](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L79) Every result carries the Pinecone match score as `_score` — HIGHER is more similar. #### Parameters [Section titled “Parameters”](#parameters-7) ##### vector [Section titled “vector”](#vector) `number`\[] ##### topK [Section titled “topK”](#topk) `number` ##### params? [Section titled “params?”](#params-3) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T` & `object`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-7) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findSimilar`](/api/mantlejs/mantle/interfaces/vectorrepository/#findsimilar) *** ### fromRecord() [Section titled “fromRecord()”](#fromrecord) > `protected` **fromRecord**(`id`, `metadata`): `T` Defined in: [pinecone-repository.ts:318](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L318) #### Parameters [Section titled “Parameters”](#parameters-8) ##### id [Section titled “id”](#id-3) `string` ##### metadata [Section titled “metadata”](#metadata) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-10) `T` *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [pinecone-repository.ts:256](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L256) #### Parameters [Section titled “Parameters”](#parameters-9) ##### id [Section titled “id”](#id-4) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data) `D` #### Returns [Section titled “Returns”](#returns-11) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-8) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`patchById`](/api/mantlejs/mantle/interfaces/vectorrepository/#patchbyid) *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [pinecone-repository.ts:213](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L213) #### Parameters [Section titled “Parameters”](#parameters-10) ##### data [Section titled “data”](#data-1) `D` #### Returns [Section titled “Returns”](#returns-12) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-9) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`save`](/api/mantlejs/mantle/interfaces/vectorrepository/#save) *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [pinecone-repository.ts:232](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L232) #### Parameters [Section titled “Parameters”](#parameters-11) ##### data [Section titled “data”](#data-2) `D`\[] #### Returns [Section titled “Returns”](#returns-13) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-10) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`saveAll`](/api/mantlejs/mantle/interfaces/vectorrepository/#saveall) *** ### toEntityField() [Section titled “toEntityField()”](#toentityfield) > `protected` **toEntityField**(`field`): `string` Defined in: [pinecone-repository.ts:313](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L313) Translates a metadata key back to its entity field name — the inverse of `toField`. #### Parameters [Section titled “Parameters”](#parameters-12) ##### field [Section titled “field”](#field) `string` #### Returns [Section titled “Returns”](#returns-14) `string` *** ### toField() [Section titled “toField()”](#tofield) > `protected` **toField**(`field`): `string` Defined in: [pinecone-repository.ts:308](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L308) Translates an entity field name to its metadata key (fieldMap override, else identity). #### Parameters [Section titled “Parameters”](#parameters-13) ##### field [Section titled “field”](#field-1) `string` #### Returns [Section titled “Returns”](#returns-15) `string` *** ### toMetadata() [Section titled “toMetadata()”](#tometadata) > `protected` **toMetadata**(`data`): `RecordMetadata` Defined in: [pinecone-repository.ts:323](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L323) #### Parameters [Section titled “Parameters”](#parameters-14) ##### data [Section titled “data”](#data-3) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-16) `RecordMetadata` *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [pinecone-repository.ts:236](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L236) #### Parameters [Section titled “Parameters”](#parameters-15) ##### id [Section titled “id”](#id-5) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-4) `D` #### Returns [Section titled “Returns”](#returns-17) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-11) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`updateById`](/api/mantlejs/mantle/interfaces/vectorrepository/#updatebyid) *** ### upsertVector() [Section titled “upsertVector()”](#upsertvector) > **upsertVector**(`id`, `vector`, `data`): `Promise`<`T`> Defined in: [pinecone-repository.ts:98](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L98) Upsert a record with its embedding vector #### Parameters [Section titled “Parameters”](#parameters-16) ##### id [Section titled “id”](#id-6) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### vector [Section titled “vector”](#vector-1) `number`\[] ##### data [Section titled “data”](#data-5) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns-18) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-12) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`upsertVector`](/api/mantlejs/mantle/interfaces/vectorrepository/#upsertvector) *** ### wrapError() [Section titled “wrapError()”](#wraperror) > `protected` **wrapError**(`err`): `Error` Defined in: [pinecone-repository.ts:343](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-repository.ts#L343) #### Parameters [Section titled “Parameters”](#parameters-17) ##### err [Section titled “err”](#err) `unknown` #### Returns [Section titled “Returns”](#returns-19) `Error` # pinecone > **pinecone**(`config?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [pinecone.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone.ts#L16) Mantle plugin that creates a Pinecone client and stores it on the application. The `apiKey` defaults to the `PINECONE_API_KEY` environment variable when omitted. ## Parameters [Section titled “Parameters”](#parameters) ### config? [Section titled “config?”](#config) [`PineconeConfig`](/api/mantlejs/pinecone/type-aliases/pineconeconfig/) = `{}` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) ## Example [Section titled “Example”](#example) ```ts const app = mantle().configure(pinecone({ apiKey: process.env.PINECONE_API_KEY })); ``` # toPineconeFilter > **toPineconeFilter**(`where`, `toField?`): `Record`<`string`, `unknown`> Defined in: [pinecone-filter.ts:27](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-filter.ts#L27) Convert a Mantle `QueryParams.where` clause to a Pinecone metadata filter object. Mapping rules: { field: value } → { field: { $eq: value } } { field: null } → { field: { $eq: null } } { field: \[a, b] } → { field: { $in: \[a, b] } } { field: { $eq/$gt/$lt/… } } → passed through unchanged { field: { $ne/$in/$nin } } → passed through unchanged { $or: \[…] } → { $or: \[mapped…] } { $and: \[…] } → { $and: \[mapped…] } Unsupported operators ($like and friends) throw `BadRequest` — Pinecone metadata filters have no pattern matching. ## Parameters [Section titled “Parameters”](#parameters) ### where [Section titled “where”](#where) [`WhereClause`](/api/mantlejs/pinecone/type-aliases/whereclause/) ### toField? [Section titled “toField?”](#tofield) (`field`) => `string` ## Returns [Section titled “Returns”](#returns) `Record`<`string`, `unknown`> # PineconeConfig > **PineconeConfig** = `Partial`<`PineconeConfiguration`> Defined in: [pinecone.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone.ts#L5) # WhereClause > **WhereClause** = `Record`<`string`, `WhereValue`> Defined in: [pinecone-filter.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-filter.ts#L5) # PINECONE_OPERATORS > `const` **PINECONE_OPERATORS**: `ReadonlySet`<`string`> Defined in: [pinecone-filter.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/pinecone/src/lib/pinecone-filter.ts#L10) All query operators supported by the Pinecone adapter. # @mantlejs/qdrant ## Classes [Section titled “Classes”](#classes) * [QdrantRepository](/api/mantlejs/qdrant/classes/qdrantrepository/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [QdrantConfig](/api/mantlejs/qdrant/type-aliases/qdrantconfig/) * [WhereClause](/api/mantlejs/qdrant/type-aliases/whereclause/) ## Variables [Section titled “Variables”](#variables) * [QDRANT_OPERATORS](/api/mantlejs/qdrant/variables/qdrant_operators/) ## Functions [Section titled “Functions”](#functions) * [qdrant](/api/mantlejs/qdrant/functions/qdrant/) * [toQdrantFilter](/api/mantlejs/qdrant/functions/toqdrantfilter/) # QdrantRepository Defined in: [qdrant-repository.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L22) A Mantle `VectorRepository` implementation backed by a Qdrant vector collection. Records are stored as Qdrant points. The `idField` value is the Qdrant point ID; all other fields are stored in the point payload. Call `upsertVector` to attach a real embedding — `save` / `saveAll` insert a zero vector placeholder that is later replaced. The collection is created automatically on the first write if it does not exist. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> The entity shape. Must extend `Record`. ### D [Section titled “D”](#d) `D` = `Partial`<`T`> The write shape (defaults to `Partial`). ## Implements [Section titled “Implements”](#implements) * [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/)<`T`, `D`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new QdrantRepository**<`T`, `D`>(`app`): `QdrantRepository`<`T`, `D`> Defined in: [qdrant-repository.ts:49](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L49) #### Parameters [Section titled “Parameters”](#parameters) ##### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) #### Returns [Section titled “Returns”](#returns) `QdrantRepository`<`T`, `D`> ## Properties [Section titled “Properties”](#properties) ### client [Section titled “client”](#client) > `protected` `readonly` **client**: `QdrantClient` Defined in: [qdrant-repository.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L25) *** ### collectionName [Section titled “collectionName”](#collectionname) > `abstract` `readonly` **collectionName**: `string` Defined in: [qdrant-repository.ts:28](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L28) The Qdrant collection name to target. *** ### createdAtField [Section titled “createdAtField”](#createdatfield) > `readonly` **createdAtField**: `string` = `"createdAt"` Defined in: [qdrant-repository.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L37) Payload key written for auto-managed creation timestamps. #### Default [Section titled “Default”](#default) ```ts "createdAt" ``` *** ### fieldMap [Section titled “fieldMap”](#fieldmap) > `readonly` **fieldMap**: `Record`<`string`, `string`> = `{}` Defined in: [qdrant-repository.ts:45](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L45) Entity-field-to-payload-key overrides, for a collection whose payload keys don’t match the entity (e.g. a brownfield collection using snake_case). Does not apply to `idField` — the point id is never stored in the payload. #### Default [Section titled “Default”](#default-1) ```ts {} ``` *** ### idField [Section titled “idField”](#idfield) > `readonly` **idField**: `string` = `"id"` Defined in: [qdrant-repository.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L33) The payload key that stores the record id. #### Default [Section titled “Default”](#default-2) ```ts "id" ``` *** ### timestamps [Section titled “timestamps”](#timestamps) > `readonly` **timestamps**: `boolean` = `true` Defined in: [qdrant-repository.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L35) When true, `save` / `saveAll` / `updateById` / `patchById` write ISO-8601 timestamps. #### Default [Section titled “Default”](#default-3) ```ts true ``` *** ### updatedAtField [Section titled “updatedAtField”](#updatedatfield) > `readonly` **updatedAtField**: `string` = `"updatedAt"` Defined in: [qdrant-repository.ts:39](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L39) Payload key written for auto-managed update timestamps. #### Default [Section titled “Default”](#default-4) ```ts "updatedAt" ``` *** ### vectorSize [Section titled “vectorSize”](#vectorsize) > `abstract` `readonly` **vectorSize**: `number` Defined in: [qdrant-repository.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L30) Number of dimensions for the vector space — must match the collection’s configuration. ## Methods [Section titled “Methods”](#methods) ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [qdrant-repository.ts:359](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L359) #### Parameters [Section titled “Parameters”](#parameters-1) ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-1) `Promise`<`number`> #### Implementation of [Section titled “Implementation of”](#implementation-of) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`count`](/api/mantlejs/mantle/interfaces/vectorrepository/#count) *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [qdrant-repository.ts:347](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L347) #### Parameters [Section titled “Parameters”](#parameters-2) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-2) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-1) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`deleteById`](/api/mantlejs/mantle/interfaces/vectorrepository/#deletebyid) *** ### deleteVector() [Section titled “deleteVector()”](#deletevector) > **deleteVector**(`id`): `Promise`<`T`> Defined in: [qdrant-repository.ts:112](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L112) Delete a vector and its associated record #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`deleteVector`](/api/mantlejs/mantle/interfaces/vectorrepository/#deletevector) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [qdrant-repository.ts:66](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L66) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-4) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`describe`](/api/mantlejs/mantle/interfaces/vectorrepository/#describe) *** ### ensureCollection() [Section titled “ensureCollection()”](#ensurecollection) > `protected` **ensureCollection**(): `Promise`<`void`> Defined in: [qdrant-repository.ts:55](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L55) #### Returns [Section titled “Returns”](#returns-5) `Promise`<`void`> *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [qdrant-repository.ts:118](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L118) #### Parameters [Section titled “Parameters”](#parameters-4) ##### params? [Section titled “params?”](#params-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findAll`](/api/mantlejs/mantle/interfaces/vectorrepository/#findall) *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [qdrant-repository.ts:228](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L228) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T` | `null`> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findById`](/api/mantlejs/mantle/interfaces/vectorrepository/#findbyid) *** ### findPage() [Section titled “findPage()”](#findpage) > **findPage**(`params?`): `Promise`<[`CursorPage`](/api/mantlejs/mantle/interfaces/cursorpage/)<`T`>> Defined in: [qdrant-repository.ts:171](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L171) Fetch one page of results using Qdrant’s native scroll pagination. The returned `cursor` encodes Qdrant’s `next_page_offset`; pass it back as `params.cursor` for the next page. `skip` and `sort` are rejected — Qdrant does not return a next-page offset when `order_by` is in play, and offsets don’t compose with cursors. #### Parameters [Section titled “Parameters”](#parameters-6) ##### params? [Section titled “params?”](#params-2) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) & `object` #### Returns [Section titled “Returns”](#returns-8) `Promise`<[`CursorPage`](/api/mantlejs/mantle/interfaces/cursorpage/)<`T`>> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findPage`](/api/mantlejs/mantle/interfaces/vectorrepository/#findpage) *** ### findSimilar() [Section titled “findSimilar()”](#findsimilar) > **findSimilar**(`vector`, `topK`, `params?`): `Promise`<`T` & `object`\[]> Defined in: [qdrant-repository.ts:79](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L79) Every result carries the Qdrant match score as `_score` — HIGHER is more similar for Cosine/Dot collections. #### Parameters [Section titled “Parameters”](#parameters-7) ##### vector [Section titled “vector”](#vector) `number`\[] ##### topK [Section titled “topK”](#topk) `number` ##### params? [Section titled “params?”](#params-3) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T` & `object`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-7) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`findSimilar`](/api/mantlejs/mantle/interfaces/vectorrepository/#findsimilar) *** ### fromPoint() [Section titled “fromPoint()”](#frompoint) > `protected` **fromPoint**(`id`, `payload`): `T` Defined in: [qdrant-repository.ts:387](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L387) #### Parameters [Section titled “Parameters”](#parameters-8) ##### id [Section titled “id”](#id-3) `string` | `number` ##### payload [Section titled “payload”](#payload) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-10) `T` *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [qdrant-repository.ts:317](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L317) #### Parameters [Section titled “Parameters”](#parameters-9) ##### id [Section titled “id”](#id-4) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data) `D` #### Returns [Section titled “Returns”](#returns-11) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-8) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`patchById`](/api/mantlejs/mantle/interfaces/vectorrepository/#patchbyid) *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [qdrant-repository.ts:242](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L242) #### Parameters [Section titled “Parameters”](#parameters-10) ##### data [Section titled “data”](#data-1) `D` #### Returns [Section titled “Returns”](#returns-12) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-9) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`save`](/api/mantlejs/mantle/interfaces/vectorrepository/#save) *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [qdrant-repository.ts:264](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L264) #### Parameters [Section titled “Parameters”](#parameters-11) ##### data [Section titled “data”](#data-2) `D`\[] #### Returns [Section titled “Returns”](#returns-13) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-10) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`saveAll`](/api/mantlejs/mantle/interfaces/vectorrepository/#saveall) *** ### toEntityField() [Section titled “toEntityField()”](#toentityfield) > `protected` **toEntityField**(`field`): `string` Defined in: [qdrant-repository.ts:382](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L382) Translates a payload key back to its entity field name — the inverse of `toField`. #### Parameters [Section titled “Parameters”](#parameters-12) ##### field [Section titled “field”](#field) `string` #### Returns [Section titled “Returns”](#returns-14) `string` *** ### toField() [Section titled “toField()”](#tofield) > `protected` **toField**(`field`): `string` Defined in: [qdrant-repository.ts:377](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L377) Translates an entity field name to its payload key (fieldMap override, else identity). #### Parameters [Section titled “Parameters”](#parameters-13) ##### field [Section titled “field”](#field-1) `string` #### Returns [Section titled “Returns”](#returns-15) `string` *** ### toPayload() [Section titled “toPayload()”](#topayload) > `protected` **toPayload**(`data`): `Record`<`string`, `unknown`> Defined in: [qdrant-repository.ts:392](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L392) #### Parameters [Section titled “Parameters”](#parameters-14) ##### data [Section titled “data”](#data-3) `Record`<`string`, `unknown`> #### Returns [Section titled “Returns”](#returns-16) `Record`<`string`, `unknown`> *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [qdrant-repository.ts:291](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L291) #### Parameters [Section titled “Parameters”](#parameters-15) ##### id [Section titled “id”](#id-5) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-4) `D` #### Returns [Section titled “Returns”](#returns-17) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-11) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`updateById`](/api/mantlejs/mantle/interfaces/vectorrepository/#updatebyid) *** ### upsertVector() [Section titled “upsertVector()”](#upsertvector) > **upsertVector**(`id`, `vector`, `data`): `Promise`<`T`> Defined in: [qdrant-repository.ts:99](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L99) Upsert a record with its embedding vector #### Parameters [Section titled “Parameters”](#parameters-16) ##### id [Section titled “id”](#id-6) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### vector [Section titled “vector”](#vector-1) `number`\[] ##### data [Section titled “data”](#data-5) `Partial`<`T`> #### Returns [Section titled “Returns”](#returns-18) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-12) [`VectorRepository`](/api/mantlejs/mantle/interfaces/vectorrepository/).[`upsertVector`](/api/mantlejs/mantle/interfaces/vectorrepository/#upsertvector) *** ### wrapError() [Section titled “wrapError()”](#wraperror) > `protected` **wrapError**(`err`): `Error` Defined in: [qdrant-repository.ts:400](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-repository.ts#L400) #### Parameters [Section titled “Parameters”](#parameters-17) ##### err [Section titled “err”](#err) `unknown` #### Returns [Section titled “Returns”](#returns-19) `Error` # qdrant > **qdrant**(`config?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [qdrant.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant.ts#L17) Mantle plugin that creates a Qdrant client and stores it on the application. The `url` defaults to `QDRANT_URL` and `apiKey` to `QDRANT_API_KEY` environment variables when omitted. ## Parameters [Section titled “Parameters”](#parameters) ### config? [Section titled “config?”](#config) `QdrantClientParams` = `{}` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) ## Example [Section titled “Example”](#example) ```ts const app = mantle().configure(qdrant({ url: process.env.QDRANT_URL, apiKey: process.env.QDRANT_API_KEY })); ``` # toQdrantFilter > **toQdrantFilter**(`where`, `toField?`): `Record`<`string`, `unknown`> Defined in: [qdrant-filter.ts:79](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-filter.ts#L79) Convert a Mantle `QueryParams.where` clause to a Qdrant payload filter object. Mapping rules: { field: value } → must: \[{ key: “field”, match: { value } }] { field: null } → must: \[{ is_null: { key: “field” } }] { field: \[a, b] } → must: \[{ key: “field”, match: { any: \[a, b] } }] { field: { $lt/$lte/$gt/$gte } } → must: \[{ key: “field”, range: { … } }] { field: { $ne: value } } → must_not: \[{ key: “field”, match: { value } }] { field: { $ne: null } } → must_not: \[{ is_null: { key: “field” } }] { field: { $in: \[…] } } → must: \[{ key: “field”, match: { any: \[…] } }] { field: { $nin: \[…] } } → must_not: \[{ key: “field”, match: { any: \[…] } }] { field: { $contains: v } } → must: \[flattened match conditions — see flattenContains] { $or: \[…] } → should: \[mapped…] { $and: \[…] } → must: \[mapped…] Nested fields (`"metadata.owner.name"`) are addressed natively — Qdrant’s payload filter `key` accepts dot-path strings directly, no translation needed. Unsupported operators (including $like/$ilike/$notlike) throw `BadRequest`. ## Parameters [Section titled “Parameters”](#parameters) ### where [Section titled “where”](#where) [`WhereClause`](/api/mantlejs/qdrant/type-aliases/whereclause/) ### toField? [Section titled “toField?”](#tofield) (`field`) => `string` ## Returns [Section titled “Returns”](#returns) `Record`<`string`, `unknown`> # QdrantConfig > **QdrantConfig** = `QdrantClientParams` Defined in: [qdrant.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant.ts#L5) # WhereClause > **WhereClause** = `Record`<`string`, `WhereValue`> Defined in: [qdrant-filter.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-filter.ts#L5) # QDRANT_OPERATORS > `const` **QDRANT_OPERATORS**: `ReadonlySet`<`string`> Defined in: [qdrant-filter.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/qdrant/src/lib/qdrant-filter.ts#L14) All query operators supported by the Qdrant adapter. `$like`/`$ilike`/`$notlike` are deliberately absent: remapping them to Qdrant full-text match would silently change their semantics. # @mantlejs/react ## Interfaces [Section titled “Interfaces”](#interfaces) * [MantleFindOptions](/api/mantlejs/react/interfaces/mantlefindoptions/) * [MantleProviderProps](/api/mantlejs/react/interfaces/mantleproviderprops/) * [MantleQueryOptions](/api/mantlejs/react/interfaces/mantlequeryoptions/) * [RealtimePatchOptions](/api/mantlejs/react/interfaces/realtimepatchoptions/) ## Functions [Section titled “Functions”](#functions) * [MantleProvider](/api/mantlejs/react/functions/mantleprovider/) * [useCreate](/api/mantlejs/react/functions/usecreate/) * [useFind](/api/mantlejs/react/functions/usefind/) * [useGet](/api/mantlejs/react/functions/useget/) * [useMantleClient](/api/mantlejs/react/functions/usemantleclient/) * [usePatch](/api/mantlejs/react/functions/usepatch/) * [useRemove](/api/mantlejs/react/functions/useremove/) * [useUpdate](/api/mantlejs/react/functions/useupdate/) # MantleProvider > **MantleProvider**(`__namedParameters`): `ReactElement` Defined in: [provider.tsx:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/provider.tsx#L18) Stores the `MantleClient` in context and wraps children in a `QueryClientProvider` (creating a default `QueryClient` when none is given). ## Parameters [Section titled “Parameters”](#parameters) ### \_\_namedParameters [Section titled “\_\_namedParameters”](#__namedparameters) [`MantleProviderProps`](/api/mantlejs/react/interfaces/mantleproviderprops/) ## Returns [Section titled “Returns”](#returns) `ReactElement` # useCreate > **useCreate**<`T`>(`service`, `options?`): `UseMutationResult`<`T`, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/), `Partial`<`T`>> Defined in: [hooks.ts:142](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/hooks.ts#L142) `useMutation` over `service.create(data)`. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Parameters [Section titled “Parameters”](#parameters) ### service [Section titled “service”](#service) `string` ### options? [Section titled “options?”](#options) `MutationHookOptions`<`T`, `Partial`<`T`>> ## Returns [Section titled “Returns”](#returns) `UseMutationResult`<`T`, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/), `Partial`<`T`>> # useFind > **useFind**<`T`>(`service`, `params?`, `options?`): `UseQueryResult`<`T`\[] | [`Paginated`](/api/mantlejs/client/interfaces/paginated/)<`T`>, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/)> Defined in: [hooks.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/hooks.ts#L42) `useQuery` over `service.find(params)` with key `[service, "find", params?]`. With `realtime: { mode: "patch" }`, service events update the cached result in place instead of triggering a refetch. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Parameters [Section titled “Parameters”](#parameters) ### service [Section titled “service”](#service) `string` ### params? [Section titled “params?”](#params) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) ### options? [Section titled “options?”](#options) `FindHookOptions`<`T`> ## Returns [Section titled “Returns”](#returns) `UseQueryResult`<`T`\[] | [`Paginated`](/api/mantlejs/client/interfaces/paginated/)<`T`>, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/)> # useGet > **useGet**<`T`>(`service`, `id`, `params?`, `options?`): `UseQueryResult`<`T`, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/)> Defined in: [hooks.ts:125](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/hooks.ts#L125) `useQuery` over `service.get(id, params)` with key `[service, "get", id, params?]`. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Parameters [Section titled “Parameters”](#parameters) ### service [Section titled “service”](#service) `string` ### id [Section titled “id”](#id) [`Id`](/api/mantlejs/client/type-aliases/id/) ### params? [Section titled “params?”](#params) [`ClientParams`](/api/mantlejs/client/interfaces/clientparams/) ### options? [Section titled “options?”](#options) `QueryHookOptions`<`T`> ## Returns [Section titled “Returns”](#returns) `UseQueryResult`<`T`, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/)> # useMantleClient > **useMantleClient**(): [`MantleClient`](/api/mantlejs/client/classes/mantleclient/) Defined in: [provider.tsx:57](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/provider.tsx#L57) The `MantleClient` from the nearest `MantleProvider`. ## Returns [Section titled “Returns”](#returns) [`MantleClient`](/api/mantlejs/client/classes/mantleclient/) # usePatch > **usePatch**<`T`>(`service`, `options?`): `UseMutationResult`<`T`, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/), { `data`: `Partial`<`T`>; `id`: [`Id`](/api/mantlejs/client/type-aliases/id/); }> Defined in: [hooks.ts:160](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/hooks.ts#L160) `useMutation` over `service.patch(id, data)`. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Parameters [Section titled “Parameters”](#parameters) ### service [Section titled “service”](#service) `string` ### options? [Section titled “options?”](#options) `MutationHookOptions`<`T`, { `data`: `Partial`<`T`>; `id`: [`Id`](/api/mantlejs/client/type-aliases/id/); }> ## Returns [Section titled “Returns”](#returns) `UseMutationResult`<`T`, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/), { `data`: `Partial`<`T`>; `id`: [`Id`](/api/mantlejs/client/type-aliases/id/); }> # useRemove > **useRemove**<`T`>(`service`, `options?`): `UseMutationResult`<`T`, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/), [`Id`](/api/mantlejs/client/type-aliases/id/)> Defined in: [hooks.ts:169](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/hooks.ts#L169) `useMutation` over `service.remove(id)`. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Parameters [Section titled “Parameters”](#parameters) ### service [Section titled “service”](#service) `string` ### options? [Section titled “options?”](#options) `MutationHookOptions`<`T`, [`Id`](/api/mantlejs/client/type-aliases/id/)> ## Returns [Section titled “Returns”](#returns) `UseMutationResult`<`T`, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/), [`Id`](/api/mantlejs/client/type-aliases/id/)> # useUpdate > **useUpdate**<`T`>(`service`, `options?`): `UseMutationResult`<`T`, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/), { `data`: `Partial`<`T`>; `id`: [`Id`](/api/mantlejs/client/type-aliases/id/); }> Defined in: [hooks.ts:151](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/hooks.ts#L151) `useMutation` over `service.update(id, data)`. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Parameters [Section titled “Parameters”](#parameters) ### service [Section titled “service”](#service) `string` ### options? [Section titled “options?”](#options) `MutationHookOptions`<`T`, { `data`: `Partial`<`T`>; `id`: [`Id`](/api/mantlejs/client/type-aliases/id/); }> ## Returns [Section titled “Returns”](#returns) `UseMutationResult`<`T`, [`MantleClientError`](/api/mantlejs/client/classes/mantleclienterror/), { `data`: `Partial`<`T`>; `id`: [`Id`](/api/mantlejs/client/type-aliases/id/); }> # MantleFindOptions Defined in: [types.ts:42](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L42) `useFind`’s options extension: `realtime` additionally accepts the in-place patch mode. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Properties [Section titled “Properties”](#properties) ### realtime? [Section titled “realtime?”](#realtime) > `optional` **realtime?**: `boolean` | [`RealtimePatchOptions`](/api/mantlejs/react/interfaces/realtimepatchoptions/)<`T`> Defined in: [types.ts:47](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L47) `true`/omitted: invalidate (refetch) on service events when a socket is configured. `false`: off. `{ mode: "patch" }`: apply events to the cached result in place, no refetch. # MantleProviderProps Defined in: [types.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L5) ## Properties [Section titled “Properties”](#properties) ### children [Section titled “children”](#children) > **children**: `ReactNode` Defined in: [types.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L10) *** ### client [Section titled “client”](#client) > **client**: [`MantleClient`](/api/mantlejs/client/classes/mantleclient/) Defined in: [types.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L7) The `@mantlejs/client` instance every hook under this provider dispatches through. *** ### queryClient? [Section titled “queryClient?”](#queryclient) > `optional` **queryClient?**: `QueryClient` Defined in: [types.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L9) Custom TanStack `QueryClient`. A default one is created when omitted. # MantleQueryOptions Defined in: [types.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L13) ## Properties [Section titled “Properties”](#properties) ### realtime? [Section titled “realtime?”](#realtime) > `optional` **realtime?**: `boolean` Defined in: [types.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L18) Automatic cache invalidation from real-time service events. Default: enabled when the client has a socket configured, off otherwise. # RealtimePatchOptions Defined in: [types.ts:28](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L28) `useFind`’s in-place cache mode: service events are written straight into this hook’s cached `find()` result instead of invalidating it, so the list changes with no refetch. Events carry the full record, but the client can’t re-run the server’s `where`/`$sort` — `matches` and `insert` mirror them. The provider still invalidates everything on socket reconnect, so events missed while disconnected are recovered by one refetch. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ## Properties [Section titled “Properties”](#properties) ### idField? [Section titled “idField?”](#idfield) > `optional` **idField?**: keyof `T` & `string` Defined in: [types.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L31) Record id field events are matched on. #### Default [Section titled “Default”](#default) ```ts "id" ``` *** ### insert? [Section titled “insert?”](#insert) > `optional` **insert?**: `"start"` | `"end"` Defined in: [types.ts:38](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L38) Where `created` records go — mirror the query’s `$sort`. #### Default [Section titled “Default”](#default-1) ```ts "end" ``` *** ### matches? [Section titled “matches?”](#matches) > `optional` **matches?**: (`record`) => `boolean` Defined in: [types.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L36) Whether a `created`/`updated`/`patched` record belongs in this result — mirror the query’s filter. A changed record that stops matching is removed. #### Parameters [Section titled “Parameters”](#parameters) ##### record [Section titled “record”](#record) `T` #### Returns [Section titled “Returns”](#returns) `boolean` #### Default [Section titled “Default”](#default-2) ```ts () => true ``` *** ### mode [Section titled “mode”](#mode) > **mode**: `"patch"` Defined in: [types.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/react/src/lib/types.ts#L29) # @mantlejs/schema ## Interfaces [Section titled “Interfaces”](#interfaces) * [QuerySyntaxOptions](/api/mantlejs/schema/interfaces/querysyntaxoptions/) * [ResolverOptions](/api/mantlejs/schema/interfaces/resolveroptions/) * [ValidateOptions](/api/mantlejs/schema/interfaces/validateoptions/) ## Type Aliases [Section titled “Type Aliases”](#type-aliases) * [FieldResolver](/api/mantlejs/schema/type-aliases/fieldresolver/) * [ResolverMap](/api/mantlejs/schema/type-aliases/resolvermap/) * [ValidatorFn](/api/mantlejs/schema/type-aliases/validatorfn/) ## Functions [Section titled “Functions”](#functions) * [querySyntax](/api/mantlejs/schema/functions/querysyntax/) * [resolver](/api/mantlejs/schema/functions/resolver/) * [validate](/api/mantlejs/schema/functions/validate/) # querySyntax > **querySyntax**<`T`>(`entitySchema`, `options?`): `TObject` Defined in: [query-syntax.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/query-syntax.ts#L24) Derive a query schema from an entity schema, following the `RepositoryService` query convention (Phase 4 TDD §14): every entity field accepts either a bare value or an operator object (`$gt`/`$gte`/`$lt`/`$lte`/`$ne`/`$in`/`$nin`, plus `$like`/`$notlike`/ `$ilike` for string fields), and the reserved keys `$limit`, `$skip`, `$sort`, `$select`, `$or`, `$and` are typed to match. Unknown keys are rejected (`additionalProperties: false`). Use it with the validate hook on the query target: ```typescript app.service("users").hooks({ before: { find: [validate(querySyntax(userSchema), { target: "query", coerce: true })] }, }); ``` ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `TObject`<`TProperties`> ## Parameters [Section titled “Parameters”](#parameters) ### entitySchema [Section titled “entitySchema”](#entityschema) `T` ### options? [Section titled “options?”](#options) [`QuerySyntaxOptions`](/api/mantlejs/schema/interfaces/querysyntaxoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) `TObject` # resolver > **resolver**<`T`, `C`>(`map`, `options?`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [resolver.ts:48](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/resolver.ts#L48) Transforms context.result field-by-field. Returning undefined from a resolver removes that field. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ### C [Section titled “C”](#c) `C` = `undefined` ## Parameters [Section titled “Parameters”](#parameters) ### map [Section titled “map”](#map) [`ResolverMap`](/api/mantlejs/schema/type-aliases/resolvermap/)<`T`, `C`> ### options? [Section titled “options?”](#options) [`ResolverOptions`](/api/mantlejs/schema/interfaces/resolveroptions/)<`T`, `C`> ## Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) # validate ## Call Signature [Section titled “Call Signature”](#call-signature) > **validate**<`T`>(`schema`, `options?`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [validate.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/validate.ts#L43) ### Type Parameters [Section titled “Type Parameters”](#type-parameters) #### T [Section titled “T”](#t) `T` *extends* `TSchema` ### Parameters [Section titled “Parameters”](#parameters) #### schema [Section titled “schema”](#schema) `T` #### options? [Section titled “options?”](#options) [`ValidateOptions`](/api/mantlejs/schema/interfaces/validateoptions/) ### Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) ## Call Signature [Section titled “Call Signature”](#call-signature-1) > **validate**(`validator`, `options?`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [validate.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/validate.ts#L44) ### Parameters [Section titled “Parameters”](#parameters-1) #### validator [Section titled “validator”](#validator) [`ValidatorFn`](/api/mantlejs/schema/type-aliases/validatorfn/) #### options? [Section titled “options?”](#options-1) `Pick`<[`ValidateOptions`](/api/mantlejs/schema/interfaces/validateoptions/), `"target"`> ### Returns [Section titled “Returns”](#returns-1) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) # QuerySyntaxOptions Defined in: [query-syntax.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/query-syntax.ts#L4) ## Properties [Section titled “Properties”](#properties) ### maxLimit? [Section titled “maxLimit?”](#maxlimit) > `optional` **maxLimit?**: `number` Defined in: [query-syntax.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/query-syntax.ts#L6) Upper bound enforced on `$limit`. Unset means no maximum. # ResolverOptions Defined in: [resolver.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/resolver.ts#L14) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ### C [Section titled “C”](#c) `C` ## Properties [Section titled “Properties”](#properties) ### createContext? [Section titled “createContext?”](#createcontext) > `optional` **createContext?**: (`record`, `ctx`) => `C` | `Promise`<`C`> Defined in: [resolver.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/resolver.ts#L20) Called once per record before field resolvers run. The return value is passed as the fourth argument to every field resolver in the map. Use this to perform a single async lookup shared across multiple fields. #### Parameters [Section titled “Parameters”](#parameters) ##### record [Section titled “record”](#record) `T` ##### ctx [Section titled “ctx”](#ctx) [`HookContext`](/api/mantlejs/mantle/interfaces/hookcontext/) #### Returns [Section titled “Returns”](#returns) `C` | `Promise`<`C`> # ValidateOptions Defined in: [validate.ts:34](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/validate.ts#L34) ## Properties [Section titled “Properties”](#properties) ### coerce? [Section titled “coerce?”](#coerce) > `optional` **coerce?**: `boolean` Defined in: [validate.ts:38](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/validate.ts#L38) Coerce input types to schema types before validation (e.g. “42” → 42). Default: false *** ### stripAdditional? [Section titled “stripAdditional?”](#stripadditional) > `optional` **stripAdditional?**: `boolean` Defined in: [validate.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/validate.ts#L40) Remove properties absent from the schema before validation. Default: false *** ### target? [Section titled “target?”](#target) > `optional` **target?**: `"data"` | `"result"` | `"query"` Defined in: [validate.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/validate.ts#L36) Source to validate. Default: ‘data’ # FieldResolver > **FieldResolver**<`T`, `K`, `C`> = (`value`, `data`, `context`, `shared`) => `Promise`<`T`\[`K`] | `undefined`> | `T`\[`K`] | `undefined` Defined in: [resolver.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/resolver.ts#L3) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ### K [Section titled “K”](#k) `K` *extends* keyof `T` ### C [Section titled “C”](#c) `C` = `undefined` ## Parameters [Section titled “Parameters”](#parameters) ### value [Section titled “value”](#value) `T`\[`K`] | `undefined` ### data [Section titled “data”](#data) `T` ### context [Section titled “context”](#context) [`HookContext`](/api/mantlejs/mantle/interfaces/hookcontext/) ### shared [Section titled “shared”](#shared) `C` ## Returns [Section titled “Returns”](#returns) `Promise`<`T`\[`K`] | `undefined`> | `T`\[`K`] | `undefined` # ResolverMap > **ResolverMap**<`T`, `C`> = `{ [K in keyof T]?: FieldResolver }` Defined in: [resolver.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/resolver.ts#L10) ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` ### C [Section titled “C”](#c) `C` = `undefined` # ValidatorFn > **ValidatorFn** = (`data`) => `object`\[] | `null` | `undefined` Defined in: [validate.ts:32](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/schema/src/lib/validate.ts#L32) Custom validator function for the BYOV overload of validate(). ## Parameters [Section titled “Parameters”](#parameters) ### data [Section titled “data”](#data) `unknown` ## Returns [Section titled “Returns”](#returns) `object`\[] | `null` | `undefined` # @mantlejs/socketio ## Interfaces [Section titled “Interfaces”](#interfaces) * [SocketioOptions](/api/mantlejs/socketio/interfaces/socketiooptions/) ## Functions [Section titled “Functions”](#functions) * [socketio](/api/mantlejs/socketio/functions/socketio/) # socketio > **socketio**(`options?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [socketio.ts:335](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/socketio/src/lib/socketio.ts#L335) ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`SocketioOptions`](/api/mantlejs/socketio/interfaces/socketiooptions/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # SocketioOptions Defined in: [socketio.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/socketio/src/lib/socketio.ts#L15) ## Properties [Section titled “Properties”](#properties) ### cors? [Section titled “cors?”](#cors) > `optional` **cors?**: `boolean` | [`CorsOptions`](/api/mantlejs/mantle/interfaces/corsoptions/) Defined in: [socketio.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/socketio/src/lib/socketio.ts#L31) Enable CORS on the socket.io handshake. socket.io attaches its own request listener to the underlying HTTP server (for the `/socket.io/*` path) ahead of the HTTP transport’s own middleware, so `@mantlejs/express`’s `cors` option — or the equivalent on `@mantlejs/koa` / `@mantlejs/http` — never runs for it; a browser client on a different origin than the API (the common case: a frontend dev server and an API dev server on different ports) cannot open a socket at all without this. `true` resolves to permissive defaults (reflects `Origin`, allows the CRUD verbs, no credentials); pass a `CorsOptions` object to customize origin/methods/headers/credentials. Disabled (no CORS headers) by default — set this whenever the client’s `url` differs from the page’s own origin. An explicit `serverOptions.cors` always wins over this option. *** ### path? [Section titled “path?”](#path) > `optional` **path?**: `string` Defined in: [socketio.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/socketio/src/lib/socketio.ts#L18) *** ### serverOptions? [Section titled “serverOptions?”](#serveroptions) > `optional` **serverOptions?**: `Partial`<`ServerOptions`> Defined in: [socketio.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/socketio/src/lib/socketio.ts#L16) *** ### timeout? [Section titled “timeout?”](#timeout) > `optional` **timeout?**: `number` Defined in: [socketio.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/socketio/src/lib/socketio.ts#L17) # @mantlejs/storage ## Interfaces [Section titled “Interfaces”](#interfaces) * [DiskStorageConfig](/api/mantlejs/storage/interfaces/diskstorageconfig/) * [GetSignedUrlOptions](/api/mantlejs/storage/interfaces/getsignedurloptions/) * [HandleUploadOptions](/api/mantlejs/storage/interfaces/handleuploadoptions/) * [StorageAdapter](/api/mantlejs/storage/interfaces/storageadapter/) * [UploadConfig](/api/mantlejs/storage/interfaces/uploadconfig/) * [UploadedFile](/api/mantlejs/storage/interfaces/uploadedfile/) * [UploadEngine](/api/mantlejs/storage/interfaces/uploadengine/) * [UploadFileInfo](/api/mantlejs/storage/interfaces/uploadfileinfo/) ## Functions [Section titled “Functions”](#functions) * [diskStorage](/api/mantlejs/storage/functions/diskstorage/) * [handleUpload](/api/mantlejs/storage/functions/handleupload/) * [upload](/api/mantlejs/storage/functions/upload/) # @mantlejs/storage-gcs ## Classes [Section titled “Classes”](#classes) * [GcsStorageAdapter](/api/mantlejs/storage-gcs/classes/gcsstorageadapter/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [GcsStorageConfig](/api/mantlejs/storage-gcs/interfaces/gcsstorageconfig/) ## Functions [Section titled “Functions”](#functions) * [gcsStorage](/api/mantlejs/storage-gcs/functions/gcsstorage/) # GcsStorageAdapter Defined in: [gcs-storage.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L17) ## Implements [Section titled “Implements”](#implements) * [`newProject`](/api/create-mantlejs/variables/newproject/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new GcsStorageAdapter**(`config`): `GcsStorageAdapter` Defined in: [gcs-storage.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L20) #### Parameters [Section titled “Parameters”](#parameters) ##### config [Section titled “config”](#config) [`GcsStorageConfig`](/api/mantlejs/storage-gcs/interfaces/gcsstorageconfig/) #### Returns [Section titled “Returns”](#returns) `GcsStorageAdapter` ## Methods [Section titled “Methods”](#methods) ### delete() [Section titled “delete()”](#delete) > **delete**(`key`): `Promise`<`void`> Defined in: [gcs-storage.ts:78](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L78) #### Parameters [Section titled “Parameters”](#parameters-1) ##### key [Section titled “key”](#key) `string` #### Returns [Section titled “Returns”](#returns-1) `Promise`<`void`> *** ### getSignedUrl() [Section titled “getSignedUrl()”](#getsignedurl) > **getSignedUrl**(`key`, `options?`): `Promise`<`string`> Defined in: [gcs-storage.ts:91](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L91) #### Parameters [Section titled “Parameters”](#parameters-2) ##### key [Section titled “key”](#key-1) `string` ##### options? [Section titled “options?”](#options) `GetSignedUrlOptions` = `{}` #### Returns [Section titled “Returns”](#returns-2) `Promise`<`string`> *** ### retrieve() [Section titled “retrieve()”](#retrieve) > **retrieve**(`key`): `Promise`<`Readable`> Defined in: [gcs-storage.ts:73](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L73) #### Parameters [Section titled “Parameters”](#parameters-3) ##### key [Section titled “key”](#key-2) `string` #### Returns [Section titled “Returns”](#returns-3) `Promise`<`Readable`> *** ### store() [Section titled “store()”](#store) > **store**(`stream`, `info`): `Promise`<`UploadedFile`> Defined in: [gcs-storage.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L35) #### Parameters [Section titled “Parameters”](#parameters-4) ##### stream [Section titled “stream”](#stream) `Readable` ##### info [Section titled “info”](#info) `UploadFileInfo` #### Returns [Section titled “Returns”](#returns-4) `Promise`<`UploadedFile`> # gcsStorage > **gcsStorage**(`config`): [`GcsStorageAdapter`](/api/mantlejs/storage-gcs/classes/gcsstorageadapter/) Defined in: [gcs-storage.ts:104](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L104) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) [`GcsStorageConfig`](/api/mantlejs/storage-gcs/interfaces/gcsstorageconfig/) ## Returns [Section titled “Returns”](#returns) [`GcsStorageAdapter`](/api/mantlejs/storage-gcs/classes/gcsstorageadapter/) # GcsStorageConfig Defined in: [gcs-storage.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L6) ## Properties [Section titled “Properties”](#properties) ### bucket [Section titled “bucket”](#bucket) > **bucket**: `string` Defined in: [gcs-storage.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L7) *** ### key? [Section titled “key?”](#key) > `optional` **key?**: (`file`) => `string` Defined in: [gcs-storage.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L12) #### Parameters [Section titled “Parameters”](#parameters) ##### file [Section titled “file”](#file) `UploadFileInfo` #### Returns [Section titled “Returns”](#returns) `string` *** ### keyFilename? [Section titled “keyFilename?”](#keyfilename) > `optional` **keyFilename?**: `string` Defined in: [gcs-storage.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L9) *** ### keyPrefix? [Section titled “keyPrefix?”](#keyprefix) > `optional` **keyPrefix?**: `string` Defined in: [gcs-storage.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L10) *** ### projectId? [Section titled “projectId?”](#projectid) > `optional` **projectId?**: `string` Defined in: [gcs-storage.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L8) *** ### public? [Section titled “public?”](#public) > `optional` **public?**: `boolean` Defined in: [gcs-storage.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-gcs/src/lib/gcs-storage.ts#L11) # @mantlejs/storage-s3 ## Classes [Section titled “Classes”](#classes) * [S3StorageAdapter](/api/mantlejs/storage-s3/classes/s3storageadapter/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [S3StorageConfig](/api/mantlejs/storage-s3/interfaces/s3storageconfig/) ## Functions [Section titled “Functions”](#functions) * [s3Storage](/api/mantlejs/storage-s3/functions/s3storage/) # S3StorageAdapter Defined in: [s3-storage.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L20) ## Implements [Section titled “Implements”](#implements) * [`newProject`](/api/create-mantlejs/variables/newproject/) ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new S3StorageAdapter**(`config`): `S3StorageAdapter` Defined in: [s3-storage.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L23) #### Parameters [Section titled “Parameters”](#parameters) ##### config [Section titled “config”](#config) [`S3StorageConfig`](/api/mantlejs/storage-s3/interfaces/s3storageconfig/) #### Returns [Section titled “Returns”](#returns) `S3StorageAdapter` ## Methods [Section titled “Methods”](#methods) ### delete() [Section titled “delete()”](#delete) > **delete**(`key`): `Promise`<`void`> Defined in: [s3-storage.ts:91](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L91) #### Parameters [Section titled “Parameters”](#parameters-1) ##### key [Section titled “key”](#key) `string` #### Returns [Section titled “Returns”](#returns-1) `Promise`<`void`> *** ### getSignedUrl() [Section titled “getSignedUrl()”](#getsignedurl) > **getSignedUrl**(`key`, `options?`): `Promise`<`string`> Defined in: [s3-storage.ts:98](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L98) #### Parameters [Section titled “Parameters”](#parameters-2) ##### key [Section titled “key”](#key-1) `string` ##### options? [Section titled “options?”](#options) `GetSignedUrlOptions` = `{}` #### Returns [Section titled “Returns”](#returns-2) `Promise`<`string`> *** ### retrieve() [Section titled “retrieve()”](#retrieve) > **retrieve**(`key`): `Promise`<`Readable`> Defined in: [s3-storage.ts:76](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L76) #### Parameters [Section titled “Parameters”](#parameters-3) ##### key [Section titled “key”](#key-2) `string` #### Returns [Section titled “Returns”](#returns-3) `Promise`<`Readable`> *** ### store() [Section titled “store()”](#store) > **store**(`stream`, `info`): `Promise`<`UploadedFile`> Defined in: [s3-storage.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L37) #### Parameters [Section titled “Parameters”](#parameters-4) ##### stream [Section titled “stream”](#stream) `Readable` ##### info [Section titled “info”](#info) `UploadFileInfo` #### Returns [Section titled “Returns”](#returns-4) `Promise`<`UploadedFile`> # s3Storage > **s3Storage**(`config`): [`S3StorageAdapter`](/api/mantlejs/storage-s3/classes/s3storageadapter/) Defined in: [s3-storage.ts:109](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L109) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) [`S3StorageConfig`](/api/mantlejs/storage-s3/interfaces/s3storageconfig/) ## Returns [Section titled “Returns”](#returns) [`S3StorageAdapter`](/api/mantlejs/storage-s3/classes/s3storageadapter/) # S3StorageConfig Defined in: [s3-storage.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L6) ## Properties [Section titled “Properties”](#properties) ### acl? [Section titled “acl?”](#acl) > `optional` **acl?**: `"private"` | `"public-read"` Defined in: [s3-storage.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L14) *** ### bucket [Section titled “bucket”](#bucket) > **bucket**: `string` Defined in: [s3-storage.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L7) *** ### credentials? [Section titled “credentials?”](#credentials) > `optional` **credentials?**: `object` Defined in: [s3-storage.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L9) #### accessKeyId [Section titled “accessKeyId”](#accesskeyid) > **accessKeyId**: `string` #### secretAccessKey [Section titled “secretAccessKey”](#secretaccesskey) > **secretAccessKey**: `string` *** ### key? [Section titled “key?”](#key) > `optional` **key?**: (`file`) => `string` Defined in: [s3-storage.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L15) #### Parameters [Section titled “Parameters”](#parameters) ##### file [Section titled “file”](#file) `UploadFileInfo` #### Returns [Section titled “Returns”](#returns) `string` *** ### keyPrefix? [Section titled “keyPrefix?”](#keyprefix) > `optional` **keyPrefix?**: `string` Defined in: [s3-storage.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L13) *** ### region [Section titled “region”](#region) > **region**: `string` Defined in: [s3-storage.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage-s3/src/lib/s3-storage.ts#L8) # diskStorage > **diskStorage**(`config`): [`StorageAdapter`](/api/mantlejs/storage/interfaces/storageadapter/) Defined in: [disk-storage.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/disk-storage.ts#L22) ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) [`DiskStorageConfig`](/api/mantlejs/storage/interfaces/diskstorageconfig/) ## Returns [Section titled “Returns”](#returns) [`StorageAdapter`](/api/mantlejs/storage/interfaces/storageadapter/) # handleUpload > **handleUpload**(`field`, `options?`): [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) Defined in: [handle-upload.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/handle-upload.ts#L8) ## Parameters [Section titled “Parameters”](#parameters) ### field [Section titled “field”](#field) `string` ### options? [Section titled “options?”](#options) [`HandleUploadOptions`](/api/mantlejs/storage/interfaces/handleuploadoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`HookFunction`](/api/mantlejs/mantle/type-aliases/hookfunction/) # upload > **upload**(`config?`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [upload.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/upload.ts#L7) ## Parameters [Section titled “Parameters”](#parameters) ### config? [Section titled “config?”](#config) [`UploadConfig`](/api/mantlejs/storage/interfaces/uploadconfig/) = `{}` ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # DiskStorageConfig Defined in: [types.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L9) ## Properties [Section titled “Properties”](#properties) ### destination [Section titled “destination”](#destination) > **destination**: `string` Defined in: [types.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L10) *** ### filename? [Section titled “filename?”](#filename) > `optional` **filename?**: (`info`) => `string` Defined in: [types.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L11) #### Parameters [Section titled “Parameters”](#parameters) ##### info [Section titled “info”](#info) [`UploadFileInfo`](/api/mantlejs/storage/interfaces/uploadfileinfo/) #### Returns [Section titled “Returns”](#returns) `string` # GetSignedUrlOptions Defined in: [types.ts:29](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L29) ## Properties [Section titled “Properties”](#properties) ### expiresIn? [Section titled “expiresIn?”](#expiresin) > `optional` **expiresIn?**: `number` Defined in: [types.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L30) # HandleUploadOptions Defined in: [types.ts:40](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L40) ## Properties [Section titled “Properties”](#properties) ### required? [Section titled “required?”](#required) > `optional` **required?**: `boolean` Defined in: [types.ts:41](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L41) # StorageAdapter Defined in: [types.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L33) ## Methods [Section titled “Methods”](#methods) ### delete() [Section titled “delete()”](#delete) > **delete**(`key`): `Promise`<`void`> Defined in: [types.ts:36](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L36) #### Parameters [Section titled “Parameters”](#parameters) ##### key [Section titled “key”](#key) `string` #### Returns [Section titled “Returns”](#returns) `Promise`<`void`> *** ### getSignedUrl()? [Section titled “getSignedUrl()?”](#getsignedurl) > `optional` **getSignedUrl**(`key`, `options?`): `Promise`<`string`> Defined in: [types.ts:37](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L37) #### Parameters [Section titled “Parameters”](#parameters-1) ##### key [Section titled “key”](#key-1) `string` ##### options? [Section titled “options?”](#options) [`GetSignedUrlOptions`](/api/mantlejs/storage/interfaces/getsignedurloptions/) #### Returns [Section titled “Returns”](#returns-1) `Promise`<`string`> *** ### retrieve() [Section titled “retrieve()”](#retrieve) > **retrieve**(`key`): `Promise`<`Readable`> Defined in: [types.ts:35](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L35) #### Parameters [Section titled “Parameters”](#parameters-2) ##### key [Section titled “key”](#key-2) `string` #### Returns [Section titled “Returns”](#returns-2) `Promise`<`Readable`> *** ### store() [Section titled “store()”](#store) > **store**(`stream`, `info`): `Promise`<[`UploadedFile`](/api/mantlejs/storage/interfaces/uploadedfile/)> Defined in: [types.ts:34](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L34) #### Parameters [Section titled “Parameters”](#parameters-3) ##### stream [Section titled “stream”](#stream) `Readable` ##### info [Section titled “info”](#info) [`UploadFileInfo`](/api/mantlejs/storage/interfaces/uploadfileinfo/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<[`UploadedFile`](/api/mantlejs/storage/interfaces/uploadedfile/)> # UploadConfig Defined in: [types.ts:3](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L3) ## Properties [Section titled “Properties”](#properties) ### allowedMimeTypes? [Section titled “allowedMimeTypes?”](#allowedmimetypes) > `optional` **allowedMimeTypes?**: `string`\[] Defined in: [types.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L5) *** ### maxFileSize? [Section titled “maxFileSize?”](#maxfilesize) > `optional` **maxFileSize?**: `number` Defined in: [types.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L4) *** ### storage? [Section titled “storage?”](#storage) > `optional` **storage?**: [`StorageAdapter`](/api/mantlejs/storage/interfaces/storageadapter/) Defined in: [types.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L6) # UploadedFile Defined in: [types.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L14) ## Properties [Section titled “Properties”](#properties) ### fieldname [Section titled “fieldname”](#fieldname) > **fieldname**: `string` Defined in: [types.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L15) *** ### key [Section titled “key”](#key) > **key**: `string` Defined in: [types.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L20) *** ### mimetype [Section titled “mimetype”](#mimetype) > **mimetype**: `string` Defined in: [types.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L17) *** ### originalname [Section titled “originalname”](#originalname) > **originalname**: `string` Defined in: [types.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L16) *** ### path [Section titled “path”](#path) > **path**: `string` Defined in: [types.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L19) *** ### size [Section titled “size”](#size) > **size**: `number` Defined in: [types.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L18) # UploadEngine Defined in: [types.ts:44](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L44) ## Properties [Section titled “Properties”](#properties) ### allowedMimeTypes [Section titled “allowedMimeTypes”](#allowedmimetypes) > `readonly` **allowedMimeTypes**: `string`\[] Defined in: [types.ts:46](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L46) *** ### maxFileSize [Section titled “maxFileSize”](#maxfilesize) > `readonly` **maxFileSize**: `number` Defined in: [types.ts:45](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L45) *** ### storage [Section titled “storage”](#storage) > `readonly` **storage**: [`StorageAdapter`](/api/mantlejs/storage/interfaces/storageadapter/) Defined in: [types.ts:47](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L47) # UploadFileInfo Defined in: [types.ts:23](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L23) ## Properties [Section titled “Properties”](#properties) ### fieldname [Section titled “fieldname”](#fieldname) > **fieldname**: `string` Defined in: [types.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L24) *** ### mimetype [Section titled “mimetype”](#mimetype) > **mimetype**: `string` Defined in: [types.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L26) *** ### originalname [Section titled “originalname”](#originalname) > **originalname**: `string` Defined in: [types.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/storage/src/lib/types.ts#L25) # @mantlejs/supabase ## Classes [Section titled “Classes”](#classes) * [SupabaseRepository](/api/mantlejs/supabase/classes/supabaserepository/) ## Interfaces [Section titled “Interfaces”](#interfaces) * [SupabaseAdapterOptions](/api/mantlejs/supabase/interfaces/supabaseadapteroptions/) * [SupabaseConfig](/api/mantlejs/supabase/interfaces/supabaseconfig/) * [SyncAdapter](/api/mantlejs/supabase/interfaces/syncadapter/) * [SyncMessage](/api/mantlejs/supabase/interfaces/syncmessage/) ## Variables [Section titled “Variables”](#variables) * [SUPABASE_OPERATORS](/api/mantlejs/supabase/variables/supabase_operators/) ## Functions [Section titled “Functions”](#functions) * [supabase](/api/mantlejs/supabase/functions/supabase/) * [supabaseAdapter](/api/mantlejs/supabase/functions/supabaseadapter/) # SupabaseRepository Defined in: [supabase-repository.ts:57](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L57) Abstract base class for Supabase-backed repositories. Extend this class and set `tableName` to implement a Mantle `Repository` backed by a Supabase (PostgreSQL) table via the Supabase JS client. ## Example [Section titled “Example”](#example) ```ts class UserRepository extends SupabaseRepository { readonly tableName = "users"; async findByEmail(email: string): Promise { const { data, error } = await this.db.select("*").eq("email", email).maybeSingle(); if (error) throw this.wrapError(error); return data ?? null; } } ``` ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### T [Section titled “T”](#t) `T` *extends* `Record`<`string`, `unknown`> ### D [Section titled “D”](#d) `D` = `Partial`<`T`> ## Implements [Section titled “Implements”](#implements) * [`Repository`](/api/mantlejs/mantle/interfaces/repository/)<`T`, `D`> ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new SupabaseRepository**<`T`, `D`>(`app`): `SupabaseRepository`<`T`, `D`> Defined in: [supabase-repository.ts:89](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L89) #### Parameters [Section titled “Parameters”](#parameters) ##### app [Section titled “app”](#app) [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) #### Returns [Section titled “Returns”](#returns) `SupabaseRepository`<`T`, `D`> ## Properties [Section titled “Properties”](#properties) ### app [Section titled “app”](#app-1) > `protected` `readonly` **app**: [`MantleApplication`](/api/mantlejs/mantle/interfaces/mantleapplication/) Defined in: [supabase-repository.ts:61](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L61) *** ### client [Section titled “client”](#client) > `protected` `readonly` **client**: `SupabaseClient` Defined in: [supabase-repository.ts:60](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L60) *** ### listenToChanges [Section titled “listenToChanges”](#listentochanges) > `readonly` **listenToChanges**: `boolean` = `false` Defined in: [supabase-repository.ts:87](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L87) When true, subscribes to Postgres Changes for this table and re-emits direct DB mutations (PostgREST, migrations, Supabase Studio) as Mantle `service:event` emissions on the app event bus. These emissions carry `{ external: true }` as their params so consumers can distinguish them from hook-pipeline events; external UPDATEs always map to `"patched"`. #### Default [Section titled “Default”](#default) ```ts false ``` *** ### primaryKey [Section titled “primaryKey”](#primarykey) > `readonly` **primaryKey**: `string` = `"id"` Defined in: [supabase-repository.ts:70](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L70) The primary key column name. #### Default [Section titled “Default”](#default-1) ```ts "id" ``` *** ### tableName [Section titled “tableName”](#tablename) > `abstract` `readonly` **tableName**: `string` Defined in: [supabase-repository.ts:64](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L64) Supabase table name. *** ### timestamps [Section titled “timestamps”](#timestamps) > `readonly` **timestamps**: `boolean` = `true` Defined in: [supabase-repository.ts:77](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L77) When true, `save` / `saveAll` / `updateById` / `patchById` automatically write `created_at` and `updated_at` ISO-8601 timestamps. #### Default [Section titled “Default”](#default-2) ```ts true ``` ## Accessors [Section titled “Accessors”](#accessors) ### db [Section titled “db”](#db) #### Get Signature [Section titled “Get Signature”](#get-signature) > **get** `protected` **db**(): `AnyQuery` Defined in: [supabase-repository.ts:153](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L153) Returns a query builder scoped to this repository’s table. Useful for writing custom queries in subclasses. ##### Returns [Section titled “Returns”](#returns-1) `AnyQuery` ## Methods [Section titled “Methods”](#methods) ### applyParams() [Section titled “applyParams()”](#applyparams) > `protected` **applyParams**(`query`, `params?`): `AnyQuery` Defined in: [supabase-repository.ts:176](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L176) Apply `QueryParams` filters, sorting, and pagination to a query builder. Subclasses may call this to re-use the standard filter logic in custom queries. #### Parameters [Section titled “Parameters”](#parameters-1) ##### query [Section titled “query”](#query) `AnyQuery` ##### params? [Section titled “params?”](#params) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-2) `AnyQuery` *** ### count() [Section titled “count()”](#count) > **count**(`params?`): `Promise`<`number`> Defined in: [supabase-repository.ts:477](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L477) #### Parameters [Section titled “Parameters”](#parameters-2) ##### params? [Section titled “params?”](#params-1) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-3) `Promise`<`number`> #### Implementation of [Section titled “Implementation of”](#implementation-of) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`count`](/api/mantlejs/mantle/interfaces/repository/#count) *** ### deleteById() [Section titled “deleteById()”](#deletebyid) > **deleteById**(`id`): `Promise`<`T`> Defined in: [supabase-repository.ts:456](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L456) #### Parameters [Section titled “Parameters”](#parameters-3) ##### id [Section titled “id”](#id) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-4) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-1) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`deleteById`](/api/mantlejs/mantle/interfaces/repository/#deletebyid) *** ### describe() [Section titled “describe()”](#describe) > **describe**(): [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) Defined in: [supabase-repository.ts:341](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L341) Optional capability introspection. All #### Returns [Section titled “Returns”](#returns-5) [`RepositoryCapabilities`](/api/mantlejs/mantle/interfaces/repositorycapabilities/) #### Mantlejs [Section titled “Mantlejs”](#mantlejs) adapters implement it; user repositories may omit it. #### Implementation of [Section titled “Implementation of”](#implementation-of-2) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`describe`](/api/mantlejs/mantle/interfaces/repository/#describe) *** ### findAll() [Section titled “findAll()”](#findall) > **findAll**(`params?`): `Promise`<`T`\[]> Defined in: [supabase-repository.ts:353](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L353) #### Parameters [Section titled “Parameters”](#parameters-4) ##### params? [Section titled “params?”](#params-2) [`QueryParams`](/api/mantlejs/mantle/interfaces/queryparams/) #### Returns [Section titled “Returns”](#returns-6) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-3) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findAll`](/api/mantlejs/mantle/interfaces/repository/#findall) *** ### findById() [Section titled “findById()”](#findbyid) > **findById**(`id`): `Promise`<`T` | `null`> Defined in: [supabase-repository.ts:369](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L369) #### Parameters [Section titled “Parameters”](#parameters-5) ##### id [Section titled “id”](#id-1) [`Id`](/api/mantlejs/mantle/type-aliases/id/) #### Returns [Section titled “Returns”](#returns-7) `Promise`<`T` | `null`> #### Implementation of [Section titled “Implementation of”](#implementation-of-4) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`findById`](/api/mantlejs/mantle/interfaces/repository/#findbyid) *** ### patchById() [Section titled “patchById()”](#patchbyid) > **patchById**(`id`, `data`): `Promise`<`T`> Defined in: [supabase-repository.ts:432](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L432) #### Parameters [Section titled “Parameters”](#parameters-6) ##### id [Section titled “id”](#id-2) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data) `D` #### Returns [Section titled “Returns”](#returns-8) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-5) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`patchById`](/api/mantlejs/mantle/interfaces/repository/#patchbyid) *** ### save() [Section titled “save()”](#save) > **save**(`data`): `Promise`<`T`> Defined in: [supabase-repository.ts:383](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L383) #### Parameters [Section titled “Parameters”](#parameters-7) ##### data [Section titled “data”](#data-1) `D` #### Returns [Section titled “Returns”](#returns-9) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-6) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`save`](/api/mantlejs/mantle/interfaces/repository/#save) *** ### saveAll() [Section titled “saveAll()”](#saveall) > **saveAll**(`data`): `Promise`<`T`\[]> Defined in: [supabase-repository.ts:398](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L398) #### Parameters [Section titled “Parameters”](#parameters-8) ##### data [Section titled “data”](#data-2) `D`\[] #### Returns [Section titled “Returns”](#returns-10) `Promise`<`T`\[]> #### Implementation of [Section titled “Implementation of”](#implementation-of-7) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`saveAll`](/api/mantlejs/mantle/interfaces/repository/#saveall) *** ### updateById() [Section titled “updateById()”](#updatebyid) > **updateById**(`id`, `data`): `Promise`<`T`> Defined in: [supabase-repository.ts:413](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L413) #### Parameters [Section titled “Parameters”](#parameters-9) ##### id [Section titled “id”](#id-3) [`Id`](/api/mantlejs/mantle/type-aliases/id/) ##### data [Section titled “data”](#data-3) `D` #### Returns [Section titled “Returns”](#returns-11) `Promise`<`T`> #### Implementation of [Section titled “Implementation of”](#implementation-of-8) [`Repository`](/api/mantlejs/mantle/interfaces/repository/).[`updateById`](/api/mantlejs/mantle/interfaces/repository/#updatebyid) *** ### wrapError() [Section titled “wrapError()”](#wraperror) > `protected` **wrapError**(`err`): `Error` Defined in: [supabase-repository.ts:504](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L504) #### Parameters [Section titled “Parameters”](#parameters-10) ##### err [Section titled “err”](#err) `unknown` #### Returns [Section titled “Returns”](#returns-12) `Error` # supabase > **supabase**(`config`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [supabase.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase.ts#L24) Mantle plugin that creates a Supabase client and stores it on the application. ## Parameters [Section titled “Parameters”](#parameters) ### config [Section titled “config”](#config) [`SupabaseConfig`](/api/mantlejs/supabase/interfaces/supabaseconfig/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) ## Example [Section titled “Example”](#example) ```ts const app = mantle().configure(supabase({ url: process.env.SUPABASE_URL!, key: process.env.SUPABASE_KEY!, })); ``` # supabaseAdapter > **supabaseAdapter**(`options?`): [`SyncAdapter`](/api/mantlejs/supabase/interfaces/syncadapter/) Defined in: [supabase-adapter.ts:43](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L43) Supabase Realtime Broadcast adapter for `@mantlejs/sync`. Uses Supabase Realtime Broadcast channels as the pub/sub transport, replacing Redis for teams already using Supabase infrastructure. ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`SupabaseAdapterOptions`](/api/mantlejs/supabase/interfaces/supabaseadapteroptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`SyncAdapter`](/api/mantlejs/supabase/interfaces/syncadapter/) ## Example [Section titled “Example”](#example) ```ts import { sync } from '@mantlejs/sync'; import { supabaseAdapter } from '@mantlejs/supabase'; app.configure(sync({ adapter: supabaseAdapter() })); ``` # SupabaseAdapterOptions Defined in: [supabase-adapter.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L20) ## Properties [Section titled “Properties”](#properties) ### key? [Section titled “key?”](#key) > `optional` **key?**: `string` Defined in: [supabase-adapter.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L24) Service role key. Falls back to SUPABASE_SERVICE_KEY or SUPABASE_KEY env var. *** ### url? [Section titled “url?”](#url) > `optional` **url?**: `string` Defined in: [supabase-adapter.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L22) Supabase project URL. Falls back to SUPABASE_URL env var. # SupabaseConfig Defined in: [supabase.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase.ts#L4) ## Properties [Section titled “Properties”](#properties) ### key [Section titled “key”](#key) > **key**: `string` Defined in: [supabase.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase.ts#L8) Supabase `anon` or `service_role` API key. *** ### options? [Section titled “options?”](#options) > `optional` **options?**: `SupabaseClientOptions`<`string`> Defined in: [supabase.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase.ts#L10) Additional Supabase client options. *** ### url [Section titled “url”](#url) > **url**: `string` Defined in: [supabase.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase.ts#L6) Supabase project URL (e.g. “”). # SyncAdapter Defined in: [supabase-adapter.ts:14](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L14) Pluggable transport interface consumed by `@mantlejs/sync`. ## Methods [Section titled “Methods”](#methods) ### close() [Section titled “close()”](#close) > **close**(): `Promise`<`void`> Defined in: [supabase-adapter.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L17) #### Returns [Section titled “Returns”](#returns) `Promise`<`void`> *** ### publish() [Section titled “publish()”](#publish) > **publish**(`channel`, `message`): `Promise`<`void`> Defined in: [supabase-adapter.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L15) #### Parameters [Section titled “Parameters”](#parameters) ##### channel [Section titled “channel”](#channel) `string` ##### message [Section titled “message”](#message) [`SyncMessage`](/api/mantlejs/supabase/interfaces/syncmessage/) #### Returns [Section titled “Returns”](#returns-1) `Promise`<`void`> *** ### subscribe() [Section titled “subscribe()”](#subscribe) > **subscribe**(`channel`, `handler`): `Promise`<`void`> Defined in: [supabase-adapter.ts:16](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L16) #### Parameters [Section titled “Parameters”](#parameters-1) ##### channel [Section titled “channel”](#channel-1) `string` ##### handler [Section titled “handler”](#handler) (`message`) => `void` #### Returns [Section titled “Returns”](#returns-2) `Promise`<`void`> # SyncMessage Defined in: [supabase-adapter.ts:4](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L4) Cross-instance sync message — mirrors the shape expected by `@mantlejs/sync`. ## Properties [Section titled “Properties”](#properties) ### event [Section titled “event”](#event) > **event**: `string` Defined in: [supabase-adapter.ts:8](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L8) *** ### originId [Section titled “originId”](#originid) > **originId**: `string` Defined in: [supabase-adapter.ts:6](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L6) UUID identifying the originating process instance. Used for deduplication. *** ### params [Section titled “params”](#params) > **params**: `Record`<`string`, `unknown`> Defined in: [supabase-adapter.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L10) *** ### path [Section titled “path”](#path) > **path**: `string` Defined in: [supabase-adapter.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L7) *** ### result [Section titled “result”](#result) > **result**: `unknown` Defined in: [supabase-adapter.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-adapter.ts#L9) # SUPABASE_OPERATORS > `const` **SUPABASE_OPERATORS**: `ReadonlySet`<`string`> Defined in: [supabase-repository.ts:22](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/supabase/src/lib/supabase-repository.ts#L22) All query operators supported by the Supabase adapter. # @mantlejs/sync ## Interfaces [Section titled “Interfaces”](#interfaces) * [RedisAdapterOptions](/api/mantlejs/sync/interfaces/redisadapteroptions/) * [SyncAdapter](/api/mantlejs/sync/interfaces/syncadapter/) * [SyncMessage](/api/mantlejs/sync/interfaces/syncmessage/) * [SyncMessageParams](/api/mantlejs/sync/interfaces/syncmessageparams/) * [SyncOptions](/api/mantlejs/sync/interfaces/syncoptions/) ## Functions [Section titled “Functions”](#functions) * [redisAdapter](/api/mantlejs/sync/functions/redisadapter/) * [sync](/api/mantlejs/sync/functions/sync/) # redisAdapter > **redisAdapter**(`options?`): [`SyncAdapter`](/api/mantlejs/sync/interfaces/syncadapter/) Defined in: [redis-adapter.ts:53](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/redis-adapter.ts#L53) Redis pub/sub adapter for `@mantlejs/sync`. Uses two separate ioredis connections — one dedicated to publishing and one to subscribing — as required by the Redis pub/sub protocol. Compatible with DragonflyDB (drop-in Redis replacement). `ioredis` must be installed as a peer dependency: ```plaintext npm install ioredis ``` ## Parameters [Section titled “Parameters”](#parameters) ### options? [Section titled “options?”](#options) [`RedisAdapterOptions`](/api/mantlejs/sync/interfaces/redisadapteroptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`SyncAdapter`](/api/mantlejs/sync/interfaces/syncadapter/) ## Example [Section titled “Example”](#example) ```ts import { sync, redisAdapter } from '@mantlejs/sync'; app.configure(sync({ adapter: redisAdapter({ url: process.env.REDIS_URL }) })); ``` # sync > **sync**(`options`): [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) Defined in: [sync.ts:81](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L81) ## Parameters [Section titled “Parameters”](#parameters) ### options [Section titled “options”](#options) [`SyncOptions`](/api/mantlejs/sync/interfaces/syncoptions/) ## Returns [Section titled “Returns”](#returns) [`MantlePlugin`](/api/mantlejs/mantle/type-aliases/mantleplugin/) # RedisAdapterOptions Defined in: [redis-adapter.ts:5](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/redis-adapter.ts#L5) ## Properties [Section titled “Properties”](#properties) ### db? [Section titled “db?”](#db) > `optional` **db?**: `number` Defined in: [redis-adapter.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/redis-adapter.ts#L15) Redis database index. Defaults to 0. *** ### host? [Section titled “host?”](#host) > `optional` **host?**: `string` Defined in: [redis-adapter.ts:7](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/redis-adapter.ts#L7) Redis server hostname. Defaults to “127.0.0.1”. Ignored when `url` is set. *** ### password? [Section titled “password?”](#password) > `optional` **password?**: `string` Defined in: [redis-adapter.ts:13](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/redis-adapter.ts#L13) Redis password / AUTH token. *** ### port? [Section titled “port?”](#port) > `optional` **port?**: `number` Defined in: [redis-adapter.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/redis-adapter.ts#L9) Redis server port. Defaults to 6379. Ignored when `url` is set. *** ### tls? [Section titled “tls?”](#tls) > `optional` **tls?**: `boolean` Defined in: [redis-adapter.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/redis-adapter.ts#L17) Enable TLS. *** ### url? [Section titled “url?”](#url) > `optional` **url?**: `string` Defined in: [redis-adapter.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/redis-adapter.ts#L11) Full Redis connection URL (e.g. “redis\://user:pass\@host:6379/0”). Takes precedence over host/port. # SyncAdapter Defined in: [sync.ts:24](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L24) ## Methods [Section titled “Methods”](#methods) ### close() [Section titled “close()”](#close) > **close**(): `Promise`<`void`> Defined in: [sync.ts:27](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L27) #### Returns [Section titled “Returns”](#returns) `Promise`<`void`> *** ### publish() [Section titled “publish()”](#publish) > **publish**(`channel`, `message`): `Promise`<`void`> Defined in: [sync.ts:25](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L25) #### Parameters [Section titled “Parameters”](#parameters) ##### channel [Section titled “channel”](#channel) `string` ##### message [Section titled “message”](#message) [`SyncMessage`](/api/mantlejs/sync/interfaces/syncmessage/) #### Returns [Section titled “Returns”](#returns-1) `Promise`<`void`> *** ### subscribe() [Section titled “subscribe()”](#subscribe) > **subscribe**(`channel`, `handler`): `Promise`<`void`> Defined in: [sync.ts:26](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L26) #### Parameters [Section titled “Parameters”](#parameters-1) ##### channel [Section titled “channel”](#channel-1) `string` ##### handler [Section titled “handler”](#handler) (`message`) => `void` #### Returns [Section titled “Returns”](#returns-2) `Promise`<`void`> # SyncMessage Defined in: [sync.ts:15](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L15) ## Properties [Section titled “Properties”](#properties) ### event [Section titled “event”](#event) > **event**: `string` Defined in: [sync.ts:19](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L19) *** ### originId [Section titled “originId”](#originid) > **originId**: `string` Defined in: [sync.ts:17](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L17) UUID identifying the originating process instance. Used for deduplication. *** ### params [Section titled “params”](#params) > **params**: [`SyncMessageParams`](/api/mantlejs/sync/interfaces/syncmessageparams/) Defined in: [sync.ts:21](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L21) *** ### path [Section titled “path”](#path) > **path**: `string` Defined in: [sync.ts:18](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L18) *** ### result [Section titled “result”](#result) > **result**: `unknown` Defined in: [sync.ts:20](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L20) # SyncMessageParams Defined in: [sync.ts:9](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L9) The whitelisted subset of `ServiceParams` that crosses the broker. Never carries `headers` (live credentials), the full `user` record, or the non-serializable `connection`. ## Properties [Section titled “Properties”](#properties) ### provider? [Section titled “provider?”](#provider) > `optional` **provider?**: `string` Defined in: [sync.ts:10](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L10) *** ### query? [Section titled “query?”](#query) > `optional` **query?**: `Record`<`string`, `unknown`> Defined in: [sync.ts:11](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L11) *** ### user? [Section titled “user?”](#user) > `optional` **user?**: `object` Defined in: [sync.ts:12](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L12) #### id [Section titled “id”](#id) > **id**: `string` | `number` # SyncOptions Defined in: [sync.ts:30](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L30) ## Properties [Section titled “Properties”](#properties) ### adapter [Section titled “adapter”](#adapter) > **adapter**: [`SyncAdapter`](/api/mantlejs/sync/interfaces/syncadapter/) Defined in: [sync.ts:31](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L31) *** ### channel? [Section titled “channel?”](#channel) > `optional` **channel?**: `string` Defined in: [sync.ts:33](https://github.com/MantleJs/mantle/blob/06f0355cec1ca3a3b29f3e7f63fc3a426630d898/packages/sync/src/lib/sync.ts#L33) Pub/sub channel name shared across all instances. Defaults to “mantle:sync”.