@aventara/next 1.0.0-rc.12 → 1.0.0-rc.14

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 CHANGED
@@ -1,5 +1,60 @@
1
1
  # @aventara/next
2
2
 
3
+ ## 1.0.0-rc.14
4
+
5
+ ### Patch Changes
6
+
7
+ - 0895a93: `avDehydrate` and `avHydrate` are typed: data handed from a Server Component to a Client Component as props keeps its type, with no cast.
8
+
9
+ - `avDehydrate(data)` (`@aventara/next`) returns an `AvDehydrated<T>` for data of type `T`, and `avHydrate(value)` (`@/aventara/client`) of an `AvDehydrated<T>` returns the `T`, its `Date`, `bigint`, `Uint8Array` and `AvDecimal` values included. Type an SWR `fallback` prop `AvDehydrated<T>` and pass `avHydrate(fallback)` to `SWRConfig` as it is.
10
+ - `AvDehydrated<T>` is new, exported by `@aventara/next` and by the `src/aventara/client.ts` that `aventara-build --client` writes; the two are one type. It is opaque: a plain object, the data itself, or the dehydrated form of another type is a compile error where an `AvDehydrated<T>` is expected.
11
+ - A Resource or enum named `AvDehydrated` or `avHydrate` is now emitted under a renamed identifier (`AvDehydratedModel`, `avHydrateModel`), with a warning, as other names the generated client owns are. Before, `src/aventara/client.ts`'s own declaration hid a Resource type of that name, which could not be imported from it.
12
+ - Read data you dehydrate through `@/aventara/universal`: `@/aventara/server` types a decimal as core's `AvDecimal`, while `avHydrate` revives it as the browser client's.
13
+ - Pass `avHydrate(fallback)` to `SWRConfig`, never the dehydrated value itself: SWR's `fallback` is typed `any`, so the compiler cannot catch it.
14
+ - `avHydrate` still takes `unknown` and returns `unknown`; `avDehydrate` of an `unknown` value returns an `AvDehydrated<unknown>`. `serializeData: avDehydrate` and `deserializeData: avHydrate` in TanStack Query are written as before.
15
+ - The agent skills show the SWR props pattern typed, with no cast. The frontend skill's refresh line no longer names `npx`, and says a file you edited is kept with a warning naming it, and replaced with `--force`.
16
+
17
+ - Updated dependencies [1c5eaa3]
18
+ - @aventara/core@1.0.0-rc.14
19
+ - @aventara/mcp@1.0.0-rc.14
20
+
21
+ ## 1.0.0-rc.13
22
+
23
+ ### Patch Changes
24
+
25
+ - e4e8abd: The same rule now holds for in-process calls. When `state` rejects with an object whose `status` or `statusCode` is
26
+ the number 401 or 403, an `avClient` call in a Next.js Server Component or Server Action rejects with an `AvAuthError`
27
+ of code `A4000` or `A4001`, and so does `framework.serverClient.$access.can` given `headers`. Its message is core's own
28
+ (`"Unauthenticated."` or `"Forbidden."`) and nothing of the rejection reaches the caller. Before, the call rejected
29
+ with the library's own error. A thrown `AvError` and any other rejection still reject the call unchanged.
30
+
31
+ An object whose `status` and `statusCode` are both numbers that disagree, such as `status: 500` beside
32
+ `statusCode: 401`, is no longer read as a refusal: a route answers it `500 A3000`. A number beside a non-number, such
33
+ as Better Auth's `status: "UNAUTHORIZED"` beside `statusCode: 401`, is still read by the number.
34
+
35
+ - e662048: A request whose `state` (the host's `state` option, or the `access` provider's `state`) rejects with an object whose
36
+ `status` or `statusCode` is the number 401 or 403 is now answered `401 A4000` or `403 A4001`, not `500 A3000`. This is
37
+ how authentication libraries refuse a bad credential: Better Auth's `APIError` for a wrong API key carries
38
+ `statusCode: 401`. The answer is core's own envelope (`"Unauthenticated."` or `"Forbidden."`); nothing of the
39
+ rejection, neither its message nor its code, reaches the caller, and it is not reported to `diagnostics`, as a thrown
40
+ `AvError("A4000", …)` is not. It applies on every protocol route that seeds state: operations, cached reads,
41
+ `/_transactions`, `/_all`, `/_access/can`, and MCP tool calls. A thrown `AvError` is still its own outcome, and any
42
+ other rejection, a 401 beside a 403 included, is still `500 A3000`.
43
+ - Updated dependencies [6c3e25c]
44
+ - Updated dependencies [2aaa27d]
45
+ - Updated dependencies [f3b291f]
46
+ - Updated dependencies [2aaa27d]
47
+ - Updated dependencies [e4e8abd]
48
+ - Updated dependencies [e662048]
49
+ - Updated dependencies [f3b291f]
50
+ - Updated dependencies [f3b291f]
51
+ - Updated dependencies [6c3e25c]
52
+ - Updated dependencies [6c3e25c]
53
+ - Updated dependencies [a708188]
54
+ - Updated dependencies [a9260d2]
55
+ - @aventara/core@1.0.0-rc.13
56
+ - @aventara/mcp@1.0.0-rc.13
57
+
3
58
  ## 1.0.0-rc.12
