@objectstack/nextjs 2.0.2 → 2.0.4

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/nextjs@2.0.2 build /home/runner/work/spec/spec/packages/adapters/nextjs
2
+ > @objectstack/nextjs@2.0.4 build /home/runner/work/spec/spec/packages/adapters/nextjs
3
3
  > tsup --config ../../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- CJS dist/index.js 4.87 KB
14
- CJS dist/index.js.map 7.94 KB
15
- CJS ⚡️ Build success in 49ms
16
- ESM dist/index.mjs 3.71 KB
17
- ESM dist/index.mjs.map 7.89 KB
18
- ESM ⚡️ Build success in 55ms
13
+ ESM dist/index.mjs 4.21 KB
14
+ ESM dist/index.mjs.map 9.13 KB
15
+ ESM ⚡️ Build success in 35ms
16
+ CJS dist/index.js 5.39 KB
17
+ CJS dist/index.js.map 9.17 KB
18
+ CJS ⚡️ Build success in 36ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 6612ms
20
+ DTS ⚡️ Build success in 6261ms
21
21
  DTS dist/index.d.mts 764.00 B
22
22
  DTS dist/index.d.ts 764.00 B
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @objectstack/nextjs
2
2
 
3
+ ## 2.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Patch release for maintenance and stability improvements
8
+ - Updated dependencies
9
+ - @objectstack/runtime@2.0.4
10
+
11
+ ## 2.0.3
12
+
13
+ ### Patch Changes
14
+
15
+ - Patch release for maintenance and stability improvements
16
+ - Updated dependencies
17
+ - @objectstack/runtime@2.0.3
18
+
3
19
  ## 2.0.2
4
20
 
5
21
  ### Patch Changes
