@danceroutine/tango-adapters-express 1.11.0 → 1.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,2 @@
1
- /**
2
- * Domain boundary barrel: centralizes this subdomain's public contract.
3
- */
4
- export { ExpressAdapter, type AdaptExpressOptions, type ExpressAPIView, type ExpressCrudViewSet, type ExpressRouteRegistrar, } from './ExpressAdapter';
1
+ import { a as ExpressCrudViewSet, i as ExpressAdapter, n as AdaptExpressOptions, o as ExpressRouteRegistrar, r as ExpressAPIView } from "../index-D686Awva.js";
2
+ export { type AdaptExpressOptions, type ExpressAPIView, ExpressAdapter, type ExpressCrudViewSet, type ExpressRouteRegistrar };
@@ -1,3 +1,2 @@
1
- import { ExpressAdapter } from "../adapter-CGeztuym.js";
2
-
3
- export { ExpressAdapter };
1
+ import { n as ExpressAdapter } from "../adapter-kC_MF6Wt.js";
2
+ export { ExpressAdapter };
@@ -1,20 +1,13 @@
1
+ import { t as __exportAll } from "./chunk-D7D4PA-g.js";
1
2
  import { Readable } from "node:stream";
2
3
  import { Router } from "express";
3
4
  import { RequestContext } from "@danceroutine/tango-resources";
4
5
  import { TangoQueryParams } from "@danceroutine/tango-core";
5
6
  import { FRAMEWORK_ADAPTER_BRAND, FrameworkAdapterRequestExecutor } from "@danceroutine/tango-adapters-core/adapter";
6
-
7
- //#region rolldown:runtime
8
- var __defProp = Object.defineProperty;
9
- var __export = (target, all) => {
10
- for (var name in all) __defProp(target, name, {
11
- get: all[name],
12
- enumerable: true
13
- });
14
- };
15
-
16
- //#endregion
17
7
  //#region src/adapter/ExpressAdapter.ts