4
59
 
5
60
  ### Patch Changes
package/README.md CHANGED
@@ -282,11 +282,29 @@ export const Providers = ({ children }: { children: React.ReactNode }) => (
282
282
  ```
283
283
 
284
284
  With SWR, pass `avDehydrate({ [key]: data })` from the Server Component and `avHydrate(fallback)` to `SWRConfig`'s
285
- `fallback` in the Client Component.
285
+ `fallback` in the Client Component. Type that prop `AvDehydrated<T>` (from `@/aventara/client`; this package exports the
286
+ same type): `avDehydrate` of a `T` is an `AvDehydrated<T>` and `avHydrate` of one is the `T` again, so no cast is
287
+ written, and a plain object or data of another type is a compile error on the prop. Pass `avHydrate(fallback)` to
288
+ `SWRConfig`, never `fallback` itself: SWR's `fallback` is typed `any`, so the compiler cannot catch it. `avHydrate` also
289
+ takes `unknown` and answers `unknown` (TanStack Query's `deserializeData`); `avDehydrate` of an `unknown` value is an
290
+ `AvDehydrated<unknown>`, and it is TanStack Query's `serializeData` as it is. Read the data you dehydrate through
291
+ `@/aventara/universal`: `@/aventara/server`'s `avClient` types a decimal as core's `AvDecimal`, while `avHydrate` revives
292
+ it as the browser client's.
286
293
 
287
294
  Importing `@/aventara/client` instead of `@/aventara/universal` in shared code opts out of running on the server: its
288
295
  calls are refused there.
289
296
 
297
+ ### The client's hooks
298
+
299
+ `onCall`, `onSuccess` and `onError` for `@/aventara/client` and `@/aventara/universal` go in
300
+ `src/aventara/client-options.ts` (or `.js`), whose default export `aventara-build --client` gives every client it writes
301
+ (`@aventara/build`'s README). The file ships to the browser too: no server-only import, no secret, and
302
+ `typeof window` when a hook must act differently on the server. On the server, `onCall`'s headers reach the in-process
303
+ request but not its state, which comes from the request being served. `@/aventara/server` is the Framework's own
304
+ client, not an `AvClient`, and takes no hooks. Calls group in the browser only when their `onCall` settles together
305
+ with equal additions: an `onCall` reading a token over I/O for each call sends each call alone, so read it through
306
+ one shared in-flight promise, cleared once it settles (`@aventara/client`'s README has the recipe).
307
+
290
308
  ## Routes
291
309
 
292
310
  `handlers` answers the protocol's routes under `api`: each operation route (`POST /api/_resources/...`),
@@ -12,10 +12,15 @@ type HostOptions<M extends AvAdapterModel> = {
12
12
  readonly adapter: AvAdapter<M> | (() => AvAdapter<M> | PromiseLike<AvAdapter<M>>);
13
13
  /**
14
14
  * Seeds a request's `ctx.$state`, once per request, from its headers and,
15
- * on a route call, the route handler's request. A throw is answered like
16
- * any developer failure: a thrown `AvError` as its own outcome, anything
17
- * else as an internal failure. Omitted, the state is `{}`, or what the
18
- * `access` provider's `state` seeds; not together with `access`.
15
+ * on a route call, the route handler's request. A thrown `AvError` is its
16
+ * own outcome. A rejection carrying `status` or `statusCode` 401 or 403
17
+ * (two numbers that disagree are neither) is `A4000` or `A4001`, with
18
+ * core's own message and nothing of the rejection's, unreported: a route
19
+ * answers `401` or `403`, and an `avClient` call in a Server Component or
20
+ * Server Action rejects with that code's `AvAuthError`. Anything else is
21
+ * `500 A3000` on a route, and rejects an `avClient` call unchanged.
22
+ * Omitted, the state is `{}`, or what the `access` provider's `state`
23
+ * seeds; not together with `access`.
19
24
  */
20
25
  state?(context: AvNextStateContext): AvState | Promise<AvState>;
21
26
  };
@@ -8,4 +8,9 @@ export declare const HYDRATION_FORMAT: {
8
8
  readonly objectTag: "object";
9
9
  /** The scalars a value is tagged as: those whose runtime values JSON does not carry. */
10
10
  readonly scalarTags: readonly ["bigint", "bytes", "datetime", "decimal"];
11
+ /**
12
+ * The key of `AvDehydrated<T>`'s brand: a property of its type only, never
13
+ * of the value, holding the `T` the value was dehydrated from.
14
+ */
15
+ readonly typeKey: "~avDehydrated";
11
16
  };
@@ -3,4 +3,5 @@ export const HYDRATION_FORMAT = {
3
3
  valueKey: "v",
4
4
  objectTag: "object",
5
5
  scalarTags: ["bigint", "bytes", "datetime", "decimal"],
6
+ typeKey: "~avDehydrated",
6
7
  };
package/dist/index.d.ts CHANGED
@@ -2,4 +2,4 @@ import "server-only";
2
2
  export { Aventara } from "./aventara.factory.js";
3
3
  export type { AvNextHandlers, AvNextStateContext, } from "./route-handlers.factory.js";
4
4
  export type { AvNextAppTransaction, AvNextClientTransaction, } from "./server-operations.factory.js";
5
- export { avDehydrate } from "./value.dehydrator.js";
5
+ export { type AvDehydrated, avDehydrate } from "./value.dehydrator.js";
@@ -1,4 +1,4 @@
1
- import { holdRequestPrincipal } from "@aventara/core/internal";
1
+ import { holdRequestPrincipal, inProcessStateRejection, } from "@aventara/core/internal";
2
2
  import { headers } from "next/headers.js";
3
3
  import { cache } from "react";
4
4
  export function createRequestState(state) {
@@ -6,7 +6,14 @@ export function createRequestState(state) {
6
6
  return async () => undefined;
7
7
  }
8
8
  return cache(async () => {
9
- const seeded = await state({ headers: await headers() });
9
+ const given = await headers();
10
+ let seeded;
11
+ try {
12
+ seeded = await state({ headers: given });
13
+ }
14
+ catch (error) {
15
+ throw inProcessStateRejection(error);
16
+ }
10
17
  holdRequestPrincipal(seeded);
11
18
  return seeded;
12
19
  });
@@ -1,3 +1,21 @@
1
+ /**
2
+ * Data of type `T` as `avDehydrate` sends it from the server: opaque, read
3
+ * back as the `T` only by `avHydrate` from `@/aventara/client`. Type a Client
4
+ * Component's prop with it to hand it server data with no cast.
5
+ *
6
+ * Pass `avHydrate(fallback)` on, never the dehydrated value itself: a
7
+ * receiver typed `any`, such as SWR's `fallback`, takes either, so the type
8
+ * cannot catch it.
9
+ *
10
+ * @example
11
+ * export function Drafts({ fallback }: { readonly fallback: AvDehydrated<{ drafts: Post[] }> }) {
12
+ * return <SWRConfig value={{ fallback: avHydrate(fallback) }}>…</SWRConfig>;
13
+ * }
14
+ */
15
+ export type AvDehydrated<T> = {
16
+ /** The type the value was dehydrated from: the type's brand, no member of the value. */
17
+ readonly "~avDehydrated": T;
18
+ };
1
19
  /**
2
20
  * Makes data an Aventara client answered safe to pass from the server to the
3
21
  * browser: each `Date`, `bigint`, `Uint8Array` and decimal becomes a tagged
@@ -6,9 +24,15 @@
6
24
  * plain objects are copied with their members dehydrated; any other value is
7
25
  * returned as it is.
8
26
  *
9
- * Give it to TanStack Query as `dehydrate(queryClient, { serializeData: avDehydrate })`,
10
- * or call it on an SWR `fallback` in a Server Component.
27
+ * Data of type `T` dehydrates to an `AvDehydrated<T>`, which `avHydrate`
28
+ * reads back as the `T`: pass it to a Client Component as a prop typed
29
+ * `AvDehydrated<T>`, such as an SWR `fallback`, and pass `avHydrate(fallback)`
30
+ * on in the browser, never the dehydrated value itself (SWR's `fallback` is
31
+ * typed `any`, so the type cannot catch it). Give it to TanStack Query as
32
+ * `dehydrate(queryClient, { serializeData: avDehydrate })`.
11
33
  *
12
34
  * @throws TypeError for a `Date` with no wire form (invalid, or a year past four digits).
13
35
  */
36
+ export declare function avDehydrate<T>(data: T): AvDehydrated<T>;
37
+ /** As a function typed `(data: unknown) => unknown`, such as TanStack Query's `serializeData`; a call resolves to the generic signature. */
14
38
  export declare function avDehydrate(data: unknown): unknown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aventara/next",
3
- "version": "1.0.0-rc.12",
3
+ "version": "1.0.0-rc.14",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "Next.js App Router host integration for Aventara.",
6
6
  "type": "module",
@@ -22,11 +22,11 @@
22
22
  "CHANGELOG.md"
23
23
  ],
24
24
  "dependencies": {
25
- "@aventara/core": "1.0.0-rc.12",
25
+ "@aventara/core": "1.0.0-rc.14",
26
26
  "server-only": "0.0.1"
27
27
  },
28
28
  "peerDependencies": {
29
- "@aventara/mcp": "1.0.0-rc.12",
29
+ "@aventara/mcp": "1.0.0-rc.14",
30
30
  "next": ">=16.4 <17",
31
31
  "react": "^19.0.0"
32
32
  },
@@ -53,10 +53,10 @@
53
53
  "access": "public"
54
54
  },
55
55
  "devDependencies": {
56
- "@aventara/build": "1.0.0-rc.12",
57
- "@aventara/client": "1.0.0-rc.12",
58
- "@aventara/mcp": "1.0.0-rc.12",
59
- "@aventara/testing": "1.0.0-rc.12",
56
+ "@aventara/build": "1.0.0-rc.14",
57
+ "@aventara/client": "1.0.0-rc.14",
58
+ "@aventara/mcp": "1.0.0-rc.14",
59
+ "@aventara/testing": "1.0.0-rc.14",
60
60
  "@modelcontextprotocol/sdk": "1.32.2",
61
61
  "@tanstack/react-query": "5.104.2",
62
62
  "@types/react": "19.3.0",