package/dist/index.js CHANGED
@@ -63,6 +63,16 @@ function createRouteHandler(options) {
63
63
  try {
64
64
  const rawRequest = req;
65
65
  if (segments[0] === "auth") {
66
+ const authService = typeof options.kernel.getService === "function" ? options.kernel.getService("auth") : null;
67
+ if (authService && typeof authService.handleRequest === "function") {
68
+ const response = await authService.handleRequest(req);
69
+ const body2 = await response.text();
70
+ const headers = {};
71
+ response.headers.forEach((v, k) => {
72
+ headers[k] = v;
73
+ });
74
+ return new import_server.NextResponse(body2, { status: response.status, headers });
75
+ }
66
76
  const subPath = segments.slice(1).join("/");
67
77
  const body = method === "POST" ? await req.json().catch(() => ({})) : {};
68
78
  const result = await dispatcher.handleAuth(subPath, method, body, { request: req });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface NextAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Creates a route handler for Next.js App Router\n * Handles /api/[...objectstack] pattern\n */\nexport function createRouteHandler(options: NextAdapterOptions) {\n const dispatcher = new HttpDispatcher(options.kernel);\n const error = (msg: string, code: number = 500) => NextResponse.json({ success: false, error: { message: msg, code } }, { status: code });\n\n // Helper to convert DispatchResult to NextResponse\n const toResponse = (result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n return NextResponse.json(result.response.body, { \n status: result.response.status, \n headers: result.response.headers \n });\n }\n if (result.result) {\n const res = result.result;\n // Redirect\n if (res.type === 'redirect' && res.url) {\n return NextResponse.redirect(res.url);\n }\n // Stream\n if (res.type === 'stream' && res.stream) {\n return new NextResponse(res.stream, {\n status: 200,\n headers: res.headers\n });\n }\n // If it's a standard response object (like from another fetch)\n // Next.js might handle it, or we return it directly\n return res; \n }\n }\n return error('Not Found', 404);\n }\n\n return async function handler(req: NextRequest, { params }: { params: { objectstack: string[] } }) {\n const resolvedParams = await Promise.resolve(params);\n const segments = resolvedParams.objectstack || [];\n const method = req.method;\n \n // --- 0. Discovery Endpoint ---\n if (segments.length === 0 && method === 'GET') {\n return NextResponse.json({ data: dispatcher.getDiscoveryInfo(options.prefix || '/api') });\n }\n\n try {\n const rawRequest = req;\n\n // --- 1. Auth ---\n if (segments[0] === 'auth') {\n const subPath = segments.slice(1).join('/');\n const body = method === 'POST' ? await req.json().catch(() => ({})) : {};\n const result = await dispatcher.handleAuth(subPath, method, body, { request: req });\n return toResponse(result);\n }\n\n // --- 2. GraphQL ---\n if (segments[0] === 'graphql' && method === 'POST') {\n const body = await req.json();\n const result = await dispatcher.handleGraphQL(body as any, { request: rawRequest } as any);\n return NextResponse.json(result);\n }\n\n // --- 3. Metadata ---\n if (segments[0] === 'meta') {\n const subPath = segments.slice(1).join('/');\n \n let body: any = undefined;\n if (method === 'PUT' || method === 'POST') {\n body = await req.json().catch(() => ({}));\n }\n\n const result = await dispatcher.handleMetadata(subPath, { request: rawRequest }, method, body);\n return toResponse(result);\n }\n\n // --- 4. Data ---\n if (segments[0] === 'data') {\n const subPath = segments.slice(1).join('/');\n let body: any = {};\n if (method === 'POST' || method === 'PATCH') {\n body = await req.json().catch(() => ({}));\n }\n \n // Extract query params\n const url = new URL(req.url);\n const queryParams: Record<string, any> = {};\n url.searchParams.forEach((val, key) => queryParams[key] = val);\n\n const result = await dispatcher.handleData(subPath, method, body, queryParams, { request: rawRequest } as any);\n return toResponse(result);\n }\n\n // --- 5. Storage ---\n if (segments[0] === 'storage') {\n const subPath = segments.slice(1).join('/');\n \n let file: any = undefined;\n if (method === 'POST' && subPath === 'upload') {\n const formData = await req.formData();\n file = formData.get('file');\n }\n\n const result = await dispatcher.handleStorage(subPath, method, file, { request: rawRequest });\n return toResponse(result);\n }\n \n return error('Not Found', 404);\n\n } catch (err: any) {\n return error(err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }\n}\n\n/**\n * Creates a discovery handler for Next.js App Router\n * Handles /.well-known/objectstack\n */\nexport function createDiscoveryHandler(options: NextAdapterOptions) {\n return async function discoveryHandler(req: NextRequest) {\n const apiPath = options.prefix || '/api';\n const url = new URL(req.url);\n const targetUrl = new URL(apiPath, url.origin);\n return NextResponse.redirect(targetUrl);\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface NextAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\n}\n\n/**\n * Creates a route handler for Next.js App Router\n * Handles /api/[...objectstack] pattern\n */\nexport function createRouteHandler(options: NextAdapterOptions) {\n const dispatcher = new HttpDispatcher(options.kernel);\n const error = (msg: string, code: number = 500) => NextResponse.json({ success: false, error: { message: msg, code } }, { status: code });\n\n // Helper to convert DispatchResult to NextResponse\n const toResponse = (result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n return NextResponse.json(result.response.body, { \n status: result.response.status, \n headers: result.response.headers \n });\n }\n if (result.result) {\n const res = result.result;\n // Redirect\n if (res.type === 'redirect' && res.url) {\n return NextResponse.redirect(res.url);\n }\n // Stream\n if (res.type === 'stream' && res.stream) {\n return new NextResponse(res.stream, {\n status: 200,\n headers: res.headers\n });\n }\n // If it's a standard response object (like from another fetch)\n // Next.js might handle it, or we return it directly\n return res; \n }\n }\n return error('Not Found', 404);\n }\n\n return async function handler(req: NextRequest, { params }: { params: { objectstack: string[] } }) {\n const resolvedParams = await Promise.resolve(params);\n const segments = resolvedParams.objectstack || [];\n const method = req.method;\n \n // --- 0. Discovery Endpoint ---\n if (segments.length === 0 && method === 'GET') {\n return NextResponse.json({ data: dispatcher.getDiscoveryInfo(options.prefix || '/api') });\n }\n\n try {\n const rawRequest = req;\n\n // --- 1. Auth ---\n if (segments[0] === 'auth') {\n // Try AuthPlugin service first (preferred path)\n const authService = typeof options.kernel.getService === 'function'\n ? options.kernel.getService<AuthService>('auth')\n : null;\n\n if (authService && typeof authService.handleRequest === 'function') {\n const response = await authService.handleRequest(req);\n // Convert Web Response to NextResponse\n const body = await response.text();\n const headers: Record<string, string> = {};\n response.headers.forEach((v: string, k: string) => { headers[k] = v; });\n return new NextResponse(body, { status: response.status, headers });\n }\n\n // Fallback to legacy dispatcher\n const subPath = segments.slice(1).join('/');\n const body = method === 'POST' ? await req.json().catch(() => ({})) : {};\n const result = await dispatcher.handleAuth(subPath, method, body, { request: req });\n return toResponse(result);\n }\n\n // --- 2. GraphQL ---\n if (segments[0] === 'graphql' && method === 'POST') {\n const body = await req.json();\n const result = await dispatcher.handleGraphQL(body as any, { request: rawRequest } as any);\n return NextResponse.json(result);\n }\n\n // --- 3. Metadata ---\n if (segments[0] === 'meta') {\n const subPath = segments.slice(1).join('/');\n \n let body: any = undefined;\n if (method === 'PUT' || method === 'POST') {\n body = await req.json().catch(() => ({}));\n }\n\n const result = await dispatcher.handleMetadata(subPath, { request: rawRequest }, method, body);\n return toResponse(result);\n }\n\n // --- 4. Data ---\n if (segments[0] === 'data') {\n const subPath = segments.slice(1).join('/');\n let body: any = {};\n if (method === 'POST' || method === 'PATCH') {\n body = await req.json().catch(() => ({}));\n }\n \n // Extract query params\n const url = new URL(req.url);\n const queryParams: Record<string, any> = {};\n url.searchParams.forEach((val, key) => queryParams[key] = val);\n\n const result = await dispatcher.handleData(subPath, method, body, queryParams, { request: rawRequest } as any);\n return toResponse(result);\n }\n\n // --- 5. Storage ---\n if (segments[0] === 'storage') {\n const subPath = segments.slice(1).join('/');\n \n let file: any = undefined;\n if (method === 'POST' && subPath === 'upload') {\n const formData = await req.formData();\n file = formData.get('file');\n }\n\n const result = await dispatcher.handleStorage(subPath, method, file, { request: rawRequest });\n return toResponse(result);\n }\n \n return error('Not Found', 404);\n\n } catch (err: any) {\n return error(err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }\n}\n\n/**\n * Creates a discovery handler for Next.js App Router\n * Handles /.well-known/objectstack\n */\nexport function createDiscoveryHandler(options: NextAdapterOptions) {\n return async function discoveryHandler(req: NextRequest) {\n const apiPath = options.prefix || '/api';\n const url = new URL(req.url);\n const targetUrl = new URL(apiPath, url.origin);\n return NextResponse.redirect(targetUrl);\n }\n}\n"],"mappings":"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","names":["body"]}
package/dist/index.mjs CHANGED
@@ -38,6 +38,16 @@ function createRouteHandler(options) {
38
38
  try {
39
39
  const rawRequest = req;
40
40
  if (segments[0] === "auth") {
41
+ const authService = typeof options.kernel.getService === "function" ? options.kernel.getService("auth") : null;
42
+ if (authService && typeof authService.handleRequest === "function") {
43
+ const response = await authService.handleRequest(req);
44
+ const body2 = await response.text();
45
+ const headers = {};
46
+ response.headers.forEach((v, k) => {
47
+ headers[k] = v;
48
+ });
49
+ return new NextResponse(body2, { status: response.status, headers });
50
+ }
41
51
  const subPath = segments.slice(1).join("/");
42
52
  const body = method === "POST" ? await req.json().catch(() => ({})) : {};
43
53
  const result = await dispatcher.handleAuth(subPath, method, body, { request: req });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface NextAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Creates a route handler for Next.js App Router\n * Handles /api/[...objectstack] pattern\n */\nexport function createRouteHandler(options: NextAdapterOptions) {\n const dispatcher = new HttpDispatcher(options.kernel);\n const error = (msg: string, code: number = 500) => NextResponse.json({ success: false, error: { message: msg, code } }, { status: code });\n\n // Helper to convert DispatchResult to NextResponse\n const toResponse = (result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n return NextResponse.json(result.response.body, { \n status: result.response.status, \n headers: result.response.headers \n });\n }\n if (result.result) {\n const res = result.result;\n // Redirect\n if (res.type === 'redirect' && res.url) {\n return NextResponse.redirect(res.url);\n }\n // Stream\n if (res.type === 'stream' && res.stream) {\n return new NextResponse(res.stream, {\n status: 200,\n headers: res.headers\n });\n }\n // If it's a standard response object (like from another fetch)\n // Next.js might handle it, or we return it directly\n return res; \n }\n }\n return error('Not Found', 404);\n }\n\n return async function handler(req: NextRequest, { params }: { params: { objectstack: string[] } }) {\n const resolvedParams = await Promise.resolve(params);\n const segments = resolvedParams.objectstack || [];\n const method = req.method;\n \n // --- 0. Discovery Endpoint ---\n if (segments.length === 0 && method === 'GET') {\n return NextResponse.json({ data: dispatcher.getDiscoveryInfo(options.prefix || '/api') });\n }\n\n try {\n const rawRequest = req;\n\n // --- 1. Auth ---\n if (segments[0] === 'auth') {\n const subPath = segments.slice(1).join('/');\n const body = method === 'POST' ? await req.json().catch(() => ({})) : {};\n const result = await dispatcher.handleAuth(subPath, method, body, { request: req });\n return toResponse(result);\n }\n\n // --- 2. GraphQL ---\n if (segments[0] === 'graphql' && method === 'POST') {\n const body = await req.json();\n const result = await dispatcher.handleGraphQL(body as any, { request: rawRequest } as any);\n return NextResponse.json(result);\n }\n\n // --- 3. Metadata ---\n if (segments[0] === 'meta') {\n const subPath = segments.slice(1).join('/');\n \n let body: any = undefined;\n if (method === 'PUT' || method === 'POST') {\n body = await req.json().catch(() => ({}));\n }\n\n const result = await dispatcher.handleMetadata(subPath, { request: rawRequest }, method, body);\n return toResponse(result);\n }\n\n // --- 4. Data ---\n if (segments[0] === 'data') {\n const subPath = segments.slice(1).join('/');\n let body: any = {};\n if (method === 'POST' || method === 'PATCH') {\n body = await req.json().catch(() => ({}));\n }\n \n // Extract query params\n const url = new URL(req.url);\n const queryParams: Record<string, any> = {};\n url.searchParams.forEach((val, key) => queryParams[key] = val);\n\n const result = await dispatcher.handleData(subPath, method, body, queryParams, { request: rawRequest } as any);\n return toResponse(result);\n }\n\n // --- 5. Storage ---\n if (segments[0] === 'storage') {\n const subPath = segments.slice(1).join('/');\n \n let file: any = undefined;\n if (method === 'POST' && subPath === 'upload') {\n const formData = await req.formData();\n file = formData.get('file');\n }\n\n const result = await dispatcher.handleStorage(subPath, method, file, { request: rawRequest });\n return toResponse(result);\n }\n \n return error('Not Found', 404);\n\n } catch (err: any) {\n return error(err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }\n}\n\n/**\n * Creates a discovery handler for Next.js App Router\n * Handles /.well-known/objectstack\n */\nexport function createDiscoveryHandler(options: NextAdapterOptions) {\n return async function discoveryHandler(req: NextRequest) {\n const apiPath = options.prefix || '/api';\n const url = new URL(req.url);\n const targetUrl = new URL(apiPath, url.origin);\n return NextResponse.redirect(targetUrl);\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime';\n\nexport interface NextAdapterOptions {\n kernel: ObjectKernel;\n prefix?: string;\n}\n\n/**\n * Auth service interface with handleRequest method\n */\ninterface AuthService {\n handleRequest(request: Request): Promise<Response>;\n}\n\n/**\n * Creates a route handler for Next.js App Router\n * Handles /api/[...objectstack] pattern\n */\nexport function createRouteHandler(options: NextAdapterOptions) {\n const dispatcher = new HttpDispatcher(options.kernel);\n const error = (msg: string, code: number = 500) => NextResponse.json({ success: false, error: { message: msg, code } }, { status: code });\n\n // Helper to convert DispatchResult to NextResponse\n const toResponse = (result: HttpDispatcherResult) => {\n if (result.handled) {\n if (result.response) {\n return NextResponse.json(result.response.body, { \n status: result.response.status, \n headers: result.response.headers \n });\n }\n if (result.result) {\n const res = result.result;\n // Redirect\n if (res.type === 'redirect' && res.url) {\n return NextResponse.redirect(res.url);\n }\n // Stream\n if (res.type === 'stream' && res.stream) {\n return new NextResponse(res.stream, {\n status: 200,\n headers: res.headers\n });\n }\n // If it's a standard response object (like from another fetch)\n // Next.js might handle it, or we return it directly\n return res; \n }\n }\n return error('Not Found', 404);\n }\n\n return async function handler(req: NextRequest, { params }: { params: { objectstack: string[] } }) {\n const resolvedParams = await Promise.resolve(params);\n const segments = resolvedParams.objectstack || [];\n const method = req.method;\n \n // --- 0. Discovery Endpoint ---\n if (segments.length === 0 && method === 'GET') {\n return NextResponse.json({ data: dispatcher.getDiscoveryInfo(options.prefix || '/api') });\n }\n\n try {\n const rawRequest = req;\n\n // --- 1. Auth ---\n if (segments[0] === 'auth') {\n // Try AuthPlugin service first (preferred path)\n const authService = typeof options.kernel.getService === 'function'\n ? options.kernel.getService<AuthService>('auth')\n : null;\n\n if (authService && typeof authService.handleRequest === 'function') {\n const response = await authService.handleRequest(req);\n // Convert Web Response to NextResponse\n const body = await response.text();\n const headers: Record<string, string> = {};\n response.headers.forEach((v: string, k: string) => { headers[k] = v; });\n return new NextResponse(body, { status: response.status, headers });\n }\n\n // Fallback to legacy dispatcher\n const subPath = segments.slice(1).join('/');\n const body = method === 'POST' ? await req.json().catch(() => ({})) : {};\n const result = await dispatcher.handleAuth(subPath, method, body, { request: req });\n return toResponse(result);\n }\n\n // --- 2. GraphQL ---\n if (segments[0] === 'graphql' && method === 'POST') {\n const body = await req.json();\n const result = await dispatcher.handleGraphQL(body as any, { request: rawRequest } as any);\n return NextResponse.json(result);\n }\n\n // --- 3. Metadata ---\n if (segments[0] === 'meta') {\n const subPath = segments.slice(1).join('/');\n \n let body: any = undefined;\n if (method === 'PUT' || method === 'POST') {\n body = await req.json().catch(() => ({}));\n }\n\n const result = await dispatcher.handleMetadata(subPath, { request: rawRequest }, method, body);\n return toResponse(result);\n }\n\n // --- 4. Data ---\n if (segments[0] === 'data') {\n const subPath = segments.slice(1).join('/');\n let body: any = {};\n if (method === 'POST' || method === 'PATCH') {\n body = await req.json().catch(() => ({}));\n }\n \n // Extract query params\n const url = new URL(req.url);\n const queryParams: Record<string, any> = {};\n url.searchParams.forEach((val, key) => queryParams[key] = val);\n\n const result = await dispatcher.handleData(subPath, method, body, queryParams, { request: rawRequest } as any);\n return toResponse(result);\n }\n\n // --- 5. Storage ---\n if (segments[0] === 'storage') {\n const subPath = segments.slice(1).join('/');\n \n let file: any = undefined;\n if (method === 'POST' && subPath === 'upload') {\n const formData = await req.formData();\n file = formData.get('file');\n }\n\n const result = await dispatcher.handleStorage(subPath, method, file, { request: rawRequest });\n return toResponse(result);\n }\n \n return error('Not Found', 404);\n\n } catch (err: any) {\n return error(err.message || 'Internal Server Error', err.statusCode || 500);\n }\n }\n}\n\n/**\n * Creates a discovery handler for Next.js App Router\n * Handles /.well-known/objectstack\n */\nexport function createDiscoveryHandler(options: NextAdapterOptions) {\n return async function discoveryHandler(req: NextRequest) {\n const apiPath = options.prefix || '/api';\n const url = new URL(req.url);\n const targetUrl = new URL(apiPath, url.origin);\n return NextResponse.redirect(targetUrl);\n }\n}\n"],"mappings":"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","names":["body"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/nextjs",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,7 +8,7 @@
8
8
  "next": "^16.1.6",
9
9
  "react": "^19.2.4",
10
10
  "react-dom": "^19.2.4",
11
- "@objectstack/runtime": "2.0.2"
11
+ "@objectstack/runtime": "2.0.4"
12
12
  },
13
13
  "devDependencies": {
14
14
  "next": "^16.1.6",
@@ -16,7 +16,7 @@
16
16
  "react-dom": "^19.2.4",
17
17
  "typescript": "^5.0.0",
18
18
  "vitest": "^4.0.18",
19
- "@objectstack/runtime": "2.0.2"
19
+ "@objectstack/runtime": "2.0.4"
20
20
  },
21
21
  "scripts": {
22
22
  "build": "tsup --config ../../../tsup.config.ts",
package/src/index.ts CHANGED
@@ -8,6 +8,13 @@ export interface NextAdapterOptions {
8
8
  prefix?: string;
9
9
  }
10
10
 
11
+ /**
12
+ * Auth service interface with handleRequest method
13
+ */
14
+ interface AuthService {
15
+ handleRequest(request: Request): Promise<Response>;
16
+ }
17
+
11
18
  /**
12
19
  * Creates a route handler for Next.js App Router
13
20
  * Handles /api/[...objectstack] pattern
@@ -61,6 +68,21 @@ export function createRouteHandler(options: NextAdapterOptions) {
61
68
 
62
69
  // --- 1. Auth ---
63
70
  if (segments[0] === 'auth') {
71
+ // Try AuthPlugin service first (preferred path)
72
+ const authService = typeof options.kernel.getService === 'function'
73
+ ? options.kernel.getService<AuthService>('auth')
74
+ : null;
75
+
76
+ if (authService && typeof authService.handleRequest === 'function') {
77
+ const response = await authService.handleRequest(req);
78
+ // Convert Web Response to NextResponse
79
+ const body = await response.text();
80
+ const headers: Record<string, string> = {};
81
+ response.headers.forEach((v: string, k: string) => { headers[k] = v; });
82
+ return new NextResponse(body, { status: response.status, headers });
83
+ }
84
+
85
+ // Fallback to legacy dispatcher
64
86
  const subPath = segments.slice(1).join('/');
65
87
  const body = method === 'POST' ? await req.json().catch(() => ({})) : {};
66
88
  const result = await dispatcher.handleAuth(subPath, method, body, { request: req });
@@ -150,6 +150,71 @@ describe('createRouteHandler', () => {
150
150
  });
151
151
  });
152
152
 
153
+ describe('Auth via AuthPlugin service', () => {
154
+ it('uses kernel.getService("auth") when available', async () => {
155
+ const mockHandleRequest = vi.fn().mockResolvedValue(
156
+ new Response(JSON.stringify({ user: { id: '1' } }), {
157
+ status: 200,
158
+ headers: { 'Content-Type': 'application/json' },
159
+ }),
160
+ );
161
+ const kernelWithAuth = {
162
+ ...mockKernel,
163
+ getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }),
164
+ };
165
+ const handler = createRouteHandler({ kernel: kernelWithAuth });
166
+ const req = makeReq('http://localhost/api/auth/sign-in/email', 'POST', { email: 'a@b.com', password: 'pass' });
167
+ const res = await handler(req, { params: { objectstack: ['auth', 'sign-in', 'email'] } });
168
+ expect(res.status).toBe(200);
169
+ expect(kernelWithAuth.getService).toHaveBeenCalledWith('auth');
170
+ expect(mockHandleRequest).toHaveBeenCalled();
171
+ expect(mockDispatcher.handleAuth).not.toHaveBeenCalled();
172
+ });
173
+
174
+ it('falls back to dispatcher.handleAuth when auth service is not available', async () => {
175
+ const kernelWithoutAuth = {
176
+ ...mockKernel,
177
+ getService: vi.fn().mockReturnValue(null),
178
+ };
179
+ const handler = createRouteHandler({ kernel: kernelWithoutAuth });
180
+ const req = makeReq('http://localhost/api/auth/login', 'POST', { email: 'a@b.com' });
181
+ const res = await handler(req, { params: { objectstack: ['auth', 'login'] } });
182
+ expect(res.status).toBe(200);
183
+ expect(mockDispatcher.handleAuth).toHaveBeenCalled();
184
+ });
185
+
186
+ it('forwards GET requests to auth service', async () => {
187
+ const mockHandleRequest = vi.fn().mockResolvedValue(
188
+ new Response(JSON.stringify({ session: { token: 'abc' } }), {
189
+ status: 200,
190
+ headers: { 'Content-Type': 'application/json' },
191
+ }),
192
+ );
193
+ const kernelWithAuth = {
194
+ ...mockKernel,
195
+ getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }),
196
+ };
197
+ const handler = createRouteHandler({ kernel: kernelWithAuth });
198
+ const req = makeReq('http://localhost/api/auth/get-session', 'GET');
199
+ const res = await handler(req, { params: { objectstack: ['auth', 'get-session'] } });
200
+ expect(res.status).toBe(200);
201
+ expect(mockHandleRequest).toHaveBeenCalled();
202
+ });
203
+
204
+ it('returns error when auth service throws', async () => {
205
+ const mockHandleRequest = vi.fn().mockRejectedValue(new Error('Auth failed'));
206
+ const kernelWithAuth = {
207
+ ...mockKernel,
208
+ getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }),
209
+ };
210
+ const handler = createRouteHandler({ kernel: kernelWithAuth });
211
+ const req = makeReq('http://localhost/api/auth/sign-in/email', 'POST', { email: 'a@b.com' });
212
+ const res = await handler(req, { params: { objectstack: ['auth', 'sign-in', 'email'] } });
213
+ expect(res.status).toBe(500);
214
+ expect(res.body.success).toBe(false);
215
+ });
216
+ });
217
+
153
218
  describe('GraphQL Endpoint', () => {
154
219
  it('POST graphql calls handleGraphQL', async () => {
155
220
  const handler = createRouteHandler({ kernel: mockKernel });