8
+ /**
9
+ * Express adapter that translates Express handlers to Tango `RequestContext`.
10
+ */
18
11
  var ExpressAdapter = class {
19
12
  __tangoBrand = FRAMEWORK_ADAPTER_BRAND;
20
13
  requestExecutor = new FrameworkAdapterRequestExecutor();
@@ -59,8 +52,7 @@ var ExpressAdapter = class {
59
52
  registrar.patch(detailPath, this.adapt((ctx, id) => viewset.update(ctx, String(id)), options));
60
53
  registrar.put(detailPath, this.adapt((ctx, id) => viewset.update(ctx, String(id)), options));
61
54
  registrar.delete(detailPath, this.adapt((ctx, id) => viewset.destroy(ctx, String(id)), options));
62
- const actions = this.getViewSetActions(viewset);
63
- actions.forEach((action) => {
55
+ this.getViewSetActions(viewset).forEach((action) => {
64
56
  const actionPath = action.scope === "detail" ? this.joinPath(detailPath, action.path) : this.joinPath(collectionPath, action.path);
65
57
  const handler = action.scope === "detail" ? this.adapt((ctx, id) => this.invokeDetailAction(viewset, action, ctx, String(id)), options) : this.adapt((ctx) => this.invokeCollectionAction(viewset, action, ctx), options);
66
58
  this.registerActionMethods(registrar, action.methods, actionPath, handler);
@@ -148,12 +140,10 @@ var ExpressAdapter = class {
148
140
  return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, Array.isArray(value) ? value[0] ?? "" : value]));
149
141
  }
150
142
  toRequestFromExpress(req) {
151
- const protocol = req.protocol || "http";
152
- const host = req.get("host") || "localhost";
153
- const url = `${protocol}://${host}${req.originalUrl || req.url}`;
143
+ const url = `${req.protocol || "http"}://${req.get("host") || "localhost"}${req.originalUrl || req.url}`;
154
144
  const headers = new Headers(req.headers);
155
145
  const body = this.normalizeBody(req);
156
- if (body !== undefined && !headers.has("content-type") && this.isJsonLike(req.body)) headers.set("content-type", "application/json; charset=utf-8");
146
+ if (body !== void 0 && !headers.has("content-type") && this.isJsonLike(req.body)) headers.set("content-type", "application/json; charset=utf-8");
157
147
  return new Request(url, {
158
148
  method: req.method,
159
149
  headers,
@@ -161,11 +151,10 @@ var ExpressAdapter = class {
161
151
  });
162
152
  }
163
153
  normalizeBody(req) {
164
- if (["GET", "HEAD"].includes(req.method)) return undefined;
165
- if (req.body === null || req.body === undefined) return undefined;
154
+ if (["GET", "HEAD"].includes(req.method)) return;
155
+ if (req.body === null || req.body === void 0) return;
166
156
  if (typeof req.body === "string" || this.hasTag(req.body, "Uint8Array") || this.hasTag(req.body, "ArrayBuffer")) return req.body;
167
157
  if (this.isJsonLike(req.body)) return JSON.stringify(req.body);
168
- return undefined;
169
158
  }
170
159
  isJsonLike(value) {
171
160
  if (value === null) return true;
@@ -175,7 +164,7 @@ var ExpressAdapter = class {
175
164
  return false;
176
165
  }
177
166
  hasTag(value, tag) {
178
- return value !== null && value !== undefined && Object.prototype.toString.call(value) === `[object ${tag}]`;
167
+ return value !== null && value !== void 0 && Object.prototype.toString.call(value) === `[object ${tag}]`;
179
168
  }
180
169
  registerMethod(registrar, method, path, handler) {
181
170
  switch (method) {
@@ -219,12 +208,10 @@ var ExpressAdapter = class {
219
208
  return constructorValue.getActions(viewset);
220
209
  }
221
210
  };
222
-
223
211
  //#endregion
224
212
  //#region src/adapter/index.ts
225
- var adapter_exports = {};
226
- __export(adapter_exports, { ExpressAdapter: () => ExpressAdapter });
227
-
213
+ var adapter_exports = /* @__PURE__ */ __exportAll({ ExpressAdapter: () => ExpressAdapter });
228
214
  //#endregion
229
- export { ExpressAdapter, adapter_exports };
230
- //# sourceMappingURL=adapter-CGeztuym.js.map
215
+ export { ExpressAdapter as n, adapter_exports as t };
216
+
217
+ //# sourceMappingURL=adapter-kC_MF6Wt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-kC_MF6Wt.js","names":[],"sources":["../src/adapter/ExpressAdapter.ts","../src/adapter/index.ts"],"sourcesContent":["import type { Request as ExpressRequest, Response as ExpressResponse, NextFunction, RequestHandler } from 'express';\nimport { Readable } from 'node:stream';\nimport type { ReadableStream as NodeReadableStream } from 'node:stream/web';\nimport { Router } from 'express';\nimport { RequestContext } from '@danceroutine/tango-resources';\nimport { TangoQueryParams, TangoResponse } from '@danceroutine/tango-core';\nimport {\n FRAMEWORK_ADAPTER_BRAND,\n FrameworkAdapterRequestExecutor,\n type FrameworkAdapter,\n type FrameworkAdapterOptions,\n} from '@danceroutine/tango-adapters-core/adapter';\n\ntype ViewSetActionMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\ntype ResolvedViewSetActionDescriptor = {\n name: string;\n scope: 'detail' | 'collection';\n methods: readonly ViewSetActionMethod[];\n path: string;\n};\n\n/**\n * Adapter options for Express integration.\n */\nexport type AdaptExpressOptions = FrameworkAdapterOptions<ExpressRequest>;\n\n/**\n * Minimal CRUD viewset contract used by adapter route registration helpers.\n */\nexport interface ExpressCrudViewSet {\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 ExpressAPIView {\n dispatch(ctx: RequestContext): Promise<TangoResponse>;\n}\n\n/**\n * Minimal route registrar interface implemented by Express apps and routers.\n */\nexport interface ExpressRouteRegistrar {\n get(path: string, handler: RequestHandler): unknown;\n post(path: string, handler: RequestHandler): unknown;\n patch(path: string, handler: RequestHandler): unknown;\n put(path: string, handler: RequestHandler): unknown;\n delete(path: string, handler: RequestHandler): unknown;\n}\n\n/**\n * Express adapter that translates Express handlers to Tango `RequestContext`.\n */\nexport class ExpressAdapter implements FrameworkAdapter<Response, RequestHandler, ExpressRequest> {\n readonly __tangoBrand: typeof FRAMEWORK_ADAPTER_BRAND = FRAMEWORK_ADAPTER_BRAND;\n private readonly requestExecutor = new FrameworkAdapterRequestExecutor();\n\n /**\n * Normalize an Express request into Tango query params.\n */\n toQueryParams(req: ExpressRequest): TangoQueryParams {\n const request = this.toRequestFromExpress(req);\n return TangoQueryParams.fromRequest(request);\n }\n\n /**\n * Adapt a Tango-style handler into an Express request handler.\n */\n adapt(\n handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>,\n options: AdaptExpressOptions = {}\n ): RequestHandler {\n return this.createHandler(handler, options);\n }\n\n /**\n * Build an Express router that wires all CRUD routes for a viewset.\n */\n createViewSetRouter(viewset: ExpressCrudViewSet, options: AdaptExpressOptions = {}): Router {\n const router = Router();\n this.registerViewSet(router, '', viewset, options);\n return router;\n }\n\n /**\n * Build an Express router for a single-path APIView.\n */\n createAPIViewRouter(apiView: ExpressAPIView, options: AdaptExpressOptions = {}): Router {\n const router = Router();\n this.registerAPIView(router, '', apiView, options);\n return router;\n }\n\n /**\n * Register all CRUD routes for a viewset under a base path.\n */\n registerViewSet(\n registrar: ExpressRouteRegistrar,\n basePath: string,\n viewset: ExpressCrudViewSet,\n options: AdaptExpressOptions = {}\n ): void {\n const collectionPath = this.normalizeBasePath(basePath);\n const detailPath = collectionPath === '/' ? '/:id' : `${collectionPath}/:id`;\n\n registrar.get(\n collectionPath,\n this.adapt((ctx) => viewset.list(ctx), options)\n );\n registrar.post(\n collectionPath,\n this.adapt((ctx) => viewset.create(ctx), options)\n );\n registrar.get(\n detailPath,\n this.adapt((ctx, id) => viewset.retrieve(ctx, String(id)), options)\n );\n registrar.patch(\n detailPath,\n this.adapt((ctx, id) => viewset.update(ctx, String(id)), options)\n );\n registrar.put(\n detailPath,\n this.adapt((ctx, id) => viewset.update(ctx, String(id)), options)\n );\n registrar.delete(\n detailPath,\n this.adapt((ctx, id) => viewset.destroy(ctx, String(id)), options)\n );\n\n const actions = this.getViewSetActions(viewset);\n actions.forEach((action) => {\n const actionPath =\n action.scope === 'detail'\n ? this.joinPath(detailPath, action.path)\n : this.joinPath(collectionPath, action.path);\n const handler =\n action.scope === 'detail'\n ? this.adapt((ctx, id) => this.invokeDetailAction(viewset, action, ctx, String(id)), options)\n : this.adapt((ctx) => this.invokeCollectionAction(viewset, action, ctx), options);\n\n this.registerActionMethods(registrar, action.methods, actionPath, handler);\n });\n }\n\n /**\n * Register one APIView on a single path and dispatch by HTTP method.\n */\n registerAPIView(\n registrar: ExpressRouteRegistrar,\n path: string,\n apiView: ExpressAPIView,\n options: AdaptExpressOptions = {}\n ): void {\n const normalizedPath = this.normalizeBasePath(path);\n this.registerAllMethods(\n registrar,\n normalizedPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n }\n\n /**\n * Register one GenericAPIView with collection and detail routes.\n */\n registerGenericAPIView(\n registrar: ExpressRouteRegistrar,\n collectionPath: string,\n detailPath: string | undefined,\n apiView: ExpressAPIView,\n options: AdaptExpressOptions = {}\n ): void {\n const normalizedCollectionPath = this.normalizeBasePath(collectionPath);\n const normalizedDetailPath = detailPath?.trim().length\n ? this.normalizeBasePath(detailPath)\n : this.joinPath(normalizedCollectionPath, ':id');\n\n registrar.get(\n normalizedCollectionPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n registrar.post(\n normalizedCollectionPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n registrar.get(\n normalizedDetailPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n registrar.put(\n normalizedDetailPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n registrar.patch(\n normalizedDetailPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n registrar.delete(\n normalizedDetailPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n }\n\n private invokeDetailAction(\n viewset: ExpressCrudViewSet,\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 throw new TypeError(`Missing detail action method '${action.name}' on viewset.`);\n }\n return (\n candidate as (this: ExpressCrudViewSet, ctx: RequestContext, id: string) => Promise<TangoResponse>\n ).call(viewset, ctx, id);\n }\n\n private invokeCollectionAction(\n viewset: ExpressCrudViewSet,\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 throw new TypeError(`Missing collection action method '${action.name}' on viewset.`);\n }\n return (candidate as (this: ExpressCrudViewSet, ctx: RequestContext) => Promise<TangoResponse>).call(\n viewset,\n ctx\n );\n }\n\n private createHandler(\n handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>,\n options: AdaptExpressOptions\n ): RequestHandler {\n return async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {\n try {\n const user = options.getUser ? await options.getUser(req) : null;\n\n const request = this.toRequestFromExpress(req);\n const ctx = RequestContext.create(request, user);\n ctx.params = this.normalizeParams(req.params);\n\n const rawId = req.params.id;\n const id = Array.isArray(rawId) ? rawId[0] : rawId;\n const tangoResponse = await this.requestExecutor\n .forHandler({ handler, ctx, id })\n .runResponse(req.method, options.transaction);\n const response = tangoResponse.toWebResponse();\n\n res.status(response.status);\n\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n\n if (response.body === null) {\n res.end();\n return;\n }\n\n if (tangoResponse.body !== null) {\n await this.pipeReadableStream(response.body, res);\n return;\n }\n\n const body = new Uint8Array<ArrayBuffer>(await response.arrayBuffer());\n res.send(this.normalizeResponseBody(body, response.headers));\n } catch (error) {\n next(error);\n }\n };\n }\n\n private async pipeReadableStream(\n body: ReadableStream<Uint8Array<ArrayBuffer>>,\n res: ExpressResponse\n ): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const stream = Readable.fromWeb(body as unknown as NodeReadableStream);\n stream.on('error', reject);\n res.on('error', reject);\n res.on('finish', () => resolve());\n stream.pipe(res);\n });\n }\n\n private normalizeResponseBody(body: Uint8Array<ArrayBuffer>, headers: Headers): string | Buffer {\n const contentType = headers.get('content-type')?.toLowerCase() ?? '';\n if (contentType.startsWith('text/') || contentType.includes('json')) {\n return new TextDecoder().decode(body);\n }\n\n return Buffer.from(body);\n }\n\n private normalizeParams(params: Record<string, string | string[]>): Record<string, string> {\n return Object.fromEntries(\n Object.entries(params).map(([key, value]) => [key, Array.isArray(value) ? (value[0] ?? '') : value])\n );\n }\n\n private toRequestFromExpress(req: ExpressRequest): Request {\n const protocol = req.protocol || 'http';\n const host = req.get('host') || 'localhost';\n const url = `${protocol}://${host}${req.originalUrl || req.url}`;\n const headers = new Headers(req.headers as HeadersInit);\n const body = this.normalizeBody(req);\n\n if (body !== undefined && !headers.has('content-type') && this.isJsonLike(req.body)) {\n headers.set('content-type', 'application/json; charset=utf-8');\n }\n\n return new Request(url, {\n method: req.method,\n headers,\n body,\n });\n }\n\n private normalizeBody(req: ExpressRequest): BodyInit | null | undefined {\n if (['GET', 'HEAD'].includes(req.method)) {\n return undefined;\n }\n\n if (req.body === null || req.body === undefined) {\n return undefined;\n }\n\n if (\n typeof req.body === 'string' ||\n this.hasTag(req.body, 'Uint8Array') ||\n this.hasTag(req.body, 'ArrayBuffer')\n ) {\n return req.body;\n }\n\n if (this.isJsonLike(req.body)) {\n return JSON.stringify(req.body);\n }\n\n return undefined;\n }\n\n private isJsonLike(value: unknown): boolean {\n if (value === null) return true;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return true;\n if (Array.isArray(value)) return value.every((item) => this.isJsonLike(item));\n if (typeof value === 'object') {\n return Object.values(value as Record<string, unknown>).every((item) => this.isJsonLike(item));\n }\n return false;\n }\n\n private hasTag(value: unknown, tag: string): boolean {\n return value !== null && value !== undefined && Object.prototype.toString.call(value) === `[object ${tag}]`;\n }\n\n private registerMethod(\n registrar: ExpressRouteRegistrar,\n method: ViewSetActionMethod,\n path: string,\n handler: RequestHandler\n ): void {\n switch (method) {\n case 'GET':\n registrar.get(path, handler);\n return;\n case 'POST':\n registrar.post(path, handler);\n return;\n case 'PATCH':\n registrar.patch(path, handler);\n return;\n case 'PUT':\n registrar.put(path, handler);\n return;\n default:\n registrar.delete(path, handler);\n }\n }\n\n private normalizeBasePath(basePath: string): string {\n const trimmed = basePath.trim();\n if (!trimmed || trimmed === '/') {\n return '/';\n }\n return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;\n }\n\n private joinPath(base: string, subPath: string): string {\n const normalizedSubPath = subPath.replace(/^\\/+|\\/+$/g, '');\n return base === '/' ? `/${normalizedSubPath}` : `${base}/${normalizedSubPath}`;\n }\n\n private registerActionMethods(\n registrar: ExpressRouteRegistrar,\n methods: readonly ViewSetActionMethod[],\n path: string,\n handler: RequestHandler\n ): void {\n for (const method of methods) {\n this.registerMethod(registrar, method, path, handler);\n }\n }\n\n private registerAllMethods(registrar: ExpressRouteRegistrar, path: string, handler: RequestHandler): void {\n this.registerMethod(registrar, 'GET', path, handler);\n this.registerMethod(registrar, 'POST', path, handler);\n this.registerMethod(registrar, 'PUT', path, handler);\n this.registerMethod(registrar, 'PATCH', path, handler);\n this.registerMethod(registrar, 'DELETE', path, handler);\n }\n\n private getViewSetActions(viewset: ExpressCrudViewSet): readonly ResolvedViewSetActionDescriptor[] {\n const constructorValue = viewset.constructor as {\n getActions?: (input: ExpressCrudViewSet) => 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 ExpressAdapter,\n type AdaptExpressOptions,\n type ExpressAPIView,\n type ExpressCrudViewSet,\n type ExpressRouteRegistrar,\n} from './ExpressAdapter';\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };
@@ -0,0 +1,90 @@
1
+ import { Request, RequestHandler, Router } from "express";
2
+ import { RequestContext } from "@danceroutine/tango-resources";
3
+ import { TangoQueryParams, TangoResponse } from "@danceroutine/tango-core";
4
+ import { FRAMEWORK_ADAPTER_BRAND, FrameworkAdapter, FrameworkAdapterOptions } from "@danceroutine/tango-adapters-core/adapter";
5
+
6
+ //#region src/adapter/ExpressAdapter.d.ts
7
+ /**
8
+ * Adapter options for Express integration.
9
+ */
10
+ type AdaptExpressOptions = FrameworkAdapterOptions<Request>;
11
+ /**
12
+ * Minimal CRUD viewset contract used by adapter route registration helpers.
13
+ */
14
+ interface ExpressCrudViewSet {
15
+ list(ctx: RequestContext): Promise<TangoResponse>;
16
+ create(ctx: RequestContext): Promise<TangoResponse>;
17
+ retrieve(ctx: RequestContext, id: string): Promise<TangoResponse>;
18
+ update(ctx: RequestContext, id: string): Promise<TangoResponse>;
19
+ destroy(ctx: RequestContext, id: string): Promise<TangoResponse>;
20
+ }
21
+ interface ExpressAPIView {
22
+ dispatch(ctx: RequestContext): Promise<TangoResponse>;
23
+ }
24
+ /**
25
+ * Minimal route registrar interface implemented by Express apps and routers.
26
+ */
27
+ interface ExpressRouteRegistrar {
28
+ get(path: string, handler: RequestHandler): unknown;
29
+ post(path: string, handler: RequestHandler): unknown;
30
+ patch(path: string, handler: RequestHandler): unknown;
31
+ put(path: string, handler: RequestHandler): unknown;
32
+ delete(path: string, handler: RequestHandler): unknown;
33
+ }
34
+ /**
35
+ * Express adapter that translates Express handlers to Tango `RequestContext`.
36
+ */
37
+ declare class ExpressAdapter implements FrameworkAdapter<Response, RequestHandler, Request> {
38
+ readonly __tangoBrand: typeof FRAMEWORK_ADAPTER_BRAND;
39
+ private readonly requestExecutor;
40
+ /**
41
+ * Normalize an Express request into Tango query params.
42
+ */
43
+ toQueryParams(req: Request): TangoQueryParams;
44
+ /**
45
+ * Adapt a Tango-style handler into an Express request handler.
46
+ */
47
+ adapt(handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>, options?: AdaptExpressOptions): RequestHandler;
48
+ /**
49
+ * Build an Express router that wires all CRUD routes for a viewset.
50
+ */
51
+ createViewSetRouter(viewset: ExpressCrudViewSet, options?: AdaptExpressOptions): Router;
52
+ /**
53
+ * Build an Express router for a single-path APIView.
54
+ */
55
+ createAPIViewRouter(apiView: ExpressAPIView, options?: AdaptExpressOptions): Router;
56
+ /**
57
+ * Register all CRUD routes for a viewset under a base path.
58
+ */
59
+ registerViewSet(registrar: ExpressRouteRegistrar, basePath: string, viewset: ExpressCrudViewSet, options?: AdaptExpressOptions): void;
60
+ /**
61
+ * Register one APIView on a single path and dispatch by HTTP method.
62
+ */
63
+ registerAPIView(registrar: ExpressRouteRegistrar, path: string, apiView: ExpressAPIView, options?: AdaptExpressOptions): void;
64
+ /**
65
+ * Register one GenericAPIView with collection and detail routes.
66
+ */
67
+ registerGenericAPIView(registrar: ExpressRouteRegistrar, collectionPath: string, detailPath: string | undefined, apiView: ExpressAPIView, options?: AdaptExpressOptions): void;
68
+ private invokeDetailAction;
69
+ private invokeCollectionAction;
70
+ private createHandler;
71
+ private pipeReadableStream;
72
+ private normalizeResponseBody;
73
+ private normalizeParams;
74
+ private toRequestFromExpress;
75
+ private normalizeBody;
76
+ private isJsonLike;
77
+ private hasTag;
78
+ private registerMethod;
79
+ private normalizeBasePath;
80
+ private joinPath;
81
+ private registerActionMethods;
82
+ private registerAllMethods;
83
+ private getViewSetActions;
84
+ }
85
+ declare namespace index_d_exports {
86
+ export { AdaptExpressOptions, ExpressAPIView, ExpressAdapter, ExpressCrudViewSet, ExpressRouteRegistrar };
87
+ }
88
+ //#endregion
89
+ export { ExpressCrudViewSet as a, ExpressAdapter as i, AdaptExpressOptions as n, ExpressRouteRegistrar as o, ExpressAPIView as r, index_d_exports as t };
90
+ //# sourceMappingURL=index-D686Awva.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,6 +1,2 @@
1
- /**
2
- * Bundled exports for Django-style domain drill-down imports, plus curated
3
- * top-level symbols for TS-native ergonomic imports.
4
- */
5
- export * as adapter from './adapter/index';
6
- export { ExpressAdapter, type AdaptExpressOptions, type ExpressAPIView, type ExpressCrudViewSet, type ExpressRouteRegistrar, } from './adapter/index';
1
+ import { a as ExpressCrudViewSet, i as ExpressAdapter, n as AdaptExpressOptions, o as ExpressRouteRegistrar, r as ExpressAPIView, t as index_d_exports } from "./index-D686Awva.js";
2
+ export { type AdaptExpressOptions, type ExpressAPIView, ExpressAdapter, type ExpressCrudViewSet, type ExpressRouteRegistrar, index_d_exports as adapter };
package/dist/index.js CHANGED
@@ -1,3 +1,2 @@
1
- import { ExpressAdapter, adapter_exports } from "./adapter-CGeztuym.js";
2
-
3
- export { ExpressAdapter, adapter_exports as adapter };
1
+ import { n as ExpressAdapter, t as adapter_exports } from "./adapter-kC_MF6Wt.js";
2
+ export { ExpressAdapter, adapter_exports as adapter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danceroutine/tango-adapters-express",
3
- "version": "1.11.0",
3
+ "version": "1.11.2",
4
4
  "description": "Express adapter for Tango",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -31,9 +31,9 @@
31
31
  "directory": "packages/adapters/express"
32
32
  },
33
33
  "dependencies": {
34
- "@danceroutine/tango-core": "1.11.0",
35
- "@danceroutine/tango-resources": "1.11.0",
36
- "@danceroutine/tango-adapters-core": "1.11.0"
34
+ "@danceroutine/tango-adapters-core": "1.11.2",
35
+ "@danceroutine/tango-resources": "1.11.2",
36
+ "@danceroutine/tango-core": "1.11.2"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "express": "^4.21.1 || ^5.0.0"
@@ -42,11 +42,11 @@
42
42
  "@types/express": "^5.0.0",
43
43
  "@types/node": "^22.9.0",
44
44
  "express": "^5.2.1",
45
- "tsdown": "^0.4.0",
45
+ "tsdown": "^0.22.1",
46
46
  "typescript": "^5.6.3",
47
- "vitest": "^4.0.6",
48
- "@danceroutine/tango-orm": "1.11.0",
49
- "@danceroutine/tango-testing": "1.11.0"
47
+ "vitest": "^4.1.7",
48
+ "@danceroutine/tango-orm": "1.11.2",
49
+ "@danceroutine/tango-testing": "1.11.2"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "tsdown",
@@ -1,83 +0,0 @@
1
- import type { Request as ExpressRequest, RequestHandler } from 'express';
2
- import { Router } from 'express';
3
- import { RequestContext } from '@danceroutine/tango-resources';
4
- import { TangoQueryParams, TangoResponse } from '@danceroutine/tango-core';
5
- import { FRAMEWORK_ADAPTER_BRAND, type FrameworkAdapter, type FrameworkAdapterOptions } from '@danceroutine/tango-adapters-core/adapter';
6
- /**
7
- * Adapter options for Express integration.
8
- */
9
- export type AdaptExpressOptions = FrameworkAdapterOptions<ExpressRequest>;
10
- /**
11
- * Minimal CRUD viewset contract used by adapter route registration helpers.
12
- */
13
- export interface ExpressCrudViewSet {
14
- list(ctx: RequestContext): Promise<TangoResponse>;
15
- create(ctx: RequestContext): Promise<TangoResponse>;
16
- retrieve(ctx: RequestContext, id: string): Promise<TangoResponse>;
17
- update(ctx: RequestContext, id: string): Promise<TangoResponse>;
18
- destroy(ctx: RequestContext, id: string): Promise<TangoResponse>;
19
- }
20
- export interface ExpressAPIView {
21
- dispatch(ctx: RequestContext): Promise<TangoResponse>;
22
- }
23
- /**
24
- * Minimal route registrar interface implemented by Express apps and routers.
25
- */
26
- export interface ExpressRouteRegistrar {
27
- get(path: string, handler: RequestHandler): unknown;
28
- post(path: string, handler: RequestHandler): unknown;
29
- patch(path: string, handler: RequestHandler): unknown;
30
- put(path: string, handler: RequestHandler): unknown;
31
- delete(path: string, handler: RequestHandler): unknown;
32
- }
33
- /**
34
- * Express adapter that translates Express handlers to Tango `RequestContext`.
35
- */
36
- export declare class ExpressAdapter implements FrameworkAdapter<Response, RequestHandler, ExpressRequest> {
37
- readonly __tangoBrand: typeof FRAMEWORK_ADAPTER_BRAND;
38
- private readonly requestExecutor;
39
- /**
40
- * Normalize an Express request into Tango query params.
41
- */
42
- toQueryParams(req: ExpressRequest): TangoQueryParams;
43
- /**
44
- * Adapt a Tango-style handler into an Express request handler.
45
- */
46
- adapt(handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>, options?: AdaptExpressOptions): RequestHandler;
47
- /**
48
- * Build an Express router that wires all CRUD routes for a viewset.
49
- */
50
- createViewSetRouter(viewset: ExpressCrudViewSet, options?: AdaptExpressOptions): Router;
51
- /**
52
- * Build an Express router for a single-path APIView.
53
- */
54
- createAPIViewRouter(apiView: ExpressAPIView, options?: AdaptExpressOptions): Router;
55
- /**
56
- * Register all CRUD routes for a viewset under a base path.
57
- */
58
- registerViewSet(registrar: ExpressRouteRegistrar, basePath: string, viewset: ExpressCrudViewSet, options?: AdaptExpressOptions): void;
59
- /**
60
- * Register one APIView on a single path and dispatch by HTTP method.
61
- */
62
- registerAPIView(registrar: ExpressRouteRegistrar, path: string, apiView: ExpressAPIView, options?: AdaptExpressOptions): void;
63
- /**
64
- * Register one GenericAPIView with collection and detail routes.
65
- */
66
- registerGenericAPIView(registrar: ExpressRouteRegistrar, collectionPath: string, detailPath: string | undefined, apiView: ExpressAPIView, options?: AdaptExpressOptions): void;
67
- private invokeDetailAction;
68
- private invokeCollectionAction;
69
- private createHandler;
70
- private pipeReadableStream;
71
- private normalizeResponseBody;
72
- private normalizeParams;
73
- private toRequestFromExpress;
74
- private normalizeBody;
75
- private isJsonLike;
76
- private hasTag;
77
- private registerMethod;
78
- private normalizeBasePath;
79
- private joinPath;
80
- private registerActionMethods;
81
- private registerAllMethods;
82
- private getViewSetActions;
83
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter-CGeztuym.js","names":["req: ExpressRequest","handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>","options: AdaptExpressOptions","viewset: ExpressCrudViewSet","apiView: ExpressAPIView","registrar: ExpressRouteRegistrar","basePath: string","path: string","collectionPath: string","detailPath: string | undefined","action: ResolvedViewSetActionDescriptor","ctx: RequestContext","id: string","res: ExpressResponse","next: NextFunction","body: ReadableStream<Uint8Array<ArrayBuffer>>","body: Uint8Array<ArrayBuffer>","headers: Headers","params: Record<string, string | string[]>","value: unknown","tag: string","method: ViewSetActionMethod","handler: RequestHandler","base: string","subPath: string","methods: readonly ViewSetActionMethod[]"],"sources":["../src/adapter/ExpressAdapter.ts","../src/adapter/index.ts"],"sourcesContent":["import type { Request as ExpressRequest, Response as ExpressResponse, NextFunction, RequestHandler } from 'express';\nimport { Readable } from 'node:stream';\nimport type { ReadableStream as NodeReadableStream } from 'node:stream/web';\nimport { Router } from 'express';\nimport { RequestContext } from '@danceroutine/tango-resources';\nimport { TangoQueryParams, TangoResponse } from '@danceroutine/tango-core';\nimport {\n FRAMEWORK_ADAPTER_BRAND,\n FrameworkAdapterRequestExecutor,\n type FrameworkAdapter,\n type FrameworkAdapterOptions,\n} from '@danceroutine/tango-adapters-core/adapter';\n\ntype ViewSetActionMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\ntype ResolvedViewSetActionDescriptor = {\n name: string;\n scope: 'detail' | 'collection';\n methods: readonly ViewSetActionMethod[];\n path: string;\n};\n\n/**\n * Adapter options for Express integration.\n */\nexport type AdaptExpressOptions = FrameworkAdapterOptions<ExpressRequest>;\n\n/**\n * Minimal CRUD viewset contract used by adapter route registration helpers.\n */\nexport interface ExpressCrudViewSet {\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 ExpressAPIView {\n dispatch(ctx: RequestContext): Promise<TangoResponse>;\n}\n\n/**\n * Minimal route registrar interface implemented by Express apps and routers.\n */\nexport interface ExpressRouteRegistrar {\n get(path: string, handler: RequestHandler): unknown;\n post(path: string, handler: RequestHandler): unknown;\n patch(path: string, handler: RequestHandler): unknown;\n put(path: string, handler: RequestHandler): unknown;\n delete(path: string, handler: RequestHandler): unknown;\n}\n\n/**\n * Express adapter that translates Express handlers to Tango `RequestContext`.\n */\nexport class ExpressAdapter implements FrameworkAdapter<Response, RequestHandler, ExpressRequest> {\n readonly __tangoBrand: typeof FRAMEWORK_ADAPTER_BRAND = FRAMEWORK_ADAPTER_BRAND;\n private readonly requestExecutor = new FrameworkAdapterRequestExecutor();\n\n /**\n * Normalize an Express request into Tango query params.\n */\n toQueryParams(req: ExpressRequest): TangoQueryParams {\n const request = this.toRequestFromExpress(req);\n return TangoQueryParams.fromRequest(request);\n }\n\n /**\n * Adapt a Tango-style handler into an Express request handler.\n */\n adapt(\n handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>,\n options: AdaptExpressOptions = {}\n ): RequestHandler {\n return this.createHandler(handler, options);\n }\n\n /**\n * Build an Express router that wires all CRUD routes for a viewset.\n */\n createViewSetRouter(viewset: ExpressCrudViewSet, options: AdaptExpressOptions = {}): Router {\n const router = Router();\n this.registerViewSet(router, '', viewset, options);\n return router;\n }\n\n /**\n * Build an Express router for a single-path APIView.\n */\n createAPIViewRouter(apiView: ExpressAPIView, options: AdaptExpressOptions = {}): Router {\n const router = Router();\n this.registerAPIView(router, '', apiView, options);\n return router;\n }\n\n /**\n * Register all CRUD routes for a viewset under a base path.\n */\n registerViewSet(\n registrar: ExpressRouteRegistrar,\n basePath: string,\n viewset: ExpressCrudViewSet,\n options: AdaptExpressOptions = {}\n ): void {\n const collectionPath = this.normalizeBasePath(basePath);\n const detailPath = collectionPath === '/' ? '/:id' : `${collectionPath}/:id`;\n\n registrar.get(\n collectionPath,\n this.adapt((ctx) => viewset.list(ctx), options)\n );\n registrar.post(\n collectionPath,\n this.adapt((ctx) => viewset.create(ctx), options)\n );\n registrar.get(\n detailPath,\n this.adapt((ctx, id) => viewset.retrieve(ctx, String(id)), options)\n );\n registrar.patch(\n detailPath,\n this.adapt((ctx, id) => viewset.update(ctx, String(id)), options)\n );\n registrar.put(\n detailPath,\n this.adapt((ctx, id) => viewset.update(ctx, String(id)), options)\n );\n registrar.delete(\n detailPath,\n this.adapt((ctx, id) => viewset.destroy(ctx, String(id)), options)\n );\n\n const actions = this.getViewSetActions(viewset);\n actions.forEach((action) => {\n const actionPath =\n action.scope === 'detail'\n ? this.joinPath(detailPath, action.path)\n : this.joinPath(collectionPath, action.path);\n const handler =\n action.scope === 'detail'\n ? this.adapt((ctx, id) => this.invokeDetailAction(viewset, action, ctx, String(id)), options)\n : this.adapt((ctx) => this.invokeCollectionAction(viewset, action, ctx), options);\n\n this.registerActionMethods(registrar, action.methods, actionPath, handler);\n });\n }\n\n /**\n * Register one APIView on a single path and dispatch by HTTP method.\n */\n registerAPIView(\n registrar: ExpressRouteRegistrar,\n path: string,\n apiView: ExpressAPIView,\n options: AdaptExpressOptions = {}\n ): void {\n const normalizedPath = this.normalizeBasePath(path);\n this.registerAllMethods(\n registrar,\n normalizedPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n }\n\n /**\n * Register one GenericAPIView with collection and detail routes.\n */\n registerGenericAPIView(\n registrar: ExpressRouteRegistrar,\n collectionPath: string,\n detailPath: string | undefined,\n apiView: ExpressAPIView,\n options: AdaptExpressOptions = {}\n ): void {\n const normalizedCollectionPath = this.normalizeBasePath(collectionPath);\n const normalizedDetailPath = detailPath?.trim().length\n ? this.normalizeBasePath(detailPath)\n : this.joinPath(normalizedCollectionPath, ':id');\n\n registrar.get(\n normalizedCollectionPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n registrar.post(\n normalizedCollectionPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n registrar.get(\n normalizedDetailPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n registrar.put(\n normalizedDetailPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n registrar.patch(\n normalizedDetailPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n registrar.delete(\n normalizedDetailPath,\n this.adapt((ctx) => apiView.dispatch(ctx), options)\n );\n }\n\n private invokeDetailAction(\n viewset: ExpressCrudViewSet,\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 throw new TypeError(`Missing detail action method '${action.name}' on viewset.`);\n }\n return (\n candidate as (this: ExpressCrudViewSet, ctx: RequestContext, id: string) => Promise<TangoResponse>\n ).call(viewset, ctx, id);\n }\n\n private invokeCollectionAction(\n viewset: ExpressCrudViewSet,\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 throw new TypeError(`Missing collection action method '${action.name}' on viewset.`);\n }\n return (candidate as (this: ExpressCrudViewSet, ctx: RequestContext) => Promise<TangoResponse>).call(\n viewset,\n ctx\n );\n }\n\n private createHandler(\n handler: (ctx: RequestContext, ...args: unknown[]) => Promise<TangoResponse>,\n options: AdaptExpressOptions\n ): RequestHandler {\n return async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {\n try {\n const user = options.getUser ? await options.getUser(req) : null;\n\n const request = this.toRequestFromExpress(req);\n const ctx = RequestContext.create(request, user);\n ctx.params = this.normalizeParams(req.params);\n\n const rawId = req.params.id;\n const id = Array.isArray(rawId) ? rawId[0] : rawId;\n const tangoResponse = await this.requestExecutor.forHandler({ handler, ctx, id }).runResponse(\n req.method,\n options.transaction\n );\n const response = tangoResponse.toWebResponse();\n\n res.status(response.status);\n\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n\n if (response.body === null) {\n res.end();\n return;\n }\n\n if (tangoResponse.body !== null) {\n await this.pipeReadableStream(response.body, res);\n return;\n }\n\n const body = new Uint8Array<ArrayBuffer>(await response.arrayBuffer());\n res.send(this.normalizeResponseBody(body, response.headers));\n } catch (error) {\n next(error);\n }\n };\n }\n\n private async pipeReadableStream(\n body: ReadableStream<Uint8Array<ArrayBuffer>>,\n res: ExpressResponse\n ): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const stream = Readable.fromWeb(body as unknown as NodeReadableStream);\n stream.on('error', reject);\n res.on('error', reject);\n res.on('finish', () => resolve());\n stream.pipe(res);\n });\n }\n\n private normalizeResponseBody(body: Uint8Array<ArrayBuffer>, headers: Headers): string | Buffer {\n const contentType = headers.get('content-type')?.toLowerCase() ?? '';\n if (contentType.startsWith('text/') || contentType.includes('json')) {\n return new TextDecoder().decode(body);\n }\n\n return Buffer.from(body);\n }\n\n private normalizeParams(params: Record<string, string | string[]>): Record<string, string> {\n return Object.fromEntries(\n Object.entries(params).map(([key, value]) => [key, Array.isArray(value) ? (value[0] ?? '') : value])\n );\n }\n\n private toRequestFromExpress(req: ExpressRequest): Request {\n const protocol = req.protocol || 'http';\n const host = req.get('host') || 'localhost';\n const url = `${protocol}://${host}${req.originalUrl || req.url}`;\n const headers = new Headers(req.headers as HeadersInit);\n const body = this.normalizeBody(req);\n\n if (body !== undefined && !headers.has('content-type') && this.isJsonLike(req.body)) {\n headers.set('content-type', 'application/json; charset=utf-8');\n }\n\n return new Request(url, {\n method: req.method,\n headers,\n body,\n });\n }\n\n private normalizeBody(req: ExpressRequest): BodyInit | null | undefined {\n if (['GET', 'HEAD'].includes(req.method)) {\n return undefined;\n }\n\n if (req.body === null || req.body === undefined) {\n return undefined;\n }\n\n if (\n typeof req.body === 'string' ||\n this.hasTag(req.body, 'Uint8Array') ||\n this.hasTag(req.body, 'ArrayBuffer')\n ) {\n return req.body;\n }\n\n if (this.isJsonLike(req.body)) {\n return JSON.stringify(req.body);\n }\n\n return undefined;\n }\n\n private isJsonLike(value: unknown): boolean {\n if (value === null) return true;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return true;\n if (Array.isArray(value)) return value.every((item) => this.isJsonLike(item));\n if (typeof value === 'object') {\n return Object.values(value as Record<string, unknown>).every((item) => this.isJsonLike(item));\n }\n return false;\n }\n\n private hasTag(value: unknown, tag: string): boolean {\n return value !== null && value !== undefined && Object.prototype.toString.call(value) === `[object ${tag}]`;\n }\n\n private registerMethod(\n registrar: ExpressRouteRegistrar,\n method: ViewSetActionMethod,\n path: string,\n handler: RequestHandler\n ): void {\n switch (method) {\n case 'GET':\n registrar.get(path, handler);\n return;\n case 'POST':\n registrar.post(path, handler);\n return;\n case 'PATCH':\n registrar.patch(path, handler);\n return;\n case 'PUT':\n registrar.put(path, handler);\n return;\n default:\n registrar.delete(path, handler);\n }\n }\n\n private normalizeBasePath(basePath: string): string {\n const trimmed = basePath.trim();\n if (!trimmed || trimmed === '/') {\n return '/';\n }\n return trimmed.startsWith('/') ? trimmed : `/${trimmed}`;\n }\n\n private joinPath(base: string, subPath: string): string {\n const normalizedSubPath = subPath.replace(/^\\/+|\\/+$/g, '');\n return base === '/' ? `/${normalizedSubPath}` : `${base}/${normalizedSubPath}`;\n }\n\n private registerActionMethods(\n registrar: ExpressRouteRegistrar,\n methods: readonly ViewSetActionMethod[],\n path: string,\n handler: RequestHandler\n ): void {\n for (const method of methods) {\n this.registerMethod(registrar, method, path, handler);\n }\n }\n\n private registerAllMethods(registrar: ExpressRouteRegistrar, path: string, handler: RequestHandler): void {\n this.registerMethod(registrar, 'GET', path, handler);\n this.registerMethod(registrar, 'POST', path, handler);\n this.registerMethod(registrar, 'PUT', path, handler);\n this.registerMethod(registrar, 'PATCH', path, handler);\n this.registerMethod(registrar, 'DELETE', path, handler);\n }\n\n private getViewSetActions(viewset: ExpressCrudViewSet): readonly ResolvedViewSetActionDescriptor[] {\n const constructorValue = viewset.constructor as {\n getActions?: (input: ExpressCrudViewSet) => 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 ExpressAdapter,\n type AdaptExpressOptions,\n type ExpressAPIView,\n type ExpressCrudViewSet,\n type ExpressRouteRegistrar,\n} from './ExpressAdapter';\n"],"mappings":"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"}