@aventara/next 0.0.0-stage → 1.0.0-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,108 @@
1
+ # @aventara/next
2
+
3
+ ## 1.0.0-rc.10
4
+
5
+ ### Patch Changes
6
+
7
+ - 04bb39a: `engines.node` is now `^22.23.4 || >=24.22.1`, the Node security releases of 2026-10-20 (CVE-2026-105481,
8
+ CVE-2026-105482, CVE-2026-105483, CVE-2026-106069, CVE-2026-106072, CVE-2026-106073, CVE-2026-106074,
9
+ CVE-2026-111497). The bins refuse an older Node in one sentence naming that range.
10
+ - 587dd01: The route handlers answer `POST {api}/_access/can` when the Framework is built with `access`, through
11
+ `AvProtocol.bind(framework).handleCan`, with the request's state seeded as on every other protocol route.
12
+ - d7e7369: `avClient.$access`: `framework.serverClient.$access` with the request filled in. `can` is answered under the
13
+ request's state; `me()` and the other Aventara Access verbs run in process with the headers of `next/headers` (a
14
+ Server Action's sign-in sets its cookie through Access itself). `ready()` registers the verbs for
15
+ `@/aventara/universal` on the server.
16
+ - 7ee547b: `avClient.$access` carries Aventara Access's `kiosk` verbs (`switchUser`, `lock`, `setPin`, `enrollDevice`,
17
+ `revokeDevice`) when the provider states them, run in process with the headers of `next/headers`, so a Server Action's
18
+ switch sets the rotated device cookie through Access itself; `ready()` registers them for `@/aventara/universal`.
19
+ - 88263a1: Documentation: `$access.can([])` answers `[]`, not a refusal; the MCP README states the query and fragment rule for
20
+ every authorization server.
21
+ - 15f3402: `avClient.$access` holds only the verbs the access provider offers, as `serverClient.$access` does. Calling another one
22
+ rejects with `AvProtocolError` `A2002`, naming why (no `access`, or a provider that does not offer that verb), instead
23
+ of a `TypeError`.
24
+
25
+ In `next dev`, an edit that changes only a function (a guard's or a pipe's body, the adapter factory) builds the
26
+ Framework again; it no longer needs a restart.
27
+
28
+ - Updated dependencies [587dd01]
29
+ - Updated dependencies [d7e7369]
30
+ - Updated dependencies [7ee547b]
31
+ - Updated dependencies [f52c499]
32
+ - Updated dependencies [2907484]
33
+ - Updated dependencies [a6bbdf0]
34
+ - Updated dependencies [3e26549]
35
+ - Updated dependencies [a6bbdf0]
36
+ - Updated dependencies [6f3f6f6]
37
+ - Updated dependencies [04bb39a]
38
+ - Updated dependencies [d7e7369]
39
+ - Updated dependencies [3fb3333]
40
+ - Updated dependencies [827ccb6]
41
+ - Updated dependencies [7f5abc3]
42
+ - Updated dependencies [88263a1]
43
+ - Updated dependencies [10341d5]
44
+ - Updated dependencies [8129844]
45
+ - Updated dependencies [88263a1]
46
+ - Updated dependencies [3020b5c]
47
+ - Updated dependencies [88263a1]
48
+ - Updated dependencies [88263a1]
49
+ - @aventara/core@1.0.0-rc.10
50
+ - @aventara/mcp@1.0.0-rc.10
51
+
52
+ ## 1.0.0-rc.9
53
+
54
+ ### Patch Changes
55
+
56
+ - 09c5703: New package `@aventara/next`: hosts an Aventara Framework in a Next.js App Router application (Next `>=16.4 <17`,
57
+ Node runtime). It is in the fixed version group, released at the same version as every other package. `react` (`^19`)
58
+ is a peer, and `@aventara/mcp` an optional one. The entry is server-only.
59
+
60
+ - `Aventara({ ...aventaraConfig, adapter, state })` takes the configuration `createFramework` takes, flat. `adapter`
61
+ may be a function that builds the adapter (it may return a promise), and `state` (`({ headers, request }) => state`)
62
+ seeds each request's `ctx.$state`. It returns at once and builds nothing: the Framework is created on first use,
63
+ once.
64
+ - It returns `{ handlers, avClient, avApp, ready }`. `handlers` is `{ GET, POST, PUT, PATCH, DELETE }` for one
65
+ catch-all route file (`src/app/api/[...aventara]/route.ts`), serving the protocol's operation routes,
66
+ `GET /_contract` and `POST /_transactions`. `avClient` is the client layer for Server Components and Server Actions
67
+ (`framework.serverClient`, with this request's `state`). `avApp` is the application layer for trusted server code
68
+ (`framework.application`). `ready()` builds the Framework once and rejects with the construction error, so
69
+ `src/instrumentation.ts` can await it and a bad configuration fails `next start`.
70
+ - `Aventara({ framework, state })` hosts a Framework you built with `createFramework`, as
71
+ `AventaraModule.forRoot({ framework })` does in NestJS. A Framework and a configuration together are a type error,
72
+ and are refused when `Aventara()` is called.
73
+ - `access`: an access provider (from `@aventara/core`). Its `policy` checks each request and its `state` seeds
74
+ `ctx.$state`, called with the same `{ headers, request }`. It does not go with `state` or `policy`.
75
+ - `aventara-build --client` writes `@/aventara/client` (the browser client) and `@/aventara/universal`, whose `avClient`
76
+ calls `/api` in the browser and answers the same request in process on the server. `avDehydrate` (this package) and
77
+ `avHydrate` (`@/aventara/client`) carry `Date`, `bigint`, `Uint8Array` and decimal values from a Server Component to
78
+ a Client Component, through TanStack Query's `serializeData`/`deserializeData` or an SWR `fallback`.
79
+ - With `mcp` configured, the same `handlers` serve `@aventara/mcp`'s endpoint and its OAuth protected-resource
80
+ metadata, each from a one-line route file. On Next.js `mcp` needs `authorization`: without it the configuration is
81
+ a type error on the `Aventara({...})` call, or refused when the Framework is built. The endpoint is named at the
82
+ origin the agent called (`X-Forwarded-Host` and `X-Forwarded-Proto` when present, else `Host`).
83
+ - Every copy of `src/aventara/server.ts` that Next.js evaluates in one server process shares one Framework per `api`
84
+ path, so `ready()` in `instrumentation.ts` builds the one the routes use. In production, a different configuration
85
+ on a path already held is refused when `Aventara()` is called; in development it replaces the held one.
86
+ - `serverExternalPackages: ["@aventara/core", "@aventara/prisma7-adapter"]` in `next.config.ts` is required: Node.js
87
+ then loads core and the adapter once per server process instead of a copy per layer, so every layer gets values
88
+ and errors of the same classes and one Prisma client. `aventara new --host next` and `aventara init` add it, or
89
+ print it when they cannot edit the config safely.
90
+ - A call reads the database, so it runs at request time: every `avClient`, `avApp` and `@/aventara/universal` call
91
+ first awaits Next's `io()` (`next/cache`). Under Cache Components (`cacheComponents: true`, create-next-app's
92
+ default) `next build` never runs one while it prerenders a page, and a page that calls one needs what any uncached
93
+ database read needs: the calling component inside `<Suspense>` (or a `loading.tsx`), `"use cache"` on a function
94
+ that makes the call, or `export const instant = false`. Without one, `next build` fails with Next's own message
95
+ naming the three. In a request, inside `"use cache"`, in a Server Action, in an `after()` callback and without
96
+ Cache Components the call runs at once.
97
+ - `AvNextHandlers` (the type of `handlers`), `AvNextClientTransaction` (`avClient.transaction`) and
98
+ `AvNextAppTransaction` (`avApp.transaction`) are exported, so a project that emits declarations
99
+ (`declaration: true`) can write `export const { handlers, avClient, avApp, ready } = Aventara(...)` with no
100
+ annotation. `AvNextStateContext`, what the `state` option is handed (`{ headers, request? }`), is exported too,
101
+ for a `state` function written in a file of its own.
102
+
103
+ - Updated dependencies [205a379]
104
+ - Updated dependencies [a0bb05e]
105
+ - Updated dependencies [86ab319]
106
+ - Updated dependencies [fdf52e6]
107
+ - @aventara/core@1.0.0-rc.9
108
+ - @aventara/mcp@1.0.0-rc.9
package/LICENSE ADDED
@@ -0,0 +1,91 @@
1
+ Required Notice: Copyright 2026 Mohamed Ragheb
2
+
3
+ # PolyForm Shield License 1.0.0
4
+
5
+ <https://polyformproject.org/licenses/shield/1.0.0>
6
+
7
+ ## Acceptance
8
+
9
+ In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses.
10
+
11
+ ## Copyright License
12
+
13
+ The licensor grants you a copyright license for the software to do everything you might do with the software that would otherwise infringe the licensor's copyright in it for any permitted purpose. However, you may only distribute the software according to [Distribution License](#distribution-license) and make changes or new works based on the software according to [Changes and New Works License](#changes-and-new-works-license).
14
+
15
+ ## Distribution License
16
+
17
+ The licensor grants you an additional copyright license to distribute copies of the software. Your license to distribute covers distributing the software with changes and new works permitted by [Changes and New Works License](#changes-and-new-works-license).
18
+
19
+ ## Notices
20
+
21
+ You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms or the URL for them above, as well as copies of any plain-text lines beginning with `Required Notice:` that the licensor provided with the software. For example:
22
+
23
+ > Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
24
+
25
+ ## Changes and New Works License
26
+
27
+ The licensor grants you an additional copyright license to make changes and new works based on the software for any permitted purpose.
28
+
29
+ ## Patent License
30
+
31
+ The licensor grants you a patent license for the software that covers patent claims the licensor can license, or becomes able to license, that you would infringe by using the software.
32
+
33
+ ## Noncompete
34
+
35
+ Any purpose is a permitted purpose, except for providing any product that competes with the software or any product the licensor or any of its affiliates provides using the software.
36
+
37
+ ## Competition
38
+
39
+ Goods and services compete even when they provide functionality through different kinds of interfaces or for different technical platforms. Applications can compete with services, libraries with plugins, frameworks with development tools, and so on, even if they're written in different programming languages or for different computer architectures. Goods and services compete even when provided free of charge. If you market a product as a practical substitute for the software or another product, it definitely competes.
40
+
41
+ ## New Products
42
+
43
+ If you are using the software to provide a product that does not compete, but the licensor or any of its affiliates brings your product into competition by providing a new version of the software or another product using the software, you may continue using versions of the software available under these terms beforehand to provide your competing product, but not any later versions.
44
+
45
+ ## Discontinued Products
46
+
47
+ You may begin using the software to compete with a product or service that the licensor or any of its affiliates has stopped providing, unless the licensor includes a plain-text line beginning with `Licensor Line of Business:` with the software that mentions that line of business. For example:
48
+
49
+ > Licensor Line of Business: YoyodyneCMS Content Management System (http://example.com/cms)
50
+
51
+ ## Sales of Business
52
+
53
+ If the licensor or any of its affiliates sells a line of business developing the software or using the software to provide a product, the buyer can also enforce [Noncompete](#noncompete) for that product.
54
+
55
+ ## Fair Use
56
+
57
+ You may have "fair use" rights for the software under the law. These terms do not limit them.
58
+
59
+ ## No Other Rights
60
+
61
+ These terms do not allow you to sublicense or transfer any of your licenses to anyone else, or prevent the licensor from granting licenses to anyone else. These terms do not imply any other licenses.
62
+
63
+ ## Patent Defense
64
+
65
+ If you make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
66
+
67
+ ## Violations
68
+
69
+ The first time you are notified in writing that you have violated any of these terms, or done anything with the software not covered by your licenses, your licenses can nonetheless continue if you come into full compliance with these terms, and take practical steps to correct past violations, within 32 days of receiving notice. Otherwise, all your licenses end immediately.
70
+
71
+ ## No Liability
72
+
73
+ ***As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.***
74
+
75
+ ## Definitions
76
+
77
+ The **licensor** is the individual or entity offering these terms, and the **software** is the software the licensor makes available under these terms.
78
+
79
+ A **product** can be a good or service, or a combination of them.
80
+
81
+ **You** refers to the individual or entity agreeing to these terms.
82
+
83
+ **Your company** is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all its affiliates.
84
+
85
+ **Affiliates** means the other organizations than an organization has control over, is under the control of, or is under common control with.
86
+
87
+ **Control** means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
88
+
89
+ **Your licenses** are all the licenses granted to you for the software under these terms.
90
+
91
+ **Use** means anything you do with the software requiring one of your licenses.
@@ -0,0 +1,9 @@
1
+ # Additional Permission — Aventara
2
+
3
+ Copyright 2026 Mohamed Ragheb
4
+
5
+ In addition to the permissions of the PolyForm Shield License 1.0.0 in `LICENSE`, the licensor grants the following additional permission:
6
+
7
+ The Noncompete section applies only to providing a product that competes with the software itself — the Aventara framework, its packages and its tooling. Providing a product that competes with any other product the licensor or its affiliates provide using the software is a permitted purpose.
8
+
9
+ This additional permission only adds to your permissions; it does not restrict or replace any term of `LICENSE`.
package/README.md CHANGED
@@ -1,3 +1,323 @@
1
- # Temporary Holding Version
1
+ # `@aventara/next`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Hosts an Aventara Framework in a Next.js App Router application (Next 16.4 or later 16.x, Node runtime). It serves the
4
+ framework's HTTP protocol (`AvProtocol.bind(framework)` from `@aventara/core/protocol`) from one catch-all route file.
5
+ Every rule of the protocol (routes, decoding, statuses, envelopes, headers, absence) is core's; this package only turns
6
+ a Web `Request` into the protocol's request and writes the protocol's answer back as a Web `Response`.
7
+
8
+ `@aventara/core` is its dependency; `next` (`>=16.4 <17`) and `react` (`^19`) are peers, and `@aventara/mcp` an
9
+ optional one (see MCP). The entry is server-only: a Client Component
10
+ importing it fails the Next.js build.
11
+
12
+ ## Setup
13
+
14
+ ```ts
15
+ // src/aventara/server.ts
16
+ import "server-only";
17
+ import { Aventara } from "@aventara/next";
18
+ import { createPrismaAdapter } from "@aventara/prisma7-adapter";
19
+ import { prisma } from "../prisma";
20
+ import adapter from "./adapter";
21
+ import { aventaraConfig } from "./config";
22
+
23
+ export const { handlers, avClient, avApp, ready } = Aventara({
24
+ ...aventaraConfig, // api: "/api", resources, policy, ...
25
+ adapter: () => createPrismaAdapter({ ...adapter, client: prisma }),
26
+ state: async ({ headers }) => ({ user: await userFrom(headers) }), // your auth library
27
+ });
28
+ ```
29
+
30
+ ```ts
31
+ // src/app/api/[...aventara]/route.ts (the folder matches api: "/api")
32
+ import { handlers } from "@/aventara/server";
33
+
34
+ export const { GET, POST, PUT, PATCH, DELETE } = handlers;
35
+ ```
36
+
37
+ | Option | What it is |
38
+ |---|---|
39
+ | everything `createFramework` takes | `api`, `resources`, `policy`, `limits`, `prebuilt`, ... checked exactly as `createFramework` checks them |
40
+ | `adapter` | the adapter, or a function that builds it (it may return a promise), called on the first request |
41
+ | `state` | `({ headers, request }) => state`: seeds each request's `ctx.$state` (where `policy.principal` reads the user) |
42
+ | `access` | an access provider (see `@aventara/core`): its `policy` checks each request and its `state` seeds `ctx.$state`, handed the same `{ headers, request }`. Not together with `state` or `policy` |
43
+
44
+ Or hand it a Framework you built, as `createFramework` resolved it (as `AventaraModule.forRoot({ framework })` takes
45
+ one in NestJS), with `state` beside it (or none, when it was built with `access`):
46
+
47
+ ```ts
48
+ const framework = await createFramework({ ...aventaraConfig, adapter: createPrismaAdapter({ ...adapter, client }) });
49
+ export const { handlers, avClient, avApp, ready } = Aventara({ framework, state });
50
+ ```
51
+
52
+ Every member works on that Framework, which is never built again: `avClient.tx` and `avApp.appTx` build handles at
53
+ once, and `ready()` checks it (a Resource named like a member, `mcp` without `authorization`) and registers the
54
+ universal path. A Framework and a configuration are two sources of one Framework, so the options carry one or the
55
+ other: a configuration key beside `framework` is a type error, and `Aventara()` refuses it when called, naming the
56
+ keys. So is a `framework` that is not one.
57
+
58
+ `Aventara()` returns at once and builds nothing: `next build` imports the route module, so the Framework (and the
59
+ adapter, when it is a function) is created on first use, or by `ready()`, once. A configuration `createFramework`
60
+ refuses rejects that use with its `AvConstructionError`.
61
+
62
+ | Member | What it is |
63
+ |---|---|
64
+ | `handlers` | `{ GET, POST, PUT, PATCH, DELETE }` for the catch-all route file |
65
+ | `avClient` | the client layer for Server Components and Server Actions: `framework.serverClient`, with this request's `state`; `avClient.tx` and `avClient.transaction([...])` |
66
+ | `avApp` | the application layer for trusted server code: `framework.application`; `avApp.appTx` and `avApp.transaction([...])` |
67
+ | `ready` | `() => Promise<void>`: builds the Framework once; rejects with the construction error; registers the in-process path `@/aventara/universal` calls on the server |
68
+
69
+ `handlers` is typed `AvNextHandlers`, and `avClient.transaction` and `avApp.transaction` `AvNextClientTransaction` and
70
+ `AvNextAppTransaction`, all three exported, so a project that emits declarations (`declaration: true`) writes
71
+ `export const { handlers, avClient, avApp, ready } = Aventara(...)` with no annotation. `AvNextStateContext` is what
72
+ `state` is handed (`{ headers, request? }`), for a `state` function written in a file of its own:
73
+ `export const state = async ({ headers }: AvNextStateContext) => ({ user: await userOf(headers) });`.
74
+
75
+ ## Server Components and Server Actions
76
+
77
+ ```tsx
78
+ // src/app/campaigns/page.tsx
79
+ import { Suspense } from "react";
80
+ import { avClient } from "@/aventara/server";
81
+
82
+ async function Campaigns() {
83
+ const campaigns = await avClient.Campaign.find.many({
84
+ where: { startsAt: { gt: new Date() } },
85
+ select: ["id", "name", "startsAt"],
86
+ });
87
+ return <ul>{campaigns.map((c) => <li key={c.id}>{c.name}</li>)}</ul>;
88
+ }
89
+
90
+ export default function Page() {
91
+ return (
92
+ <Suspense fallback={<p>Loading…</p>}>
93
+ <Campaigns />
94
+ </Suspense>
95
+ );
96
+ }
97
+ ```
98
+
99
+ `avClient.<Resource>.<family>.<variant>(args, options?)` is `framework.serverClient`: typed from the Client
100
+ Contract's view (what the client layer hides is absent), taking and answering application values (`Date`, `bigint`,
101
+ `AvDecimal`, `Uint8Array`). A call runs in process, with no HTTP: the same client pipelines, validation, exposure and
102
+ policy as a request to `handlers`, with the principal `policy.principal` reads from `state`. A refusal is thrown as
103
+ its `AvError` class (`AvAuthError` for the policy).
104
+
105
+ `state` is called with `{ headers }` from `next/headers` (there is no `request` outside a route call), once per
106
+ server render however many calls the render makes. Outside a render, in a Server Action's body for example, it runs
107
+ once per call. A call that names its own `state` in its options uses it as given. Without a `state` option, calls
108
+ start from `{}` and read no headers.
109
+
110
+ ### Request-time data
111
+
112
+ A call reads the database, so Next.js renders it at request time, never into the page `next build` prerenders. Every
113
+ `avClient`, `avApp` and `@/aventara/universal` call first awaits Next's `io()` (`next/cache`). Under Cache Components
114
+ (`cacheComponents: true`, which create-next-app sets), a page that calls one needs what any uncached database read
115
+ needs, one of:
116
+
117
+ - `<Suspense>` around the component that calls it (or a `loading.tsx` beside the page): the fallback is prerendered and
118
+ the data streams in at request time, as above;
119
+ - `"use cache"` on a function that makes the call: the result is cached, and prerendered with the page;
120
+ - `export const instant = false` in the page: it renders whole, at request time.
121
+
122
+ With none of them, `next build` fails with Next's own message, which names the three. In a request, inside
123
+ `"use cache"`, in a Server Action, in an `after()` callback and without Cache Components the call runs at once.
124
+
125
+ With `access`, `avClient.$access` is `framework.serverClient.$access` with this request filled in: `can([...])` is
126
+ answered under the request's state, and `me()`, `signIn(...)` and the other verbs the provider offers run Aventara
127
+ Access in process with the headers of `next/headers`. In a Server Action, Access sets a sign-in's cookie itself
128
+ (Better Auth's `nextCookies`). `@/aventara/universal`'s `$access` does the same on the server, through what `ready()`
129
+ registers, and calls Access's own routes in the browser. `$access` holds the verbs the provider offers, read from the
130
+ options before the Framework is built, as `serverClient.$access` holds them. Calling another one rejects with
131
+ `AvProtocolError` `A2002`, naming why: no `access`, or a provider whose `client` does not offer that verb.
132
+
133
+ On Access's `kiosk` profile, call `switchUser`, `lock` and `enrollDevice` from a Server Action (or from the browser),
134
+ never while rendering a Server Component: each rotates the device's credential cookie, which Next lets Access set only
135
+ in a Server Action or a Route Handler, so a rotation during a render is lost and the device's next switch is refused. Never call a verb that
136
+ changes anything (`signIn`, `signUp`, `signOut`, `resetPassword`, and the kiosk's five) inside `"use cache"`: a cached
137
+ result would replay one caller's answer to another and skip the call. Each verb's answer is projected onto its result
138
+ schema on the server too, so no session token or device secret reaches your code.
139
+
140
+ `avApp` is `framework.application`: trusted server code. It reads no request, and the policy checks a call only when
141
+ it names a principal (`{ as: principal }`).
142
+
143
+ Transactions keep their layer: `avClient.tx` builds client-layer handles (`framework.serverClientTx`) and
144
+ `avClient.transaction([...])` runs them with this request's state; `avApp.appTx` builds application handles (and
145
+ `avApp.appTx.$sql`), and `avApp.transaction([...])` runs them. A list that mixes the two is a type error, and refused
146
+ at runtime (`A2004`). Handles are built on the built Framework: `await ready()` before building them.
147
+
148
+ ## Startup
149
+
150
+ ```ts
151
+ // src/instrumentation.ts
152
+ export async function register() {
153
+ if (process.env.NEXT_RUNTIME === "nodejs") {
154
+ const { ready } = await import("@/aventara/server");
155
+ await ready();
156
+ }
157
+ }
158
+ ```
159
+
160
+ `ready()` builds the Framework and its routes, once: a second call returns the same promise. It rejects with the
161
+ construction error, so a bad configuration fails `next start`. It reads no request, so it runs where there is none
162
+ (`instrumentation.ts`, a script).
163
+
164
+ ### `serverExternalPackages` (required)
165
+
166
+ ```ts
167
+ // next.config.ts
168
+ const nextConfig: NextConfig = {
169
+ serverExternalPackages: ["@aventara/core", "@aventara/prisma7-adapter"],
170
+ };
171
+ ```
172
+
173
+ Next.js bundles a package into each layer of the server (instrumentation, route handlers, Server Components) as a
174
+ copy of its own. Listed here, `@aventara/core` and the adapter are loaded by Node.js once per server process instead:
175
+ one core, so the Framework every layer shares (below) answers each layer with values and errors of the same classes
176
+ (`AvDecimal`, `AvValidationError`, ...), and one Prisma client. `aventara new --host next` and `aventara init` add the
177
+ line (an existing config they cannot edit safely gets it printed instead). `@aventara/next` itself stays bundled: it
178
+ relies on Next.js's server aliases (`next/navigation`, `next/headers`, `server-only`).
179
+
180
+ Next.js evaluates `src/aventara/server.ts` more than once in one server process (once for `instrumentation.ts`, again
181
+ for the routes and pages). Every copy that calls `Aventara()` with the same options shares one Framework, held per
182
+ `api` path for the process, so `ready()` in `instrumentation.ts` builds the one the routes use, with one adapter, and
183
+ `avClient.tx` works from the first request. Two instances on two `api` paths hold two. In production, a different
184
+ configuration on a path already held is refused when `Aventara()` is called; in development it replaces the held one,
185
+ as `next dev` evaluates the file again after an edit. Copies are told apart by the options' data (Next.js rewrites a
186
+ function's source in each layer). An edit that changes only a function, a guard's or a pipe's body or the adapter
187
+ factory, is told by each layer from the text that layer saw last: in development the first layer to see it builds the
188
+ Framework again, and the other layers join that build. A Framework given as `{ framework }` is that call's own.
189
+
190
+ Once the Framework is built, `ready()` also registers the path `@/aventara/universal`'s `avClient` takes on the server
191
+ (below). Without `instrumentation.ts`, that `avClient` refuses its calls on the server in one sentence.
192
+
193
+ ## Prefetch and hydrate: `@/aventara/universal`, `avDehydrate`, `avHydrate`
194
+
195
+ `aventara-build --client` writes `src/aventara/universal.ts`. Its `avClient` has the browser client's types and runs on
196
+ both sides: in the browser it calls `/api`; on the server it answers the same request in process, through the bound
197
+ protocol and with this request's `state`, so a query prefetched in a Server Component and refetched in the browser
198
+ gets the same values (`Date`, `bigint`, the client's own `AvDecimal`).
199
+
200
+ A decimal is a class instance, which React refuses to pass from a Server Component to a Client Component. `avDehydrate`
201
+ (this package, server side) turns each `Date`, `bigint`, `Uint8Array` and decimal into a tagged plain object, and
202
+ `avHydrate` (`@/aventara/client`, browser side) turns them back.
203
+
204
+ ```ts
205
+ // src/app/notes/notes.queries.ts (server and browser)
206
+ import { queryOptions } from "@tanstack/react-query";
207
+ import { avClient } from "@/aventara/universal";
208
+
209
+ export const notesQuery = queryOptions({
210
+ queryKey: ["notes"],
211
+ queryFn: () => avClient.Note.find.many({ select: ["id", "title", "createdAt"] }),
212
+ });
213
+ ```
214
+
215
+ ```tsx
216
+ // src/app/notes/page.tsx (Server Component)
217
+ import { dehydrate, HydrationBoundary, QueryClient } from "@tanstack/react-query";
218
+ import { Suspense } from "react";
219
+ import { avDehydrate } from "@aventara/next";
220
+ import { notesQuery } from "./notes.queries";
221
+ import { NoteList } from "./note-list";
222
+
223
+ async function Notes() {
224
+ const queryClient = new QueryClient();
225
+ await queryClient.prefetchQuery(notesQuery);
226
+ return (
227
+ <HydrationBoundary state={dehydrate(queryClient, { serializeData: avDehydrate })}>
228
+ <NoteList />
229
+ </HydrationBoundary>
230
+ );
231
+ }
232
+
233
+ export default function Page() {
234
+ return (
235
+ <Suspense fallback={<p>Loading…</p>}>
236
+ <Notes />
237
+ </Suspense>
238
+ );
239
+ }
240
+ ```
241
+
242
+ ```tsx
243
+ // src/app/providers.tsx
244
+ "use client";
245
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
246
+ import { avHydrate } from "@/aventara/client";
247
+
248
+ const queryClient = new QueryClient({ defaultOptions: { hydrate: { deserializeData: avHydrate } } });
249
+ export const Providers = ({ children }: { children: React.ReactNode }) => (
250
+ <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
251
+ );
252
+ ```
253
+
254
+ With SWR, pass `avDehydrate({ [key]: data })` from the Server Component and `avHydrate(fallback)` to `SWRConfig`'s
255
+ `fallback` in the Client Component.
256
+
257
+ Importing `@/aventara/client` instead of `@/aventara/universal` in shared code opts out of running on the server: its
258
+ calls are refused there.
259
+
260
+ ## Routes
261
+
262
+ `handlers` answers the protocol's routes under `api`: each operation route (`POST /api/_resources/...`),
263
+ `GET /api/_contract` (with its `ETag`, and `304` on `If-None-Match`) and `POST /api/_transactions` when the
264
+ ClientContract's transactions are interactive. Every method is exported so that a wrong method on an operation route
265
+ gets the protocol's `405` (`A2012`) with `Allow`. A path the protocol does not own goes to Next's `notFound()`.
266
+
267
+ A request body is read up to `limits.maxRequestBytes` decoded bytes; `gzip`, `deflate` and `br` bodies are inflated,
268
+ and any other content coding is the protocol's `415` (`A2011`).
269
+
270
+ ## MCP
271
+
272
+ With `mcp` in the configuration, the same `handlers` also serve `@aventara/mcp`'s endpoint and its OAuth
273
+ protected-resource metadata. Add one route file for each path:
274
+
275
+ ```ts
276
+ // src/aventara/server.ts
277
+ export const { handlers } = Aventara({
278
+ ...aventaraConfig,
279
+ mcp: {
280
+ path: "/mcp",
281
+ authorization: {
282
+ servers: ["https://auth.example.com"],
283
+ verify: async (token) => (await verifyAccessToken(token)) ?? null, // your token check
284
+ },
285
+ },
286
+ adapter: () => createPrismaAdapter({ ...adapter, client: prisma }),
287
+ });
288
+ ```
289
+
290
+ ```ts
291
+ // src/app/mcp/route.ts
292
+ import { handlers } from "@/aventara/server";
293
+
294
+ export const { GET, POST, DELETE } = handlers;
295
+ ```
296
+
297
+ ```ts
298
+ // src/app/.well-known/oauth-protected-resource/mcp/route.ts
299
+ import { handlers } from "@/aventara/server";
300
+
301
+ export const { GET } = handlers;
302
+ ```
303
+
304
+ Install `@aventara/mcp` at the same version as `@aventara/core` (`npm install --save-exact @aventara/mcp@<version>`).
305
+ It is an optional peer, loaded only when `mcp` is configured; when it is missing, the first request fails with a
306
+ sentence that names the install command.
307
+
308
+ On Next.js, `mcp` needs `authorization`. Without it, `@aventara/mcp` answers only callers on the same machine that
309
+ send no `x-forwarded-for`, and Next.js adds `x-forwarded-for` to every request, so every caller would be refused. An
310
+ `mcp` without `authorization` is a type error on the `Aventara({...})` call, and, for a configuration typed wider than its literal, refused when the Framework is built (the first request), with a sentence that says so.
311
+
312
+ Everything the endpoint answers is `@aventara/mcp`'s: the bearer check and its `401` with `WWW-Authenticate`, the
313
+ tools, the tool calls (each one an agent-origin protocol request, with `state` and the token's claims in
314
+ `ctx.$state.mcp.claims`), the `405` on `GET` and `DELETE` (the endpoint is stateless) and the metadata document. The
315
+ request body is read up to the Agent Contract's `maxRequestBytes` and handed on as sent.
316
+
317
+ ## Status
318
+
319
+ A prerelease, installed with its tag:
320
+
321
+ ```bash
322
+ npm install @aventara/next@rc
323
+ ```