@objectstack/nextjs 4.0.1 → 4.0.3
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/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +13 -0
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- package/src/index.ts +5 -1
- package/src/metadata-api.test.ts +9 -0
- package/src/nextjs.test.ts +6 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/nextjs@4.0.
|
|
2
|
+
> @objectstack/nextjs@4.0.3 build /home/runner/work/framework/framework/packages/adapters/nextjs
|
|
3
3
|
> tsup --config ../../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
7
|
[34mCLI[39m tsup v8.5.1
|
|
8
|
-
[34mCLI[39m Using tsup config: /home/runner/work/
|
|
8
|
+
[34mCLI[39m Using tsup config: /home/runner/work/framework/framework/tsup.config.ts
|
|
9
9
|
[34mCLI[39m Target: es2020
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[32mCJS[39m [1mdist/index.js [22m[32m8.
|
|
14
|
-
[32mCJS[39m [1mdist/index.js.map [22m[32m16.
|
|
15
|
-
[32mCJS[39m ⚡️ Build success in
|
|
16
|
-
[32mESM[39m [1mdist/index.mjs [22m[32m7.
|
|
17
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[32m16.
|
|
18
|
-
[32mESM[39m ⚡️ Build success in
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m8.99 KB[39m
|
|
14
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m16.97 KB[39m
|
|
15
|
+
[32mCJS[39m ⚡️ Build success in 70ms
|
|
16
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m7.73 KB[39m
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m16.92 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 70ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 9961ms
|
|
21
21
|
[32mDTS[39m [1mdist/index.d.mts [22m[32m2.63 KB[39m
|
|
22
22
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m2.63 KB[39m
|
package/CHANGELOG.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -61,6 +61,9 @@ function createRouteHandler(options) {
|
|
|
61
61
|
if (segments.length === 0 && method === "GET") {
|
|
62
62
|
return import_server.NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || "/api") });
|
|
63
63
|
}
|
|
64
|
+
if (segments.length === 1 && segments[0] === "discovery" && method === "GET") {
|
|
65
|
+
return import_server.NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || "/api") });
|
|
66
|
+
}
|
|
64
67
|
try {
|
|
65
68
|
const rawRequest = req;
|
|
66
69
|
if (segments[0] === "auth") {
|
|
@@ -111,7 +114,7 @@ function createRouteHandler(options) {
|
|
|
111
114
|
const url = new URL(req.url);
|
|
112
115
|
const queryParams = {};
|
|
113
116
|
url.searchParams.forEach((val, key) => queryParams[key] = val);
|
|
114
|
-
const result = await dispatcher.dispatch(method, path, body, queryParams, { request: rawRequest });
|
|
117
|
+
const result = await dispatcher.dispatch(method, path, body, queryParams, { request: rawRequest }, options.prefix || "/api");
|
|
115
118
|
return toResponse(result);
|
|
116
119
|
} catch (err) {
|
|
117
120
|
return error(err.message || "Internal Server Error", err.statusCode || 500);
|
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 * 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 *\n * Only auth, GraphQL, storage, and discovery need explicit handling.\n * All other routes delegate to `HttpDispatcher.dispatch()` automatically.\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: await dispatcher.getDiscoveryInfo(options.prefix || '/api') });\n }\n\n try {\n const rawRequest = req;\n\n // --- 1. Auth (needs auth service integration) ---\n if (segments[0] === 'auth') {\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\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 (returns raw result, not HttpDispatcherResult) ---\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. Storage (needs formData parsing) ---\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 // --- 4. Catch-all: delegate to dispatcher.dispatch() ---\n // Handles meta, data, packages, analytics, automation, i18n, ui,\n // openapi, custom API endpoints, and any future routes.\n const path = '/' + segments.join('/');\n\n let body: any = undefined;\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n body = await req.json().catch(() => ({}));\n }\n\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.dispatch(method, path, body, queryParams, { request: rawRequest });\n return toResponse(result);\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\n// ─── Server Actions ──────────────────────────────────────────────────────────\n\n/**\n * Result type for server actions\n */\nexport interface ServerActionResult<T = any> {\n success: boolean;\n data?: T;\n error?: { message: string; code: number };\n}\n\n/**\n * Creates type-safe React Server Actions for ObjectStack data operations.\n * Each action maps to a dispatcher method and can be called directly from\n * React Server Components or client components via `\"use server\"`.\n *\n * @example\n * ```ts\n * // app/actions.ts\n * \"use server\";\n * import { createServerActions } from '@objectstack/nextjs';\n * import { kernel } from '@/lib/kernel';\n *\n * const actions = createServerActions({ kernel });\n *\n * export async function getAccounts() {\n * return actions.query('account');\n * }\n *\n * export async function createAccount(formData: FormData) {\n * return actions.create('account', {\n * name: formData.get('name') as string,\n * });\n * }\n * ```\n */\nexport function createServerActions(options: NextAdapterOptions) {\n const dispatcher = new HttpDispatcher(options.kernel);\n const emptyContext = { request: undefined };\n\n return {\n /**\n * Query records from an object\n */\n async query(objectName: string, params?: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}`, 'GET', {}, params || {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Get a single record by ID\n */\n async getById(objectName: string, id: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'GET', {}, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Create a new record\n */\n async create(objectName: string, data: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}`, 'POST', data, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Create failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Update a record by ID\n */\n async update(objectName: string, id: string, data: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'PATCH', data, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Update failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Delete a record by ID\n */\n async remove(objectName: string, id: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'DELETE', {}, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Delete failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Get metadata for objects\n */\n async getMetadata(path?: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleMetadata(path || '', emptyContext, 'GET');\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n };\n}\n"],"mappings":"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","names":["body","result"]}
|
|
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 *\n * Only auth, GraphQL, storage, and discovery need explicit handling.\n * All other routes delegate to `HttpDispatcher.dispatch()` automatically.\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: await dispatcher.getDiscoveryInfo(options.prefix || '/api') });\n }\n\n if (segments.length === 1 && segments[0] === 'discovery' && method === 'GET') {\n return NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || '/api') });\n }\n\n try {\n const rawRequest = req;\n\n // --- 1. Auth (needs auth service integration) ---\n if (segments[0] === 'auth') {\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\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 (returns raw result, not HttpDispatcherResult) ---\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. Storage (needs formData parsing) ---\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 // --- 4. Catch-all: delegate to dispatcher.dispatch() ---\n // Handles meta, data, packages, analytics, automation, i18n, ui,\n // openapi, custom API endpoints, and any future routes.\n const path = '/' + segments.join('/');\n\n let body: any = undefined;\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n body = await req.json().catch(() => ({}));\n }\n\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.dispatch(method, path, body, queryParams, { request: rawRequest }, options.prefix || '/api');\n return toResponse(result);\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\n// ─── Server Actions ──────────────────────────────────────────────────────────\n\n/**\n * Result type for server actions\n */\nexport interface ServerActionResult<T = any> {\n success: boolean;\n data?: T;\n error?: { message: string; code: number };\n}\n\n/**\n * Creates type-safe React Server Actions for ObjectStack data operations.\n * Each action maps to a dispatcher method and can be called directly from\n * React Server Components or client components via `\"use server\"`.\n *\n * @example\n * ```ts\n * // app/actions.ts\n * \"use server\";\n * import { createServerActions } from '@objectstack/nextjs';\n * import { kernel } from '@/lib/kernel';\n *\n * const actions = createServerActions({ kernel });\n *\n * export async function getAccounts() {\n * return actions.query('account');\n * }\n *\n * export async function createAccount(formData: FormData) {\n * return actions.create('account', {\n * name: formData.get('name') as string,\n * });\n * }\n * ```\n */\nexport function createServerActions(options: NextAdapterOptions) {\n const dispatcher = new HttpDispatcher(options.kernel);\n const emptyContext = { request: undefined };\n\n return {\n /**\n * Query records from an object\n */\n async query(objectName: string, params?: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}`, 'GET', {}, params || {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Get a single record by ID\n */\n async getById(objectName: string, id: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'GET', {}, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Create a new record\n */\n async create(objectName: string, data: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}`, 'POST', data, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Create failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Update a record by ID\n */\n async update(objectName: string, id: string, data: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'PATCH', data, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Update failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Delete a record by ID\n */\n async remove(objectName: string, id: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'DELETE', {}, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Delete failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Get metadata for objects\n */\n async getMetadata(path?: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleMetadata(path || '', emptyContext, 'GET');\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n };\n}\n"],"mappings":"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","names":["body","result"]}
|
package/dist/index.mjs
CHANGED
|
@@ -35,6 +35,9 @@ function createRouteHandler(options) {
|
|
|
35
35
|
if (segments.length === 0 && method === "GET") {
|
|
36
36
|
return NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || "/api") });
|
|
37
37
|
}
|
|
38
|
+
if (segments.length === 1 && segments[0] === "discovery" && method === "GET") {
|
|
39
|
+
return NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || "/api") });
|
|
40
|
+
}
|
|
38
41
|
try {
|
|
39
42
|
const rawRequest = req;
|
|
40
43
|
if (segments[0] === "auth") {
|
|
@@ -85,7 +88,7 @@ function createRouteHandler(options) {
|
|
|
85
88
|
const url = new URL(req.url);
|
|
86
89
|
const queryParams = {};
|
|
87
90
|
url.searchParams.forEach((val, key) => queryParams[key] = val);
|
|
88
|
-
const result = await dispatcher.dispatch(method, path, body, queryParams, { request: rawRequest });
|
|
91
|
+
const result = await dispatcher.dispatch(method, path, body, queryParams, { request: rawRequest }, options.prefix || "/api");
|
|
89
92
|
return toResponse(result);
|
|
90
93
|
} catch (err) {
|
|
91
94
|
return error(err.message || "Internal Server Error", err.statusCode || 500);
|
package/dist/index.mjs.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 * 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 *\n * Only auth, GraphQL, storage, and discovery need explicit handling.\n * All other routes delegate to `HttpDispatcher.dispatch()` automatically.\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: await dispatcher.getDiscoveryInfo(options.prefix || '/api') });\n }\n\n try {\n const rawRequest = req;\n\n // --- 1. Auth (needs auth service integration) ---\n if (segments[0] === 'auth') {\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\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 (returns raw result, not HttpDispatcherResult) ---\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. Storage (needs formData parsing) ---\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 // --- 4. Catch-all: delegate to dispatcher.dispatch() ---\n // Handles meta, data, packages, analytics, automation, i18n, ui,\n // openapi, custom API endpoints, and any future routes.\n const path = '/' + segments.join('/');\n\n let body: any = undefined;\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n body = await req.json().catch(() => ({}));\n }\n\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.dispatch(method, path, body, queryParams, { request: rawRequest });\n return toResponse(result);\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\n// ─── Server Actions ──────────────────────────────────────────────────────────\n\n/**\n * Result type for server actions\n */\nexport interface ServerActionResult<T = any> {\n success: boolean;\n data?: T;\n error?: { message: string; code: number };\n}\n\n/**\n * Creates type-safe React Server Actions for ObjectStack data operations.\n * Each action maps to a dispatcher method and can be called directly from\n * React Server Components or client components via `\"use server\"`.\n *\n * @example\n * ```ts\n * // app/actions.ts\n * \"use server\";\n * import { createServerActions } from '@objectstack/nextjs';\n * import { kernel } from '@/lib/kernel';\n *\n * const actions = createServerActions({ kernel });\n *\n * export async function getAccounts() {\n * return actions.query('account');\n * }\n *\n * export async function createAccount(formData: FormData) {\n * return actions.create('account', {\n * name: formData.get('name') as string,\n * });\n * }\n * ```\n */\nexport function createServerActions(options: NextAdapterOptions) {\n const dispatcher = new HttpDispatcher(options.kernel);\n const emptyContext = { request: undefined };\n\n return {\n /**\n * Query records from an object\n */\n async query(objectName: string, params?: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}`, 'GET', {}, params || {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Get a single record by ID\n */\n async getById(objectName: string, id: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'GET', {}, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Create a new record\n */\n async create(objectName: string, data: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}`, 'POST', data, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Create failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Update a record by ID\n */\n async update(objectName: string, id: string, data: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'PATCH', data, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Update failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Delete a record by ID\n */\n async remove(objectName: string, id: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'DELETE', {}, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Delete failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Get metadata for objects\n */\n async getMetadata(path?: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleMetadata(path || '', emptyContext, 'GET');\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n };\n}\n"],"mappings":"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","names":["body","result"]}
|
|
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 *\n * Only auth, GraphQL, storage, and discovery need explicit handling.\n * All other routes delegate to `HttpDispatcher.dispatch()` automatically.\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: await dispatcher.getDiscoveryInfo(options.prefix || '/api') });\n }\n\n if (segments.length === 1 && segments[0] === 'discovery' && method === 'GET') {\n return NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || '/api') });\n }\n\n try {\n const rawRequest = req;\n\n // --- 1. Auth (needs auth service integration) ---\n if (segments[0] === 'auth') {\n // Try AuthPlugin service first (prefer async to support factory-based services)\n let authService: AuthService | null = null;\n try {\n if (typeof options.kernel.getServiceAsync === 'function') {\n authService = await options.kernel.getServiceAsync<AuthService>('auth');\n } else if (typeof options.kernel.getService === 'function') {\n authService = options.kernel.getService<AuthService>('auth');\n }\n } catch {\n // Service not registered — fall through to dispatcher\n authService = null;\n }\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 (returns raw result, not HttpDispatcherResult) ---\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. Storage (needs formData parsing) ---\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 // --- 4. Catch-all: delegate to dispatcher.dispatch() ---\n // Handles meta, data, packages, analytics, automation, i18n, ui,\n // openapi, custom API endpoints, and any future routes.\n const path = '/' + segments.join('/');\n\n let body: any = undefined;\n if (method === 'POST' || method === 'PUT' || method === 'PATCH') {\n body = await req.json().catch(() => ({}));\n }\n\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.dispatch(method, path, body, queryParams, { request: rawRequest }, options.prefix || '/api');\n return toResponse(result);\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\n// ─── Server Actions ──────────────────────────────────────────────────────────\n\n/**\n * Result type for server actions\n */\nexport interface ServerActionResult<T = any> {\n success: boolean;\n data?: T;\n error?: { message: string; code: number };\n}\n\n/**\n * Creates type-safe React Server Actions for ObjectStack data operations.\n * Each action maps to a dispatcher method and can be called directly from\n * React Server Components or client components via `\"use server\"`.\n *\n * @example\n * ```ts\n * // app/actions.ts\n * \"use server\";\n * import { createServerActions } from '@objectstack/nextjs';\n * import { kernel } from '@/lib/kernel';\n *\n * const actions = createServerActions({ kernel });\n *\n * export async function getAccounts() {\n * return actions.query('account');\n * }\n *\n * export async function createAccount(formData: FormData) {\n * return actions.create('account', {\n * name: formData.get('name') as string,\n * });\n * }\n * ```\n */\nexport function createServerActions(options: NextAdapterOptions) {\n const dispatcher = new HttpDispatcher(options.kernel);\n const emptyContext = { request: undefined };\n\n return {\n /**\n * Query records from an object\n */\n async query(objectName: string, params?: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}`, 'GET', {}, params || {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Get a single record by ID\n */\n async getById(objectName: string, id: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'GET', {}, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Create a new record\n */\n async create(objectName: string, data: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}`, 'POST', data, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Create failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Update a record by ID\n */\n async update(objectName: string, id: string, data: Record<string, any>): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'PATCH', data, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Update failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Delete a record by ID\n */\n async remove(objectName: string, id: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleData(`/${objectName}/${id}`, 'DELETE', {}, {}, emptyContext);\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Delete failed', code: 500 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n\n /**\n * Get metadata for objects\n */\n async getMetadata(path?: string): Promise<ServerActionResult> {\n try {\n const result = await dispatcher.handleMetadata(path || '', emptyContext, 'GET');\n if (result.handled && result.response) {\n return { success: true, data: result.response.body };\n }\n return { success: false, error: { message: 'Not found', code: 404 } };\n } catch (err: any) {\n return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } };\n }\n },\n };\n}\n"],"mappings":"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","names":["body","result"]}
|
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/nextjs",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"peerDependencies": {
|
|
8
|
-
"next": "^16.2.
|
|
9
|
-
"react": "^19.2.
|
|
10
|
-
"react-dom": "^19.2.
|
|
11
|
-
"@objectstack/runtime": "^4.0.
|
|
8
|
+
"next": "^16.2.3",
|
|
9
|
+
"react": "^19.2.5",
|
|
10
|
+
"react-dom": "^19.2.5",
|
|
11
|
+
"@objectstack/runtime": "^4.0.3"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"next": "^16.2.
|
|
15
|
-
"react": "^19.2.
|
|
16
|
-
"react-dom": "^19.2.
|
|
14
|
+
"next": "^16.2.3",
|
|
15
|
+
"react": "^19.2.5",
|
|
16
|
+
"react-dom": "^19.2.5",
|
|
17
17
|
"typescript": "^6.0.2",
|
|
18
|
-
"vitest": "^4.1.
|
|
19
|
-
"@objectstack/runtime": "4.0.
|
|
18
|
+
"vitest": "^4.1.4",
|
|
19
|
+
"@objectstack/runtime": "4.0.3"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "tsup --config ../../../tsup.config.ts",
|
package/src/index.ts
CHANGED
|
@@ -66,6 +66,10 @@ export function createRouteHandler(options: NextAdapterOptions) {
|
|
|
66
66
|
return NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || '/api') });
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
if (segments.length === 1 && segments[0] === 'discovery' && method === 'GET') {
|
|
70
|
+
return NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || '/api') });
|
|
71
|
+
}
|
|
72
|
+
|
|
69
73
|
try {
|
|
70
74
|
const rawRequest = req;
|
|
71
75
|
|
|
@@ -135,7 +139,7 @@ export function createRouteHandler(options: NextAdapterOptions) {
|
|
|
135
139
|
const queryParams: Record<string, any> = {};
|
|
136
140
|
url.searchParams.forEach((val, key) => queryParams[key] = val);
|
|
137
141
|
|
|
138
|
-
const result = await dispatcher.dispatch(method, path, body, queryParams, { request: rawRequest });
|
|
142
|
+
const result = await dispatcher.dispatch(method, path, body, queryParams, { request: rawRequest }, options.prefix || '/api');
|
|
139
143
|
return toResponse(result);
|
|
140
144
|
|
|
141
145
|
} catch (err: any) {
|
package/src/metadata-api.test.ts
CHANGED
|
@@ -132,6 +132,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
132
132
|
undefined,
|
|
133
133
|
{},
|
|
134
134
|
expect.objectContaining({ request: expect.anything() }),
|
|
135
|
+
'/api',
|
|
135
136
|
);
|
|
136
137
|
});
|
|
137
138
|
});
|
|
@@ -159,6 +160,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
159
160
|
undefined,
|
|
160
161
|
{},
|
|
161
162
|
expect.objectContaining({ request: expect.anything() }),
|
|
163
|
+
'/api',
|
|
162
164
|
);
|
|
163
165
|
});
|
|
164
166
|
});
|
|
@@ -185,6 +187,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
185
187
|
body,
|
|
186
188
|
{},
|
|
187
189
|
expect.objectContaining({ request: expect.anything() }),
|
|
190
|
+
'/api',
|
|
188
191
|
);
|
|
189
192
|
});
|
|
190
193
|
});
|
|
@@ -207,6 +210,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
207
210
|
body,
|
|
208
211
|
{},
|
|
209
212
|
expect.objectContaining({ request: expect.anything() }),
|
|
213
|
+
'/api',
|
|
210
214
|
);
|
|
211
215
|
});
|
|
212
216
|
});
|
|
@@ -238,6 +242,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
238
242
|
undefined,
|
|
239
243
|
{},
|
|
240
244
|
expect.objectContaining({ request: expect.anything() }),
|
|
245
|
+
'/api',
|
|
241
246
|
);
|
|
242
247
|
});
|
|
243
248
|
|
|
@@ -250,6 +255,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
250
255
|
undefined,
|
|
251
256
|
{},
|
|
252
257
|
expect.objectContaining({ request: expect.anything() }),
|
|
258
|
+
'/api',
|
|
253
259
|
);
|
|
254
260
|
});
|
|
255
261
|
|
|
@@ -262,6 +268,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
262
268
|
undefined,
|
|
263
269
|
{},
|
|
264
270
|
expect.objectContaining({ request: expect.anything() }),
|
|
271
|
+
'/api',
|
|
265
272
|
);
|
|
266
273
|
});
|
|
267
274
|
});
|
|
@@ -696,6 +703,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
696
703
|
undefined,
|
|
697
704
|
{},
|
|
698
705
|
expect.objectContaining({ request: expect.anything() }),
|
|
706
|
+
'/api',
|
|
699
707
|
);
|
|
700
708
|
});
|
|
701
709
|
|
|
@@ -709,6 +717,7 @@ describe('Next.js Metadata API Integration Tests', () => {
|
|
|
709
717
|
undefined,
|
|
710
718
|
{},
|
|
711
719
|
expect.objectContaining({ request: expect.anything() }),
|
|
720
|
+
'/api',
|
|
712
721
|
);
|
|
713
722
|
});
|
|
714
723
|
});
|
package/src/nextjs.test.ts
CHANGED
|
@@ -284,6 +284,7 @@ describe('createRouteHandler', () => {
|
|
|
284
284
|
undefined,
|
|
285
285
|
expect.any(Object),
|
|
286
286
|
expect.objectContaining({ request: expect.anything() }),
|
|
287
|
+
'/api',
|
|
287
288
|
);
|
|
288
289
|
});
|
|
289
290
|
|
|
@@ -299,6 +300,7 @@ describe('createRouteHandler', () => {
|
|
|
299
300
|
body,
|
|
300
301
|
expect.any(Object),
|
|
301
302
|
expect.objectContaining({ request: expect.anything() }),
|
|
303
|
+
'/api',
|
|
302
304
|
);
|
|
303
305
|
});
|
|
304
306
|
|
|
@@ -314,6 +316,7 @@ describe('createRouteHandler', () => {
|
|
|
314
316
|
body,
|
|
315
317
|
expect.any(Object),
|
|
316
318
|
expect.objectContaining({ request: expect.anything() }),
|
|
319
|
+
'/api',
|
|
317
320
|
);
|
|
318
321
|
});
|
|
319
322
|
});
|
|
@@ -330,6 +333,7 @@ describe('createRouteHandler', () => {
|
|
|
330
333
|
undefined,
|
|
331
334
|
expect.any(Object),
|
|
332
335
|
expect.objectContaining({ request: expect.anything() }),
|
|
336
|
+
'/api',
|
|
333
337
|
);
|
|
334
338
|
});
|
|
335
339
|
|
|
@@ -345,6 +349,7 @@ describe('createRouteHandler', () => {
|
|
|
345
349
|
body,
|
|
346
350
|
expect.any(Object),
|
|
347
351
|
expect.objectContaining({ request: expect.anything() }),
|
|
352
|
+
'/api',
|
|
348
353
|
);
|
|
349
354
|
});
|
|
350
355
|
|
|
@@ -360,6 +365,7 @@ describe('createRouteHandler', () => {
|
|
|
360
365
|
body,
|
|
361
366
|
expect.any(Object),
|
|
362
367
|
expect.objectContaining({ request: expect.anything() }),
|
|
368
|
+
'/api',
|
|
363
369
|
);
|
|
364
370
|
});
|
|
365
371
|
|