@ontahi/runtime-nextjs 1.0.0-alpha.11 → 1.0.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,12 +24,19 @@ Export a `POST` handler from an App Router route such as `app/api/runtime/route.
24
24
  import { createRuntimeProtocolDispatcher } from '@ontahi/core/runtime/protocol';
25
25
  import { createNextRuntimeProtocolRouteHandler } from '@ontahi/runtime-nextjs/runtime-protocol';
26
26
 
27
- const dispatcher = createRuntimeProtocolDispatcher({
27
+ type RuntimeAuthority = {
28
+ principal: Awaited<ReturnType<typeof resolvePrincipal>>;
29
+ };
30
+
31
+ const dispatcher = createRuntimeProtocolDispatcher<RuntimeAuthority>({
28
32
  handlers: {
29
- operation: operationDispatcher,
33
+ operation: (request, authority) =>
34
+ TodoApplication.app.runtime.withInvocationContext(authority, () =>
35
+ operationDispatcher(request),
36
+ ),
30
37
  'durable.operation': durableObservationHandler,
31
- 'graph.read': graphReadDispatcher,
32
- 'graph.command': graphCommandDispatcher,
38
+ 'graph.read': (request, authority) => graphReadDispatcher(request, { authority }),
39
+ 'graph.command': (request, authority) => graphCommandDispatcher(request, { authority }),
33
40
  },
34
41
  });
35
42
 
@@ -44,13 +51,27 @@ export const POST = createNextRuntimeProtocolRouteHandler({
44
51
  The adapter validates JSON, the common envelope, and the canonical family body before calling the
45
52
  context factory. It installs no handlers, policies, authorization, or capabilities; the host owns
46
53
  the dispatcher and derives trusted context only from the received server request. All registered
47
- families pass through the same dispatcher without Next.js-specific routing.
54
+ families pass through the same dispatcher without Next.js-specific routing. Family adapters feed
55
+ the server-derived context to the canonical Operation and Graph policy boundaries; clients never
56
+ author authority or policies.
57
+
58
+ Point the Fetch client at the App Router route once; Operations, Graph Reads, Graph Commands, and
59
+ Durable inspection then share that Runtime Transport:
60
+
61
+ ```ts
62
+ import { createFetchGraphClient } from '@ontahi/react/graph';
63
+
64
+ const client = createFetchGraphClient({
65
+ runtimeTransport: { endpoint: '/api/runtime' },
66
+ });
67
+ ```
48
68
 
49
69
  Common invalid requests return `400`, a known family without an installed handler returns `501`,
50
70
  unavailable dispatch returns `503`, invalid upstream responses return `502`, and complete family
51
71
  responses—including semantic rejections and family protocol errors—return `200`. Existing
52
- `operation-invocation` and `graph-read` adapters remain available while clients migrate from their
53
- family-specific paths.
72
+ `operation-invocation` and `graph-read` adapters remain explicit compatibility surfaces. Select
73
+ them with `createFetchGraphClient({ compatibility: ... })` only while a host is migrating; the
74
+ client never falls back or replays a transmitted request against a second endpoint.
54
75
 
55
76
  ## Operation invocation
56
77
 
@@ -1 +1 @@
1
- {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/graph-read/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,mBAAmB,EAEzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAE1E,MAAM,MAAM,6BAA6B,CAAC,UAAU,IAAI,CACtD,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,OAAO,KACb,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,KAAK,sCAAsC,GAAG;IAC5C,iBAAiB,CAAC,EAAE,4BAA4B,CAAC;IACjD,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1D,CAAC;AAEF,MAAM,MAAM,sCAAsC,CAAC,UAAU,GAAG,iBAAiB,IAC/E,sCAAsC,GACpC,CACI;IACE,UAAU,EAAE,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;IACnD,SAAS,CAAC,EAAE,KAAK,CAAC;CACnB,GACD;IACE,UAAU,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAC5C,SAAS,EAAE,6BAA6B,CAAC,UAAU,CAAC,CAAC;CACtD,CACJ,CAAC;AASN,eAAO,MAAM,+BAA+B,GACzC,UAAU,EAAE,SAAS,sCAAsC,CAAC,UAAU,CAAC,MACjE,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAoCzC,CAAC"}
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/graph-read/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,mBAAmB,EAEzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAE1E,MAAM,MAAM,6BAA6B,CAAC,UAAU,IAAI,CACtD,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,OAAO,KACb,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,KAAK,sCAAsC,GAAG;IAC5C,iBAAiB,CAAC,EAAE,4BAA4B,CAAC;IACjD,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1D,CAAC;AAEF,MAAM,MAAM,sCAAsC,CAAC,UAAU,GAAG,iBAAiB,IAC/E,sCAAsC,GACpC,CACI;IACE,UAAU,EAAE,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;IACnD,SAAS,CAAC,EAAE,KAAK,CAAC;CACnB,GACD;IACE,UAAU,EAAE,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAC5C,SAAS,EAAE,6BAA6B,CAAC,UAAU,CAAC,CAAC;CACtD,CACJ,CAAC;AAUN,eAAO,MAAM,+BAA+B,GACzC,UAAU,EAAE,SAAS,sCAAsC,CAAC,UAAU,CAAC,MACjE,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAoCzC,CAAC"}
@@ -1,16 +1,18 @@
1
- import { graphReadProtocolError, parseGraphReadRequest, } from '@ontahi/core/data-graph';
1
+ import { graphReadProtocolError, parseGraphReadFamilyRequest, } from '@ontahi/core/data-graph';
2
2
  import { getCurrentInvocationContext, withInvocationContext, } from '@ontahi/core/runtime/server';
3
3
  const responseStatus = (response) => {
4
- if (response.kind === 'graph-read-result')
4
+ if (response.kind !== 'protocol-error')
5
5
  return 200;
6
6
  if (response.error.code === 'access_denied')
7
7
  return 403;
8
+ if (response.error.code === 'cardinality_mismatch')
9
+ return 409;
8
10
  if (response.error.code === 'execution_unavailable')
9
11
  return 503;
10
12
  return 400;
11
13
  };
12
14
  export const createNextGraphReadRouteHandler = (options) => async (request) => {
13
- const parsed = parseGraphReadRequest(await request.json().catch(() => null));
15
+ const parsed = parseGraphReadFamilyRequest(await request.json().catch(() => null));
14
16
  if (!parsed.success) {
15
17
  return Response.json(parsed.error, { status: 400 });
16
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/graph-read/route.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GAGtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,GAEtB,MAAM,6BAA6B,CAAC;AA2BrC,MAAM,cAAc,GAAG,CAAC,QAAmC,EAAE,EAAE;IAC7D,IAAI,QAAQ,CAAC,IAAI,KAAK,mBAAmB;QAAE,OAAO,GAAG,CAAC;IACtD,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe;QAAE,OAAO,GAAG,CAAC;IACxD,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,uBAAuB;QAAE,OAAO,GAAG,CAAC;IAChE,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,+BAA+B,GAC1C,CAAa,OAA2D,EAAE,EAAE,CAC5E,KAAK,EAAE,OAAgB,EAAqB,EAAE;IAC5C,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7E,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;YAC1B,MAAM,cAAc,GAAG,2BAA2B,EAAE,CAAC;YACrD,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;YAC3E,CAAC;YACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtB,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE;oBACxC,SAAS,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,cAAc,EAAE,OAAO,CAAC;iBAC5D,CAAC,CAAC;YACL,CAAC;YACD,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC;QAC3E,CAAC,CAAC;QACF,MAAM,gBAAgB,GAAG,MAAM,qBAAqB,CAClD,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,EACzE,QAAQ,CACT,CAAC;QAEF,OAAO,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,MAAM,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAEtC,OAAO,QAAQ,CAAC,IAAI,CAClB,sBAAsB,CACpB,uBAAuB,EACvB,uDAAuD,CACxD,EACD,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;IACJ,CAAC;AACH,CAAC,CAAC","sourcesContent":["import {\n graphReadProtocolError,\n parseGraphReadRequest,\n type GraphReadDispatcher,\n type GraphReadDispatchResponse,\n} from '@ontahi/core/data-graph';\nimport {\n getCurrentInvocationContext,\n withInvocationContext,\n type InvocationContext,\n} from '@ontahi/core/runtime/server';\n\nimport type { NextInvocationContextFactory } from '../request-context.js';\n\nexport type NextGraphReadAuthorityFactory<TAuthority> = (\n context: InvocationContext,\n request: Request,\n) => TAuthority | Promise<TAuthority>;\n\ntype NextGraphReadRouteHandlerCommonOptions = {\n invocationContext?: NextInvocationContextFactory;\n reportError?: (error: unknown, request: Request) => void;\n};\n\nexport type CreateNextGraphReadRouteHandlerOptions<TAuthority = InvocationContext> =\n NextGraphReadRouteHandlerCommonOptions &\n (\n | {\n dispatcher: GraphReadDispatcher<InvocationContext>;\n authority?: never;\n }\n | {\n dispatcher: GraphReadDispatcher<TAuthority>;\n authority: NextGraphReadAuthorityFactory<TAuthority>;\n }\n );\n\nconst responseStatus = (response: GraphReadDispatchResponse) => {\n if (response.kind === 'graph-read-result') return 200;\n if (response.error.code === 'access_denied') return 403;\n if (response.error.code === 'execution_unavailable') return 503;\n return 400;\n};\n\nexport const createNextGraphReadRouteHandler =\n <TAuthority>(options: CreateNextGraphReadRouteHandlerOptions<TAuthority>) =>\n async (request: Request): Promise<Response> => {\n const parsed = parseGraphReadRequest(await request.json().catch(() => null));\n if (!parsed.success) {\n return Response.json(parsed.error, { status: 400 });\n }\n\n try {\n const dispatch = async () => {\n const currentContext = getCurrentInvocationContext();\n if (!currentContext) {\n throw new Error('Next.js graph read invocation context is unavailable.');\n }\n if (options.authority) {\n return options.dispatcher(parsed.request, {\n authority: await options.authority(currentContext, request),\n });\n }\n return options.dispatcher(parsed.request, { authority: currentContext });\n };\n const protocolResponse = await withInvocationContext(\n options.invocationContext ? await options.invocationContext(request) : {},\n dispatch,\n );\n\n return Response.json(protocolResponse, { status: responseStatus(protocolResponse) });\n } catch (error) {\n options.reportError?.(error, request);\n\n return Response.json(\n graphReadProtocolError(\n 'execution_unavailable',\n 'Data graph read execution is temporarily unavailable.',\n ),\n { status: 503 },\n );\n }\n };\n"]}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/graph-read/route.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,2BAA2B,GAG5B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,GAEtB,MAAM,6BAA6B,CAAC;AA2BrC,MAAM,cAAc,GAAG,CAAC,QAAmC,EAAE,EAAE;IAC7D,IAAI,QAAQ,CAAC,IAAI,KAAK,gBAAgB;QAAE,OAAO,GAAG,CAAC;IACnD,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,eAAe;QAAE,OAAO,GAAG,CAAC;IACxD,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,sBAAsB;QAAE,OAAO,GAAG,CAAC;IAC/D,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,uBAAuB;QAAE,OAAO,GAAG,CAAC;IAChE,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,+BAA+B,GAC1C,CAAa,OAA2D,EAAE,EAAE,CAC5E,KAAK,EAAE,OAAgB,EAAqB,EAAE;IAC5C,MAAM,MAAM,GAAG,2BAA2B,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACnF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;YAC1B,MAAM,cAAc,GAAG,2BAA2B,EAAE,CAAC;YACrD,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;YAC3E,CAAC;YACD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtB,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE;oBACxC,SAAS,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,cAAc,EAAE,OAAO,CAAC;iBAC5D,CAAC,CAAC;YACL,CAAC;YACD,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC;QAC3E,CAAC,CAAC;QACF,MAAM,gBAAgB,GAAG,MAAM,qBAAqB,CAClD,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,EACzE,QAAQ,CACT,CAAC;QAEF,OAAO,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,MAAM,EAAE,cAAc,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAEtC,OAAO,QAAQ,CAAC,IAAI,CAClB,sBAAsB,CACpB,uBAAuB,EACvB,uDAAuD,CACxD,EACD,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;IACJ,CAAC;AACH,CAAC,CAAC","sourcesContent":["import {\n graphReadProtocolError,\n parseGraphReadFamilyRequest,\n type GraphReadDispatcher,\n type GraphReadDispatchResponse,\n} from '@ontahi/core/data-graph';\nimport {\n getCurrentInvocationContext,\n withInvocationContext,\n type InvocationContext,\n} from '@ontahi/core/runtime/server';\n\nimport type { NextInvocationContextFactory } from '../request-context.js';\n\nexport type NextGraphReadAuthorityFactory<TAuthority> = (\n context: InvocationContext,\n request: Request,\n) => TAuthority | Promise<TAuthority>;\n\ntype NextGraphReadRouteHandlerCommonOptions = {\n invocationContext?: NextInvocationContextFactory;\n reportError?: (error: unknown, request: Request) => void;\n};\n\nexport type CreateNextGraphReadRouteHandlerOptions<TAuthority = InvocationContext> =\n NextGraphReadRouteHandlerCommonOptions &\n (\n | {\n dispatcher: GraphReadDispatcher<InvocationContext>;\n authority?: never;\n }\n | {\n dispatcher: GraphReadDispatcher<TAuthority>;\n authority: NextGraphReadAuthorityFactory<TAuthority>;\n }\n );\n\nconst responseStatus = (response: GraphReadDispatchResponse) => {\n if (response.kind !== 'protocol-error') return 200;\n if (response.error.code === 'access_denied') return 403;\n if (response.error.code === 'cardinality_mismatch') return 409;\n if (response.error.code === 'execution_unavailable') return 503;\n return 400;\n};\n\nexport const createNextGraphReadRouteHandler =\n <TAuthority>(options: CreateNextGraphReadRouteHandlerOptions<TAuthority>) =>\n async (request: Request): Promise<Response> => {\n const parsed = parseGraphReadFamilyRequest(await request.json().catch(() => null));\n if (!parsed.success) {\n return Response.json(parsed.error, { status: 400 });\n }\n\n try {\n const dispatch = async () => {\n const currentContext = getCurrentInvocationContext();\n if (!currentContext) {\n throw new Error('Next.js graph read invocation context is unavailable.');\n }\n if (options.authority) {\n return options.dispatcher(parsed.request, {\n authority: await options.authority(currentContext, request),\n });\n }\n return options.dispatcher(parsed.request, { authority: currentContext });\n };\n const protocolResponse = await withInvocationContext(\n options.invocationContext ? await options.invocationContext(request) : {},\n dispatch,\n );\n\n return Response.json(protocolResponse, { status: responseStatus(protocolResponse) });\n } catch (error) {\n options.reportError?.(error, request);\n\n return Response.json(\n graphReadProtocolError(\n 'execution_unavailable',\n 'Data graph read execution is temporarily unavailable.',\n ),\n { status: 503 },\n );\n }\n };\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ontahi/runtime-nextjs",
3
- "version": "1.0.0-alpha.11",
3
+ "version": "1.0.0-alpha.12",
4
4
  "description": "Next.js runtime and transport adapters for Ontahi applications",
5
5
  "homepage": "https://github.com/javifernandes/ontahi/tree/main/packages/runtime-nextjs",
6
6
  "license": "Apache-2.0",
@@ -55,13 +55,15 @@
55
55
  "provenance": true
56
56
  },
57
57
  "dependencies": {
58
+ "@ontahi/core": "1.0.0-alpha.12",
58
59
  "effect": "^3.21.0",
59
60
  "next-safe-action": "^8.3.0",
60
61
  "server-only": "^0.0.1",
61
- "zod": "^4.3.6",
62
- "@ontahi/core": "1.0.0-alpha.11"
62
+ "zod": "^4.3.6"
63
63
  },
64
64
  "devDependencies": {
65
+ "@ontahi/react": "1.0.0-alpha.12",
66
+ "@ontahi/runtime-express": "1.0.0-alpha.12",
65
67
  "@types/node": "^20.0.0",
66
68
  "@types/react": "^18.3.0",
67
69
  "@types/react-dom": "^18.3.0",