@danceroutine/tango-adapters-next 1.11.14 → 1.11.15
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/dist/adapter/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as NextAdapter } from "../adapter-
|
|
1
|
+
import { n as NextAdapter } from "../adapter-Cj_ViN2G.js";
|
|
2
2
|
export { NextAdapter };
|
|
@@ -216,10 +216,10 @@ var NextAdapter = class {
|
|
|
216
216
|
return catchAll.split("/").filter(Boolean);
|
|
217
217
|
}
|
|
218
218
|
methodNotAllowedResponse() {
|
|
219
|
-
return TangoResponse.
|
|
219
|
+
return TangoResponse.methodNotAllowed(void 0, "Method not allowed for this route.");
|
|
220
220
|
}
|
|
221
221
|
notFoundResponse() {
|
|
222
|
-
return TangoResponse.
|
|
222
|
+
return TangoResponse.notFound("Not found.");
|
|
223
223
|
}
|
|
224
224
|
resolveActionMatch(viewset, method, segments) {
|
|
225
225
|
if (segments.length === 0) return null;
|
|
@@ -254,4 +254,4 @@ var adapter_exports = /* @__PURE__ */ __exportAll({ NextAdapter: () => NextAdapt
|
|
|
254
254
|
//#endregion
|
|
255
255
|
export { NextAdapter as n, adapter_exports as t };
|
|
256
256
|
|
|
257
|
-
//# sourceMappingURL=adapter-
|
|
257
|
+
//# sourceMappingURL=adapter-Cj_ViN2G.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-DStdtW--.js","names":[],"sources":["../src/adapter/NextAdapter.ts","../src/adapter/index.ts"],"sourcesContent":["import type { NextRequest } from 'next/server';\nimport { RequestContext } from '@danceroutine/tango-resources';\nimport {\n HttpErrorFactory,\n TangoQueryParams,\n TangoRequest,\n TangoResponse,\n getLogger,\n type JsonValue,\n} from '@danceroutine/tango-core';\nimport {\n FRAMEWORK_ADAPTER_BRAND,\n FrameworkAdapterRequestExecutor,\n type FrameworkAdapter,\n type FrameworkAdapterOptions,\n} from '@danceroutine/tango-adapters-core/adapter';\nimport {\n InternalHttpMethod,\n InternalActionScope,\n InternalActionMatchKind,\n type ActionScope,\n type HttpMethod,\n} from '@danceroutine/tango-adapters-core';\n\ntype ResolvedViewSetActionDescriptor = {\n name: string;\n scope: ActionScope;\n methods: readonly HttpMethod[];\n path: string;\n};\n\n/**\n * Next.js route handler signature produced by the adapter.\n */\nexport type NextRouteHandler = (\n request: NextRequest,\n context?: { params?: Promise<Record<string, string | string[]>> }\n) => Promise<Response>;\n\nexport type NextDynamicRouteContext = {\n params: Promise<Record<string, string | string[]>>;\n};\n\nexport type NextDynamicRouteHandler = (request: NextRequest, context: NextDynamicRouteContext) => Promise<Response>;\n\n/**\n * Adapter options for Next.js integration.\n */\nexport type AdaptNextOptions = FrameworkAdapterOptions<NextRequest>;\n\n/**\n * Minimal CRUD viewset contract used by the Next adapter route helpers.\n */\nexport interface NextCrudViewSet {\n list(ctx: RequestContext): Promise<TangoResponse>;\n create(ctx: RequestContext): Promise<TangoResponse>;\n retrieve(ctx: RequestContext, id: string): Promise<TangoResponse>;\n update(ctx: RequestContext, id: string): Promise<TangoResponse>;\n destroy(ctx: RequestContext, id: string): Promise<TangoResponse>;\n}\n\nexport interface NextAPIView {\n dispatch(ctx: RequestContext): Promise<TangoResponse>;\n}\n\nexport type NextViewSetFactory = () => NextCrudViewSet | Promise<NextCrudViewSet>;\nexport type NextAPIViewFactory = () => NextAPIView | Promise<NextAPIView>;\n\n/**\n * Options for auto-generated viewset route handlers.\n */\nexport type AdaptNextViewSetOptions = AdaptNextOptions & {\n paramKey?: string;\n};\n\n/**\n * HTTP method handlers generated from a CRUD viewset.\n */\nexport interface NextViewSetRouteHandlers {\n GET: NextDynamicRouteHandler;\n POST: NextDynamicRouteHandler;\n PATCH: NextDynamicRouteHandler;\n PUT: NextDynamicRouteHandler;\n DELETE: NextDynamicRouteHandler;\n}\n\ntype ActionMatch =\n | { kind: typeof InternalActionMatchKind.DETAIL; action: ResolvedViewSetActionDescriptor; id: string }\n | { kind: typeof InternalActionMatchKind.COLLECTION; action: ResolvedViewSetActionDescriptor }\n | { kind: typeof InternalActionMatchKind.METHOD_NOT_ALLOWED };\n\n/**\n * Next.js adapter that translates route handlers to Tango `RequestContext`.\n */\nexport class NextAdapter implements FrameworkAdapter<Response, NextRouteHandler, NextRequest> {\n readonly __tangoBrand: typeof FRAMEWORK_ADAPTER_BRAND = FRAMEWORK_ADAPTER_BRAND;\n private readonly logger = getLogger('tango.adapter.next');\n private readonly requestExecutor = new FrameworkAdapterRequestExecutor();\n /**\n * Normalize Next.js-style route search params into Tango query params.\n */\n toQueryParams(searchParams: Record<string, string | string[] | undefined>): TangoQueryParams {\n return TangoQueryParams.fromRecord(searchParams);\n }\n\n /**\n * Adapt a Tango-style handler into a Next.js route handler.\n */\n adapt(\n handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>,\n options: AdaptNextOptions = {}\n ): NextRouteHandler {\n return this.createHandler(handler, options);\n }\n\n /**\n * Build Next route handlers that map HTTP verbs to standard CRUD viewset actions.\n */\n adaptViewSet(viewset: NextCrudViewSet, options: AdaptNextViewSetOptions = {}): NextViewSetRouteHandlers {\n const paramKey = options.paramKey ?? 'tango';\n return {\n GET: this.adapt(\n (ctx) =>\n this.dispatchViewSetAction(viewset, InternalHttpMethod.GET, ctx, paramKey, (segments, directId) => {\n if (directId) return viewset.retrieve(ctx, directId);\n if (segments.length === 0) return viewset.list(ctx);\n if (segments.length === 1) return viewset.retrieve(ctx, segments[0] as string);\n return this.notFoundResponse();\n }),\n options\n ),\n POST: this.adapt(\n (ctx) =>\n this.dispatchViewSetAction(\n viewset,\n InternalHttpMethod.POST,\n ctx,\n paramKey,\n (segments, directId) => {\n if (segments.length === 0 && !directId) return viewset.create(ctx);\n return this.methodNotAllowedResponse();\n }\n ),\n options\n ),\n PATCH: this.adapt(\n (ctx) =>\n this.dispatchViewSetAction(\n viewset,\n InternalHttpMethod.PATCH,\n ctx,\n paramKey,\n (segments, directId) => {\n const id = directId ?? segments[0];\n return id ? viewset.update(ctx, id) : this.methodNotAllowedResponse();\n }\n ),\n options\n ),\n PUT: this.adapt(\n (ctx) =>\n this.dispatchViewSetAction(viewset, InternalHttpMethod.PUT, ctx, paramKey, (segments, directId) => {\n const id = directId ?? segments[0];\n return id ? viewset.update(ctx, id) : this.methodNotAllowedResponse();\n }),\n options\n ),\n DELETE: this.adapt(\n (ctx) =>\n this.dispatchViewSetAction(\n viewset,\n InternalHttpMethod.DELETE,\n ctx,\n paramKey,\n (segments, directId) => {\n const id = directId ?? segments[0];\n return id ? viewset.destroy(ctx, id) : this.methodNotAllowedResponse();\n }\n ),\n options\n ),\n };\n }\n\n /**\n * Build Next route handlers from a lazy viewset factory and memoize initialization.\n * Initialization failures clear the memoized promise so subsequent requests can retry.\n */\n adaptViewSetFactory(factory: NextViewSetFactory, options: AdaptNextViewSetOptions = {}): NextViewSetRouteHandlers {\n return this.adaptRouteHandlersFactory(async () => {\n const viewset = await factory();\n return this.adaptViewSet(viewset, options);\n });\n }\n\n /**\n * Build Next route handlers from a lazy GenericAPIView factory and memoize initialization.\n */\n adaptGenericAPIViewFactory(\n factory: NextAPIViewFactory,\n options: AdaptNextViewSetOptions = {}\n ): NextViewSetRouteHandlers {\n return this.adaptRouteHandlersFactory(async () => {\n const apiView = await factory();\n return this.adaptGenericAPIView(apiView, options);\n });\n }\n\n /**\n * Build Next route handlers that dispatch an APIView by HTTP method.\n */\n adaptAPIView(apiView: NextAPIView, options: AdaptNextOptions = {}): NextViewSetRouteHandlers {\n return {\n GET: this.adapt((ctx) => apiView.dispatch(ctx), options),\n POST: this.adapt((ctx) => apiView.dispatch(ctx), options),\n PATCH: this.adapt((ctx) => apiView.dispatch(ctx), options),\n PUT: this.adapt((ctx) => apiView.dispatch(ctx), options),\n DELETE: this.adapt((ctx) => apiView.dispatch(ctx), options),\n };\n }\n\n /**\n * Build handlers for GenericAPIView-style collection/detail splits in catch-all routes.\n */\n adaptGenericAPIView(apiView: NextAPIView, options: AdaptNextViewSetOptions = {}): NextViewSetRouteHandlers {\n // Default catch-all param matches the Next.js [[...tango]] route convention.\n const paramKey = options.paramKey ?? 'tango';\n return {\n GET: this.adapt(async (ctx) => {\n const detailId = this.resolveDetailId(ctx.params, paramKey);\n if (!detailId) {\n return apiView.dispatch(ctx);\n }\n ctx.params.id = detailId;\n return apiView.dispatch(ctx);\n }, options),\n POST: this.adapt(async (ctx) => {\n const detailId = this.resolveDetailId(ctx.params, paramKey);\n if (detailId) {\n return this.methodNotAllowedResponse();\n }\n return apiView.dispatch(ctx);\n }, options),\n PATCH: this.adapt(async (ctx) => {\n const detailId = this.resolveDetailId(ctx.params, paramKey);\n if (!detailId) {\n return this.methodNotAllowedResponse();\n }\n ctx.params.id = detailId;\n return apiView.dispatch(ctx);\n }, options),\n PUT: this.adapt(async (ctx) => {\n const detailId = this.resolveDetailId(ctx.params, paramKey);\n if (!detailId) {\n return this.methodNotAllowedResponse();\n }\n ctx.params.id = detailId;\n return apiView.dispatch(ctx);\n }, options),\n DELETE: this.adapt(async (ctx) => {\n const detailId = this.resolveDetailId(ctx.params, paramKey);\n if (!detailId) {\n return this.methodNotAllowedResponse();\n }\n ctx.params.id = detailId;\n return apiView.dispatch(ctx);\n }, options),\n };\n }\n\n private toTangoRequest(request: NextRequest): TangoRequest {\n if (TangoRequest.isTangoRequest(request)) {\n return request;\n }\n\n // oxlint-disable-next-line eslint-js/no-restricted-syntax\n if (request instanceof Request) {\n return new TangoRequest(request);\n }\n\n return new TangoRequest(String((request as { url?: unknown }).url ?? 'http://localhost'));\n }\n\n private dispatchViewSetAction(\n viewset: NextCrudViewSet,\n method: HttpMethod,\n ctx: RequestContext,\n paramKey: string,\n fallback: (segments: string[], directId: string | null) => TangoResponse | Promise<TangoResponse>\n ): Promise<TangoResponse> {\n const segments = this.extractCatchAllSegments(ctx.params, paramKey);\n const directId = this.extractDirectId(ctx.params);\n const actionMatch = this.resolveActionMatch(viewset, method, segments);\n\n if (actionMatch?.kind === InternalActionMatchKind.METHOD_NOT_ALLOWED) {\n return Promise.resolve(this.methodNotAllowedResponse());\n }\n if (actionMatch?.kind === InternalActionMatchKind.DETAIL) {\n return this.invokeDetailAction(viewset, actionMatch.action, ctx, actionMatch.id);\n }\n if (actionMatch?.kind === InternalActionMatchKind.COLLECTION) {\n return this.invokeCollectionAction(viewset, actionMatch.action, ctx);\n }\n\n return Promise.resolve(fallback(segments, directId));\n }\n\n private invokeDetailAction(\n viewset: NextCrudViewSet,\n action: ResolvedViewSetActionDescriptor,\n ctx: RequestContext,\n id: string\n ): Promise<TangoResponse> {\n const candidate = (viewset as unknown as Record<string, unknown>)[action.name];\n if (typeof candidate !== 'function') {\n return Promise.resolve(this.notFoundResponse());\n }\n return (candidate as (this: NextCrudViewSet, ctx: RequestContext, id: string) => Promise<TangoResponse>).call(\n viewset,\n ctx,\n id\n );\n }\n\n private invokeCollectionAction(\n viewset: NextCrudViewSet,\n action: ResolvedViewSetActionDescriptor,\n ctx: RequestContext\n ): Promise<TangoResponse> {\n const candidate = (viewset as unknown as Record<string, unknown>)[action.name];\n if (typeof candidate !== 'function') {\n return Promise.resolve(this.notFoundResponse());\n }\n return (candidate as (this: NextCrudViewSet, ctx: RequestContext) => Promise<TangoResponse>).call(viewset, ctx);\n }\n\n private createHandler(\n handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>,\n options: AdaptNextOptions\n ): NextRouteHandler {\n return async (request: NextRequest, routeContext) => {\n try {\n const user = options.getUser ? await options.getUser(request) : null;\n const rawParams = routeContext?.params ? await routeContext.params : {};\n const params = this.normalizeRouteParams(rawParams);\n\n const ctx = RequestContext.create(this.toTangoRequest(request), user);\n if (Object.keys(params).length > 0) {\n ctx.params = params;\n }\n\n const id = params?.id;\n return await this.requestExecutor\n .forHandler({ handler, ctx, id })\n .runWebResponse(request.method, options.transaction);\n } catch (error) {\n return this.internalServerError(error);\n }\n };\n }\n\n private internalServerError(error: unknown): Response {\n this.logger.error('Adapter error:', error);\n const httpError = HttpErrorFactory.toHttpError(error);\n return TangoResponse.json(httpError.body as JsonValue, { status: httpError.status }).toWebResponse();\n }\n\n private adaptRouteHandlersFactory(factory: () => Promise<NextViewSetRouteHandlers>): NextViewSetRouteHandlers {\n let handlersPromise: Promise<NextViewSetRouteHandlers> | null = null;\n\n const getHandlers = async (): Promise<NextViewSetRouteHandlers> => {\n if (!handlersPromise) {\n const initializing = factory();\n handlersPromise = initializing.catch((error) => {\n handlersPromise = null;\n throw error;\n });\n }\n return handlersPromise;\n };\n\n const createLazyHandler = (method: keyof NextViewSetRouteHandlers): NextDynamicRouteHandler => {\n return async (request, context) => {\n try {\n const handlers = await getHandlers();\n return handlers[method](request, context);\n } catch (error) {\n return this.internalServerError(error);\n }\n };\n };\n\n return {\n GET: createLazyHandler('GET'),\n POST: createLazyHandler('POST'),\n PATCH: createLazyHandler('PATCH'),\n PUT: createLazyHandler('PUT'),\n DELETE: createLazyHandler('DELETE'),\n };\n }\n\n private resolveDetailId(params: Record<string, string>, paramKey: string): string | null {\n const directId = this.extractDirectId(params);\n if (directId) {\n return directId;\n }\n const segments = this.extractCatchAllSegments(params, paramKey);\n if (segments.length !== 1) {\n return null;\n }\n return segments[0] as string;\n }\n\n private normalizeRouteParams(raw: Record<string, string | string[]>): Record<string, string> {\n const entries = Object.entries(raw).map(([key, value]) => {\n return [key, Array.isArray(value) ? value.join('/') : value];\n });\n\n return Object.fromEntries(entries);\n }\n\n private extractDirectId(params: Record<string, string>): string | null {\n const directId = params.id?.trim();\n return directId || null;\n }\n\n private extractCatchAllSegments(params: Record<string, string>, paramKey: string): string[] {\n const catchAll = params[paramKey]?.trim() ?? '';\n if (!catchAll) {\n return [];\n }\n return catchAll.split('/').filter(Boolean);\n }\n\n private methodNotAllowedResponse(): TangoResponse {\n return TangoResponse.json(\n {\n error: 'Method not allowed for this route.',\n },\n { status: 405 }\n );\n }\n\n private notFoundResponse(): TangoResponse {\n return TangoResponse.json(\n {\n error: 'Not found.',\n },\n { status: 404 }\n );\n }\n\n private resolveActionMatch(viewset: NextCrudViewSet, method: HttpMethod, segments: string[]): ActionMatch | null {\n if (segments.length === 0) {\n return null;\n }\n\n const actions = this.getViewSetActions(viewset);\n\n if (segments.length >= 2) {\n const detailPath = segments.slice(1).join('/');\n const detailMatch = actions.find(\n (action) => action.scope === InternalActionScope.DETAIL && action.path === detailPath\n );\n if (detailMatch) {\n return detailMatch.methods.includes(method)\n ? { kind: InternalActionMatchKind.DETAIL, action: detailMatch, id: segments[0] as string }\n : { kind: InternalActionMatchKind.METHOD_NOT_ALLOWED };\n }\n }\n\n if (method === InternalHttpMethod.GET && segments.length === 1) {\n return null;\n }\n\n const collectionPath = segments.join('/');\n const collectionMatch = actions.find(\n (action) => action.scope === InternalActionScope.COLLECTION && action.path === collectionPath\n );\n if (!collectionMatch) {\n return null;\n }\n return collectionMatch.methods.includes(method)\n ? { kind: InternalActionMatchKind.COLLECTION, action: collectionMatch }\n : { kind: InternalActionMatchKind.METHOD_NOT_ALLOWED };\n }\n\n private getViewSetActions(viewset: NextCrudViewSet): readonly ResolvedViewSetActionDescriptor[] {\n const constructorValue = viewset.constructor as {\n getActions?: (input: NextCrudViewSet) => readonly ResolvedViewSetActionDescriptor[];\n };\n\n if (typeof constructorValue.getActions !== 'function') {\n return [];\n }\n\n return constructorValue.getActions(viewset);\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport {\n NextAdapter,\n type AdaptNextOptions,\n type AdaptNextViewSetOptions,\n type NextAPIView,\n type NextAPIViewFactory,\n type NextCrudViewSet,\n type NextDynamicRouteContext,\n type NextDynamicRouteHandler,\n type NextRouteHandler,\n type NextViewSetFactory,\n type NextViewSetRouteHandlers,\n} from './NextAdapter';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"adapter-Cj_ViN2G.js","names":[],"sources":["../src/adapter/NextAdapter.ts","../src/adapter/index.ts"],"sourcesContent":["import type { NextRequest } from 'next/server';\nimport { RequestContext } from '@danceroutine/tango-resources';\nimport {\n HttpErrorFactory,\n TangoQueryParams,\n TangoRequest,\n TangoResponse,\n getLogger,\n type JsonValue,\n} from '@danceroutine/tango-core';\nimport {\n FRAMEWORK_ADAPTER_BRAND,\n FrameworkAdapterRequestExecutor,\n type FrameworkAdapter,\n type FrameworkAdapterOptions,\n} from '@danceroutine/tango-adapters-core/adapter';\nimport {\n InternalHttpMethod,\n InternalActionScope,\n InternalActionMatchKind,\n type ActionScope,\n type HttpMethod,\n} from '@danceroutine/tango-adapters-core';\n\ntype ResolvedViewSetActionDescriptor = {\n name: string;\n scope: ActionScope;\n methods: readonly HttpMethod[];\n path: string;\n};\n\n/**\n * Next.js route handler signature produced by the adapter.\n */\nexport type NextRouteHandler = (\n request: NextRequest,\n context?: { params?: Promise<Record<string, string | string[]>> }\n) => Promise<Response>;\n\nexport type NextDynamicRouteContext = {\n params: Promise<Record<string, string | string[]>>;\n};\n\nexport type NextDynamicRouteHandler = (request: NextRequest, context: NextDynamicRouteContext) => Promise<Response>;\n\n/**\n * Adapter options for Next.js integration.\n */\nexport type AdaptNextOptions = FrameworkAdapterOptions<NextRequest>;\n\n/**\n * Minimal CRUD viewset contract used by the Next adapter route helpers.\n */\nexport interface NextCrudViewSet {\n list(ctx: RequestContext): Promise<TangoResponse>;\n create(ctx: RequestContext): Promise<TangoResponse>;\n retrieve(ctx: RequestContext, id: string): Promise<TangoResponse>;\n update(ctx: RequestContext, id: string): Promise<TangoResponse>;\n destroy(ctx: RequestContext, id: string): Promise<TangoResponse>;\n}\n\nexport interface NextAPIView {\n dispatch(ctx: RequestContext): Promise<TangoResponse>;\n}\n\nexport type NextViewSetFactory = () => NextCrudViewSet | Promise<NextCrudViewSet>;\nexport type NextAPIViewFactory = () => NextAPIView | Promise<NextAPIView>;\n\n/**\n * Options for auto-generated viewset route handlers.\n */\nexport type AdaptNextViewSetOptions = AdaptNextOptions & {\n paramKey?: string;\n};\n\n/**\n * HTTP method handlers generated from a CRUD viewset.\n */\nexport interface NextViewSetRouteHandlers {\n GET: NextDynamicRouteHandler;\n POST: NextDynamicRouteHandler;\n PATCH: NextDynamicRouteHandler;\n PUT: NextDynamicRouteHandler;\n DELETE: NextDynamicRouteHandler;\n}\n\ntype ActionMatch =\n | { kind: typeof InternalActionMatchKind.DETAIL; action: ResolvedViewSetActionDescriptor; id: string }\n | { kind: typeof InternalActionMatchKind.COLLECTION; action: ResolvedViewSetActionDescriptor }\n | { kind: typeof InternalActionMatchKind.METHOD_NOT_ALLOWED };\n\n/**\n * Next.js adapter that translates route handlers to Tango `RequestContext`.\n */\nexport class NextAdapter implements FrameworkAdapter<Response, NextRouteHandler, NextRequest> {\n readonly __tangoBrand: typeof FRAMEWORK_ADAPTER_BRAND = FRAMEWORK_ADAPTER_BRAND;\n private readonly logger = getLogger('tango.adapter.next');\n private readonly requestExecutor = new FrameworkAdapterRequestExecutor();\n /**\n * Normalize Next.js-style route search params into Tango query params.\n */\n toQueryParams(searchParams: Record<string, string | string[] | undefined>): TangoQueryParams {\n return TangoQueryParams.fromRecord(searchParams);\n }\n\n /**\n * Adapt a Tango-style handler into a Next.js route handler.\n */\n adapt(\n handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>,\n options: AdaptNextOptions = {}\n ): NextRouteHandler {\n return this.createHandler(handler, options);\n }\n\n /**\n * Build Next route handlers that map HTTP verbs to standard CRUD viewset actions.\n */\n adaptViewSet(viewset: NextCrudViewSet, options: AdaptNextViewSetOptions = {}): NextViewSetRouteHandlers {\n const paramKey = options.paramKey ?? 'tango';\n return {\n GET: this.adapt(\n (ctx) =>\n this.dispatchViewSetAction(viewset, InternalHttpMethod.GET, ctx, paramKey, (segments, directId) => {\n if (directId) return viewset.retrieve(ctx, directId);\n if (segments.length === 0) return viewset.list(ctx);\n if (segments.length === 1) return viewset.retrieve(ctx, segments[0] as string);\n return this.notFoundResponse();\n }),\n options\n ),\n POST: this.adapt(\n (ctx) =>\n this.dispatchViewSetAction(\n viewset,\n InternalHttpMethod.POST,\n ctx,\n paramKey,\n (segments, directId) => {\n if (segments.length === 0 && !directId) return viewset.create(ctx);\n return this.methodNotAllowedResponse();\n }\n ),\n options\n ),\n PATCH: this.adapt(\n (ctx) =>\n this.dispatchViewSetAction(\n viewset,\n InternalHttpMethod.PATCH,\n ctx,\n paramKey,\n (segments, directId) => {\n const id = directId ?? segments[0];\n return id ? viewset.update(ctx, id) : this.methodNotAllowedResponse();\n }\n ),\n options\n ),\n PUT: this.adapt(\n (ctx) =>\n this.dispatchViewSetAction(viewset, InternalHttpMethod.PUT, ctx, paramKey, (segments, directId) => {\n const id = directId ?? segments[0];\n return id ? viewset.update(ctx, id) : this.methodNotAllowedResponse();\n }),\n options\n ),\n DELETE: this.adapt(\n (ctx) =>\n this.dispatchViewSetAction(\n viewset,\n InternalHttpMethod.DELETE,\n ctx,\n paramKey,\n (segments, directId) => {\n const id = directId ?? segments[0];\n return id ? viewset.destroy(ctx, id) : this.methodNotAllowedResponse();\n }\n ),\n options\n ),\n };\n }\n\n /**\n * Build Next route handlers from a lazy viewset factory and memoize initialization.\n * Initialization failures clear the memoized promise so subsequent requests can retry.\n */\n adaptViewSetFactory(factory: NextViewSetFactory, options: AdaptNextViewSetOptions = {}): NextViewSetRouteHandlers {\n return this.adaptRouteHandlersFactory(async () => {\n const viewset = await factory();\n return this.adaptViewSet(viewset, options);\n });\n }\n\n /**\n * Build Next route handlers from a lazy GenericAPIView factory and memoize initialization.\n */\n adaptGenericAPIViewFactory(\n factory: NextAPIViewFactory,\n options: AdaptNextViewSetOptions = {}\n ): NextViewSetRouteHandlers {\n return this.adaptRouteHandlersFactory(async () => {\n const apiView = await factory();\n return this.adaptGenericAPIView(apiView, options);\n });\n }\n\n /**\n * Build Next route handlers that dispatch an APIView by HTTP method.\n */\n adaptAPIView(apiView: NextAPIView, options: AdaptNextOptions = {}): NextViewSetRouteHandlers {\n return {\n GET: this.adapt((ctx) => apiView.dispatch(ctx), options),\n POST: this.adapt((ctx) => apiView.dispatch(ctx), options),\n PATCH: this.adapt((ctx) => apiView.dispatch(ctx), options),\n PUT: this.adapt((ctx) => apiView.dispatch(ctx), options),\n DELETE: this.adapt((ctx) => apiView.dispatch(ctx), options),\n };\n }\n\n /**\n * Build handlers for GenericAPIView-style collection/detail splits in catch-all routes.\n */\n adaptGenericAPIView(apiView: NextAPIView, options: AdaptNextViewSetOptions = {}): NextViewSetRouteHandlers {\n // Default catch-all param matches the Next.js [[...tango]] route convention.\n const paramKey = options.paramKey ?? 'tango';\n return {\n GET: this.adapt(async (ctx) => {\n const detailId = this.resolveDetailId(ctx.params, paramKey);\n if (!detailId) {\n return apiView.dispatch(ctx);\n }\n ctx.params.id = detailId;\n return apiView.dispatch(ctx);\n }, options),\n POST: this.adapt(async (ctx) => {\n const detailId = this.resolveDetailId(ctx.params, paramKey);\n if (detailId) {\n return this.methodNotAllowedResponse();\n }\n return apiView.dispatch(ctx);\n }, options),\n PATCH: this.adapt(async (ctx) => {\n const detailId = this.resolveDetailId(ctx.params, paramKey);\n if (!detailId) {\n return this.methodNotAllowedResponse();\n }\n ctx.params.id = detailId;\n return apiView.dispatch(ctx);\n }, options),\n PUT: this.adapt(async (ctx) => {\n const detailId = this.resolveDetailId(ctx.params, paramKey);\n if (!detailId) {\n return this.methodNotAllowedResponse();\n }\n ctx.params.id = detailId;\n return apiView.dispatch(ctx);\n }, options),\n DELETE: this.adapt(async (ctx) => {\n const detailId = this.resolveDetailId(ctx.params, paramKey);\n if (!detailId) {\n return this.methodNotAllowedResponse();\n }\n ctx.params.id = detailId;\n return apiView.dispatch(ctx);\n }, options),\n };\n }\n\n private toTangoRequest(request: NextRequest): TangoRequest {\n if (TangoRequest.isTangoRequest(request)) {\n return request;\n }\n\n // oxlint-disable-next-line eslint-js/no-restricted-syntax\n if (request instanceof Request) {\n return new TangoRequest(request);\n }\n\n return new TangoRequest(String((request as { url?: unknown }).url ?? 'http://localhost'));\n }\n\n private dispatchViewSetAction(\n viewset: NextCrudViewSet,\n method: HttpMethod,\n ctx: RequestContext,\n paramKey: string,\n fallback: (segments: string[], directId: string | null) => TangoResponse | Promise<TangoResponse>\n ): Promise<TangoResponse> {\n const segments = this.extractCatchAllSegments(ctx.params, paramKey);\n const directId = this.extractDirectId(ctx.params);\n const actionMatch = this.resolveActionMatch(viewset, method, segments);\n\n if (actionMatch?.kind === InternalActionMatchKind.METHOD_NOT_ALLOWED) {\n return Promise.resolve(this.methodNotAllowedResponse());\n }\n if (actionMatch?.kind === InternalActionMatchKind.DETAIL) {\n return this.invokeDetailAction(viewset, actionMatch.action, ctx, actionMatch.id);\n }\n if (actionMatch?.kind === InternalActionMatchKind.COLLECTION) {\n return this.invokeCollectionAction(viewset, actionMatch.action, ctx);\n }\n\n return Promise.resolve(fallback(segments, directId));\n }\n\n private invokeDetailAction(\n viewset: NextCrudViewSet,\n action: ResolvedViewSetActionDescriptor,\n ctx: RequestContext,\n id: string\n ): Promise<TangoResponse> {\n const candidate = (viewset as unknown as Record<string, unknown>)[action.name];\n if (typeof candidate !== 'function') {\n return Promise.resolve(this.notFoundResponse());\n }\n return (candidate as (this: NextCrudViewSet, ctx: RequestContext, id: string) => Promise<TangoResponse>).call(\n viewset,\n ctx,\n id\n );\n }\n\n private invokeCollectionAction(\n viewset: NextCrudViewSet,\n action: ResolvedViewSetActionDescriptor,\n ctx: RequestContext\n ): Promise<TangoResponse> {\n const candidate = (viewset as unknown as Record<string, unknown>)[action.name];\n if (typeof candidate !== 'function') {\n return Promise.resolve(this.notFoundResponse());\n }\n return (candidate as (this: NextCrudViewSet, ctx: RequestContext) => Promise<TangoResponse>).call(viewset, ctx);\n }\n\n private createHandler(\n handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>,\n options: AdaptNextOptions\n ): NextRouteHandler {\n return async (request: NextRequest, routeContext) => {\n try {\n const user = options.getUser ? await options.getUser(request) : null;\n const rawParams = routeContext?.params ? await routeContext.params : {};\n const params = this.normalizeRouteParams(rawParams);\n\n const ctx = RequestContext.create(this.toTangoRequest(request), user);\n if (Object.keys(params).length > 0) {\n ctx.params = params;\n }\n\n const id = params?.id;\n return await this.requestExecutor\n .forHandler({ handler, ctx, id })\n .runWebResponse(request.method, options.transaction);\n } catch (error) {\n return this.internalServerError(error);\n }\n };\n }\n\n private internalServerError(error: unknown): Response {\n this.logger.error('Adapter error:', error);\n const httpError = HttpErrorFactory.toHttpError(error);\n return TangoResponse.json(httpError.body as JsonValue, { status: httpError.status }).toWebResponse();\n }\n\n private adaptRouteHandlersFactory(factory: () => Promise<NextViewSetRouteHandlers>): NextViewSetRouteHandlers {\n let handlersPromise: Promise<NextViewSetRouteHandlers> | null = null;\n\n const getHandlers = async (): Promise<NextViewSetRouteHandlers> => {\n if (!handlersPromise) {\n const initializing = factory();\n handlersPromise = initializing.catch((error) => {\n handlersPromise = null;\n throw error;\n });\n }\n return handlersPromise;\n };\n\n const createLazyHandler = (method: keyof NextViewSetRouteHandlers): NextDynamicRouteHandler => {\n return async (request, context) => {\n try {\n const handlers = await getHandlers();\n return handlers[method](request, context);\n } catch (error) {\n return this.internalServerError(error);\n }\n };\n };\n\n return {\n GET: createLazyHandler('GET'),\n POST: createLazyHandler('POST'),\n PATCH: createLazyHandler('PATCH'),\n PUT: createLazyHandler('PUT'),\n DELETE: createLazyHandler('DELETE'),\n };\n }\n\n private resolveDetailId(params: Record<string, string>, paramKey: string): string | null {\n const directId = this.extractDirectId(params);\n if (directId) {\n return directId;\n }\n const segments = this.extractCatchAllSegments(params, paramKey);\n if (segments.length !== 1) {\n return null;\n }\n return segments[0] as string;\n }\n\n private normalizeRouteParams(raw: Record<string, string | string[]>): Record<string, string> {\n const entries = Object.entries(raw).map(([key, value]) => {\n return [key, Array.isArray(value) ? value.join('/') : value];\n });\n\n return Object.fromEntries(entries);\n }\n\n private extractDirectId(params: Record<string, string>): string | null {\n const directId = params.id?.trim();\n return directId || null;\n }\n\n private extractCatchAllSegments(params: Record<string, string>, paramKey: string): string[] {\n const catchAll = params[paramKey]?.trim() ?? '';\n if (!catchAll) {\n return [];\n }\n return catchAll.split('/').filter(Boolean);\n }\n\n private methodNotAllowedResponse(): TangoResponse {\n return TangoResponse.methodNotAllowed(undefined, 'Method not allowed for this route.');\n }\n\n private notFoundResponse(): TangoResponse {\n return TangoResponse.notFound('Not found.');\n }\n\n private resolveActionMatch(viewset: NextCrudViewSet, method: HttpMethod, segments: string[]): ActionMatch | null {\n if (segments.length === 0) {\n return null;\n }\n\n const actions = this.getViewSetActions(viewset);\n\n if (segments.length >= 2) {\n const detailPath = segments.slice(1).join('/');\n const detailMatch = actions.find(\n (action) => action.scope === InternalActionScope.DETAIL && action.path === detailPath\n );\n if (detailMatch) {\n return detailMatch.methods.includes(method)\n ? { kind: InternalActionMatchKind.DETAIL, action: detailMatch, id: segments[0] as string }\n : { kind: InternalActionMatchKind.METHOD_NOT_ALLOWED };\n }\n }\n\n if (method === InternalHttpMethod.GET && segments.length === 1) {\n return null;\n }\n\n const collectionPath = segments.join('/');\n const collectionMatch = actions.find(\n (action) => action.scope === InternalActionScope.COLLECTION && action.path === collectionPath\n );\n if (!collectionMatch) {\n return null;\n }\n return collectionMatch.methods.includes(method)\n ? { kind: InternalActionMatchKind.COLLECTION, action: collectionMatch }\n : { kind: InternalActionMatchKind.METHOD_NOT_ALLOWED };\n }\n\n private getViewSetActions(viewset: NextCrudViewSet): readonly ResolvedViewSetActionDescriptor[] {\n const constructorValue = viewset.constructor as {\n getActions?: (input: NextCrudViewSet) => readonly ResolvedViewSetActionDescriptor[];\n };\n\n if (typeof constructorValue.getActions !== 'function') {\n return [];\n }\n\n return constructorValue.getActions(viewset);\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport {\n NextAdapter,\n type AdaptNextOptions,\n type AdaptNextViewSetOptions,\n type NextAPIView,\n type NextAPIViewFactory,\n type NextCrudViewSet,\n type NextDynamicRouteContext,\n type NextDynamicRouteHandler,\n type NextRouteHandler,\n type NextViewSetFactory,\n type NextViewSetRouteHandlers,\n} from './NextAdapter';\n"],"mappings":";;;;;;;;;AA8FA,IAAa,cAAb,MAA8F;CAC1F,eAAwD;CACxD,SAA0B,UAAU,oBAAoB;CACxD,kBAAmC,IAAI,gCAAgC;;;;CAIvE,cAAc,cAA+E;EACzF,OAAO,iBAAiB,WAAW,YAAY;CACnD;;;;CAKA,MACI,SACA,UAA4B,CAAC,GACb;EAChB,OAAO,KAAK,cAAc,SAAS,OAAO;CAC9C;;;;CAKA,aAAa,SAA0B,UAAmC,CAAC,GAA6B;EACpG,MAAM,WAAW,QAAQ,YAAY;EACrC,OAAO;GACH,KAAK,KAAK,OACL,QACG,KAAK,sBAAsB,SAAS,mBAAmB,KAAK,KAAK,WAAW,UAAU,aAAa;IAC/F,IAAI,UAAU,OAAO,QAAQ,SAAS,KAAK,QAAQ;IACnD,IAAI,SAAS,WAAW,GAAG,OAAO,QAAQ,KAAK,GAAG;IAClD,IAAI,SAAS,WAAW,GAAG,OAAO,QAAQ,SAAS,KAAK,SAAS,EAAY;IAC7E,OAAO,KAAK,iBAAiB;GACjC,CAAC,GACL,OACJ;GACA,MAAM,KAAK,OACN,QACG,KAAK,sBACD,SACA,mBAAmB,MACnB,KACA,WACC,UAAU,aAAa;IACpB,IAAI,SAAS,WAAW,KAAK,CAAC,UAAU,OAAO,QAAQ,OAAO,GAAG;IACjE,OAAO,KAAK,yBAAyB;GACzC,CACJ,GACJ,OACJ;GACA,OAAO,KAAK,OACP,QACG,KAAK,sBACD,SACA,mBAAmB,OACnB,KACA,WACC,UAAU,aAAa;IACpB,MAAM,KAAK,YAAY,SAAS;IAChC,OAAO,KAAK,QAAQ,OAAO,KAAK,EAAE,IAAI,KAAK,yBAAyB;GACxE,CACJ,GACJ,OACJ;GACA,KAAK,KAAK,OACL,QACG,KAAK,sBAAsB,SAAS,mBAAmB,KAAK,KAAK,WAAW,UAAU,aAAa;IAC/F,MAAM,KAAK,YAAY,SAAS;IAChC,OAAO,KAAK,QAAQ,OAAO,KAAK,EAAE,IAAI,KAAK,yBAAyB;GACxE,CAAC,GACL,OACJ;GACA,QAAQ,KAAK,OACR,QACG,KAAK,sBACD,SACA,mBAAmB,QACnB,KACA,WACC,UAAU,aAAa;IACpB,MAAM,KAAK,YAAY,SAAS;IAChC,OAAO,KAAK,QAAQ,QAAQ,KAAK,EAAE,IAAI,KAAK,yBAAyB;GACzE,CACJ,GACJ,OACJ;EACJ;CACJ;;;;;CAMA,oBAAoB,SAA6B,UAAmC,CAAC,GAA6B;EAC9G,OAAO,KAAK,0BAA0B,YAAY;GAC9C,MAAM,UAAU,MAAM,QAAQ;GAC9B,OAAO,KAAK,aAAa,SAAS,OAAO;EAC7C,CAAC;CACL;;;;CAKA,2BACI,SACA,UAAmC,CAAC,GACZ;EACxB,OAAO,KAAK,0BAA0B,YAAY;GAC9C,MAAM,UAAU,MAAM,QAAQ;GAC9B,OAAO,KAAK,oBAAoB,SAAS,OAAO;EACpD,CAAC;CACL;;;;CAKA,aAAa,SAAsB,UAA4B,CAAC,GAA6B;EACzF,OAAO;GACH,KAAK,KAAK,OAAO,QAAQ,QAAQ,SAAS,GAAG,GAAG,OAAO;GACvD,MAAM,KAAK,OAAO,QAAQ,QAAQ,SAAS,GAAG,GAAG,OAAO;GACxD,OAAO,KAAK,OAAO,QAAQ,QAAQ,SAAS,GAAG,GAAG,OAAO;GACzD,KAAK,KAAK,OAAO,QAAQ,QAAQ,SAAS,GAAG,GAAG,OAAO;GACvD,QAAQ,KAAK,OAAO,QAAQ,QAAQ,SAAS,GAAG,GAAG,OAAO;EAC9D;CACJ;;;;CAKA,oBAAoB,SAAsB,UAAmC,CAAC,GAA6B;EAEvG,MAAM,WAAW,QAAQ,YAAY;EACrC,OAAO;GACH,KAAK,KAAK,MAAM,OAAO,QAAQ;IAC3B,MAAM,WAAW,KAAK,gBAAgB,IAAI,QAAQ,QAAQ;IAC1D,IAAI,CAAC,UACD,OAAO,QAAQ,SAAS,GAAG;IAE/B,IAAI,OAAO,KAAK;IAChB,OAAO,QAAQ,SAAS,GAAG;GAC/B,GAAG,OAAO;GACV,MAAM,KAAK,MAAM,OAAO,QAAQ;IAE5B,IADiB,KAAK,gBAAgB,IAAI,QAAQ,QACvC,GACP,OAAO,KAAK,yBAAyB;IAEzC,OAAO,QAAQ,SAAS,GAAG;GAC/B,GAAG,OAAO;GACV,OAAO,KAAK,MAAM,OAAO,QAAQ;IAC7B,MAAM,WAAW,KAAK,gBAAgB,IAAI,QAAQ,QAAQ;IAC1D,IAAI,CAAC,UACD,OAAO,KAAK,yBAAyB;IAEzC,IAAI,OAAO,KAAK;IAChB,OAAO,QAAQ,SAAS,GAAG;GAC/B,GAAG,OAAO;GACV,KAAK,KAAK,MAAM,OAAO,QAAQ;IAC3B,MAAM,WAAW,KAAK,gBAAgB,IAAI,QAAQ,QAAQ;IAC1D,IAAI,CAAC,UACD,OAAO,KAAK,yBAAyB;IAEzC,IAAI,OAAO,KAAK;IAChB,OAAO,QAAQ,SAAS,GAAG;GAC/B,GAAG,OAAO;GACV,QAAQ,KAAK,MAAM,OAAO,QAAQ;IAC9B,MAAM,WAAW,KAAK,gBAAgB,IAAI,QAAQ,QAAQ;IAC1D,IAAI,CAAC,UACD,OAAO,KAAK,yBAAyB;IAEzC,IAAI,OAAO,KAAK;IAChB,OAAO,QAAQ,SAAS,GAAG;GAC/B,GAAG,OAAO;EACd;CACJ;CAEA,eAAuB,SAAoC;EACvD,IAAI,aAAa,eAAe,OAAO,GACnC,OAAO;EAIX,IAAI,mBAAmB,SACnB,OAAO,IAAI,aAAa,OAAO;EAGnC,OAAO,IAAI,aAAa,OAAQ,QAA8B,OAAO,kBAAkB,CAAC;CAC5F;CAEA,sBACI,SACA,QACA,KACA,UACA,UACsB;EACtB,MAAM,WAAW,KAAK,wBAAwB,IAAI,QAAQ,QAAQ;EAClE,MAAM,WAAW,KAAK,gBAAgB,IAAI,MAAM;EAChD,MAAM,cAAc,KAAK,mBAAmB,SAAS,QAAQ,QAAQ;EAErE,IAAI,aAAa,SAAS,wBAAwB,oBAC9C,OAAO,QAAQ,QAAQ,KAAK,yBAAyB,CAAC;EAE1D,IAAI,aAAa,SAAS,wBAAwB,QAC9C,OAAO,KAAK,mBAAmB,SAAS,YAAY,QAAQ,KAAK,YAAY,EAAE;EAEnF,IAAI,aAAa,SAAS,wBAAwB,YAC9C,OAAO,KAAK,uBAAuB,SAAS,YAAY,QAAQ,GAAG;EAGvE,OAAO,QAAQ,QAAQ,SAAS,UAAU,QAAQ,CAAC;CACvD;CAEA,mBACI,SACA,QACA,KACA,IACsB;EACtB,MAAM,YAAa,QAA+C,OAAO;EACzE,IAAI,OAAO,cAAc,YACrB,OAAO,QAAQ,QAAQ,KAAK,iBAAiB,CAAC;EAElD,OAAQ,UAAiG,KACrG,SACA,KACA,EACJ;CACJ;CAEA,uBACI,SACA,QACA,KACsB;EACtB,MAAM,YAAa,QAA+C,OAAO;EACzE,IAAI,OAAO,cAAc,YACrB,OAAO,QAAQ,QAAQ,KAAK,iBAAiB,CAAC;EAElD,OAAQ,UAAqF,KAAK,SAAS,GAAG;CAClH;CAEA,cACI,SACA,SACgB;EAChB,OAAO,OAAO,SAAsB,iBAAiB;GACjD,IAAI;IACA,MAAM,OAAO,QAAQ,UAAU,MAAM,QAAQ,QAAQ,OAAO,IAAI;IAChE,MAAM,YAAY,cAAc,SAAS,MAAM,aAAa,SAAS,CAAC;IACtE,MAAM,SAAS,KAAK,qBAAqB,SAAS;IAElD,MAAM,MAAM,eAAe,OAAO,KAAK,eAAe,OAAO,GAAG,IAAI;IACpE,IAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAC7B,IAAI,SAAS;IAGjB,MAAM,KAAK,QAAQ;IACnB,OAAO,MAAM,KAAK,gBACb,WAAW;KAAE;KAAS;KAAK;IAAG,CAAC,EAC/B,eAAe,QAAQ,QAAQ,QAAQ,WAAW;GAC3D,SAAS,OAAO;IACZ,OAAO,KAAK,oBAAoB,KAAK;GACzC;EACJ;CACJ;CAEA,oBAA4B,OAA0B;EAClD,KAAK,OAAO,MAAM,kBAAkB,KAAK;EACzC,MAAM,YAAY,iBAAiB,YAAY,KAAK;EACpD,OAAO,cAAc,KAAK,UAAU,MAAmB,EAAE,QAAQ,UAAU,OAAO,CAAC,EAAE,cAAc;CACvG;CAEA,0BAAkC,SAA4E;EAC1G,IAAI,kBAA4D;EAEhE,MAAM,cAAc,YAA+C;GAC/D,IAAI,CAAC,iBAED,kBADqB,QACQ,EAAE,OAAO,UAAU;IAC5C,kBAAkB;IAClB,MAAM;GACV,CAAC;GAEL,OAAO;EACX;EAEA,MAAM,qBAAqB,WAAoE;GAC3F,OAAO,OAAO,SAAS,YAAY;IAC/B,IAAI;KAEA,QAAO,MADgB,YAAY,GACnB,QAAQ,SAAS,OAAO;IAC5C,SAAS,OAAO;KACZ,OAAO,KAAK,oBAAoB,KAAK;IACzC;GACJ;EACJ;EAEA,OAAO;GACH,KAAK,kBAAkB,KAAK;GAC5B,MAAM,kBAAkB,MAAM;GAC9B,OAAO,kBAAkB,OAAO;GAChC,KAAK,kBAAkB,KAAK;GAC5B,QAAQ,kBAAkB,QAAQ;EACtC;CACJ;CAEA,gBAAwB,QAAgC,UAAiC;EACrF,MAAM,WAAW,KAAK,gBAAgB,MAAM;EAC5C,IAAI,UACA,OAAO;EAEX,MAAM,WAAW,KAAK,wBAAwB,QAAQ,QAAQ;EAC9D,IAAI,SAAS,WAAW,GACpB,OAAO;EAEX,OAAO,SAAS;CACpB;CAEA,qBAA6B,KAAgE;EACzF,MAAM,UAAU,OAAO,QAAQ,GAAG,EAAE,KAAK,CAAC,KAAK,WAAW;GACtD,OAAO,CAAC,KAAK,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,GAAG,IAAI,KAAK;EAC/D,CAAC;EAED,OAAO,OAAO,YAAY,OAAO;CACrC;CAEA,gBAAwB,QAA+C;EAEnE,OADiB,OAAO,IAAI,KAAK,KACd;CACvB;CAEA,wBAAgC,QAAgC,UAA4B;EACxF,MAAM,WAAW,OAAO,WAAW,KAAK,KAAK;EAC7C,IAAI,CAAC,UACD,OAAO,CAAC;EAEZ,OAAO,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;CAC7C;CAEA,2BAAkD;EAC9C,OAAO,cAAc,iBAAiB,KAAA,GAAW,oCAAoC;CACzF;CAEA,mBAA0C;EACtC,OAAO,cAAc,SAAS,YAAY;CAC9C;CAEA,mBAA2B,SAA0B,QAAoB,UAAwC;EAC7G,IAAI,SAAS,WAAW,GACpB,OAAO;EAGX,MAAM,UAAU,KAAK,kBAAkB,OAAO;EAE9C,IAAI,SAAS,UAAU,GAAG;GACtB,MAAM,aAAa,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG;GAC7C,MAAM,cAAc,QAAQ,MACvB,WAAW,OAAO,UAAU,oBAAoB,UAAU,OAAO,SAAS,UAC/E;GACA,IAAI,aACA,OAAO,YAAY,QAAQ,SAAS,MAAM,IACpC;IAAE,MAAM,wBAAwB;IAAQ,QAAQ;IAAa,IAAI,SAAS;GAAa,IACvF,EAAE,MAAM,wBAAwB,mBAAmB;EAEjE;EAEA,IAAI,WAAW,mBAAmB,OAAO,SAAS,WAAW,GACzD,OAAO;EAGX,MAAM,iBAAiB,SAAS,KAAK,GAAG;EACxC,MAAM,kBAAkB,QAAQ,MAC3B,WAAW,OAAO,UAAU,oBAAoB,cAAc,OAAO,SAAS,cACnF;EACA,IAAI,CAAC,iBACD,OAAO;EAEX,OAAO,gBAAgB,QAAQ,SAAS,MAAM,IACxC;GAAE,MAAM,wBAAwB;GAAY,QAAQ;EAAgB,IACpE,EAAE,MAAM,wBAAwB,mBAAmB;CAC7D;CAEA,kBAA0B,SAAsE;EAC5F,MAAM,mBAAmB,QAAQ;EAIjC,IAAI,OAAO,iBAAiB,eAAe,YACvC,OAAO,CAAC;EAGZ,OAAO,iBAAiB,WAAW,OAAO;CAC9C;AACJ"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as NextAdapter, t as adapter_exports } from "./adapter-
|
|
1
|
+
import { n as NextAdapter, t as adapter_exports } from "./adapter-Cj_ViN2G.js";
|
|
2
2
|
export { NextAdapter, adapter_exports as adapter };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danceroutine/tango-adapters-next",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.15",
|
|
4
4
|
"description": "Next.js App Router adapter for Tango viewsets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"directory": "packages/adapters/next"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@danceroutine/tango-core": "1.11.
|
|
36
|
-
"@danceroutine/tango-resources": "1.11.
|
|
37
|
-
"@danceroutine/tango-adapters-core": "1.11.
|
|
35
|
+
"@danceroutine/tango-core": "1.11.15",
|
|
36
|
+
"@danceroutine/tango-resources": "1.11.15",
|
|
37
|
+
"@danceroutine/tango-adapters-core": "1.11.15"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"next": "^15.0.0"
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"next": "^16.2.6",
|
|
45
45
|
"tsdown": "^0.22.1",
|
|
46
46
|
"typescript": "^6.0.3",
|
|
47
|
-
"vitest": "^4.1.
|
|
47
|
+
"vitest": "^4.1.8"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsdown",
|