@cleverbrush/server-openapi 3.1.0 → 4.1.0
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/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import*as w from"fs";import*as U from"path";import{ObjectSchemaBuilder as F,SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR as x}from"@cleverbrush/schema";import{toJsonSchema as W}from"@cleverbrush/schema-json";function z(e){return typeof e!="string"&&typeof e.validate=="function"}function H(e){let n=[];return{converted:e.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,(r,o)=>(n.push(o),`{${o}}`)),paramNames:n}}function J(e){let n=e.introspect(),t=n.templateDefinition,i=n.objectSchema.introspect().properties??{},a="";for(let s=0;s<t.segments.length;s++)a+=t.literals[s]+`{${t.segments[s].path}}`;a+=t.literals[t.segments.length]??"";let c=t.segments.map(s=>({name:s.path,schema:i[s.path]?W(i[s.path],{$schema:!1}):{type:"string"}}));return{pathString:a,parameters:c}}function P(e){let n=e.basePath.replace(/\/$/,""),t=e.pathTemplate;if(z(t)){let{pathString:p,parameters:l}=J(t);return{path:M(n+p),parameters:l}}let o=n+(t==="/"?"":t),{converted:i,paramNames:a}=H(o),c=M(i),s=a.map(p=>({name:p,schema:{type:"string"}}));return{path:c,parameters:s}}function M(e){let n=e.replace(/\/+/g,"/");return n.startsWith("/")||(n="/"+n),n.length>1&&n.endsWith("/")&&(n=n.slice(0,-1)),n||"/"}import{toJsonSchema as K}from"@cleverbrush/schema-json";function S(e,n){if(e==null)return{};let t;return n&&(typeof n=="function"?t=r=>n(r)??null:t=r=>n.getName(r)),K(e,{$schema:!1,draft:"2020-12",nameResolver:t})}var O=class{byInstance=new Map;byName=new Map;register(n){let t=n.introspect().schemaName;if(typeof t!="string")return;let r=this.byName.get(t);if(r!==void 0){if(r===n)return;throw new Error(`Schema name "${t}" is already registered by a different schema instance. Each named schema must be a single, reused constant. If you intended to register the same schema, ensure you are passing the same object reference (not a rebuilt schema).`)}this.byInstance.set(n,t),this.byName.set(t,n)}getName(n){return this.byInstance.get(n)??null}entries(){return this.byName.entries()}get isEmpty(){return this.byName.size===0}};function h(e,n,t=new Set){if(t.has(e))return;t.add(e),n.register(e);let r=e.introspect();switch(r.type){case"object":{let o=r.properties;if(o)for(let i of Object.values(o))h(i,n,t);break}case"array":r.elementSchema&&h(r.elementSchema,n,t);break;case"tuple":{let o=r.elements??[];for(let i of o)h(i,n,t);r.restSchema&&h(r.restSchema,n,t);break}case"union":{let o=r.options??[];for(let i of o)h(i,n,t);break}case"record":r.keySchema&&h(r.keySchema,n,t),r.valueSchema&&h(r.valueSchema,n,t);break;case"lazy":{let o=e.resolve();h(o,n,t);break}default:break}}function B(e){if(!e)return{};let n={};for(let t of e.schemes){let r=t.name,o=t.challenge?.();if("openIdConnectUrl"in t&&t.openIdConnectUrl)n[r]={type:"openIdConnect",openIdConnectUrl:t.openIdConnectUrl};else if("flows"in t&&t.flows)n[r]={type:"oauth2",flows:t.flows};else if(o?.headerValue?.toLowerCase().startsWith("bearer")||r==="jwt")n[r]={type:"http",scheme:"bearer",bearerFormat:"JWT"};else if(r==="cookie"||t.cookieName){let i=t.cookieName??t._options?.cookieName??"session";n[r]={type:"apiKey",in:"cookie",name:i}}else o?n[r]={type:"http",scheme:o.headerValue.split(" ")[0].toLowerCase()}:n[r]={type:"http",scheme:r}}return n}function E(e,n){return e===null?[]:n.length===0?[]:n.map(t=>({[t]:[...e]}))}function D(e,n,t,r,o){let i={name:e,in:n,schema:t};return r&&(i.required=!0),o&&(i.description=o),i}function q(e,n,t,r){let o=S(e,n),i=e.introspect(),a={schema:o};t!=null?a.example=t:r!=null&&(a.examples=r);let c={required:i.isRequired!==!1,content:{"application/json":a}};return typeof i.description=="string"&&i.description!==""&&(c.description=i.description),c}var Z={200:"OK",201:"Created",202:"Accepted",204:"No Content",400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",409:"Conflict",422:"Unprocessable Entity",500:"Internal Server Error"},N={type:"object",properties:{status:{type:"integer"},title:{type:"string"},detail:{type:"string"}}};function V(e,n){let r=e.introspect().properties??{},o={};for(let[i,a]of Object.entries(r)){let c=a.introspect(),s={schema:S(a,n)};typeof c.description=="string"&&c.description!==""&&(s.description=c.description),o[i]=s}return o}function G(e,n,t){let r={};if(e.producesFile){let o=e.producesFile.contentType??"application/octet-stream",i=e.producesFile.description??"File download";r[200]={description:i,content:{[o]:{schema:{type:"string",format:"binary"}}}}}else if(e.responsesSchemas)for(let[o,i]of Object.entries(e.responsesSchemas)){let a=Number(o),c=Z[a]??`Response ${o}`;if(i){let s=S(i,t),p=i.introspect(),l=typeof p.description=="string"&&p.description!==""?p.description:c;r[o]={description:l,content:{"application/json":{schema:s}}}}else r[o]={description:c}}else if(e.responseSchema){let o=S(e.responseSchema,t),i=e.responseSchema.introspect(),a=typeof i.description=="string"&&i.description!==""?i.description:"Successful response";r[200]={description:a,content:{"application/json":{schema:o}}}}else n==="DELETE"||n==="HEAD"?r[204]={description:"No content"}:r[200]={description:"Successful response"};if(e.bodySchema&&!r[422]&&(r[422]={description:"Validation error",content:{"application/problem+json":{schema:N}}}),e.authRoles!==null&&(r[401]||(r[401]={description:"Unauthorized",content:{"application/problem+json":{schema:N}}}),r[403]||(r[403]={description:"Forbidden",content:{"application/problem+json":{schema:N}}})),e.produces&&!e.producesFile)for(let o of Object.keys(r)){let a=r[o].content;if(!a||a["application/problem+json"])continue;let c=a["application/json"]?.schema??{};for(let[s,p]of Object.entries(e.produces))s!=="application/json"&&(a[s]={schema:p.schema?S(p.schema,t):c})}if(e.responseHeaderSchema){let o=V(e.responseHeaderSchema,t);for(let i of Object.keys(r))r[i].headers=o}if(e.links){let o={};for(let[a,c]of Object.entries(e.links)){let s={operationId:c.operationId};if(c.description&&(s.description=c.description),c.requestBody&&(s.requestBody=c.requestBody),c.parameters){let p;if(typeof c.parameters=="function"&&e.responseSchema){let l=F.getPropertiesFor(e.responseSchema),y=c.parameters(l);p={};for(let[u,f]of Object.entries(y))if(f&&typeof f=="object"&&x in f){let A=f[x].toJsonPointer();p[u]=`$response.body#${A}`}else p[u]=f}else p=c.parameters;s.parameters=p}o[a]=s}let i=Object.keys(r).filter(a=>Number(a)>=200&&Number(a)<300).sort()[0];i&&(r[i].links=o)}return r}function Y(e,n,t,r){let o={};e.summary&&(o.summary=e.summary),e.description&&(o.description=e.description),e.tags.length>0&&(o.tags=[...e.tags]),e.operationId&&(o.operationId=e.operationId),e.deprecated&&(o.deprecated=!0);let i=[];for(let c of n)i.push(D(c.name,"path",c.schema,!0));if(e.querySchema){let s=e.querySchema.introspect().properties??{};for(let[p,l]of Object.entries(s)){let y=l.introspect(),u=y.isRequired!==!1,f=typeof y.description=="string"&&y.description!==""?y.description:void 0;i.push(D(p,"query",S(l,r),u,f))}}if(e.headerSchema){let s=e.headerSchema.introspect().properties??{};for(let[p,l]of Object.entries(s)){let y=l.introspect(),u=y.isRequired!==!1,f=typeof y.description=="string"&&y.description!==""?y.description:void 0;i.push(D(p,"header",S(l,r),u,f))}}i.length>0&&(o.parameters=i),e.bodySchema&&(o.requestBody=q(e.bodySchema,r,e.example,e.examples)),o.responses=G(e,e.method.toUpperCase(),r);let a=E(Q(e),t);if(a.length>0&&(o.security=a),e.externalDocs){let c={url:e.externalDocs.url};e.externalDocs.description&&(c.description=e.externalDocs.description),o.externalDocs=c}if(e.callbacks){let c={};for(let[s,p]of Object.entries(e.callbacks)){let l;if(p.expression)l=p.expression;else if(p.urlFrom&&e.bodySchema){let A=F.getPropertiesFor(e.bodySchema),v=p.urlFrom(A);v&&typeof v=="object"&&x in v?l=`{$request.body#${v[x].toJsonPointer()}}`:l=String(v??"{$request.body#/callbackUrl}")}else l="{$request.body#/callbackUrl}";let y=(p.method??"post").toLowerCase(),u={};p.summary&&(u.summary=p.summary),p.description&&(u.description=p.description),p.body&&(u.requestBody=q(p.body,r));let f={};p.response?f[200]={description:"OK",content:{"application/json":{schema:S(p.response,r)}}}:f[200]={description:"OK"},u.responses=f,c[s]={[l]:{[y]:u}}}o.callbacks=c}return o}function Q(e){return e.authRoles}function R(e){let n=e.server,t=e.registrations??n?.getRegistrations()??[],r=e.authConfig!==void 0?e.authConfig:n?.getAuthenticationConfig()??void 0,o=e.webhooks??(n&&n.getWebhooks().length>0?n.getWebhooks():void 0),{info:i,servers:a,securitySchemes:c,tags:s}=e,p=c??B(r),l=Object.keys(p),y=new O,u=new Set;for(let m of t){let d=m.endpoint;if(d.bodySchema&&h(d.bodySchema,y,u),d.responseSchema&&h(d.responseSchema,y,u),d.responsesSchemas)for(let g of Object.values(d.responsesSchemas))g&&h(g,y,u);if(d.querySchema){let g=d.querySchema.introspect().properties??{};for(let b of Object.values(g))h(b,y,u)}if(d.headerSchema){let g=d.headerSchema.introspect().properties??{};for(let b of Object.values(g))h(b,y,u)}if(d.callbacks)for(let g of Object.values(d.callbacks))g.body&&h(g.body,y,u),g.response&&h(g.response,y,u)}if(o)for(let m of o)m.body&&h(m.body,y,u),m.response&&h(m.response,y,u);let f=m=>{let d=!1;return g=>{if(g===m&&!d){d=!0;return}return y.getName(g)??void 0}},A={};for(let m of t){let d=m.endpoint,{path:g,parameters:b}=P(d),j=d.method.toLowerCase();A[g]||(A[g]={}),A[g][j]=Y(d,b,l,y)}let v=new Set((s??[]).map(m=>m.name)),I=[];for(let m of t)for(let d of m.endpoint.tags)!v.has(d)&&!I.includes(d)&&I.push(d);I.sort();let L=[...s??[],...I.map(m=>({name:m}))],k={openapi:"3.1.0",info:{...i}};if(a&&a.length>0&&(k.servers=a.map(m=>({...m}))),L.length>0&&(k.tags=L.map(m=>({...m}))),k.paths=A,o&&o.length>0){let m={};for(let d of o){let g=(d.method??"post").toLowerCase(),b={};d.summary&&(b.summary=d.summary),d.description&&(b.description=d.description),d.tags&&d.tags.length>0&&(b.tags=[...d.tags]),d.body&&(b.requestBody=q(d.body,y));let j={};d.response?j[200]={description:"OK",content:{"application/json":{schema:S(d.response,y)}}}:j[200]={description:"OK"},b.responses=j,m[d.name]={[g]:b}}k.webhooks=m}let C={};for(let[m,d]of y.entries())C[m]=S(d,f(d));let T=Object.keys(C).length>0;if(l.length>0||T){let m={};l.length>0&&(m.securitySchemes={...p}),T&&(m.schemas=C),k.components=m}return k}function X(e,n){let t=R(e),r=U.dirname(n);return r&&!w.existsSync(r)&&w.mkdirSync(r,{recursive:!0}),w.writeFileSync(n,JSON.stringify(t,null,2),"utf-8"),t}function ee(e){return e.replace(/^\//,"").replace(/[/{}]/g,"_").replace(/_+/g,"_").replace(/_$/,"")}function ne(e,n){let t=e.replace(/\/$/,"");if(typeof n=="string"){let s=(t+(n==="/"?"":n)).replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,"{$1}");return _(s)}let o=n.introspect?.()?.templateDefinition;if(!o)return _(t);let i="";for(let a=0;a<o.segments.length;a++)i+=o.literals[a]+`{${o.segments[a].path}}`;return i+=o.literals[o.segments.length]??"",_(t+i)}function _(e){let n=e.replace(/\/+/g,"/");return n.startsWith("/")||(n="/"+n),n.length>1&&n.endsWith("/")&&(n=n.slice(0,-1)),n||"/"}function $(e){let n=e.subscriptions??e.server?.getSubscriptionRegistrations()??[],t=new O,r=new Set;for(let c of n){let s=c.endpoint;s.incomingSchema&&h(s.incomingSchema,t,r),s.outgoingSchema&&h(s.outgoingSchema,t,r)}let o={},i={};for(let c of n){let s=c.endpoint,p=ne(s.basePath,s.pathTemplate),l=s.operationId??ee(p),y={};if(s.outgoingSchema){let f=s.outgoingSchema.introspect();y.ServerEvent={name:"ServerEvent",...f.description?{summary:f.description}:{},payload:S(s.outgoingSchema,t)}}if(s.incomingSchema){let f=s.incomingSchema.introspect();y.ClientMessage={name:"ClientMessage",...f.description?{summary:f.description}:{},payload:S(s.incomingSchema,t)}}let u={address:p};if(s.summary&&(u.summary=s.summary),s.description&&(u.description=s.description),s.deprecated&&(u.deprecated=!0),s.tags.length>0&&(u.tags=s.tags.map(f=>({name:f}))),s.externalDocs&&(u.externalDocs=s.externalDocs),Object.keys(y).length>0&&(u.messages=y),o[l]=u,s.outgoingSchema){let f=s.operationId?`${s.operationId}Events`:`${l}_send`;i[f]={action:"send",channel:{$ref:`#/channels/${l}`},messages:[{$ref:`#/channels/${l}/messages/ServerEvent`}]}}if(s.incomingSchema){let f=s.operationId?`${s.operationId}Messages`:`${l}_receive`;i[f]={action:"receive",channel:{$ref:`#/channels/${l}`},messages:[{$ref:`#/channels/${l}/messages/ClientMessage`}]}}}let a={asyncapi:"3.0.0",info:{title:e.info.title,version:e.info.version,...e.info.description?{description:e.info.description}:{},...e.info.termsOfService?{termsOfService:e.info.termsOfService}:{},...e.info.contact?{contact:e.info.contact}:{},...e.info.license?{license:e.info.license}:{}}};if(e.servers&&Object.keys(e.servers).length>0&&(a.servers=e.servers),a.channels=o,a.operations=i,!t.isEmpty){let c={};for(let[s,p]of t.entries())c[s]=S(p,t);a.components={schemas:c}}return a}import{endpoint as te}from"@cleverbrush/server";function re(e){let n=e.path??"/openapi.json",t=e.server,r=e.getRegistrations??(t?()=>t.getRegistrations():()=>[]),o=te.get(n).summary("OpenAPI specification").tags("OpenAPI").operationId("getOpenApiSpec"),i=null;return{endpoint:o,handler:()=>(i||(i=R({registrations:r(),info:e.info,servers:e.servers,authConfig:e.authConfig!==void 0?e.authConfig:t?.getAuthenticationConfig()??void 0,securitySchemes:e.securitySchemes})),i)}}function oe(e){let n=e.path??"/asyncapi.json",t=e.server,r=e.getSubscriptionRegistrations??(t?()=>t.getSubscriptionRegistrations():()=>[]),o=null;return async(i,a)=>{let s=i.url.pathname;if(i.method.toUpperCase()==="GET"&&s===n){o||(o=$({subscriptions:r(),info:e.info,servers:e.servers}));let l=JSON.stringify(o);i.response.writeHead(200,{"content-type":"application/json","content-length":Buffer.byteLength(l).toString()}),i.response.end(l);return}await a()}}function se(e){let n=e.path??"/openapi.json",t=e.server,r=e.getRegistrations??(t?()=>t.getRegistrations():()=>[]),o=null;return async(i,a)=>{let s=i.url.pathname;if(i.method.toUpperCase()==="GET"&&s===n){o||(o=R({registrations:r(),info:e.info,servers:e.servers,authConfig:e.authConfig!==void 0?e.authConfig:t?.getAuthenticationConfig()??void 0,securitySchemes:e.securitySchemes}));let l=JSON.stringify(o);i.response.writeHead(200,{"content-type":"application/json","content-length":Buffer.byteLength(l).toString()}),i.response.end(l);return}await a()}}export{O as SchemaRegistry,S as convertSchema,re as createOpenApiEndpoint,$ as generateAsyncApiSpec,R as generateOpenApiSpec,E as mapOperationSecurity,B as mapSecuritySchemes,P as resolvePath,oe as serveAsyncApi,se as serveOpenApi,h as walkSchemas,X as writeOpenApiSpec};
|
|
1
|
+
import*as w from"fs";import*as F from"path";import{ObjectSchemaBuilder as U,SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR as x}from"@cleverbrush/schema";import{toJsonSchema as W}from"@cleverbrush/schema-json";function z(e){return typeof e!="string"&&typeof e.validate=="function"}function H(e){let n=[];return{converted:e.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,(r,o)=>(n.push(o),`{${o}}`)),paramNames:n}}function J(e){let n=e.introspect(),t=n.templateDefinition,i=n.objectSchema.introspect().properties??{},a="";for(let s=0;s<t.segments.length;s++)a+=t.literals[s]+`{${t.segments[s].path}}`;a+=t.literals[t.segments.length]??"";let c=t.segments.map(s=>({name:s.path,schema:i[s.path]?W(i[s.path],{$schema:!1}):{type:"string"}}));return{pathString:a,parameters:c}}function P(e){let n=e.basePath.replace(/\/$/,""),t=e.pathTemplate;if(z(t)){let{pathString:p,parameters:l}=J(t);return{path:M(n+p),parameters:l}}let o=n+(t==="/"?"":t),{converted:i,paramNames:a}=H(o),c=M(i),s=a.map(p=>({name:p,schema:{type:"string"}}));return{path:c,parameters:s}}function M(e){let n=e.replace(/\/+/g,"/");return n.startsWith("/")||(n="/"+n),n.length>1&&n.endsWith("/")&&(n=n.slice(0,-1)),n||"/"}import{toJsonSchema as K}from"@cleverbrush/schema-json";function S(e,n){if(e==null)return{};let t;return n&&(typeof n=="function"?t=r=>n(r)??null:t=r=>n.getName(r)),K(e,{$schema:!1,draft:"2020-12",nameResolver:t})}var O=class{byInstance=new Map;byName=new Map;register(n){let t=n.introspect().schemaName;if(typeof t!="string")return;let r=this.byName.get(t);if(r!==void 0){if(r===n)return;throw new Error(`Schema name "${t}" is already registered by a different schema instance. Each named schema must be a single, reused constant. If you intended to register the same schema, ensure you are passing the same object reference (not a rebuilt schema).`)}this.byInstance.set(n,t),this.byName.set(t,n)}getName(n){return this.byInstance.get(n)??null}entries(){return this.byName.entries()}get isEmpty(){return this.byName.size===0}};function h(e,n,t=new Set){if(t.has(e))return;t.add(e),n.register(e);let r=e.introspect();switch(r.type){case"object":{let o=r.properties;if(o)for(let i of Object.values(o))h(i,n,t);break}case"array":r.elementSchema&&h(r.elementSchema,n,t);break;case"tuple":{let o=r.elements??[];for(let i of o)h(i,n,t);r.restSchema&&h(r.restSchema,n,t);break}case"union":{let o=r.options??[];for(let i of o)h(i,n,t);break}case"record":r.keySchema&&h(r.keySchema,n,t),r.valueSchema&&h(r.valueSchema,n,t);break;case"lazy":{let o=e.resolve();h(o,n,t);break}default:break}}function B(e){if(!e)return{};let n={};for(let t of e.schemes){let r=t.name,o=t.challenge?.();if("openIdConnectUrl"in t&&t.openIdConnectUrl)n[r]={type:"openIdConnect",openIdConnectUrl:t.openIdConnectUrl};else if("flows"in t&&t.flows)n[r]={type:"oauth2",flows:t.flows};else if(o?.headerValue?.toLowerCase().startsWith("bearer")||r==="jwt")n[r]={type:"http",scheme:"bearer",bearerFormat:"JWT"};else if(r==="cookie"||t.cookieName){let i=t.cookieName??t._options?.cookieName??"session";n[r]={type:"apiKey",in:"cookie",name:i}}else o?n[r]={type:"http",scheme:o.headerValue.split(" ")[0].toLowerCase()}:n[r]={type:"http",scheme:r}}return n}function E(e,n){return e===null?[]:n.length===0?[]:n.map(t=>({[t]:[...e]}))}function D(e,n,t,r,o){let i={name:e,in:n,schema:t};return r&&(i.required=!0),o&&(i.description=o),i}function q(e,n,t,r,o){let i=e.introspect(),a={required:i.isRequired!==!1};if(o){let s={schema:S(e,n)};a.content={"multipart/form-data":s}}else{let s={schema:S(e,n)};t!=null?s.example=t:r!=null&&(s.examples=r),a.content={"application/json":s}}return typeof i.description=="string"&&i.description!==""&&(a.description=i.description),a}var Z={200:"OK",201:"Created",202:"Accepted",204:"No Content",400:"Bad Request",401:"Unauthorized",403:"Forbidden",404:"Not Found",409:"Conflict",422:"Unprocessable Entity",500:"Internal Server Error"},N={type:"object",properties:{status:{type:"integer"},title:{type:"string"},detail:{type:"string"}}};function V(e,n){let r=e.introspect().properties??{},o={};for(let[i,a]of Object.entries(r)){let c=a.introspect(),s={schema:S(a,n)};typeof c.description=="string"&&c.description!==""&&(s.description=c.description),o[i]=s}return o}function G(e,n,t){let r={};if(e.producesFile){let o=e.producesFile.contentType??"application/octet-stream",i=e.producesFile.description??"File download";r[200]={description:i,content:{[o]:{schema:{type:"string",format:"binary"}}}}}else if(e.responsesSchemas)for(let[o,i]of Object.entries(e.responsesSchemas)){let a=Number(o),c=Z[a]??`Response ${o}`;if(i){let s=S(i,t),p=i.introspect(),l=typeof p.description=="string"&&p.description!==""?p.description:c;r[o]={description:l,content:{"application/json":{schema:s}}}}else r[o]={description:c}}else if(e.responseSchema){let o=S(e.responseSchema,t),i=e.responseSchema.introspect(),a=typeof i.description=="string"&&i.description!==""?i.description:"Successful response";r[200]={description:a,content:{"application/json":{schema:o}}}}else n==="DELETE"||n==="HEAD"?r[204]={description:"No content"}:r[200]={description:"Successful response"};if(e.bodySchema&&!r[422]&&(r[422]={description:"Validation error",content:{"application/problem+json":{schema:N}}}),e.authRoles!==null&&(r[401]||(r[401]={description:"Unauthorized",content:{"application/problem+json":{schema:N}}}),r[403]||(r[403]={description:"Forbidden",content:{"application/problem+json":{schema:N}}})),e.produces&&!e.producesFile)for(let o of Object.keys(r)){let a=r[o].content;if(!a||a["application/problem+json"])continue;let c=a["application/json"]?.schema??{};for(let[s,p]of Object.entries(e.produces))s!=="application/json"&&(a[s]={schema:p.schema?S(p.schema,t):c})}if(e.responseHeaderSchema){let o=V(e.responseHeaderSchema,t);for(let i of Object.keys(r))r[i].headers=o}if(e.links){let o={};for(let[a,c]of Object.entries(e.links)){let s={operationId:c.operationId};if(c.description&&(s.description=c.description),c.requestBody&&(s.requestBody=c.requestBody),c.parameters){let p;if(typeof c.parameters=="function"&&e.responseSchema){let l=U.getPropertiesFor(e.responseSchema),y=c.parameters(l);p={};for(let[u,f]of Object.entries(y))if(f&&typeof f=="object"&&x in f){let A=f[x].toJsonPointer();p[u]=`$response.body#${A}`}else p[u]=f}else p=c.parameters;s.parameters=p}o[a]=s}let i=Object.keys(r).filter(a=>Number(a)>=200&&Number(a)<300).sort()[0];i&&(r[i].links=o)}return r}function Y(e,n,t,r){let o={};e.summary&&(o.summary=e.summary),e.description&&(o.description=e.description),e.tags.length>0&&(o.tags=[...e.tags]),e.operationId&&(o.operationId=e.operationId),e.deprecated&&(o.deprecated=!0);let i=[];for(let c of n)i.push(D(c.name,"path",c.schema,!0));if(e.querySchema){let s=e.querySchema.introspect().properties??{};for(let[p,l]of Object.entries(s)){let y=l.introspect(),u=y.isRequired!==!1,f=typeof y.description=="string"&&y.description!==""?y.description:void 0;i.push(D(p,"query",S(l,r),u,f))}}if(e.headerSchema){let s=e.headerSchema.introspect().properties??{};for(let[p,l]of Object.entries(s)){let y=l.introspect(),u=y.isRequired!==!1,f=typeof y.description=="string"&&y.description!==""?y.description:void 0;i.push(D(p,"header",S(l,r),u,f))}}i.length>0&&(o.parameters=i),e.bodySchema&&(o.requestBody=q(e.bodySchema,r,e.example,e.examples,e.fileUpload)),o.responses=G(e,e.method.toUpperCase(),r);let a=E(Q(e),t);if(a.length>0&&(o.security=a),e.externalDocs){let c={url:e.externalDocs.url};e.externalDocs.description&&(c.description=e.externalDocs.description),o.externalDocs=c}if(e.callbacks){let c={};for(let[s,p]of Object.entries(e.callbacks)){let l;if(p.expression)l=p.expression;else if(p.urlFrom&&e.bodySchema){let A=U.getPropertiesFor(e.bodySchema),v=p.urlFrom(A);v&&typeof v=="object"&&x in v?l=`{$request.body#${v[x].toJsonPointer()}}`:l=String(v??"{$request.body#/callbackUrl}")}else l="{$request.body#/callbackUrl}";let y=(p.method??"post").toLowerCase(),u={};p.summary&&(u.summary=p.summary),p.description&&(u.description=p.description),p.body&&(u.requestBody=q(p.body,r));let f={};p.response?f[200]={description:"OK",content:{"application/json":{schema:S(p.response,r)}}}:f[200]={description:"OK"},u.responses=f,c[s]={[l]:{[y]:u}}}o.callbacks=c}return o}function Q(e){return e.authRoles}function R(e){let n=e.server,t=e.registrations??n?.getRegistrations()??[],r=e.authConfig!==void 0?e.authConfig:n?.getAuthenticationConfig()??void 0,o=e.webhooks??(n&&n.getWebhooks().length>0?n.getWebhooks():void 0),{info:i,servers:a,securitySchemes:c,tags:s}=e,p=c??B(r),l=Object.keys(p),y=new O,u=new Set;for(let m of t){let d=m.endpoint;if(d.bodySchema&&h(d.bodySchema,y,u),d.responseSchema&&h(d.responseSchema,y,u),d.responsesSchemas)for(let g of Object.values(d.responsesSchemas))g&&h(g,y,u);if(d.querySchema){let g=d.querySchema.introspect().properties??{};for(let b of Object.values(g))h(b,y,u)}if(d.headerSchema){let g=d.headerSchema.introspect().properties??{};for(let b of Object.values(g))h(b,y,u)}if(d.callbacks)for(let g of Object.values(d.callbacks))g.body&&h(g.body,y,u),g.response&&h(g.response,y,u)}if(o)for(let m of o)m.body&&h(m.body,y,u),m.response&&h(m.response,y,u);let f=m=>{let d=!1;return g=>{if(g===m&&!d){d=!0;return}return y.getName(g)??void 0}},A={};for(let m of t){let d=m.endpoint,{path:g,parameters:b}=P(d),j=d.method.toLowerCase();A[g]||(A[g]={}),A[g][j]=Y(d,b,l,y)}let v=new Set((s??[]).map(m=>m.name)),I=[];for(let m of t)for(let d of m.endpoint.tags)!v.has(d)&&!I.includes(d)&&I.push(d);I.sort();let L=[...s??[],...I.map(m=>({name:m}))],k={openapi:"3.1.0",info:{...i}};if(a&&a.length>0&&(k.servers=a.map(m=>({...m}))),L.length>0&&(k.tags=L.map(m=>({...m}))),k.paths=A,o&&o.length>0){let m={};for(let d of o){let g=(d.method??"post").toLowerCase(),b={};d.summary&&(b.summary=d.summary),d.description&&(b.description=d.description),d.tags&&d.tags.length>0&&(b.tags=[...d.tags]),d.body&&(b.requestBody=q(d.body,y));let j={};d.response?j[200]={description:"OK",content:{"application/json":{schema:S(d.response,y)}}}:j[200]={description:"OK"},b.responses=j,m[d.name]={[g]:b}}k.webhooks=m}let C={};for(let[m,d]of y.entries())C[m]=S(d,f(d));let T=Object.keys(C).length>0;if(l.length>0||T){let m={};l.length>0&&(m.securitySchemes={...p}),T&&(m.schemas=C),k.components=m}return k}function X(e,n){let t=R(e),r=F.dirname(n);return r&&!w.existsSync(r)&&w.mkdirSync(r,{recursive:!0}),w.writeFileSync(n,JSON.stringify(t,null,2),"utf-8"),t}function ee(e){return e.replace(/^\//,"").replace(/[/{}]/g,"_").replace(/_+/g,"_").replace(/_$/,"")}function ne(e,n){let t=e.replace(/\/$/,"");if(typeof n=="string"){let s=(t+(n==="/"?"":n)).replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g,"{$1}");return _(s)}let o=n.introspect?.()?.templateDefinition;if(!o)return _(t);let i="";for(let a=0;a<o.segments.length;a++)i+=o.literals[a]+`{${o.segments[a].path}}`;return i+=o.literals[o.segments.length]??"",_(t+i)}function _(e){let n=e.replace(/\/+/g,"/");return n.startsWith("/")||(n="/"+n),n.length>1&&n.endsWith("/")&&(n=n.slice(0,-1)),n||"/"}function $(e){let n=e.subscriptions??e.server?.getSubscriptionRegistrations()??[],t=new O,r=new Set;for(let c of n){let s=c.endpoint;s.incomingSchema&&h(s.incomingSchema,t,r),s.outgoingSchema&&h(s.outgoingSchema,t,r)}let o={},i={};for(let c of n){let s=c.endpoint,p=ne(s.basePath,s.pathTemplate),l=s.operationId??ee(p),y={};if(s.outgoingSchema){let f=s.outgoingSchema.introspect();y.ServerEvent={name:"ServerEvent",...f.description?{summary:f.description}:{},payload:S(s.outgoingSchema,t)}}if(s.incomingSchema){let f=s.incomingSchema.introspect();y.ClientMessage={name:"ClientMessage",...f.description?{summary:f.description}:{},payload:S(s.incomingSchema,t)}}let u={address:p};if(s.summary&&(u.summary=s.summary),s.description&&(u.description=s.description),s.deprecated&&(u.deprecated=!0),s.tags.length>0&&(u.tags=s.tags.map(f=>({name:f}))),s.externalDocs&&(u.externalDocs=s.externalDocs),Object.keys(y).length>0&&(u.messages=y),o[l]=u,s.outgoingSchema){let f=s.operationId?`${s.operationId}Events`:`${l}_send`;i[f]={action:"send",channel:{$ref:`#/channels/${l}`},messages:[{$ref:`#/channels/${l}/messages/ServerEvent`}]}}if(s.incomingSchema){let f=s.operationId?`${s.operationId}Messages`:`${l}_receive`;i[f]={action:"receive",channel:{$ref:`#/channels/${l}`},messages:[{$ref:`#/channels/${l}/messages/ClientMessage`}]}}}let a={asyncapi:"3.0.0",info:{title:e.info.title,version:e.info.version,...e.info.description?{description:e.info.description}:{},...e.info.termsOfService?{termsOfService:e.info.termsOfService}:{},...e.info.contact?{contact:e.info.contact}:{},...e.info.license?{license:e.info.license}:{}}};if(e.servers&&Object.keys(e.servers).length>0&&(a.servers=e.servers),a.channels=o,a.operations=i,!t.isEmpty){let c={};for(let[s,p]of t.entries())c[s]=S(p,t);a.components={schemas:c}}return a}import{endpoint as te}from"@cleverbrush/server";function re(e){let n=e.path??"/openapi.json",t=e.server,r=e.getRegistrations??(t?()=>t.getRegistrations():()=>[]),o=te.get(n).summary("OpenAPI specification").tags("OpenAPI").operationId("getOpenApiSpec"),i=null;return{endpoint:o,handler:()=>(i||(i=R({registrations:r(),info:e.info,servers:e.servers,authConfig:e.authConfig!==void 0?e.authConfig:t?.getAuthenticationConfig()??void 0,securitySchemes:e.securitySchemes})),i)}}function oe(e){let n=e.path??"/asyncapi.json",t=e.server,r=e.getSubscriptionRegistrations??(t?()=>t.getSubscriptionRegistrations():()=>[]),o=null;return async(i,a)=>{let s=i.url.pathname;if(i.method.toUpperCase()==="GET"&&s===n){o||(o=$({subscriptions:r(),info:e.info,servers:e.servers}));let l=JSON.stringify(o);i.response.writeHead(200,{"content-type":"application/json","content-length":Buffer.byteLength(l).toString()}),i.response.end(l);return}await a()}}function se(e){let n=e.path??"/openapi.json",t=e.server,r=e.getRegistrations??(t?()=>t.getRegistrations():()=>[]),o=null;return async(i,a)=>{let s=i.url.pathname;if(i.method.toUpperCase()==="GET"&&s===n){o||(o=R({registrations:r(),info:e.info,servers:e.servers,authConfig:e.authConfig!==void 0?e.authConfig:t?.getAuthenticationConfig()??void 0,securitySchemes:e.securitySchemes}));let l=JSON.stringify(o);i.response.writeHead(200,{"content-type":"application/json","content-length":Buffer.byteLength(l).toString()}),i.response.end(l);return}await a()}}export{O as SchemaRegistry,S as convertSchema,re as createOpenApiEndpoint,$ as generateAsyncApiSpec,R as generateOpenApiSpec,E as mapOperationSecurity,B as mapSecuritySchemes,P as resolvePath,oe as serveAsyncApi,se as serveOpenApi,h as walkSchemas,X as writeOpenApiSpec};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/generateOpenApiSpec.ts","../src/pathUtils.ts","../src/schemaConverter.ts","../src/schemaRegistry.ts","../src/securityMapper.ts","../src/generateAsyncApiSpec.ts","../src/openApiEndpoint.ts","../src/serveAsyncApi.ts","../src/serveOpenApi.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n generateOpenApiSpec,\n type OpenApiDocument,\n type OpenApiOptions\n} from './generateOpenApiSpec.js';\n\n/**\n * Generate an OpenAPI spec and write it to a file.\n *\n * @param options - Same options as `generateOpenApiSpec()`.\n * @param outputPath - File path to write the JSON spec to.\n * @returns The generated spec document.\n *\n * @example\n * ```ts\n * import { writeOpenApiSpec } from '@cleverbrush/server-openapi';\n * import { createServer, endpoint } from '@cleverbrush/server';\n *\n * const builder = createServer()\n * .handle(endpoint.get('/api/health'), () => ({ ok: true }));\n *\n * writeOpenApiSpec(\n * {\n * registrations: builder.getRegistrations(),\n * info: { title: 'My API', version: '1.0.0' }\n * },\n * './openapi.json'\n * );\n * ```\n */\nexport function writeOpenApiSpec(\n options: OpenApiOptions,\n outputPath: string\n): OpenApiDocument {\n const spec = generateOpenApiSpec(options);\n const dir = path.dirname(outputPath);\n if (dir && !fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(outputPath, JSON.stringify(spec, null, 2), 'utf-8');\n return spec;\n}\n","import {\n ObjectSchemaBuilder,\n type SchemaBuilder,\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n} from '@cleverbrush/schema';\nimport type {\n AuthenticationConfig,\n EndpointMetadata,\n EndpointRegistration,\n WebhookDefinition\n} from '@cleverbrush/server';\nimport { resolvePath } from './pathUtils.js';\nimport { convertSchema } from './schemaConverter.js';\nimport { SchemaRegistry, walkSchemas } from './schemaRegistry.js';\nimport {\n mapOperationSecurity,\n mapSecuritySchemes,\n type OpenApiSecurityScheme\n} from './securityMapper.js';\n\n// ---------------------------------------------------------------------------\n// Server interface (structural — avoids hard dependency on ServerBuilder class)\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal interface for a `@cleverbrush/server` server instance. Matches\n * the relevant subset of `ServerBuilder` so that callers can pass the\n * server directly without importing the class.\n */\nexport interface OpenApiServer_ServerLike {\n getRegistrations(): readonly EndpointRegistration[];\n getAuthenticationConfig(): AuthenticationConfig | null;\n getWebhooks(): readonly WebhookDefinition[];\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * API metadata included in the OpenAPI `info` object.\n * Maps directly to the OpenAPI 3.1 Info Object.\n */\nexport interface OpenApiInfo {\n readonly title: string;\n readonly version: string;\n readonly description?: string;\n readonly termsOfService?: string;\n readonly contact?: {\n readonly name?: string;\n readonly url?: string;\n readonly email?: string;\n };\n readonly license?: {\n readonly name: string;\n readonly url?: string;\n };\n}\n\n/**\n * A server entry in the OpenAPI `servers` array.\n * Describes a base URL where the API is accessible.\n */\nexport interface OpenApiServer {\n readonly url: string;\n readonly description?: string;\n}\n\n/**\n * A tag entry in the OpenAPI top-level `tags` array.\n * Provides a name, optional description, and optional external documentation\n * for a tag group.\n *\n * @see https://spec.openapis.org/oas/v3.1.0#tag-object\n */\nexport interface OpenApiTag {\n /** Tag name. Must match the tag strings used on individual operations. */\n readonly name: string;\n /** Short description for the tag group, displayed in Swagger UI / Redoc. */\n readonly description?: string;\n /** Link to external documentation for this tag. */\n readonly externalDocs?: {\n readonly url: string;\n readonly description?: string;\n };\n}\n\n/**\n * Options passed to {@link generateOpenApiSpec}.\n *\n * When `server` is provided, `registrations` and `authConfig` are derived\n * from it automatically (unless explicitly overridden).\n */\nexport interface OpenApiOptions {\n /**\n * A `ServerBuilder` (or any object implementing the same methods).\n * When set, `registrations`, `authConfig`, and `webhooks` are\n * automatically read from the server instance. Explicit values for\n * those fields take precedence over the server-derived ones.\n */\n readonly server?: OpenApiServer_ServerLike;\n readonly registrations?: readonly EndpointRegistration[];\n readonly info: OpenApiInfo;\n readonly servers?: readonly OpenApiServer[];\n readonly authConfig?: AuthenticationConfig | null;\n readonly securitySchemes?: Record<string, OpenApiSecurityScheme>;\n /**\n * Top-level tag definitions with optional descriptions and external docs.\n *\n * When provided, these entries are emitted as the top-level `tags` array.\n * Any tag names used by registered endpoints but absent from this list are\n * automatically appended as name-only entries (sorted alphabetically).\n *\n * When omitted, unique tag names are still auto-collected from all\n * registered endpoints and emitted as name-only entries.\n *\n * @see https://spec.openapis.org/oas/v3.1.0#tag-object\n */\n readonly tags?: readonly OpenApiTag[];\n /**\n * Webhook definitions to emit in the top-level `webhooks` map of the\n * generated OpenAPI document.\n *\n * Webhooks are not served as HTTP routes — they merely document async\n * out-of-band requests that your API sends to subscribers.\n *\n * @see https://spec.openapis.org/oas/v3.1.0#fixed-fields\n */\n readonly webhooks?: readonly WebhookDefinition[];\n}\n\n// ---------------------------------------------------------------------------\n// OpenAPI Document (partial typing — plain objects for flexibility)\n// ---------------------------------------------------------------------------\n\n/**\n * A generated OpenAPI 3.1 document. Typed as a plain object map to allow\n * any extension fields without requiring a full OpenAPI type library.\n */\nexport type OpenApiDocument = Record<string, unknown>;\n\n// ---------------------------------------------------------------------------\n// Generator\n// ---------------------------------------------------------------------------\n\nfunction buildParameterObject(\n name: string,\n location: 'query' | 'header' | 'path',\n schema: Record<string, unknown>,\n required: boolean,\n description?: string\n): Record<string, unknown> {\n const param: Record<string, unknown> = {\n name,\n in: location,\n schema\n };\n if (required) param['required'] = true;\n if (description) param['description'] = description;\n return param;\n}\n\nfunction buildRequestBody(\n bodySchema: SchemaBuilder<any, any, any, any, any>,\n registry: SchemaRegistry,\n example?: unknown | null,\n examples?: Record<\n string,\n { summary?: string; description?: string; value: unknown }\n > | null\n): Record<string, unknown> {\n const jsonSchema = convertSchema(bodySchema, registry);\n const bodyInfo = bodySchema.introspect() as any;\n const mediaType: Record<string, unknown> = { schema: jsonSchema };\n if (example != null) {\n mediaType['example'] = example;\n } else if (examples != null) {\n mediaType['examples'] = examples;\n }\n const body: Record<string, unknown> = {\n required: bodyInfo.isRequired !== false,\n content: {\n 'application/json': mediaType\n }\n };\n if (typeof bodyInfo.description === 'string' && bodyInfo.description !== '')\n body['description'] = bodyInfo.description;\n return body;\n}\n\n// Default descriptions for common HTTP status codes\nconst HTTP_STATUS_DESCRIPTIONS: Record<number, string> = {\n 200: 'OK',\n 201: 'Created',\n 202: 'Accepted',\n 204: 'No Content',\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 409: 'Conflict',\n 422: 'Unprocessable Entity',\n 500: 'Internal Server Error'\n};\n\n// Minimal inline schema for ProblemDetails error responses\nconst PROBLEM_DETAILS_SCHEMA = {\n type: 'object',\n properties: {\n status: { type: 'integer' },\n title: { type: 'string' },\n detail: { type: 'string' }\n }\n};\n\nfunction buildResponseHeaders(\n schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n registry: SchemaRegistry\n): Record<string, unknown> {\n const info = schema.introspect() as any;\n const props: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > = info.properties ?? {};\n const headers: Record<string, unknown> = {};\n for (const [name, propSchema] of Object.entries(props)) {\n const propInfo = propSchema.introspect() as any;\n const header: Record<string, unknown> = {\n schema: convertSchema(propSchema, registry)\n };\n if (\n typeof propInfo.description === 'string' &&\n propInfo.description !== ''\n ) {\n header['description'] = propInfo.description;\n }\n headers[name] = header;\n }\n return headers;\n}\n\nfunction buildResponses(\n meta: EndpointMetadata,\n method: string,\n registry: SchemaRegistry\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n // Binary file response — takes precedence over JSON schema\n if (meta.producesFile) {\n const ct = meta.producesFile.contentType ?? 'application/octet-stream';\n const desc = meta.producesFile.description ?? 'File download';\n result['200'] = {\n description: desc,\n content: {\n [ct]: { schema: { type: 'string', format: 'binary' } }\n }\n };\n } else if (meta.responsesSchemas) {\n for (const [codeStr, schema] of Object.entries(meta.responsesSchemas)) {\n const code = Number(codeStr);\n const desc =\n HTTP_STATUS_DESCRIPTIONS[code] ?? `Response ${codeStr}`;\n if (schema) {\n const jsonSchema = convertSchema(schema, registry);\n const respInfo = schema.introspect() as any;\n const customDesc =\n typeof respInfo.description === 'string' &&\n respInfo.description !== ''\n ? respInfo.description\n : desc;\n result[codeStr] = {\n description: customDesc,\n content: { 'application/json': { schema: jsonSchema } }\n };\n } else {\n result[codeStr] = { description: desc };\n }\n }\n } else if (meta.responseSchema) {\n // Legacy single-code path — .returns() was called\n const jsonSchema = convertSchema(meta.responseSchema, registry);\n const respInfo = meta.responseSchema.introspect() as any;\n const desc =\n typeof respInfo.description === 'string' &&\n respInfo.description !== ''\n ? respInfo.description\n : 'Successful response';\n result['200'] = {\n description: desc,\n content: { 'application/json': { schema: jsonSchema } }\n };\n } else if (method === 'DELETE' || method === 'HEAD') {\n result['204'] = { description: 'No content' };\n } else {\n result['200'] = { description: 'Successful response' };\n }\n\n // Auto-add framework-generated error responses\n if (meta.bodySchema && !result['422']) {\n result['422'] = {\n description: 'Validation error',\n content: {\n 'application/problem+json': { schema: PROBLEM_DETAILS_SCHEMA }\n }\n };\n }\n\n if (meta.authRoles !== null) {\n if (!result['401']) {\n result['401'] = {\n description: 'Unauthorized',\n content: {\n 'application/problem+json': {\n schema: PROBLEM_DETAILS_SCHEMA\n }\n }\n };\n }\n if (!result['403']) {\n result['403'] = {\n description: 'Forbidden',\n content: {\n 'application/problem+json': {\n schema: PROBLEM_DETAILS_SCHEMA\n }\n }\n };\n }\n }\n\n // Multiple content types — augment each response's content map with extra\n // MIME types from .produces(). producesFile already handled above (binary wins).\n if (meta.produces && !meta.producesFile) {\n for (const code of Object.keys(result)) {\n const entry = result[code] as Record<string, unknown>;\n const existingContent = entry['content'] as\n | Record<string, unknown>\n | undefined;\n // Only augment success responses that already have a content map\n // (skip error responses whose content is application/problem+json)\n if (\n !existingContent ||\n existingContent['application/problem+json']\n ) {\n continue;\n }\n const baseSchema =\n (existingContent['application/json'] as Record<string, unknown>)\n ?.schema ?? {};\n for (const [mimeType, typeEntry] of Object.entries(meta.produces)) {\n if (mimeType === 'application/json') continue;\n existingContent[mimeType] = {\n schema: typeEntry.schema\n ? convertSchema(typeEntry.schema, registry)\n : baseSchema\n };\n }\n }\n }\n\n // Response headers — inject into every response code when declared\n if (meta.responseHeaderSchema) {\n const headers = buildResponseHeaders(\n meta.responseHeaderSchema,\n registry\n );\n for (const code of Object.keys(result)) {\n (result[code] as Record<string, unknown>)['headers'] = headers;\n }\n }\n\n // Links — attach to the lowest 2xx response\n if (meta.links) {\n const linksOut: Record<string, unknown> = {};\n for (const [linkName, linkDef] of Object.entries(meta.links)) {\n const link: Record<string, unknown> = {\n operationId: linkDef.operationId\n };\n if (linkDef.description) link['description'] = linkDef.description;\n if (linkDef.requestBody) link['requestBody'] = linkDef.requestBody;\n if (linkDef.parameters) {\n let params: Record<string, unknown>;\n if (\n typeof linkDef.parameters === 'function' &&\n meta.responseSchema\n ) {\n const tree = ObjectSchemaBuilder.getPropertiesFor(\n meta.responseSchema as ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n );\n const raw = (\n linkDef.parameters as (\n r: any\n ) => Record<string, unknown>\n )(tree);\n params = {};\n for (const [paramName, value] of Object.entries(raw)) {\n if (\n value &&\n typeof value === 'object' &&\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR in\n (value as object)\n ) {\n const pointer = (value as any)[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ].toJsonPointer();\n params[paramName] = `$response.body#${pointer}`;\n } else {\n params[paramName] = value;\n }\n }\n } else {\n params = linkDef.parameters as Record<string, unknown>;\n }\n link['parameters'] = params;\n }\n linksOut[linkName] = link;\n }\n const successCode = Object.keys(result)\n .filter(k => Number(k) >= 200 && Number(k) < 300)\n .sort()[0];\n if (successCode) {\n (result[successCode] as Record<string, unknown>)['links'] =\n linksOut;\n }\n }\n\n return result;\n}\n\nfunction buildOperation(\n meta: EndpointMetadata,\n pathParams: { name: string; schema: Record<string, unknown> }[],\n securitySchemeNames: string[],\n registry: SchemaRegistry\n): Record<string, unknown> {\n const operation: Record<string, unknown> = {};\n\n // Metadata\n if (meta.summary) operation['summary'] = meta.summary;\n if (meta.description) operation['description'] = meta.description;\n if (meta.tags.length > 0) operation['tags'] = [...meta.tags];\n if (meta.operationId) operation['operationId'] = meta.operationId;\n if (meta.deprecated) operation['deprecated'] = true;\n\n // Parameters\n const parameters: Record<string, unknown>[] = [];\n\n // Path parameters\n for (const pp of pathParams) {\n parameters.push(buildParameterObject(pp.name, 'path', pp.schema, true));\n }\n\n // Query parameters\n if (meta.querySchema) {\n const queryInfo = meta.querySchema.introspect() as any;\n const props: Record<\n string,\n SchemaBuilder<any, any, any>\n > = queryInfo.properties ?? {};\n for (const [name, propSchema] of Object.entries(props)) {\n const propInfo = propSchema.introspect() as any;\n const isRequired = propInfo.isRequired !== false;\n const description =\n typeof propInfo.description === 'string' &&\n propInfo.description !== ''\n ? propInfo.description\n : undefined;\n parameters.push(\n buildParameterObject(\n name,\n 'query',\n convertSchema(propSchema, registry),\n isRequired,\n description\n )\n );\n }\n }\n\n // Header parameters\n if (meta.headerSchema) {\n const headerInfo = meta.headerSchema.introspect() as any;\n const props: Record<\n string,\n SchemaBuilder<any, any, any>\n > = headerInfo.properties ?? {};\n for (const [name, propSchema] of Object.entries(props)) {\n const propInfo = propSchema.introspect() as any;\n const isRequired = propInfo.isRequired !== false;\n const description =\n typeof propInfo.description === 'string' &&\n propInfo.description !== ''\n ? propInfo.description\n : undefined;\n parameters.push(\n buildParameterObject(\n name,\n 'header',\n convertSchema(propSchema, registry),\n isRequired,\n description\n )\n );\n }\n }\n\n if (parameters.length > 0) operation['parameters'] = parameters;\n\n // Request body\n if (meta.bodySchema) {\n operation['requestBody'] = buildRequestBody(\n meta.bodySchema,\n registry,\n meta.example,\n meta.examples\n );\n }\n\n // Responses\n operation['responses'] = buildResponses(\n meta,\n meta.method.toUpperCase(),\n registry\n );\n\n // Security\n const security = mapOperationSecurity(authRoles(meta), securitySchemeNames);\n if (security.length > 0) operation['security'] = security;\n\n // External documentation\n if (meta.externalDocs) {\n const ed: Record<string, unknown> = { url: meta.externalDocs.url };\n if (meta.externalDocs.description)\n ed['description'] = meta.externalDocs.description;\n operation['externalDocs'] = ed;\n }\n\n // Callbacks\n if (meta.callbacks) {\n const callbacksOut: Record<string, unknown> = {};\n for (const [cbName, cbDef] of Object.entries(meta.callbacks)) {\n // Determine URL expression\n let expression: string;\n if (cbDef.expression) {\n expression = cbDef.expression;\n } else if (cbDef.urlFrom && meta.bodySchema) {\n const tree = ObjectSchemaBuilder.getPropertiesFor(\n meta.bodySchema as ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n );\n const selected = cbDef.urlFrom(tree);\n if (\n selected &&\n typeof selected === 'object' &&\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR in (selected as object)\n ) {\n const pointer = (selected as any)[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ].toJsonPointer();\n expression = `{$request.body#${pointer}}`;\n } else {\n expression = String(\n selected ?? '{$request.body#/callbackUrl}'\n );\n }\n } else {\n expression = '{$request.body#/callbackUrl}';\n }\n\n const cbMethod = (cbDef.method ?? 'post').toLowerCase();\n const cbOperation: Record<string, unknown> = {};\n if (cbDef.summary) cbOperation['summary'] = cbDef.summary;\n if (cbDef.description)\n cbOperation['description'] = cbDef.description;\n if (cbDef.body) {\n cbOperation['requestBody'] = buildRequestBody(\n cbDef.body,\n registry\n );\n }\n const cbResponses: Record<string, unknown> = {};\n if (cbDef.response) {\n cbResponses['200'] = {\n description: 'OK',\n content: {\n 'application/json': {\n schema: convertSchema(cbDef.response, registry)\n }\n }\n };\n } else {\n cbResponses['200'] = { description: 'OK' };\n }\n cbOperation['responses'] = cbResponses;\n callbacksOut[cbName] = {\n [expression]: { [cbMethod]: cbOperation }\n };\n }\n operation['callbacks'] = callbacksOut;\n }\n\n return operation;\n}\n\nfunction authRoles(meta: EndpointMetadata): readonly string[] | null {\n return meta.authRoles;\n}\n\n/**\n * Generate an OpenAPI 3.1 specification document from registered endpoints.\n */\nexport function generateOpenApiSpec(options: OpenApiOptions): OpenApiDocument {\n const srv = options.server;\n const registrations =\n options.registrations ?? srv?.getRegistrations() ?? [];\n const authConfig =\n options.authConfig !== undefined\n ? options.authConfig\n : (srv?.getAuthenticationConfig() ?? undefined);\n const webhooks =\n options.webhooks ??\n (srv && srv.getWebhooks().length > 0 ? srv.getWebhooks() : undefined);\n const { info, servers, securitySchemes, tags } = options;\n\n // Security schemes — from explicit config or auto-mapped\n const resolvedSchemes: Record<string, OpenApiSecurityScheme> =\n securitySchemes ?? mapSecuritySchemes(authConfig);\n const securitySchemeNames = Object.keys(resolvedSchemes);\n\n // Pre-pass: collect all named schemas from every endpoint into a registry.\n // Walking happens before path generation so that $ref pointers are emitted\n // correctly at every call site within buildOperation.\n const registry = new SchemaRegistry();\n const visited = new Set<SchemaBuilder<any, any, any>>();\n for (const reg of registrations) {\n const meta = reg.endpoint;\n if (meta.bodySchema) walkSchemas(meta.bodySchema, registry, visited);\n if (meta.responseSchema)\n walkSchemas(meta.responseSchema, registry, visited);\n if (meta.responsesSchemas) {\n for (const schema of Object.values(meta.responsesSchemas)) {\n if (schema) walkSchemas(schema, registry, visited);\n }\n }\n if (meta.querySchema) {\n const queryProps =\n (meta.querySchema.introspect() as any).properties ?? {};\n for (const propSchema of Object.values<\n SchemaBuilder<any, any, any>\n >(queryProps)) {\n walkSchemas(propSchema, registry, visited);\n }\n }\n if (meta.headerSchema) {\n const headerProps =\n (meta.headerSchema.introspect() as any).properties ?? {};\n for (const propSchema of Object.values<\n SchemaBuilder<any, any, any>\n >(headerProps)) {\n walkSchemas(propSchema, registry, visited);\n }\n }\n if (meta.callbacks) {\n for (const cbDef of Object.values(meta.callbacks)) {\n if (cbDef.body) walkSchemas(cbDef.body, registry, visited);\n if (cbDef.response)\n walkSchemas(cbDef.response, registry, visited);\n }\n }\n }\n if (webhooks) {\n for (const webhook of webhooks) {\n if (webhook.body) walkSchemas(webhook.body, registry, visited);\n if (webhook.response)\n walkSchemas(webhook.response, registry, visited);\n }\n }\n\n const resolveComponentSchemaName = (\n rootSchema: SchemaBuilder<any, any, any>\n ) => {\n // The root schema must be inlined exactly once — for the component\n // definition itself. Any subsequent encounter (e.g. through a lazy\n // self-reference) should emit a $ref instead of inlining again,\n // which would otherwise cause infinite recursion.\n let inlinedRoot = false;\n return (\n candidate: SchemaBuilder<any, any, any>\n ): string | undefined => {\n if (candidate === rootSchema && !inlinedRoot) {\n inlinedRoot = true;\n return undefined; // inline the root definition once\n }\n return registry.getName(candidate) ?? undefined;\n };\n };\n\n // Build paths\n const paths: Record<string, Record<string, unknown>> = {};\n\n for (const reg of registrations) {\n const meta = reg.endpoint;\n const { path, parameters: pathParams } = resolvePath(meta);\n const method = meta.method.toLowerCase();\n\n if (!paths[path]) paths[path] = {};\n paths[path][method] = buildOperation(\n meta,\n pathParams as { name: string; schema: Record<string, unknown> }[],\n securitySchemeNames,\n registry\n );\n }\n\n // Build top-level tags array: explicit entries first, then auto-collected\n // tag names from endpoints that are not already covered.\n const explicitNames = new Set((tags ?? []).map(t => t.name));\n const autoNames: string[] = [];\n for (const reg of registrations) {\n for (const tag of reg.endpoint.tags) {\n if (!explicitNames.has(tag) && !autoNames.includes(tag)) {\n autoNames.push(tag);\n }\n }\n }\n autoNames.sort();\n const mergedTags: OpenApiTag[] = [\n ...(tags ?? []),\n ...autoNames.map(name => ({ name }))\n ];\n\n // Assemble document\n const doc: OpenApiDocument = {\n openapi: '3.1.0',\n info: { ...info }\n };\n\n if (servers && servers.length > 0) {\n doc['servers'] = servers.map(s => ({ ...s }));\n }\n\n if (mergedTags.length > 0) {\n doc['tags'] = mergedTags.map(t => ({ ...t }));\n }\n\n doc['paths'] = paths;\n\n // Webhooks — out-of-band async requests documented alongside paths\n if (webhooks && webhooks.length > 0) {\n const webhooksObj: Record<string, unknown> = {};\n for (const webhook of webhooks) {\n const whMethod = (webhook.method ?? 'post').toLowerCase();\n const whOperation: Record<string, unknown> = {};\n if (webhook.summary) whOperation['summary'] = webhook.summary;\n if (webhook.description)\n whOperation['description'] = webhook.description;\n if (webhook.tags && webhook.tags.length > 0)\n whOperation['tags'] = [...webhook.tags];\n if (webhook.body) {\n whOperation['requestBody'] = buildRequestBody(\n webhook.body,\n registry\n );\n }\n const whResponses: Record<string, unknown> = {};\n if (webhook.response) {\n whResponses['200'] = {\n description: 'OK',\n content: {\n 'application/json': {\n schema: convertSchema(webhook.response, registry)\n }\n }\n };\n } else {\n whResponses['200'] = { description: 'OK' };\n }\n whOperation['responses'] = whResponses;\n webhooksObj[webhook.name] = { [whMethod]: whOperation };\n }\n doc['webhooks'] = webhooksObj;\n }\n\n // Components — security schemes + named component schemas\n const componentSchemas: Record<string, unknown> = {};\n for (const [name, schema] of registry.entries()) {\n // Inline the root schema to avoid a self-referential $ref, but resolve\n // nested named schemas through the shared registry so component\n // definitions can still deduplicate via $ref.\n componentSchemas[name] = convertSchema(\n schema,\n resolveComponentSchemaName(schema)\n );\n }\n const hasSchemas = Object.keys(componentSchemas).length > 0;\n\n if (securitySchemeNames.length > 0 || hasSchemas) {\n const components: Record<string, unknown> = {};\n if (securitySchemeNames.length > 0)\n components['securitySchemes'] = { ...resolvedSchemes };\n if (hasSchemas) components['schemas'] = componentSchemas;\n doc['components'] = components;\n }\n\n return doc;\n}\n","import type {\n ParseStringSchemaBuilder,\n SchemaBuilder\n} from '@cleverbrush/schema';\nimport { toJsonSchema } from '@cleverbrush/schema-json';\nimport type { EndpointMetadata } from '@cleverbrush/server';\n\ntype RoutePath = string | ParseStringSchemaBuilder<any, any, any, any, any>;\n\nfunction isParseStringSchema(\n p: RoutePath\n): p is ParseStringSchemaBuilder<any, any, any, any, any> {\n return typeof p !== 'string' && typeof (p as any).validate === 'function';\n}\n\n/**\n * Describes a single path parameter extracted from a route template,\n * including its name and the JSON Schema representation of its type.\n */\nexport interface PathParameterInfo {\n readonly name: string;\n readonly schema: Record<string, unknown>;\n}\n\n/**\n * The output of resolving a route path template to an OpenAPI-compatible\n * path string and its parameter list.\n */\nexport interface ResolvedPath {\n /** OpenAPI-formatted path, e.g. `/api/users/{id}` */\n readonly path: string;\n /** Extracted path parameters with their JSON Schema */\n readonly parameters: readonly PathParameterInfo[];\n}\n\n/**\n * Convert a colon-style static path to OpenAPI `{param}` format.\n * E.g. `/users/:id/posts/:pid` → `/users/{id}/posts/{pid}`\n */\nfunction convertColonParams(path: string): {\n converted: string;\n paramNames: string[];\n} {\n const paramNames: string[] = [];\n const converted = path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, name) => {\n paramNames.push(name);\n return `{${name}}`;\n });\n return { converted, paramNames };\n}\n\n/**\n * Convert a `ParseStringSchemaBuilder` path template to an OpenAPI-formatted\n * path string and extract parameter schemas.\n */\nfunction convertParseStringPath(\n pathTemplate: ParseStringSchemaBuilder<any, any, any, any, any>\n): { pathString: string; parameters: PathParameterInfo[] } {\n const info = pathTemplate.introspect() as any;\n const templateDef = info.templateDefinition as {\n literals: string[];\n segments: { path: string }[];\n };\n const objectSchema = info.objectSchema;\n const objInfo = objectSchema.introspect() as any;\n const props: Record<\n string,\n SchemaBuilder<any, any, any>\n > = objInfo.properties ?? {};\n\n let pathString = '';\n for (let i = 0; i < templateDef.segments.length; i++) {\n pathString +=\n templateDef.literals[i] + `{${templateDef.segments[i].path}}`;\n }\n pathString += templateDef.literals[templateDef.segments.length] ?? '';\n\n const parameters: PathParameterInfo[] = templateDef.segments.map(seg => ({\n name: seg.path,\n schema: props[seg.path]\n ? toJsonSchema(props[seg.path], { $schema: false })\n : { type: 'string' }\n }));\n\n return { pathString, parameters };\n}\n\n/**\n * Combines `basePath` and `pathTemplate` from an endpoint into an\n * OpenAPI-formatted path with extracted parameter information.\n */\nexport function resolvePath(meta: EndpointMetadata): ResolvedPath {\n const basePath = meta.basePath.replace(/\\/$/, '');\n const pathTemplate = meta.pathTemplate;\n\n if (isParseStringSchema(pathTemplate)) {\n const { pathString, parameters } = convertParseStringPath(pathTemplate);\n const fullPath = normalizeSlashes(basePath + pathString);\n return { path: fullPath, parameters };\n }\n\n // Static string path template\n const templateStr = pathTemplate === '/' ? '' : pathTemplate;\n const combined = basePath + templateStr;\n const { converted, paramNames } = convertColonParams(combined);\n const fullPath = normalizeSlashes(converted);\n\n const parameters: PathParameterInfo[] = paramNames.map(name => ({\n name,\n schema: { type: 'string' }\n }));\n\n return { path: fullPath, parameters };\n}\n\nfunction normalizeSlashes(path: string): string {\n // Replace double slashes with single, ensure leading slash\n let result = path.replace(/\\/+/g, '/');\n if (!result.startsWith('/')) result = '/' + result;\n if (result.length > 1 && result.endsWith('/')) result = result.slice(0, -1);\n return result || '/';\n}\n","import type { SchemaBuilder } from '@cleverbrush/schema';\nimport { toJsonSchema } from '@cleverbrush/schema-json';\nimport type { SchemaRegistry } from './schemaRegistry.js';\n\ntype NameResolver = (\n schema: SchemaBuilder<any, any, any>\n) => string | null | undefined;\n\n/**\n * Converts a `@cleverbrush/schema` builder to a JSON Schema object suitable\n * for embedding in an OpenAPI 3.1 spec (no `$schema` header, Draft 2020-12).\n *\n * Returns an empty schema `{}` when the input is `null` or `undefined`.\n *\n * When a {@link SchemaRegistry} or a custom resolver function is provided, any\n * schema instance that resolves to a name will be emitted as a\n * `$ref: '#/components/schemas/<name>'` pointer instead of being inlined.\n *\n * @param schema - The schema to convert, or `null`/`undefined`.\n * @param registry - Optional registry or resolver function for `$ref` deduplication.\n */\nexport function convertSchema(\n schema: SchemaBuilder<any, any, any, any, any> | null | undefined,\n registry?: SchemaRegistry | NameResolver\n): Record<string, unknown> {\n if (schema == null) return {};\n\n let nameResolver:\n | ((s: SchemaBuilder<any, any, any>) => string | null)\n | undefined;\n if (registry) {\n if (typeof registry === 'function') {\n nameResolver = s => registry(s) ?? null;\n } else {\n nameResolver = s => registry.getName(s);\n }\n }\n\n return toJsonSchema(schema, {\n $schema: false,\n draft: '2020-12',\n nameResolver\n });\n}\n","import type { SchemaBuilder } from '@cleverbrush/schema';\n\n// ---------------------------------------------------------------------------\n// SchemaRegistry\n// ---------------------------------------------------------------------------\n\n/**\n * Collects schemas that carry an explicit component name (set via\n * `.schemaName()`) and provides a reference-based lookup used during OpenAPI\n * spec generation to replace inline schema objects with\n * `$ref: '#/components/schemas/<name>'` pointers.\n *\n * **Conflict rule**: registering two *different* schema instances (different\n * object references) under the same name throws immediately. Re-registering\n * the same instance is a no-op.\n *\n * @example\n * ```ts\n * const registry = new SchemaRegistry();\n * registry.register(UserSchema); // UserSchema.schemaName('User')\n * registry.getName(UserSchema); // 'User'\n * registry.getName(someOtherSchema); // null\n * ```\n */\nexport class SchemaRegistry {\n /** schema instance → registered name */\n private readonly byInstance = new Map<\n SchemaBuilder<any, any, any>,\n string\n >();\n /** name → first-registered schema instance */\n private readonly byName = new Map<string, SchemaBuilder<any, any, any>>();\n\n /**\n * Attempts to register `schema` in the registry.\n *\n * - If the schema has no `schemaName` in its introspect output, it is\n * silently skipped.\n * - If the same instance is already registered, this is a no-op.\n * - If a **different** instance is already registered under the same name,\n * an error is thrown.\n *\n * @param schema - The schema builder to register.\n * @throws {Error} When two distinct schema instances share the same name.\n */\n register(schema: SchemaBuilder<any, any, any>): void {\n const name = (schema.introspect() as any).schemaName as\n | string\n | undefined;\n if (typeof name !== 'string') return;\n\n const existing = this.byName.get(name);\n if (existing !== undefined) {\n // Same instance → idempotent, nothing to do\n if (existing === schema) return;\n // Different instance → conflict\n throw new Error(\n `Schema name \"${name}\" is already registered by a different schema instance. ` +\n `Each named schema must be a single, reused constant. ` +\n `If you intended to register the same schema, ensure you are passing ` +\n `the same object reference (not a rebuilt schema).`\n );\n }\n\n this.byInstance.set(schema, name);\n this.byName.set(name, schema);\n }\n\n /**\n * Returns the component name for a given schema instance, or `null` if it\n * was not registered.\n *\n * @param schema - The schema builder to look up.\n * @returns The registered name, or `null`.\n */\n getName(schema: SchemaBuilder<any, any, any>): string | null {\n return this.byInstance.get(schema) ?? null;\n }\n\n /**\n * Iterates over all registered `[name, schema]` pairs in insertion order.\n *\n * Used to emit the `components.schemas` section of an OpenAPI document.\n */\n entries(): IterableIterator<[string, SchemaBuilder<any, any, any>]> {\n return this.byName.entries();\n }\n\n /** Returns `true` when at least one schema has been registered. */\n get isEmpty(): boolean {\n return this.byName.size === 0;\n }\n}\n\n// ---------------------------------------------------------------------------\n// walkSchemas\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively visits every {@link SchemaBuilder} reachable from `schema` and\n * calls {@link SchemaRegistry.register} on each node.\n *\n * Cycle detection is performed via a `visited` `Set` of object references, so\n * schemas may safely be shared across multiple branches without causing\n * infinite recursion.\n *\n * **Excluded schema types**\n * - `lazy` — deferred resolution would require calling the getter, which may\n * itself reference the parent schema; lazy schemas are handled separately.\n *\n * @param schema - Root schema to start the walk from.\n * @param registry - Registry to register named schemas into.\n * @param visited - Shared set for cycle detection; pass a new `Set()` for the\n * top-level call.\n */\nexport function walkSchemas(\n schema: SchemaBuilder<any, any, any>,\n registry: SchemaRegistry,\n visited: Set<SchemaBuilder<any, any, any>> = new Set()\n): void {\n if (visited.has(schema)) return;\n visited.add(schema);\n\n registry.register(schema);\n\n const info = schema.introspect() as any;\n\n switch (info.type) {\n case 'object': {\n const props = info.properties as\n | Record<string, SchemaBuilder<any, any, any>>\n | undefined;\n if (props) {\n for (const child of Object.values(props)) {\n walkSchemas(child, registry, visited);\n }\n }\n break;\n }\n case 'array':\n if (info.elementSchema) {\n walkSchemas(info.elementSchema, registry, visited);\n }\n break;\n case 'tuple': {\n const elements: SchemaBuilder<any, any, any>[] =\n info.elements ?? [];\n for (const el of elements) {\n walkSchemas(el, registry, visited);\n }\n if (info.restSchema) {\n walkSchemas(info.restSchema, registry, visited);\n }\n break;\n }\n case 'union': {\n const options: SchemaBuilder<any, any, any>[] = info.options ?? [];\n for (const opt of options) {\n walkSchemas(opt, registry, visited);\n }\n break;\n }\n case 'record':\n if (info.keySchema) {\n walkSchemas(info.keySchema, registry, visited);\n }\n if (info.valueSchema) {\n walkSchemas(info.valueSchema, registry, visited);\n }\n break;\n case 'lazy': {\n // Resolve the inner schema and walk it so that any named schema\n // reachable through a lazy boundary is registered. Cycle-safety is\n // provided by the `visited` set — the lazy wrapper itself was\n // already added above, preventing infinite recursion on\n // self-referential schemas.\n const resolved = (schema as any).resolve() as SchemaBuilder<\n any,\n any,\n any\n >;\n walkSchemas(resolved, registry, visited);\n break;\n }\n default:\n break;\n }\n}\n","import type { AuthenticationConfig } from '@cleverbrush/server';\n\n/**\n * An OpenAPI 3.1 Security Scheme Object.\n * Describes how requests to the API are authenticated.\n *\n * @see {@link https://spec.openapis.org/oas/v3.1.0#security-scheme-object OpenAPI Security Scheme Object}\n */\nexport interface OpenApiSecurityScheme {\n readonly type: string;\n readonly scheme?: string;\n readonly bearerFormat?: string;\n readonly in?: string;\n readonly name?: string;\n readonly flows?: Record<string, unknown>;\n readonly openIdConnectUrl?: string;\n}\n\n/**\n * Map `@cleverbrush/auth` authentication schemes to OpenAPI security scheme objects.\n */\nexport function mapSecuritySchemes(\n authConfig: AuthenticationConfig | null | undefined\n): Record<string, OpenApiSecurityScheme> {\n if (!authConfig) return {};\n\n const result: Record<string, OpenApiSecurityScheme> = {};\n for (const scheme of authConfig.schemes) {\n const name = scheme.name;\n const challenge = scheme.challenge?.();\n\n if ('openIdConnectUrl' in scheme && (scheme as any).openIdConnectUrl) {\n result[name] = {\n type: 'openIdConnect',\n openIdConnectUrl: (scheme as any).openIdConnectUrl\n };\n } else if ('flows' in scheme && (scheme as any).flows) {\n result[name] = {\n type: 'oauth2',\n flows: (scheme as any).flows\n };\n } else if (\n challenge?.headerValue?.toLowerCase().startsWith('bearer') ||\n name === 'jwt'\n ) {\n result[name] = {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT'\n };\n } else if (name === 'cookie' || (scheme as any).cookieName) {\n // Cookie scheme — extract cookie name from options if available\n const cookieName =\n (scheme as any).cookieName ??\n (scheme as any)._options?.cookieName ??\n 'session';\n result[name] = {\n type: 'apiKey',\n in: 'cookie',\n name: cookieName\n };\n } else if (challenge) {\n // Generic scheme with a challenge header\n result[name] = {\n type: 'http',\n scheme: challenge.headerValue.split(' ')[0].toLowerCase()\n };\n } else {\n // Fallback — treat as generic HTTP scheme\n result[name] = { type: 'http', scheme: name };\n }\n }\n return result;\n}\n\n/**\n * Map endpoint `authRoles` to an OpenAPI operation-level `security` array.\n *\n * - `null` → empty array (public endpoint, no security)\n * - `[]` → `[{ <schemeName>: [] }]` (any authenticated user)\n * - `['admin']` → `[{ <schemeName>: ['admin'] }]` (require specific roles)\n */\nexport function mapOperationSecurity(\n authRoles: readonly string[] | null,\n securitySchemeNames: string[]\n): Record<string, string[]>[] {\n if (authRoles === null) return [];\n if (securitySchemeNames.length === 0) return [];\n\n // Each security scheme listed as an option (OR semantics in OpenAPI)\n return securitySchemeNames.map(name => ({\n [name]: [...authRoles]\n }));\n}\n","import type { SchemaBuilder } from '@cleverbrush/schema';\nimport type { SubscriptionRegistration } from '@cleverbrush/server';\nimport { convertSchema } from './schemaConverter.js';\nimport { SchemaRegistry, walkSchemas } from './schemaRegistry.js';\n\n// ---------------------------------------------------------------------------\n// Server interface (structural — avoids hard dependency on ServerBuilder class)\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal interface for a `@cleverbrush/server` server instance. Matches\n * the relevant subset of `ServerBuilder` needed for AsyncAPI generation.\n */\nexport interface AsyncApiServer_ServerLike {\n getSubscriptionRegistrations(): readonly SubscriptionRegistration[];\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * API metadata included in the AsyncAPI `info` object.\n * Maps to the AsyncAPI 3.0 Info Object.\n */\nexport interface AsyncApiInfo {\n /** The title of the application. */\n readonly title: string;\n /** The application version. */\n readonly version: string;\n /** A short description of the application. */\n readonly description?: string;\n /** A URL to the Terms of Service. */\n readonly termsOfService?: string;\n /** Contact information for the application. */\n readonly contact?: {\n readonly name?: string;\n readonly url?: string;\n readonly email?: string;\n };\n /** License information for the application. */\n readonly license?: {\n readonly name: string;\n readonly url?: string;\n };\n}\n\n/**\n * A server entry in the AsyncAPI 3.0 `servers` map.\n * Describes a connection endpoint where the API is accessible.\n *\n * @see https://www.asyncapi.com/docs/reference/specification/v3.0.0#serverObject\n */\nexport interface AsyncApiServerEntry {\n /**\n * The host of the server (hostname + optional port).\n * @example `'api.example.com'`, `'localhost:3000'`\n */\n readonly host: string;\n /**\n * The protocol used to connect to the server.\n */\n readonly protocol: 'ws' | 'wss' | 'http' | 'https';\n /** An optional string describing the server. */\n readonly description?: string;\n /** The path component of the server URL. */\n readonly pathname?: string;\n}\n\n/**\n * Options passed to {@link generateAsyncApiSpec}.\n *\n * When `server` is provided, `subscriptions` are derived from it\n * automatically (unless explicitly overridden).\n */\nexport interface AsyncApiOptions {\n /**\n * A `ServerBuilder` (or any object implementing `getSubscriptionRegistrations()`).\n * When set, subscription registrations are automatically read from the\n * server instance. Explicit `subscriptions` values take precedence.\n */\n readonly server?: AsyncApiServer_ServerLike;\n /**\n * Subscription registrations to document.\n * When omitted, registrations are read from the `server` option.\n */\n readonly subscriptions?: readonly SubscriptionRegistration[];\n /** API metadata (title, version, description). */\n readonly info: AsyncApiInfo;\n /**\n * Server entries to include in the spec's `servers` map.\n * Keys are server IDs (e.g. `'production'`, `'staging'`).\n */\n readonly servers?: Record<string, AsyncApiServerEntry>;\n /**\n * Path prefix to strip from all channel addresses.\n * Useful when the subscription endpoints are mounted under a prefix\n * that differs from how the AsyncAPI doc should describe them.\n */\n readonly pathPrefix?: string;\n}\n\n/**\n * A generated AsyncAPI 3.0 document. Typed as a plain object map to allow\n * extension fields without requiring a full AsyncAPI type library.\n */\nexport type AsyncApiDocument = Record<string, unknown>;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Converts a URL path into a valid AsyncAPI channel ID.\n * Strips leading slash, replaces `/` and path-param braces with underscores.\n *\n * @example `'/ws/events'` → `'ws_events'`\n * @example `'/ws/rooms/{id}'` → `'ws_rooms_id'`\n */\nfunction pathToChannelId(path: string): string {\n return path\n .replace(/^\\//, '')\n .replace(/[/{}]/g, '_')\n .replace(/_+/g, '_')\n .replace(/_$/, '');\n}\n\n/**\n * Resolves the WebSocket address from a subscription's basePath and\n * pathTemplate. String templates with colon params (`:id`) are converted\n * to AsyncAPI `{id}` style.\n */\nfunction resolveSubscriptionAddress(\n basePath: string,\n pathTemplate: string | { serialize?: unknown; introspect?: () => unknown }\n): string {\n const base = basePath.replace(/\\/$/, '');\n\n if (typeof pathTemplate === 'string') {\n const tpl = pathTemplate === '/' ? '' : pathTemplate;\n const combined = base + tpl;\n // Colon params → {param}\n const converted = combined.replace(\n /:([a-zA-Z_][a-zA-Z0-9_]*)/g,\n '{$1}'\n );\n return normalizeSlashes(converted);\n }\n\n // ParseStringSchemaBuilder — extract templateDefinition\n const info = (pathTemplate as any).introspect?.() as any;\n const templateDef = info?.templateDefinition as\n | { literals: string[]; segments: { path: string }[] }\n | undefined;\n\n if (!templateDef) {\n return normalizeSlashes(base);\n }\n\n let pathString = '';\n for (let i = 0; i < templateDef.segments.length; i++) {\n pathString +=\n templateDef.literals[i] + `{${templateDef.segments[i].path}}`;\n }\n pathString += templateDef.literals[templateDef.segments.length] ?? '';\n\n return normalizeSlashes(base + pathString);\n}\n\nfunction normalizeSlashes(path: string): string {\n let result = path.replace(/\\/+/g, '/');\n if (!result.startsWith('/')) result = '/' + result;\n if (result.length > 1 && result.endsWith('/')) result = result.slice(0, -1);\n return result || '/';\n}\n\n// ---------------------------------------------------------------------------\n// Generator\n// ---------------------------------------------------------------------------\n\n/**\n * Generates an AsyncAPI 3.0 document from a set of WebSocket subscription\n * registrations.\n *\n * Each subscription becomes:\n * - A **channel** with its address and optional messages (for outgoing\n * server→client events and/or incoming client→server messages).\n * - One or two **operations**: a `send` operation for server→client events\n * and/or a `receive` operation for client→server messages.\n *\n * Named schemas (those with `.schemaName()` set) are automatically collected\n * and emitted as `components.schemas` entries with `$ref` pointers.\n *\n * @param options - Configuration including subscription registrations and API info.\n * @returns A plain AsyncAPI 3.0 document object suitable for `JSON.stringify`.\n *\n * @example\n * ```ts\n * import { generateAsyncApiSpec } from '@cleverbrush/server-openapi';\n * import { server } from './server.js';\n *\n * const spec = generateAsyncApiSpec({\n * server,\n * info: { title: 'My API', version: '1.0.0' },\n * servers: {\n * production: { host: 'api.example.com', protocol: 'wss' },\n * },\n * });\n * ```\n */\nexport function generateAsyncApiSpec(\n options: AsyncApiOptions\n): AsyncApiDocument {\n const subscriptions: readonly SubscriptionRegistration[] =\n options.subscriptions ??\n options.server?.getSubscriptionRegistrations() ??\n [];\n\n // Collect named schemas\n const registry = new SchemaRegistry();\n const visited = new Set<SchemaBuilder<any, any, any>>();\n for (const sub of subscriptions) {\n const m = sub.endpoint;\n if (m.incomingSchema) walkSchemas(m.incomingSchema, registry, visited);\n if (m.outgoingSchema) walkSchemas(m.outgoingSchema, registry, visited);\n }\n\n // Build channels and operations\n const channels: Record<string, unknown> = {};\n const operations: Record<string, unknown> = {};\n\n for (const sub of subscriptions) {\n const m = sub.endpoint;\n const address = resolveSubscriptionAddress(m.basePath, m.pathTemplate);\n\n // Channel ID: prefer operationId, otherwise derive from address\n const channelId = m.operationId ?? pathToChannelId(address);\n\n const messages: Record<string, unknown> = {};\n\n // Server→client events (outgoing) — the server SENDS these\n if (m.outgoingSchema) {\n const outInfo = m.outgoingSchema.introspect() as any;\n messages['ServerEvent'] = {\n name: 'ServerEvent',\n ...(outInfo.description\n ? { summary: outInfo.description }\n : {}),\n payload: convertSchema(m.outgoingSchema, registry)\n };\n }\n\n // Client→server messages (incoming) — the server RECEIVES these\n if (m.incomingSchema) {\n const inInfo = m.incomingSchema.introspect() as any;\n messages['ClientMessage'] = {\n name: 'ClientMessage',\n ...(inInfo.description ? { summary: inInfo.description } : {}),\n payload: convertSchema(m.incomingSchema, registry)\n };\n }\n\n const channel: Record<string, unknown> = { address };\n if (m.summary) channel.summary = m.summary;\n if (m.description) channel.description = m.description;\n if (m.deprecated) channel.deprecated = true;\n if (m.tags.length > 0) channel.tags = m.tags.map(t => ({ name: t }));\n if (m.externalDocs) channel.externalDocs = m.externalDocs;\n if (Object.keys(messages).length > 0) channel.messages = messages;\n\n channels[channelId] = channel;\n\n // Operation: server sends events to clients (server→client)\n if (m.outgoingSchema) {\n const opId = m.operationId\n ? `${m.operationId}Events`\n : `${channelId}_send`;\n operations[opId] = {\n action: 'send',\n channel: { $ref: `#/channels/${channelId}` },\n messages: [\n { $ref: `#/channels/${channelId}/messages/ServerEvent` }\n ]\n };\n }\n\n // Operation: server receives messages from clients (client→server)\n if (m.incomingSchema) {\n const opId = m.operationId\n ? `${m.operationId}Messages`\n : `${channelId}_receive`;\n operations[opId] = {\n action: 'receive',\n channel: { $ref: `#/channels/${channelId}` },\n messages: [\n { $ref: `#/channels/${channelId}/messages/ClientMessage` }\n ]\n };\n }\n }\n\n // Assemble the document\n const doc: Record<string, unknown> = {\n asyncapi: '3.0.0',\n info: {\n title: options.info.title,\n version: options.info.version,\n ...(options.info.description\n ? { description: options.info.description }\n : {}),\n ...(options.info.termsOfService\n ? { termsOfService: options.info.termsOfService }\n : {}),\n ...(options.info.contact ? { contact: options.info.contact } : {}),\n ...(options.info.license ? { license: options.info.license } : {})\n }\n };\n\n if (options.servers && Object.keys(options.servers).length > 0) {\n doc.servers = options.servers;\n }\n\n doc.channels = channels;\n doc.operations = operations;\n\n if (!registry.isEmpty) {\n const schemas: Record<string, unknown> = {};\n for (const [name, schema] of registry.entries()) {\n schemas[name] = convertSchema(schema, registry);\n }\n doc.components = { schemas };\n }\n\n return doc;\n}\n","import type {\n AuthenticationConfig,\n EndpointRegistration\n} from '@cleverbrush/server';\nimport { endpoint } from '@cleverbrush/server';\nimport {\n generateOpenApiSpec,\n type OpenApiDocument,\n type OpenApiInfo,\n type OpenApiServer,\n type OpenApiServer_ServerLike\n} from './generateOpenApiSpec.js';\nimport type { OpenApiSecurityScheme } from './securityMapper.js';\n\n/**\n * Options for {@link createOpenApiEndpoint}.\n *\n * When `server` is provided, `getRegistrations` and `authConfig` are derived\n * from it automatically (unless explicitly overridden).\n */\nexport interface OpenApiEndpointOptions {\n /**\n * A `ServerBuilder` (or any structurally compatible object). When set,\n * endpoint registrations and auth config are read from the server\n * automatically. Explicit `getRegistrations` / `authConfig` values\n * take precedence.\n */\n readonly server?: OpenApiServer_ServerLike;\n /** Function that returns endpoint registrations. */\n readonly getRegistrations?: () => readonly EndpointRegistration[];\n /** OpenAPI info metadata. */\n readonly info: OpenApiInfo;\n /** Optional server entries. */\n readonly servers?: readonly OpenApiServer[];\n /** Optional auth config for security scheme generation. */\n readonly authConfig?: AuthenticationConfig | null;\n /** Override security schemes manually. */\n readonly securitySchemes?: Record<string, OpenApiSecurityScheme>;\n /** Path to serve the spec at (default: `/openapi.json`). */\n readonly path?: string;\n}\n\n/**\n * Creates an endpoint definition and handler that serves the OpenAPI spec\n * as JSON. Register it with `builder.handle(ep, handler)`.\n *\n * The spec is lazily generated on first request and cached.\n *\n * @example\n * ```ts\n * const { endpoint: openApiEp, handler } = createOpenApiEndpoint({\n * server,\n * info: { title: 'My API', version: '1.0.0' }\n * });\n * server.handle(openApiEp, handler);\n * ```\n */\nexport function createOpenApiEndpoint(options: OpenApiEndpointOptions): {\n endpoint: ReturnType<(typeof endpoint)['get']>;\n handler: () => OpenApiDocument;\n} {\n const servePath = options.path ?? '/openapi.json';\n const srv = options.server;\n const getRegistrations =\n options.getRegistrations ??\n (srv ? () => srv.getRegistrations() : () => []);\n\n const ep = endpoint\n .get(servePath)\n .summary('OpenAPI specification')\n .tags('OpenAPI')\n .operationId('getOpenApiSpec');\n\n let cachedSpec: OpenApiDocument | null = null;\n\n const handler = (): OpenApiDocument => {\n if (!cachedSpec) {\n cachedSpec = generateOpenApiSpec({\n registrations: getRegistrations(),\n info: options.info,\n servers: options.servers,\n authConfig:\n options.authConfig !== undefined\n ? options.authConfig\n : (srv?.getAuthenticationConfig() ?? undefined),\n securitySchemes: options.securitySchemes\n });\n }\n return cachedSpec;\n };\n\n return { endpoint: ep, handler };\n}\n","import type {\n RequestContext,\n SubscriptionRegistration\n} from '@cleverbrush/server';\nimport {\n type AsyncApiDocument,\n type AsyncApiInfo,\n type AsyncApiServer_ServerLike,\n type AsyncApiServerEntry,\n generateAsyncApiSpec\n} from './generateAsyncApiSpec.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * Options for the {@link serveAsyncApi} middleware.\n *\n * When `server` is provided, `getSubscriptionRegistrations` is derived\n * from it automatically (unless explicitly overridden).\n */\nexport interface ServeAsyncApiOptions {\n /**\n * A `ServerBuilder` (or any structurally compatible object). When set,\n * subscription registrations are read from the server automatically.\n * An explicit `getSubscriptionRegistrations` value takes precedence.\n */\n readonly server?: AsyncApiServer_ServerLike;\n /**\n * Function that returns subscription registrations.\n * Takes precedence over `server.getSubscriptionRegistrations()`.\n */\n readonly getSubscriptionRegistrations?: () => readonly SubscriptionRegistration[];\n /** AsyncAPI info metadata (title, version, description). */\n readonly info: AsyncApiInfo;\n /**\n * Server entries to include in the spec's `servers` map.\n * Keys are server IDs (e.g. `'production'`, `'staging'`).\n */\n readonly servers?: Record<string, AsyncApiServerEntry>;\n /** Path to serve the spec at. Defaults to `/asyncapi.json`. */\n readonly path?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Middleware\n// ---------------------------------------------------------------------------\n\n/**\n * Returns a server middleware that serves the AsyncAPI 3.0 spec as JSON at\n * the configured path (default: `/asyncapi.json`).\n *\n * The spec is lazily generated on the first request and cached for subsequent\n * requests.\n *\n * @example\n * ```ts\n * import { serveAsyncApi } from '@cleverbrush/server-openapi';\n *\n * server.use(\n * serveAsyncApi({\n * server,\n * info: { title: 'My API', version: '1.0.0' },\n * servers: {\n * production: { host: 'api.example.com', protocol: 'wss' },\n * },\n * })\n * );\n * ```\n */\nexport function serveAsyncApi(\n options: ServeAsyncApiOptions\n): (context: RequestContext, next: () => Promise<void>) => Promise<void> {\n const servePath = options.path ?? '/asyncapi.json';\n const srv = options.server;\n const getSubscriptionRegistrations =\n options.getSubscriptionRegistrations ??\n (srv ? () => srv.getSubscriptionRegistrations() : () => []);\n let cachedSpec: AsyncApiDocument | null = null;\n\n return async (context, next) => {\n const url = context.url;\n const pathname = url.pathname;\n const method = context.method.toUpperCase();\n\n if (method === 'GET' && pathname === servePath) {\n if (!cachedSpec) {\n cachedSpec = generateAsyncApiSpec({\n subscriptions: getSubscriptionRegistrations(),\n info: options.info,\n servers: options.servers\n });\n }\n const body = JSON.stringify(cachedSpec);\n context.response.writeHead(200, {\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(body).toString()\n });\n context.response.end(body);\n return;\n }\n\n await next();\n };\n}\n","import type {\n AuthenticationConfig,\n EndpointRegistration,\n RequestContext\n} from '@cleverbrush/server';\nimport {\n generateOpenApiSpec,\n type OpenApiDocument,\n type OpenApiInfo,\n type OpenApiServer,\n type OpenApiServer_ServerLike\n} from './generateOpenApiSpec.js';\nimport type { OpenApiSecurityScheme } from './securityMapper.js';\n\n/**\n * Options for the {@link serveOpenApi} middleware.\n *\n * When `server` is provided, `getRegistrations` and `authConfig` are derived\n * from it automatically (unless explicitly overridden).\n */\nexport interface ServeOpenApiOptions {\n /**\n * A `ServerBuilder` (or any structurally compatible object). When set,\n * endpoint registrations and auth config are read from the server\n * automatically. Explicit `getRegistrations` / `authConfig` values\n * take precedence.\n */\n readonly server?: OpenApiServer_ServerLike;\n /** Function that returns endpoint registrations. */\n readonly getRegistrations?: () => readonly EndpointRegistration[];\n /** OpenAPI info metadata. */\n readonly info: OpenApiInfo;\n /** Optional server entries. */\n readonly servers?: readonly OpenApiServer[];\n /** Optional auth config for security scheme generation. */\n readonly authConfig?: AuthenticationConfig | null;\n /** Override security schemes manually. */\n readonly securitySchemes?: Record<string, OpenApiSecurityScheme>;\n /** Path to serve the spec at (default: `/openapi.json`). */\n readonly path?: string;\n}\n\n/**\n * Returns a server middleware that serves the OpenAPI spec as JSON at\n * the configured path (default: `/openapi.json`).\n *\n * The spec is lazily generated on first request and cached.\n */\nexport function serveOpenApi(\n options: ServeOpenApiOptions\n): (context: RequestContext, next: () => Promise<void>) => Promise<void> {\n const servePath = options.path ?? '/openapi.json';\n const srv = options.server;\n const getRegistrations =\n options.getRegistrations ??\n (srv ? () => srv.getRegistrations() : () => []);\n let cachedSpec: OpenApiDocument | null = null;\n\n return async (context, next) => {\n const url = context.url;\n const pathname = url.pathname;\n const method = context.method.toUpperCase();\n\n if (method === 'GET' && pathname === servePath) {\n if (!cachedSpec) {\n cachedSpec = generateOpenApiSpec({\n registrations: getRegistrations(),\n info: options.info,\n servers: options.servers,\n authConfig:\n options.authConfig !== undefined\n ? options.authConfig\n : (srv?.getAuthenticationConfig() ?? undefined),\n securitySchemes: options.securitySchemes\n });\n }\n const body = JSON.stringify(cachedSpec);\n context.response.writeHead(200, {\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(body).toString()\n });\n context.response.end(body);\n return;\n }\n\n await next();\n };\n}\n"],"mappings":"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","names":["fs","path","ObjectSchemaBuilder","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","toJsonSchema","isParseStringSchema","p","convertColonParams","path","paramNames","_","name","convertParseStringPath","pathTemplate","info","templateDef","props","pathString","i","parameters","seg","resolvePath","meta","basePath","normalizeSlashes","combined","converted","fullPath","result","toJsonSchema","convertSchema","schema","registry","nameResolver","s","SchemaRegistry","schema","name","existing","walkSchemas","registry","visited","info","props","child","elements","el","options","opt","resolved","mapSecuritySchemes","authConfig","result","scheme","name","challenge","cookieName","mapOperationSecurity","authRoles","securitySchemeNames","buildParameterObject","name","location","schema","required","description","param","buildRequestBody","bodySchema","registry","example","examples","jsonSchema","convertSchema","bodyInfo","mediaType","body","HTTP_STATUS_DESCRIPTIONS","PROBLEM_DETAILS_SCHEMA","buildResponseHeaders","props","headers","propSchema","propInfo","header","buildResponses","meta","method","result","ct","desc","codeStr","code","respInfo","customDesc","existingContent","baseSchema","mimeType","typeEntry","linksOut","linkName","linkDef","link","params","tree","ObjectSchemaBuilder","raw","paramName","value","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","pointer","successCode","k","buildOperation","pathParams","securitySchemeNames","operation","parameters","pp","isRequired","security","mapOperationSecurity","authRoles","ed","callbacksOut","cbName","cbDef","expression","selected","cbMethod","cbOperation","cbResponses","generateOpenApiSpec","options","srv","registrations","authConfig","webhooks","info","servers","securitySchemes","tags","resolvedSchemes","mapSecuritySchemes","SchemaRegistry","visited","reg","walkSchemas","queryProps","headerProps","webhook","resolveComponentSchemaName","rootSchema","inlinedRoot","candidate","paths","path","resolvePath","explicitNames","t","autoNames","tag","mergedTags","doc","s","webhooksObj","whMethod","whOperation","whResponses","componentSchemas","hasSchemas","components","writeOpenApiSpec","options","outputPath","spec","generateOpenApiSpec","dir","pathToChannelId","path","resolveSubscriptionAddress","basePath","pathTemplate","base","converted","normalizeSlashes","templateDef","pathString","i","result","generateAsyncApiSpec","options","subscriptions","registry","SchemaRegistry","visited","sub","m","walkSchemas","channels","operations","address","channelId","messages","outInfo","convertSchema","inInfo","channel","t","opId","doc","schemas","name","schema","endpoint","createOpenApiEndpoint","options","servePath","srv","getRegistrations","ep","endpoint","cachedSpec","generateOpenApiSpec","serveAsyncApi","options","servePath","srv","getSubscriptionRegistrations","cachedSpec","context","next","pathname","generateAsyncApiSpec","body","serveOpenApi","options","servePath","srv","getRegistrations","cachedSpec","context","next","pathname","generateOpenApiSpec","body"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/generateOpenApiSpec.ts","../src/pathUtils.ts","../src/schemaConverter.ts","../src/schemaRegistry.ts","../src/securityMapper.ts","../src/generateAsyncApiSpec.ts","../src/openApiEndpoint.ts","../src/serveAsyncApi.ts","../src/serveOpenApi.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n generateOpenApiSpec,\n type OpenApiDocument,\n type OpenApiOptions\n} from './generateOpenApiSpec.js';\n\n/**\n * Generate an OpenAPI spec and write it to a file.\n *\n * @param options - Same options as `generateOpenApiSpec()`.\n * @param outputPath - File path to write the JSON spec to.\n * @returns The generated spec document.\n *\n * @example\n * ```ts\n * import { writeOpenApiSpec } from '@cleverbrush/server-openapi';\n * import { createServer, endpoint } from '@cleverbrush/server';\n *\n * const builder = createServer()\n * .handle(endpoint.get('/api/health'), () => ({ ok: true }));\n *\n * writeOpenApiSpec(\n * {\n * registrations: builder.getRegistrations(),\n * info: { title: 'My API', version: '1.0.0' }\n * },\n * './openapi.json'\n * );\n * ```\n */\nexport function writeOpenApiSpec(\n options: OpenApiOptions,\n outputPath: string\n): OpenApiDocument {\n const spec = generateOpenApiSpec(options);\n const dir = path.dirname(outputPath);\n if (dir && !fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(outputPath, JSON.stringify(spec, null, 2), 'utf-8');\n return spec;\n}\n","import {\n ObjectSchemaBuilder,\n type SchemaBuilder,\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n} from '@cleverbrush/schema';\nimport type {\n AuthenticationConfig,\n EndpointMetadata,\n EndpointRegistration,\n UploadOptions,\n WebhookDefinition\n} from '@cleverbrush/server';\nimport { resolvePath } from './pathUtils.js';\nimport { convertSchema } from './schemaConverter.js';\nimport { SchemaRegistry, walkSchemas } from './schemaRegistry.js';\nimport {\n mapOperationSecurity,\n mapSecuritySchemes,\n type OpenApiSecurityScheme\n} from './securityMapper.js';\n\n// ---------------------------------------------------------------------------\n// Server interface (structural — avoids hard dependency on ServerBuilder class)\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal interface for a `@cleverbrush/server` server instance. Matches\n * the relevant subset of `ServerBuilder` so that callers can pass the\n * server directly without importing the class.\n */\nexport interface OpenApiServer_ServerLike {\n getRegistrations(): readonly EndpointRegistration[];\n getAuthenticationConfig(): AuthenticationConfig | null;\n getWebhooks(): readonly WebhookDefinition[];\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * API metadata included in the OpenAPI `info` object.\n * Maps directly to the OpenAPI 3.1 Info Object.\n */\nexport interface OpenApiInfo {\n readonly title: string;\n readonly version: string;\n readonly description?: string;\n readonly termsOfService?: string;\n readonly contact?: {\n readonly name?: string;\n readonly url?: string;\n readonly email?: string;\n };\n readonly license?: {\n readonly name: string;\n readonly url?: string;\n };\n}\n\n/**\n * A server entry in the OpenAPI `servers` array.\n * Describes a base URL where the API is accessible.\n */\nexport interface OpenApiServer {\n readonly url: string;\n readonly description?: string;\n}\n\n/**\n * A tag entry in the OpenAPI top-level `tags` array.\n * Provides a name, optional description, and optional external documentation\n * for a tag group.\n *\n * @see https://spec.openapis.org/oas/v3.1.0#tag-object\n */\nexport interface OpenApiTag {\n /** Tag name. Must match the tag strings used on individual operations. */\n readonly name: string;\n /** Short description for the tag group, displayed in Swagger UI / Redoc. */\n readonly description?: string;\n /** Link to external documentation for this tag. */\n readonly externalDocs?: {\n readonly url: string;\n readonly description?: string;\n };\n}\n\n/**\n * Options passed to {@link generateOpenApiSpec}.\n *\n * When `server` is provided, `registrations` and `authConfig` are derived\n * from it automatically (unless explicitly overridden).\n */\nexport interface OpenApiOptions {\n /**\n * A `ServerBuilder` (or any object implementing the same methods).\n * When set, `registrations`, `authConfig`, and `webhooks` are\n * automatically read from the server instance. Explicit values for\n * those fields take precedence over the server-derived ones.\n */\n readonly server?: OpenApiServer_ServerLike;\n readonly registrations?: readonly EndpointRegistration[];\n readonly info: OpenApiInfo;\n readonly servers?: readonly OpenApiServer[];\n readonly authConfig?: AuthenticationConfig | null;\n readonly securitySchemes?: Record<string, OpenApiSecurityScheme>;\n /**\n * Top-level tag definitions with optional descriptions and external docs.\n *\n * When provided, these entries are emitted as the top-level `tags` array.\n * Any tag names used by registered endpoints but absent from this list are\n * automatically appended as name-only entries (sorted alphabetically).\n *\n * When omitted, unique tag names are still auto-collected from all\n * registered endpoints and emitted as name-only entries.\n *\n * @see https://spec.openapis.org/oas/v3.1.0#tag-object\n */\n readonly tags?: readonly OpenApiTag[];\n /**\n * Webhook definitions to emit in the top-level `webhooks` map of the\n * generated OpenAPI document.\n *\n * Webhooks are not served as HTTP routes — they merely document async\n * out-of-band requests that your API sends to subscribers.\n *\n * @see https://spec.openapis.org/oas/v3.1.0#fixed-fields\n */\n readonly webhooks?: readonly WebhookDefinition[];\n}\n\n// ---------------------------------------------------------------------------\n// OpenAPI Document (partial typing — plain objects for flexibility)\n// ---------------------------------------------------------------------------\n\n/**\n * A generated OpenAPI 3.1 document. Typed as a plain object map to allow\n * any extension fields without requiring a full OpenAPI type library.\n */\nexport type OpenApiDocument = Record<string, unknown>;\n\n// ---------------------------------------------------------------------------\n// Generator\n// ---------------------------------------------------------------------------\n\nfunction buildParameterObject(\n name: string,\n location: 'query' | 'header' | 'path',\n schema: Record<string, unknown>,\n required: boolean,\n description?: string\n): Record<string, unknown> {\n const param: Record<string, unknown> = {\n name,\n in: location,\n schema\n };\n if (required) param['required'] = true;\n if (description) param['description'] = description;\n return param;\n}\n\nfunction buildRequestBody(\n bodySchema: SchemaBuilder<any, any, any, any, any>,\n registry: SchemaRegistry,\n example?: unknown | null,\n examples?: Record<\n string,\n { summary?: string; description?: string; value: unknown }\n > | null,\n fileUpload?: UploadOptions | null\n): Record<string, unknown> {\n const bodyInfo = bodySchema.introspect() as any;\n const body: Record<string, unknown> = {\n required: bodyInfo.isRequired !== false\n };\n\n // When file uploads are enabled, emit multipart/form-data\n if (fileUpload) {\n const jsonSchema = convertSchema(bodySchema, registry);\n const mediaType: Record<string, unknown> = { schema: jsonSchema };\n body['content'] = {\n 'multipart/form-data': mediaType\n };\n } else {\n const jsonSchema = convertSchema(bodySchema, registry);\n const mediaType: Record<string, unknown> = { schema: jsonSchema };\n if (example != null) {\n mediaType['example'] = example;\n } else if (examples != null) {\n mediaType['examples'] = examples;\n }\n body['content'] = {\n 'application/json': mediaType\n };\n }\n\n if (typeof bodyInfo.description === 'string' && bodyInfo.description !== '')\n body['description'] = bodyInfo.description;\n return body;\n}\n\n// Default descriptions for common HTTP status codes\nconst HTTP_STATUS_DESCRIPTIONS: Record<number, string> = {\n 200: 'OK',\n 201: 'Created',\n 202: 'Accepted',\n 204: 'No Content',\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Not Found',\n 409: 'Conflict',\n 422: 'Unprocessable Entity',\n 500: 'Internal Server Error'\n};\n\n// Minimal inline schema for ProblemDetails error responses\nconst PROBLEM_DETAILS_SCHEMA = {\n type: 'object',\n properties: {\n status: { type: 'integer' },\n title: { type: 'string' },\n detail: { type: 'string' }\n }\n};\n\nfunction buildResponseHeaders(\n schema: ObjectSchemaBuilder<any, any, any, any, any, any, any>,\n registry: SchemaRegistry\n): Record<string, unknown> {\n const info = schema.introspect() as any;\n const props: Record<\n string,\n SchemaBuilder<any, any, any, any, any>\n > = info.properties ?? {};\n const headers: Record<string, unknown> = {};\n for (const [name, propSchema] of Object.entries(props)) {\n const propInfo = propSchema.introspect() as any;\n const header: Record<string, unknown> = {\n schema: convertSchema(propSchema, registry)\n };\n if (\n typeof propInfo.description === 'string' &&\n propInfo.description !== ''\n ) {\n header['description'] = propInfo.description;\n }\n headers[name] = header;\n }\n return headers;\n}\n\nfunction buildResponses(\n meta: EndpointMetadata,\n method: string,\n registry: SchemaRegistry\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n // Binary file response — takes precedence over JSON schema\n if (meta.producesFile) {\n const ct = meta.producesFile.contentType ?? 'application/octet-stream';\n const desc = meta.producesFile.description ?? 'File download';\n result['200'] = {\n description: desc,\n content: {\n [ct]: { schema: { type: 'string', format: 'binary' } }\n }\n };\n } else if (meta.responsesSchemas) {\n for (const [codeStr, schema] of Object.entries(meta.responsesSchemas)) {\n const code = Number(codeStr);\n const desc =\n HTTP_STATUS_DESCRIPTIONS[code] ?? `Response ${codeStr}`;\n if (schema) {\n const jsonSchema = convertSchema(schema, registry);\n const respInfo = schema.introspect() as any;\n const customDesc =\n typeof respInfo.description === 'string' &&\n respInfo.description !== ''\n ? respInfo.description\n : desc;\n result[codeStr] = {\n description: customDesc,\n content: { 'application/json': { schema: jsonSchema } }\n };\n } else {\n result[codeStr] = { description: desc };\n }\n }\n } else if (meta.responseSchema) {\n // Legacy single-code path — .returns() was called\n const jsonSchema = convertSchema(meta.responseSchema, registry);\n const respInfo = meta.responseSchema.introspect() as any;\n const desc =\n typeof respInfo.description === 'string' &&\n respInfo.description !== ''\n ? respInfo.description\n : 'Successful response';\n result['200'] = {\n description: desc,\n content: { 'application/json': { schema: jsonSchema } }\n };\n } else if (method === 'DELETE' || method === 'HEAD') {\n result['204'] = { description: 'No content' };\n } else {\n result['200'] = { description: 'Successful response' };\n }\n\n // Auto-add framework-generated error responses\n if (meta.bodySchema && !result['422']) {\n result['422'] = {\n description: 'Validation error',\n content: {\n 'application/problem+json': { schema: PROBLEM_DETAILS_SCHEMA }\n }\n };\n }\n\n if (meta.authRoles !== null) {\n if (!result['401']) {\n result['401'] = {\n description: 'Unauthorized',\n content: {\n 'application/problem+json': {\n schema: PROBLEM_DETAILS_SCHEMA\n }\n }\n };\n }\n if (!result['403']) {\n result['403'] = {\n description: 'Forbidden',\n content: {\n 'application/problem+json': {\n schema: PROBLEM_DETAILS_SCHEMA\n }\n }\n };\n }\n }\n\n // Multiple content types — augment each response's content map with extra\n // MIME types from .produces(). producesFile already handled above (binary wins).\n if (meta.produces && !meta.producesFile) {\n for (const code of Object.keys(result)) {\n const entry = result[code] as Record<string, unknown>;\n const existingContent = entry['content'] as\n | Record<string, unknown>\n | undefined;\n // Only augment success responses that already have a content map\n // (skip error responses whose content is application/problem+json)\n if (\n !existingContent ||\n existingContent['application/problem+json']\n ) {\n continue;\n }\n const baseSchema =\n (existingContent['application/json'] as Record<string, unknown>)\n ?.schema ?? {};\n for (const [mimeType, typeEntry] of Object.entries(meta.produces)) {\n if (mimeType === 'application/json') continue;\n existingContent[mimeType] = {\n schema: typeEntry.schema\n ? convertSchema(typeEntry.schema, registry)\n : baseSchema\n };\n }\n }\n }\n\n // Response headers — inject into every response code when declared\n if (meta.responseHeaderSchema) {\n const headers = buildResponseHeaders(\n meta.responseHeaderSchema,\n registry\n );\n for (const code of Object.keys(result)) {\n (result[code] as Record<string, unknown>)['headers'] = headers;\n }\n }\n\n // Links — attach to the lowest 2xx response\n if (meta.links) {\n const linksOut: Record<string, unknown> = {};\n for (const [linkName, linkDef] of Object.entries(meta.links)) {\n const link: Record<string, unknown> = {\n operationId: linkDef.operationId\n };\n if (linkDef.description) link['description'] = linkDef.description;\n if (linkDef.requestBody) link['requestBody'] = linkDef.requestBody;\n if (linkDef.parameters) {\n let params: Record<string, unknown>;\n if (\n typeof linkDef.parameters === 'function' &&\n meta.responseSchema\n ) {\n const tree = ObjectSchemaBuilder.getPropertiesFor(\n meta.responseSchema as ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n );\n const raw = (\n linkDef.parameters as (\n r: any\n ) => Record<string, unknown>\n )(tree);\n params = {};\n for (const [paramName, value] of Object.entries(raw)) {\n if (\n value &&\n typeof value === 'object' &&\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR in\n (value as object)\n ) {\n const pointer = (value as any)[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ].toJsonPointer();\n params[paramName] = `$response.body#${pointer}`;\n } else {\n params[paramName] = value;\n }\n }\n } else {\n params = linkDef.parameters as Record<string, unknown>;\n }\n link['parameters'] = params;\n }\n linksOut[linkName] = link;\n }\n const successCode = Object.keys(result)\n .filter(k => Number(k) >= 200 && Number(k) < 300)\n .sort()[0];\n if (successCode) {\n (result[successCode] as Record<string, unknown>)['links'] =\n linksOut;\n }\n }\n\n return result;\n}\n\nfunction buildOperation(\n meta: EndpointMetadata,\n pathParams: { name: string; schema: Record<string, unknown> }[],\n securitySchemeNames: string[],\n registry: SchemaRegistry\n): Record<string, unknown> {\n const operation: Record<string, unknown> = {};\n\n // Metadata\n if (meta.summary) operation['summary'] = meta.summary;\n if (meta.description) operation['description'] = meta.description;\n if (meta.tags.length > 0) operation['tags'] = [...meta.tags];\n if (meta.operationId) operation['operationId'] = meta.operationId;\n if (meta.deprecated) operation['deprecated'] = true;\n\n // Parameters\n const parameters: Record<string, unknown>[] = [];\n\n // Path parameters\n for (const pp of pathParams) {\n parameters.push(buildParameterObject(pp.name, 'path', pp.schema, true));\n }\n\n // Query parameters\n if (meta.querySchema) {\n const queryInfo = meta.querySchema.introspect() as any;\n const props: Record<\n string,\n SchemaBuilder<any, any, any>\n > = queryInfo.properties ?? {};\n for (const [name, propSchema] of Object.entries(props)) {\n const propInfo = propSchema.introspect() as any;\n const isRequired = propInfo.isRequired !== false;\n const description =\n typeof propInfo.description === 'string' &&\n propInfo.description !== ''\n ? propInfo.description\n : undefined;\n parameters.push(\n buildParameterObject(\n name,\n 'query',\n convertSchema(propSchema, registry),\n isRequired,\n description\n )\n );\n }\n }\n\n // Header parameters\n if (meta.headerSchema) {\n const headerInfo = meta.headerSchema.introspect() as any;\n const props: Record<\n string,\n SchemaBuilder<any, any, any>\n > = headerInfo.properties ?? {};\n for (const [name, propSchema] of Object.entries(props)) {\n const propInfo = propSchema.introspect() as any;\n const isRequired = propInfo.isRequired !== false;\n const description =\n typeof propInfo.description === 'string' &&\n propInfo.description !== ''\n ? propInfo.description\n : undefined;\n parameters.push(\n buildParameterObject(\n name,\n 'header',\n convertSchema(propSchema, registry),\n isRequired,\n description\n )\n );\n }\n }\n\n if (parameters.length > 0) operation['parameters'] = parameters;\n\n // Request body\n if (meta.bodySchema) {\n operation['requestBody'] = buildRequestBody(\n meta.bodySchema,\n registry,\n meta.example,\n meta.examples,\n meta.fileUpload\n );\n }\n\n // Responses\n operation['responses'] = buildResponses(\n meta,\n meta.method.toUpperCase(),\n registry\n );\n\n // Security\n const security = mapOperationSecurity(authRoles(meta), securitySchemeNames);\n if (security.length > 0) operation['security'] = security;\n\n // External documentation\n if (meta.externalDocs) {\n const ed: Record<string, unknown> = { url: meta.externalDocs.url };\n if (meta.externalDocs.description)\n ed['description'] = meta.externalDocs.description;\n operation['externalDocs'] = ed;\n }\n\n // Callbacks\n if (meta.callbacks) {\n const callbacksOut: Record<string, unknown> = {};\n for (const [cbName, cbDef] of Object.entries(meta.callbacks)) {\n // Determine URL expression\n let expression: string;\n if (cbDef.expression) {\n expression = cbDef.expression;\n } else if (cbDef.urlFrom && meta.bodySchema) {\n const tree = ObjectSchemaBuilder.getPropertiesFor(\n meta.bodySchema as ObjectSchemaBuilder<\n any,\n any,\n any,\n any,\n any,\n any,\n any\n >\n );\n const selected = cbDef.urlFrom(tree);\n if (\n selected &&\n typeof selected === 'object' &&\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR in (selected as object)\n ) {\n const pointer = (selected as any)[\n SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR\n ].toJsonPointer();\n expression = `{$request.body#${pointer}}`;\n } else {\n expression = String(\n selected ?? '{$request.body#/callbackUrl}'\n );\n }\n } else {\n expression = '{$request.body#/callbackUrl}';\n }\n\n const cbMethod = (cbDef.method ?? 'post').toLowerCase();\n const cbOperation: Record<string, unknown> = {};\n if (cbDef.summary) cbOperation['summary'] = cbDef.summary;\n if (cbDef.description)\n cbOperation['description'] = cbDef.description;\n if (cbDef.body) {\n cbOperation['requestBody'] = buildRequestBody(\n cbDef.body,\n registry\n );\n }\n const cbResponses: Record<string, unknown> = {};\n if (cbDef.response) {\n cbResponses['200'] = {\n description: 'OK',\n content: {\n 'application/json': {\n schema: convertSchema(cbDef.response, registry)\n }\n }\n };\n } else {\n cbResponses['200'] = { description: 'OK' };\n }\n cbOperation['responses'] = cbResponses;\n callbacksOut[cbName] = {\n [expression]: { [cbMethod]: cbOperation }\n };\n }\n operation['callbacks'] = callbacksOut;\n }\n\n return operation;\n}\n\nfunction authRoles(meta: EndpointMetadata): readonly string[] | null {\n return meta.authRoles;\n}\n\n/**\n * Generate an OpenAPI 3.1 specification document from registered endpoints.\n */\nexport function generateOpenApiSpec(options: OpenApiOptions): OpenApiDocument {\n const srv = options.server;\n const registrations =\n options.registrations ?? srv?.getRegistrations() ?? [];\n const authConfig =\n options.authConfig !== undefined\n ? options.authConfig\n : (srv?.getAuthenticationConfig() ?? undefined);\n const webhooks =\n options.webhooks ??\n (srv && srv.getWebhooks().length > 0 ? srv.getWebhooks() : undefined);\n const { info, servers, securitySchemes, tags } = options;\n\n // Security schemes — from explicit config or auto-mapped\n const resolvedSchemes: Record<string, OpenApiSecurityScheme> =\n securitySchemes ?? mapSecuritySchemes(authConfig);\n const securitySchemeNames = Object.keys(resolvedSchemes);\n\n // Pre-pass: collect all named schemas from every endpoint into a registry.\n // Walking happens before path generation so that $ref pointers are emitted\n // correctly at every call site within buildOperation.\n const registry = new SchemaRegistry();\n const visited = new Set<SchemaBuilder<any, any, any>>();\n for (const reg of registrations) {\n const meta = reg.endpoint;\n if (meta.bodySchema) walkSchemas(meta.bodySchema, registry, visited);\n if (meta.responseSchema)\n walkSchemas(meta.responseSchema, registry, visited);\n if (meta.responsesSchemas) {\n for (const schema of Object.values(meta.responsesSchemas)) {\n if (schema) walkSchemas(schema, registry, visited);\n }\n }\n if (meta.querySchema) {\n const queryProps =\n (meta.querySchema.introspect() as any).properties ?? {};\n for (const propSchema of Object.values<\n SchemaBuilder<any, any, any>\n >(queryProps)) {\n walkSchemas(propSchema, registry, visited);\n }\n }\n if (meta.headerSchema) {\n const headerProps =\n (meta.headerSchema.introspect() as any).properties ?? {};\n for (const propSchema of Object.values<\n SchemaBuilder<any, any, any>\n >(headerProps)) {\n walkSchemas(propSchema, registry, visited);\n }\n }\n if (meta.callbacks) {\n for (const cbDef of Object.values(meta.callbacks)) {\n if (cbDef.body) walkSchemas(cbDef.body, registry, visited);\n if (cbDef.response)\n walkSchemas(cbDef.response, registry, visited);\n }\n }\n }\n if (webhooks) {\n for (const webhook of webhooks) {\n if (webhook.body) walkSchemas(webhook.body, registry, visited);\n if (webhook.response)\n walkSchemas(webhook.response, registry, visited);\n }\n }\n\n const resolveComponentSchemaName = (\n rootSchema: SchemaBuilder<any, any, any>\n ) => {\n // The root schema must be inlined exactly once — for the component\n // definition itself. Any subsequent encounter (e.g. through a lazy\n // self-reference) should emit a $ref instead of inlining again,\n // which would otherwise cause infinite recursion.\n let inlinedRoot = false;\n return (\n candidate: SchemaBuilder<any, any, any>\n ): string | undefined => {\n if (candidate === rootSchema && !inlinedRoot) {\n inlinedRoot = true;\n return undefined; // inline the root definition once\n }\n return registry.getName(candidate) ?? undefined;\n };\n };\n\n // Build paths\n const paths: Record<string, Record<string, unknown>> = {};\n\n for (const reg of registrations) {\n const meta = reg.endpoint;\n const { path, parameters: pathParams } = resolvePath(meta);\n const method = meta.method.toLowerCase();\n\n if (!paths[path]) paths[path] = {};\n paths[path][method] = buildOperation(\n meta,\n pathParams as { name: string; schema: Record<string, unknown> }[],\n securitySchemeNames,\n registry\n );\n }\n\n // Build top-level tags array: explicit entries first, then auto-collected\n // tag names from endpoints that are not already covered.\n const explicitNames = new Set((tags ?? []).map(t => t.name));\n const autoNames: string[] = [];\n for (const reg of registrations) {\n for (const tag of reg.endpoint.tags) {\n if (!explicitNames.has(tag) && !autoNames.includes(tag)) {\n autoNames.push(tag);\n }\n }\n }\n autoNames.sort();\n const mergedTags: OpenApiTag[] = [\n ...(tags ?? []),\n ...autoNames.map(name => ({ name }))\n ];\n\n // Assemble document\n const doc: OpenApiDocument = {\n openapi: '3.1.0',\n info: { ...info }\n };\n\n if (servers && servers.length > 0) {\n doc['servers'] = servers.map(s => ({ ...s }));\n }\n\n if (mergedTags.length > 0) {\n doc['tags'] = mergedTags.map(t => ({ ...t }));\n }\n\n doc['paths'] = paths;\n\n // Webhooks — out-of-band async requests documented alongside paths\n if (webhooks && webhooks.length > 0) {\n const webhooksObj: Record<string, unknown> = {};\n for (const webhook of webhooks) {\n const whMethod = (webhook.method ?? 'post').toLowerCase();\n const whOperation: Record<string, unknown> = {};\n if (webhook.summary) whOperation['summary'] = webhook.summary;\n if (webhook.description)\n whOperation['description'] = webhook.description;\n if (webhook.tags && webhook.tags.length > 0)\n whOperation['tags'] = [...webhook.tags];\n if (webhook.body) {\n whOperation['requestBody'] = buildRequestBody(\n webhook.body,\n registry\n );\n }\n const whResponses: Record<string, unknown> = {};\n if (webhook.response) {\n whResponses['200'] = {\n description: 'OK',\n content: {\n 'application/json': {\n schema: convertSchema(webhook.response, registry)\n }\n }\n };\n } else {\n whResponses['200'] = { description: 'OK' };\n }\n whOperation['responses'] = whResponses;\n webhooksObj[webhook.name] = { [whMethod]: whOperation };\n }\n doc['webhooks'] = webhooksObj;\n }\n\n // Components — security schemes + named component schemas\n const componentSchemas: Record<string, unknown> = {};\n for (const [name, schema] of registry.entries()) {\n // Inline the root schema to avoid a self-referential $ref, but resolve\n // nested named schemas through the shared registry so component\n // definitions can still deduplicate via $ref.\n componentSchemas[name] = convertSchema(\n schema,\n resolveComponentSchemaName(schema)\n );\n }\n const hasSchemas = Object.keys(componentSchemas).length > 0;\n\n if (securitySchemeNames.length > 0 || hasSchemas) {\n const components: Record<string, unknown> = {};\n if (securitySchemeNames.length > 0)\n components['securitySchemes'] = { ...resolvedSchemes };\n if (hasSchemas) components['schemas'] = componentSchemas;\n doc['components'] = components;\n }\n\n return doc;\n}\n","import type {\n ParseStringSchemaBuilder,\n SchemaBuilder\n} from '@cleverbrush/schema';\nimport { toJsonSchema } from '@cleverbrush/schema-json';\nimport type { EndpointMetadata } from '@cleverbrush/server';\n\ntype RoutePath = string | ParseStringSchemaBuilder<any, any, any, any, any>;\n\nfunction isParseStringSchema(\n p: RoutePath\n): p is ParseStringSchemaBuilder<any, any, any, any, any> {\n return typeof p !== 'string' && typeof (p as any).validate === 'function';\n}\n\n/**\n * Describes a single path parameter extracted from a route template,\n * including its name and the JSON Schema representation of its type.\n */\nexport interface PathParameterInfo {\n readonly name: string;\n readonly schema: Record<string, unknown>;\n}\n\n/**\n * The output of resolving a route path template to an OpenAPI-compatible\n * path string and its parameter list.\n */\nexport interface ResolvedPath {\n /** OpenAPI-formatted path, e.g. `/api/users/{id}` */\n readonly path: string;\n /** Extracted path parameters with their JSON Schema */\n readonly parameters: readonly PathParameterInfo[];\n}\n\n/**\n * Convert a colon-style static path to OpenAPI `{param}` format.\n * E.g. `/users/:id/posts/:pid` → `/users/{id}/posts/{pid}`\n */\nfunction convertColonParams(path: string): {\n converted: string;\n paramNames: string[];\n} {\n const paramNames: string[] = [];\n const converted = path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, name) => {\n paramNames.push(name);\n return `{${name}}`;\n });\n return { converted, paramNames };\n}\n\n/**\n * Convert a `ParseStringSchemaBuilder` path template to an OpenAPI-formatted\n * path string and extract parameter schemas.\n */\nfunction convertParseStringPath(\n pathTemplate: ParseStringSchemaBuilder<any, any, any, any, any>\n): { pathString: string; parameters: PathParameterInfo[] } {\n const info = pathTemplate.introspect() as any;\n const templateDef = info.templateDefinition as {\n literals: string[];\n segments: { path: string }[];\n };\n const objectSchema = info.objectSchema;\n const objInfo = objectSchema.introspect() as any;\n const props: Record<\n string,\n SchemaBuilder<any, any, any>\n > = objInfo.properties ?? {};\n\n let pathString = '';\n for (let i = 0; i < templateDef.segments.length; i++) {\n pathString +=\n templateDef.literals[i] + `{${templateDef.segments[i].path}}`;\n }\n pathString += templateDef.literals[templateDef.segments.length] ?? '';\n\n const parameters: PathParameterInfo[] = templateDef.segments.map(seg => ({\n name: seg.path,\n schema: props[seg.path]\n ? toJsonSchema(props[seg.path], { $schema: false })\n : { type: 'string' }\n }));\n\n return { pathString, parameters };\n}\n\n/**\n * Combines `basePath` and `pathTemplate` from an endpoint into an\n * OpenAPI-formatted path with extracted parameter information.\n */\nexport function resolvePath(meta: EndpointMetadata): ResolvedPath {\n const basePath = meta.basePath.replace(/\\/$/, '');\n const pathTemplate = meta.pathTemplate;\n\n if (isParseStringSchema(pathTemplate)) {\n const { pathString, parameters } = convertParseStringPath(pathTemplate);\n const fullPath = normalizeSlashes(basePath + pathString);\n return { path: fullPath, parameters };\n }\n\n // Static string path template\n const templateStr = pathTemplate === '/' ? '' : pathTemplate;\n const combined = basePath + templateStr;\n const { converted, paramNames } = convertColonParams(combined);\n const fullPath = normalizeSlashes(converted);\n\n const parameters: PathParameterInfo[] = paramNames.map(name => ({\n name,\n schema: { type: 'string' }\n }));\n\n return { path: fullPath, parameters };\n}\n\nfunction normalizeSlashes(path: string): string {\n // Replace double slashes with single, ensure leading slash\n let result = path.replace(/\\/+/g, '/');\n if (!result.startsWith('/')) result = '/' + result;\n if (result.length > 1 && result.endsWith('/')) result = result.slice(0, -1);\n return result || '/';\n}\n","import type { SchemaBuilder } from '@cleverbrush/schema';\nimport { toJsonSchema } from '@cleverbrush/schema-json';\nimport type { SchemaRegistry } from './schemaRegistry.js';\n\ntype NameResolver = (\n schema: SchemaBuilder<any, any, any>\n) => string | null | undefined;\n\n/**\n * Converts a `@cleverbrush/schema` builder to a JSON Schema object suitable\n * for embedding in an OpenAPI 3.1 spec (no `$schema` header, Draft 2020-12).\n *\n * Returns an empty schema `{}` when the input is `null` or `undefined`.\n *\n * When a {@link SchemaRegistry} or a custom resolver function is provided, any\n * schema instance that resolves to a name will be emitted as a\n * `$ref: '#/components/schemas/<name>'` pointer instead of being inlined.\n *\n * @param schema - The schema to convert, or `null`/`undefined`.\n * @param registry - Optional registry or resolver function for `$ref` deduplication.\n */\nexport function convertSchema(\n schema: SchemaBuilder<any, any, any, any, any> | null | undefined,\n registry?: SchemaRegistry | NameResolver\n): Record<string, unknown> {\n if (schema == null) return {};\n\n let nameResolver:\n | ((s: SchemaBuilder<any, any, any>) => string | null)\n | undefined;\n if (registry) {\n if (typeof registry === 'function') {\n nameResolver = s => registry(s) ?? null;\n } else {\n nameResolver = s => registry.getName(s);\n }\n }\n\n return toJsonSchema(schema, {\n $schema: false,\n draft: '2020-12',\n nameResolver\n });\n}\n","import type { SchemaBuilder } from '@cleverbrush/schema';\n\n// ---------------------------------------------------------------------------\n// SchemaRegistry\n// ---------------------------------------------------------------------------\n\n/**\n * Collects schemas that carry an explicit component name (set via\n * `.schemaName()`) and provides a reference-based lookup used during OpenAPI\n * spec generation to replace inline schema objects with\n * `$ref: '#/components/schemas/<name>'` pointers.\n *\n * **Conflict rule**: registering two *different* schema instances (different\n * object references) under the same name throws immediately. Re-registering\n * the same instance is a no-op.\n *\n * @example\n * ```ts\n * const registry = new SchemaRegistry();\n * registry.register(UserSchema); // UserSchema.schemaName('User')\n * registry.getName(UserSchema); // 'User'\n * registry.getName(someOtherSchema); // null\n * ```\n */\nexport class SchemaRegistry {\n /** schema instance → registered name */\n private readonly byInstance = new Map<\n SchemaBuilder<any, any, any>,\n string\n >();\n /** name → first-registered schema instance */\n private readonly byName = new Map<string, SchemaBuilder<any, any, any>>();\n\n /**\n * Attempts to register `schema` in the registry.\n *\n * - If the schema has no `schemaName` in its introspect output, it is\n * silently skipped.\n * - If the same instance is already registered, this is a no-op.\n * - If a **different** instance is already registered under the same name,\n * an error is thrown.\n *\n * @param schema - The schema builder to register.\n * @throws {Error} When two distinct schema instances share the same name.\n */\n register(schema: SchemaBuilder<any, any, any>): void {\n const name = (schema.introspect() as any).schemaName as\n | string\n | undefined;\n if (typeof name !== 'string') return;\n\n const existing = this.byName.get(name);\n if (existing !== undefined) {\n // Same instance → idempotent, nothing to do\n if (existing === schema) return;\n // Different instance → conflict\n throw new Error(\n `Schema name \"${name}\" is already registered by a different schema instance. ` +\n `Each named schema must be a single, reused constant. ` +\n `If you intended to register the same schema, ensure you are passing ` +\n `the same object reference (not a rebuilt schema).`\n );\n }\n\n this.byInstance.set(schema, name);\n this.byName.set(name, schema);\n }\n\n /**\n * Returns the component name for a given schema instance, or `null` if it\n * was not registered.\n *\n * @param schema - The schema builder to look up.\n * @returns The registered name, or `null`.\n */\n getName(schema: SchemaBuilder<any, any, any>): string | null {\n return this.byInstance.get(schema) ?? null;\n }\n\n /**\n * Iterates over all registered `[name, schema]` pairs in insertion order.\n *\n * Used to emit the `components.schemas` section of an OpenAPI document.\n */\n entries(): IterableIterator<[string, SchemaBuilder<any, any, any>]> {\n return this.byName.entries();\n }\n\n /** Returns `true` when at least one schema has been registered. */\n get isEmpty(): boolean {\n return this.byName.size === 0;\n }\n}\n\n// ---------------------------------------------------------------------------\n// walkSchemas\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively visits every {@link SchemaBuilder} reachable from `schema` and\n * calls {@link SchemaRegistry.register} on each node.\n *\n * Cycle detection is performed via a `visited` `Set` of object references, so\n * schemas may safely be shared across multiple branches without causing\n * infinite recursion.\n *\n * **Excluded schema types**\n * - `lazy` — deferred resolution would require calling the getter, which may\n * itself reference the parent schema; lazy schemas are handled separately.\n *\n * @param schema - Root schema to start the walk from.\n * @param registry - Registry to register named schemas into.\n * @param visited - Shared set for cycle detection; pass a new `Set()` for the\n * top-level call.\n */\nexport function walkSchemas(\n schema: SchemaBuilder<any, any, any>,\n registry: SchemaRegistry,\n visited: Set<SchemaBuilder<any, any, any>> = new Set()\n): void {\n if (visited.has(schema)) return;\n visited.add(schema);\n\n registry.register(schema);\n\n const info = schema.introspect() as any;\n\n switch (info.type) {\n case 'object': {\n const props = info.properties as\n | Record<string, SchemaBuilder<any, any, any>>\n | undefined;\n if (props) {\n for (const child of Object.values(props)) {\n walkSchemas(child, registry, visited);\n }\n }\n break;\n }\n case 'array':\n if (info.elementSchema) {\n walkSchemas(info.elementSchema, registry, visited);\n }\n break;\n case 'tuple': {\n const elements: SchemaBuilder<any, any, any>[] =\n info.elements ?? [];\n for (const el of elements) {\n walkSchemas(el, registry, visited);\n }\n if (info.restSchema) {\n walkSchemas(info.restSchema, registry, visited);\n }\n break;\n }\n case 'union': {\n const options: SchemaBuilder<any, any, any>[] = info.options ?? [];\n for (const opt of options) {\n walkSchemas(opt, registry, visited);\n }\n break;\n }\n case 'record':\n if (info.keySchema) {\n walkSchemas(info.keySchema, registry, visited);\n }\n if (info.valueSchema) {\n walkSchemas(info.valueSchema, registry, visited);\n }\n break;\n case 'lazy': {\n // Resolve the inner schema and walk it so that any named schema\n // reachable through a lazy boundary is registered. Cycle-safety is\n // provided by the `visited` set — the lazy wrapper itself was\n // already added above, preventing infinite recursion on\n // self-referential schemas.\n const resolved = (schema as any).resolve() as SchemaBuilder<\n any,\n any,\n any\n >;\n walkSchemas(resolved, registry, visited);\n break;\n }\n default:\n break;\n }\n}\n","import type { AuthenticationConfig } from '@cleverbrush/server';\n\n/**\n * An OpenAPI 3.1 Security Scheme Object.\n * Describes how requests to the API are authenticated.\n *\n * @see {@link https://spec.openapis.org/oas/v3.1.0#security-scheme-object OpenAPI Security Scheme Object}\n */\nexport interface OpenApiSecurityScheme {\n readonly type: string;\n readonly scheme?: string;\n readonly bearerFormat?: string;\n readonly in?: string;\n readonly name?: string;\n readonly flows?: Record<string, unknown>;\n readonly openIdConnectUrl?: string;\n}\n\n/**\n * Map `@cleverbrush/auth` authentication schemes to OpenAPI security scheme objects.\n */\nexport function mapSecuritySchemes(\n authConfig: AuthenticationConfig | null | undefined\n): Record<string, OpenApiSecurityScheme> {\n if (!authConfig) return {};\n\n const result: Record<string, OpenApiSecurityScheme> = {};\n for (const scheme of authConfig.schemes) {\n const name = scheme.name;\n const challenge = scheme.challenge?.();\n\n if ('openIdConnectUrl' in scheme && (scheme as any).openIdConnectUrl) {\n result[name] = {\n type: 'openIdConnect',\n openIdConnectUrl: (scheme as any).openIdConnectUrl\n };\n } else if ('flows' in scheme && (scheme as any).flows) {\n result[name] = {\n type: 'oauth2',\n flows: (scheme as any).flows\n };\n } else if (\n challenge?.headerValue?.toLowerCase().startsWith('bearer') ||\n name === 'jwt'\n ) {\n result[name] = {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT'\n };\n } else if (name === 'cookie' || (scheme as any).cookieName) {\n // Cookie scheme — extract cookie name from options if available\n const cookieName =\n (scheme as any).cookieName ??\n (scheme as any)._options?.cookieName ??\n 'session';\n result[name] = {\n type: 'apiKey',\n in: 'cookie',\n name: cookieName\n };\n } else if (challenge) {\n // Generic scheme with a challenge header\n result[name] = {\n type: 'http',\n scheme: challenge.headerValue.split(' ')[0].toLowerCase()\n };\n } else {\n // Fallback — treat as generic HTTP scheme\n result[name] = { type: 'http', scheme: name };\n }\n }\n return result;\n}\n\n/**\n * Map endpoint `authRoles` to an OpenAPI operation-level `security` array.\n *\n * - `null` → empty array (public endpoint, no security)\n * - `[]` → `[{ <schemeName>: [] }]` (any authenticated user)\n * - `['admin']` → `[{ <schemeName>: ['admin'] }]` (require specific roles)\n */\nexport function mapOperationSecurity(\n authRoles: readonly string[] | null,\n securitySchemeNames: string[]\n): Record<string, string[]>[] {\n if (authRoles === null) return [];\n if (securitySchemeNames.length === 0) return [];\n\n // Each security scheme listed as an option (OR semantics in OpenAPI)\n return securitySchemeNames.map(name => ({\n [name]: [...authRoles]\n }));\n}\n","import type { SchemaBuilder } from '@cleverbrush/schema';\nimport type { SubscriptionRegistration } from '@cleverbrush/server';\nimport { convertSchema } from './schemaConverter.js';\nimport { SchemaRegistry, walkSchemas } from './schemaRegistry.js';\n\n// ---------------------------------------------------------------------------\n// Server interface (structural — avoids hard dependency on ServerBuilder class)\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal interface for a `@cleverbrush/server` server instance. Matches\n * the relevant subset of `ServerBuilder` needed for AsyncAPI generation.\n */\nexport interface AsyncApiServer_ServerLike {\n getSubscriptionRegistrations(): readonly SubscriptionRegistration[];\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * API metadata included in the AsyncAPI `info` object.\n * Maps to the AsyncAPI 3.0 Info Object.\n */\nexport interface AsyncApiInfo {\n /** The title of the application. */\n readonly title: string;\n /** The application version. */\n readonly version: string;\n /** A short description of the application. */\n readonly description?: string;\n /** A URL to the Terms of Service. */\n readonly termsOfService?: string;\n /** Contact information for the application. */\n readonly contact?: {\n readonly name?: string;\n readonly url?: string;\n readonly email?: string;\n };\n /** License information for the application. */\n readonly license?: {\n readonly name: string;\n readonly url?: string;\n };\n}\n\n/**\n * A server entry in the AsyncAPI 3.0 `servers` map.\n * Describes a connection endpoint where the API is accessible.\n *\n * @see https://www.asyncapi.com/docs/reference/specification/v3.0.0#serverObject\n */\nexport interface AsyncApiServerEntry {\n /**\n * The host of the server (hostname + optional port).\n * @example `'api.example.com'`, `'localhost:3000'`\n */\n readonly host: string;\n /**\n * The protocol used to connect to the server.\n */\n readonly protocol: 'ws' | 'wss' | 'http' | 'https';\n /** An optional string describing the server. */\n readonly description?: string;\n /** The path component of the server URL. */\n readonly pathname?: string;\n}\n\n/**\n * Options passed to {@link generateAsyncApiSpec}.\n *\n * When `server` is provided, `subscriptions` are derived from it\n * automatically (unless explicitly overridden).\n */\nexport interface AsyncApiOptions {\n /**\n * A `ServerBuilder` (or any object implementing `getSubscriptionRegistrations()`).\n * When set, subscription registrations are automatically read from the\n * server instance. Explicit `subscriptions` values take precedence.\n */\n readonly server?: AsyncApiServer_ServerLike;\n /**\n * Subscription registrations to document.\n * When omitted, registrations are read from the `server` option.\n */\n readonly subscriptions?: readonly SubscriptionRegistration[];\n /** API metadata (title, version, description). */\n readonly info: AsyncApiInfo;\n /**\n * Server entries to include in the spec's `servers` map.\n * Keys are server IDs (e.g. `'production'`, `'staging'`).\n */\n readonly servers?: Record<string, AsyncApiServerEntry>;\n /**\n * Path prefix to strip from all channel addresses.\n * Useful when the subscription endpoints are mounted under a prefix\n * that differs from how the AsyncAPI doc should describe them.\n */\n readonly pathPrefix?: string;\n}\n\n/**\n * A generated AsyncAPI 3.0 document. Typed as a plain object map to allow\n * extension fields without requiring a full AsyncAPI type library.\n */\nexport type AsyncApiDocument = Record<string, unknown>;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Converts a URL path into a valid AsyncAPI channel ID.\n * Strips leading slash, replaces `/` and path-param braces with underscores.\n *\n * @example `'/ws/events'` → `'ws_events'`\n * @example `'/ws/rooms/{id}'` → `'ws_rooms_id'`\n */\nfunction pathToChannelId(path: string): string {\n return path\n .replace(/^\\//, '')\n .replace(/[/{}]/g, '_')\n .replace(/_+/g, '_')\n .replace(/_$/, '');\n}\n\n/**\n * Resolves the WebSocket address from a subscription's basePath and\n * pathTemplate. String templates with colon params (`:id`) are converted\n * to AsyncAPI `{id}` style.\n */\nfunction resolveSubscriptionAddress(\n basePath: string,\n pathTemplate: string | { serialize?: unknown; introspect?: () => unknown }\n): string {\n const base = basePath.replace(/\\/$/, '');\n\n if (typeof pathTemplate === 'string') {\n const tpl = pathTemplate === '/' ? '' : pathTemplate;\n const combined = base + tpl;\n // Colon params → {param}\n const converted = combined.replace(\n /:([a-zA-Z_][a-zA-Z0-9_]*)/g,\n '{$1}'\n );\n return normalizeSlashes(converted);\n }\n\n // ParseStringSchemaBuilder — extract templateDefinition\n const info = (pathTemplate as any).introspect?.() as any;\n const templateDef = info?.templateDefinition as\n | { literals: string[]; segments: { path: string }[] }\n | undefined;\n\n if (!templateDef) {\n return normalizeSlashes(base);\n }\n\n let pathString = '';\n for (let i = 0; i < templateDef.segments.length; i++) {\n pathString +=\n templateDef.literals[i] + `{${templateDef.segments[i].path}}`;\n }\n pathString += templateDef.literals[templateDef.segments.length] ?? '';\n\n return normalizeSlashes(base + pathString);\n}\n\nfunction normalizeSlashes(path: string): string {\n let result = path.replace(/\\/+/g, '/');\n if (!result.startsWith('/')) result = '/' + result;\n if (result.length > 1 && result.endsWith('/')) result = result.slice(0, -1);\n return result || '/';\n}\n\n// ---------------------------------------------------------------------------\n// Generator\n// ---------------------------------------------------------------------------\n\n/**\n * Generates an AsyncAPI 3.0 document from a set of WebSocket subscription\n * registrations.\n *\n * Each subscription becomes:\n * - A **channel** with its address and optional messages (for outgoing\n * server→client events and/or incoming client→server messages).\n * - One or two **operations**: a `send` operation for server→client events\n * and/or a `receive` operation for client→server messages.\n *\n * Named schemas (those with `.schemaName()` set) are automatically collected\n * and emitted as `components.schemas` entries with `$ref` pointers.\n *\n * @param options - Configuration including subscription registrations and API info.\n * @returns A plain AsyncAPI 3.0 document object suitable for `JSON.stringify`.\n *\n * @example\n * ```ts\n * import { generateAsyncApiSpec } from '@cleverbrush/server-openapi';\n * import { server } from './server.js';\n *\n * const spec = generateAsyncApiSpec({\n * server,\n * info: { title: 'My API', version: '1.0.0' },\n * servers: {\n * production: { host: 'api.example.com', protocol: 'wss' },\n * },\n * });\n * ```\n */\nexport function generateAsyncApiSpec(\n options: AsyncApiOptions\n): AsyncApiDocument {\n const subscriptions: readonly SubscriptionRegistration[] =\n options.subscriptions ??\n options.server?.getSubscriptionRegistrations() ??\n [];\n\n // Collect named schemas\n const registry = new SchemaRegistry();\n const visited = new Set<SchemaBuilder<any, any, any>>();\n for (const sub of subscriptions) {\n const m = sub.endpoint;\n if (m.incomingSchema) walkSchemas(m.incomingSchema, registry, visited);\n if (m.outgoingSchema) walkSchemas(m.outgoingSchema, registry, visited);\n }\n\n // Build channels and operations\n const channels: Record<string, unknown> = {};\n const operations: Record<string, unknown> = {};\n\n for (const sub of subscriptions) {\n const m = sub.endpoint;\n const address = resolveSubscriptionAddress(m.basePath, m.pathTemplate);\n\n // Channel ID: prefer operationId, otherwise derive from address\n const channelId = m.operationId ?? pathToChannelId(address);\n\n const messages: Record<string, unknown> = {};\n\n // Server→client events (outgoing) — the server SENDS these\n if (m.outgoingSchema) {\n const outInfo = m.outgoingSchema.introspect() as any;\n messages['ServerEvent'] = {\n name: 'ServerEvent',\n ...(outInfo.description\n ? { summary: outInfo.description }\n : {}),\n payload: convertSchema(m.outgoingSchema, registry)\n };\n }\n\n // Client→server messages (incoming) — the server RECEIVES these\n if (m.incomingSchema) {\n const inInfo = m.incomingSchema.introspect() as any;\n messages['ClientMessage'] = {\n name: 'ClientMessage',\n ...(inInfo.description ? { summary: inInfo.description } : {}),\n payload: convertSchema(m.incomingSchema, registry)\n };\n }\n\n const channel: Record<string, unknown> = { address };\n if (m.summary) channel.summary = m.summary;\n if (m.description) channel.description = m.description;\n if (m.deprecated) channel.deprecated = true;\n if (m.tags.length > 0) channel.tags = m.tags.map(t => ({ name: t }));\n if (m.externalDocs) channel.externalDocs = m.externalDocs;\n if (Object.keys(messages).length > 0) channel.messages = messages;\n\n channels[channelId] = channel;\n\n // Operation: server sends events to clients (server→client)\n if (m.outgoingSchema) {\n const opId = m.operationId\n ? `${m.operationId}Events`\n : `${channelId}_send`;\n operations[opId] = {\n action: 'send',\n channel: { $ref: `#/channels/${channelId}` },\n messages: [\n { $ref: `#/channels/${channelId}/messages/ServerEvent` }\n ]\n };\n }\n\n // Operation: server receives messages from clients (client→server)\n if (m.incomingSchema) {\n const opId = m.operationId\n ? `${m.operationId}Messages`\n : `${channelId}_receive`;\n operations[opId] = {\n action: 'receive',\n channel: { $ref: `#/channels/${channelId}` },\n messages: [\n { $ref: `#/channels/${channelId}/messages/ClientMessage` }\n ]\n };\n }\n }\n\n // Assemble the document\n const doc: Record<string, unknown> = {\n asyncapi: '3.0.0',\n info: {\n title: options.info.title,\n version: options.info.version,\n ...(options.info.description\n ? { description: options.info.description }\n : {}),\n ...(options.info.termsOfService\n ? { termsOfService: options.info.termsOfService }\n : {}),\n ...(options.info.contact ? { contact: options.info.contact } : {}),\n ...(options.info.license ? { license: options.info.license } : {})\n }\n };\n\n if (options.servers && Object.keys(options.servers).length > 0) {\n doc.servers = options.servers;\n }\n\n doc.channels = channels;\n doc.operations = operations;\n\n if (!registry.isEmpty) {\n const schemas: Record<string, unknown> = {};\n for (const [name, schema] of registry.entries()) {\n schemas[name] = convertSchema(schema, registry);\n }\n doc.components = { schemas };\n }\n\n return doc;\n}\n","import type {\n AuthenticationConfig,\n EndpointRegistration\n} from '@cleverbrush/server';\nimport { endpoint } from '@cleverbrush/server';\nimport {\n generateOpenApiSpec,\n type OpenApiDocument,\n type OpenApiInfo,\n type OpenApiServer,\n type OpenApiServer_ServerLike\n} from './generateOpenApiSpec.js';\nimport type { OpenApiSecurityScheme } from './securityMapper.js';\n\n/**\n * Options for {@link createOpenApiEndpoint}.\n *\n * When `server` is provided, `getRegistrations` and `authConfig` are derived\n * from it automatically (unless explicitly overridden).\n */\nexport interface OpenApiEndpointOptions {\n /**\n * A `ServerBuilder` (or any structurally compatible object). When set,\n * endpoint registrations and auth config are read from the server\n * automatically. Explicit `getRegistrations` / `authConfig` values\n * take precedence.\n */\n readonly server?: OpenApiServer_ServerLike;\n /** Function that returns endpoint registrations. */\n readonly getRegistrations?: () => readonly EndpointRegistration[];\n /** OpenAPI info metadata. */\n readonly info: OpenApiInfo;\n /** Optional server entries. */\n readonly servers?: readonly OpenApiServer[];\n /** Optional auth config for security scheme generation. */\n readonly authConfig?: AuthenticationConfig | null;\n /** Override security schemes manually. */\n readonly securitySchemes?: Record<string, OpenApiSecurityScheme>;\n /** Path to serve the spec at (default: `/openapi.json`). */\n readonly path?: string;\n}\n\n/**\n * Creates an endpoint definition and handler that serves the OpenAPI spec\n * as JSON. Register it with `builder.handle(ep, handler)`.\n *\n * The spec is lazily generated on first request and cached.\n *\n * @example\n * ```ts\n * const { endpoint: openApiEp, handler } = createOpenApiEndpoint({\n * server,\n * info: { title: 'My API', version: '1.0.0' }\n * });\n * server.handle(openApiEp, handler);\n * ```\n */\nexport function createOpenApiEndpoint(options: OpenApiEndpointOptions): {\n endpoint: ReturnType<(typeof endpoint)['get']>;\n handler: () => OpenApiDocument;\n} {\n const servePath = options.path ?? '/openapi.json';\n const srv = options.server;\n const getRegistrations =\n options.getRegistrations ??\n (srv ? () => srv.getRegistrations() : () => []);\n\n const ep = endpoint\n .get(servePath)\n .summary('OpenAPI specification')\n .tags('OpenAPI')\n .operationId('getOpenApiSpec');\n\n let cachedSpec: OpenApiDocument | null = null;\n\n const handler = (): OpenApiDocument => {\n if (!cachedSpec) {\n cachedSpec = generateOpenApiSpec({\n registrations: getRegistrations(),\n info: options.info,\n servers: options.servers,\n authConfig:\n options.authConfig !== undefined\n ? options.authConfig\n : (srv?.getAuthenticationConfig() ?? undefined),\n securitySchemes: options.securitySchemes\n });\n }\n return cachedSpec;\n };\n\n return { endpoint: ep, handler };\n}\n","import type {\n RequestContext,\n SubscriptionRegistration\n} from '@cleverbrush/server';\nimport {\n type AsyncApiDocument,\n type AsyncApiInfo,\n type AsyncApiServer_ServerLike,\n type AsyncApiServerEntry,\n generateAsyncApiSpec\n} from './generateAsyncApiSpec.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * Options for the {@link serveAsyncApi} middleware.\n *\n * When `server` is provided, `getSubscriptionRegistrations` is derived\n * from it automatically (unless explicitly overridden).\n */\nexport interface ServeAsyncApiOptions {\n /**\n * A `ServerBuilder` (or any structurally compatible object). When set,\n * subscription registrations are read from the server automatically.\n * An explicit `getSubscriptionRegistrations` value takes precedence.\n */\n readonly server?: AsyncApiServer_ServerLike;\n /**\n * Function that returns subscription registrations.\n * Takes precedence over `server.getSubscriptionRegistrations()`.\n */\n readonly getSubscriptionRegistrations?: () => readonly SubscriptionRegistration[];\n /** AsyncAPI info metadata (title, version, description). */\n readonly info: AsyncApiInfo;\n /**\n * Server entries to include in the spec's `servers` map.\n * Keys are server IDs (e.g. `'production'`, `'staging'`).\n */\n readonly servers?: Record<string, AsyncApiServerEntry>;\n /** Path to serve the spec at. Defaults to `/asyncapi.json`. */\n readonly path?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Middleware\n// ---------------------------------------------------------------------------\n\n/**\n * Returns a server middleware that serves the AsyncAPI 3.0 spec as JSON at\n * the configured path (default: `/asyncapi.json`).\n *\n * The spec is lazily generated on the first request and cached for subsequent\n * requests.\n *\n * @example\n * ```ts\n * import { serveAsyncApi } from '@cleverbrush/server-openapi';\n *\n * server.use(\n * serveAsyncApi({\n * server,\n * info: { title: 'My API', version: '1.0.0' },\n * servers: {\n * production: { host: 'api.example.com', protocol: 'wss' },\n * },\n * })\n * );\n * ```\n */\nexport function serveAsyncApi(\n options: ServeAsyncApiOptions\n): (context: RequestContext, next: () => Promise<void>) => Promise<void> {\n const servePath = options.path ?? '/asyncapi.json';\n const srv = options.server;\n const getSubscriptionRegistrations =\n options.getSubscriptionRegistrations ??\n (srv ? () => srv.getSubscriptionRegistrations() : () => []);\n let cachedSpec: AsyncApiDocument | null = null;\n\n return async (context, next) => {\n const url = context.url;\n const pathname = url.pathname;\n const method = context.method.toUpperCase();\n\n if (method === 'GET' && pathname === servePath) {\n if (!cachedSpec) {\n cachedSpec = generateAsyncApiSpec({\n subscriptions: getSubscriptionRegistrations(),\n info: options.info,\n servers: options.servers\n });\n }\n const body = JSON.stringify(cachedSpec);\n context.response.writeHead(200, {\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(body).toString()\n });\n context.response.end(body);\n return;\n }\n\n await next();\n };\n}\n","import type {\n AuthenticationConfig,\n EndpointRegistration,\n RequestContext\n} from '@cleverbrush/server';\nimport {\n generateOpenApiSpec,\n type OpenApiDocument,\n type OpenApiInfo,\n type OpenApiServer,\n type OpenApiServer_ServerLike\n} from './generateOpenApiSpec.js';\nimport type { OpenApiSecurityScheme } from './securityMapper.js';\n\n/**\n * Options for the {@link serveOpenApi} middleware.\n *\n * When `server` is provided, `getRegistrations` and `authConfig` are derived\n * from it automatically (unless explicitly overridden).\n */\nexport interface ServeOpenApiOptions {\n /**\n * A `ServerBuilder` (or any structurally compatible object). When set,\n * endpoint registrations and auth config are read from the server\n * automatically. Explicit `getRegistrations` / `authConfig` values\n * take precedence.\n */\n readonly server?: OpenApiServer_ServerLike;\n /** Function that returns endpoint registrations. */\n readonly getRegistrations?: () => readonly EndpointRegistration[];\n /** OpenAPI info metadata. */\n readonly info: OpenApiInfo;\n /** Optional server entries. */\n readonly servers?: readonly OpenApiServer[];\n /** Optional auth config for security scheme generation. */\n readonly authConfig?: AuthenticationConfig | null;\n /** Override security schemes manually. */\n readonly securitySchemes?: Record<string, OpenApiSecurityScheme>;\n /** Path to serve the spec at (default: `/openapi.json`). */\n readonly path?: string;\n}\n\n/**\n * Returns a server middleware that serves the OpenAPI spec as JSON at\n * the configured path (default: `/openapi.json`).\n *\n * The spec is lazily generated on first request and cached.\n */\nexport function serveOpenApi(\n options: ServeOpenApiOptions\n): (context: RequestContext, next: () => Promise<void>) => Promise<void> {\n const servePath = options.path ?? '/openapi.json';\n const srv = options.server;\n const getRegistrations =\n options.getRegistrations ??\n (srv ? () => srv.getRegistrations() : () => []);\n let cachedSpec: OpenApiDocument | null = null;\n\n return async (context, next) => {\n const url = context.url;\n const pathname = url.pathname;\n const method = context.method.toUpperCase();\n\n if (method === 'GET' && pathname === servePath) {\n if (!cachedSpec) {\n cachedSpec = generateOpenApiSpec({\n registrations: getRegistrations(),\n info: options.info,\n servers: options.servers,\n authConfig:\n options.authConfig !== undefined\n ? options.authConfig\n : (srv?.getAuthenticationConfig() ?? undefined),\n securitySchemes: options.securitySchemes\n });\n }\n const body = JSON.stringify(cachedSpec);\n context.response.writeHead(200, {\n 'content-type': 'application/json',\n 'content-length': Buffer.byteLength(body).toString()\n });\n context.response.end(body);\n return;\n }\n\n await next();\n };\n}\n"],"mappings":"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","names":["fs","path","ObjectSchemaBuilder","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","toJsonSchema","isParseStringSchema","p","convertColonParams","path","paramNames","_","name","convertParseStringPath","pathTemplate","info","templateDef","props","pathString","i","parameters","seg","resolvePath","meta","basePath","normalizeSlashes","combined","converted","fullPath","result","toJsonSchema","convertSchema","schema","registry","nameResolver","s","SchemaRegistry","schema","name","existing","walkSchemas","registry","visited","info","props","child","elements","el","options","opt","resolved","mapSecuritySchemes","authConfig","result","scheme","name","challenge","cookieName","mapOperationSecurity","authRoles","securitySchemeNames","buildParameterObject","name","location","schema","required","description","param","buildRequestBody","bodySchema","registry","example","examples","fileUpload","bodyInfo","body","mediaType","convertSchema","HTTP_STATUS_DESCRIPTIONS","PROBLEM_DETAILS_SCHEMA","buildResponseHeaders","props","headers","propSchema","propInfo","header","buildResponses","meta","method","result","ct","desc","codeStr","code","jsonSchema","respInfo","customDesc","existingContent","baseSchema","mimeType","typeEntry","linksOut","linkName","linkDef","link","params","tree","ObjectSchemaBuilder","raw","paramName","value","SYMBOL_SCHEMA_PROPERTY_DESCRIPTOR","pointer","successCode","k","buildOperation","pathParams","securitySchemeNames","operation","parameters","pp","isRequired","security","mapOperationSecurity","authRoles","ed","callbacksOut","cbName","cbDef","expression","selected","cbMethod","cbOperation","cbResponses","generateOpenApiSpec","options","srv","registrations","authConfig","webhooks","info","servers","securitySchemes","tags","resolvedSchemes","mapSecuritySchemes","SchemaRegistry","visited","reg","walkSchemas","queryProps","headerProps","webhook","resolveComponentSchemaName","rootSchema","inlinedRoot","candidate","paths","path","resolvePath","explicitNames","t","autoNames","tag","mergedTags","doc","s","webhooksObj","whMethod","whOperation","whResponses","componentSchemas","hasSchemas","components","writeOpenApiSpec","options","outputPath","spec","generateOpenApiSpec","dir","pathToChannelId","path","resolveSubscriptionAddress","basePath","pathTemplate","base","converted","normalizeSlashes","templateDef","pathString","i","result","generateAsyncApiSpec","options","subscriptions","registry","SchemaRegistry","visited","sub","m","walkSchemas","channels","operations","address","channelId","messages","outInfo","convertSchema","inInfo","channel","t","opId","doc","schemas","name","schema","endpoint","createOpenApiEndpoint","options","servePath","srv","getRegistrations","ep","endpoint","cachedSpec","generateOpenApiSpec","serveAsyncApi","options","servePath","srv","getSubscriptionRegistrations","cachedSpec","context","next","pathname","generateAsyncApiSpec","body","serveOpenApi","options","servePath","srv","getRegistrations","cachedSpec","context","next","pathname","generateOpenApiSpec","body"]}
|
package/package.json
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
"email": "andrew_zol@cleverbrush.com"
|
|
6
6
|
},
|
|
7
7
|
"peerDependencies": {
|
|
8
|
-
"@cleverbrush/server": "^
|
|
9
|
-
"@cleverbrush/schema": "^
|
|
10
|
-
"@cleverbrush/schema-json": "^
|
|
11
|
-
"@cleverbrush/auth": "^
|
|
8
|
+
"@cleverbrush/server": "^4.0.0",
|
|
9
|
+
"@cleverbrush/schema": "^4.0.0",
|
|
10
|
+
"@cleverbrush/schema-json": "^4.0.0",
|
|
11
|
+
"@cleverbrush/auth": "^4.0.0"
|
|
12
12
|
},
|
|
13
13
|
"description": "OpenAPI 3.1 spec generation for @cleverbrush/server — automatic endpoint documentation",
|
|
14
14
|
"files": [
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
},
|
|
46
46
|
"type": "module",
|
|
47
47
|
"types": "./dist/index.d.ts",
|
|
48
|
-
"version": "
|
|
48
|
+
"version": "4.1.0"
|
|
49
49
|
}
|