@warlock.js/core 5.26.0 → 5.28.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/esm/cli/commands/openapi-generator.action.mjs +43 -0
  3. package/esm/cli/commands/openapi-generator.action.mjs.map +1 -0
  4. package/esm/cli/commands/openapi-generator.command.mjs +44 -0
  5. package/esm/cli/commands/openapi-generator.command.mjs.map +1 -0
  6. package/esm/cli/commands/postman-generator.action.mjs +47 -0
  7. package/esm/cli/commands/postman-generator.action.mjs.map +1 -0
  8. package/esm/cli/commands/postman-generator.command.mjs +43 -0
  9. package/esm/cli/commands/postman-generator.command.mjs.map +1 -0
  10. package/esm/cli/framework-cli-commands.mjs +4 -0
  11. package/esm/cli/framework-cli-commands.mjs.map +1 -1
  12. package/esm/dev-server/build-development-openapi-document.mjs +36 -0
  13. package/esm/dev-server/build-development-openapi-document.mjs.map +1 -0
  14. package/esm/dev-server/collect-development-api-routes.mjs +27 -0
  15. package/esm/dev-server/collect-development-api-routes.mjs.map +1 -0
  16. package/esm/dev-server/dev-logger.mjs +9 -1
  17. package/esm/dev-server/dev-logger.mjs.map +1 -1
  18. package/esm/dev-server/development-server.mjs +6 -3
  19. package/esm/dev-server/development-server.mjs.map +1 -1
  20. package/esm/dev-server/files-orchestrator.mjs +17 -1
  21. package/esm/dev-server/files-orchestrator.mjs.map +1 -1
  22. package/esm/dev-server/model-resource-typings.mjs +246 -0
  23. package/esm/dev-server/model-resource-typings.mjs.map +1 -0
  24. package/esm/dev-server/type-generator.mjs +46 -2
  25. package/esm/dev-server/type-generator.mjs.map +1 -1
  26. package/esm/http/index.d.mts +2 -1
  27. package/esm/http/middleware/inject-request-context.d.mts.map +1 -1
  28. package/esm/http/middleware/inject-request-context.mjs +29 -3
  29. package/esm/http/middleware/inject-request-context.mjs.map +1 -1
  30. package/esm/http/serialized.d.mts +114 -0
  31. package/esm/http/serialized.d.mts.map +1 -0
  32. package/esm/index.d.mts +11 -3
  33. package/esm/index.mjs +6 -1
  34. package/esm/openapi/build-openapi-document.d.mts +17 -0
  35. package/esm/openapi/build-openapi-document.d.mts.map +1 -0
  36. package/esm/openapi/build-openapi-document.mjs +124 -0
  37. package/esm/openapi/build-openapi-document.mjs.map +1 -0
  38. package/esm/openapi/cast-schema.mjs +89 -0
  39. package/esm/openapi/cast-schema.mjs.map +1 -0
  40. package/esm/openapi/collect-openapi-document.mjs +40 -0
  41. package/esm/openapi/collect-openapi-document.mjs.map +1 -0
  42. package/esm/openapi/development-openapi-provider.d.mts +28 -0
  43. package/esm/openapi/development-openapi-provider.d.mts.map +1 -0
  44. package/esm/openapi/development-openapi-provider.mjs +35 -0
  45. package/esm/openapi/development-openapi-provider.mjs.map +1 -0
  46. package/esm/openapi/error-responses.mjs +67 -0
  47. package/esm/openapi/error-responses.mjs.map +1 -0
  48. package/esm/openapi/index.d.mts +6 -0
  49. package/esm/openapi/index.mjs +6 -0
  50. package/esm/openapi/openapi-child-output.mjs +57 -0
  51. package/esm/openapi/openapi-child-output.mjs.map +1 -0
  52. package/esm/openapi/openapi-protocol.mjs +30 -0
  53. package/esm/openapi/openapi-protocol.mjs.map +1 -0
  54. package/esm/openapi/paths.mjs +63 -0
  55. package/esm/openapi/paths.mjs.map +1 -0
  56. package/esm/openapi/postman/build-request-body.mjs +86 -0
  57. package/esm/openapi/postman/build-request-body.mjs.map +1 -0
  58. package/esm/openapi/postman/build-request-url.mjs +85 -0
  59. package/esm/openapi/postman/build-request-url.mjs.map +1 -0
  60. package/esm/openapi/postman/build-saved-responses.mjs +60 -0
  61. package/esm/openapi/postman/build-saved-responses.mjs.map +1 -0
  62. package/esm/openapi/postman/example-from-schema.d.mts +17 -0
  63. package/esm/openapi/postman/example-from-schema.d.mts.map +1 -0
  64. package/esm/openapi/postman/example-from-schema.mjs +92 -0
  65. package/esm/openapi/postman/example-from-schema.mjs.map +1 -0
  66. package/esm/openapi/postman/http-status-text.mjs +31 -0
  67. package/esm/openapi/postman/http-status-text.mjs.map +1 -0
  68. package/esm/openapi/postman/index.d.mts +3 -0
  69. package/esm/openapi/postman/openapi-to-postman-collection.d.mts +22 -0
  70. package/esm/openapi/postman/openapi-to-postman-collection.d.mts.map +1 -0
  71. package/esm/openapi/postman/openapi-to-postman-collection.mjs +109 -0
  72. package/esm/openapi/postman/openapi-to-postman-collection.mjs.map +1 -0
  73. package/esm/openapi/postman/postman-types.d.mts +119 -0
  74. package/esm/openapi/postman/postman-types.d.mts.map +1 -0
  75. package/esm/openapi/postman/resolve-operation-auth.mjs +51 -0
  76. package/esm/openapi/postman/resolve-operation-auth.mjs.map +1 -0
  77. package/esm/openapi/postman/schema-refs.d.mts +7 -0
  78. package/esm/openapi/postman/schema-refs.d.mts.map +1 -0
  79. package/esm/openapi/postman/schema-refs.mjs +41 -0
  80. package/esm/openapi/postman/schema-refs.mjs.map +1 -0
  81. package/esm/openapi/postman/uuid-v5.mjs +29 -0
  82. package/esm/openapi/postman/uuid-v5.mjs.map +1 -0
  83. package/esm/openapi/request-schemas.mjs +138 -0
  84. package/esm/openapi/request-schemas.mjs.map +1 -0
  85. package/esm/openapi/response-schemas.mjs +85 -0
  86. package/esm/openapi/response-schemas.mjs.map +1 -0
  87. package/esm/openapi/schema-registry.mjs +143 -0
  88. package/esm/openapi/schema-registry.mjs.map +1 -0
  89. package/esm/openapi/security.mjs +98 -0
  90. package/esm/openapi/security.mjs.map +1 -0
  91. package/esm/openapi/types.d.mts +119 -0
  92. package/esm/openapi/types.d.mts.map +1 -0
  93. package/esm/production/registration-child-runner.mjs +139 -0
  94. package/esm/production/registration-child-runner.mjs.map +1 -0
  95. package/esm/production/route-registration-child.mjs +30 -2
  96. package/esm/production/route-registration-child.mjs.map +1 -1
  97. package/esm/production/route-registration-snapshot.mjs +22 -119
  98. package/esm/production/route-registration-snapshot.mjs.map +1 -1
  99. package/esm/resource/define-resource.d.mts +44 -14
  100. package/esm/resource/define-resource.d.mts.map +1 -1
  101. package/esm/resource/define-resource.mjs +0 -40
  102. package/esm/resource/define-resource.mjs.map +1 -1
  103. package/esm/resource/index.d.mts +1 -1
  104. package/esm/resource/resource.d.mts +4 -4
  105. package/esm/resource/resource.d.mts.map +1 -1
  106. package/esm/resource/resource.mjs +2 -4
  107. package/esm/resource/resource.mjs.map +1 -1
  108. package/esm/resource/types.d.mts +89 -4
  109. package/esm/resource/types.d.mts.map +1 -1
  110. package/esm/router/index.d.mts +1 -1
  111. package/esm/router/named-api-routes-with-responses.mjs +59 -0
  112. package/esm/router/named-api-routes-with-responses.mjs.map +1 -0
  113. package/esm/router/response-schema-types.mjs +100 -0
  114. package/esm/router/response-schema-types.mjs.map +1 -0
  115. package/esm/router/router.d.mts +2 -2
  116. package/esm/router/router.d.mts.map +1 -1
  117. package/esm/router/router.mjs +38 -11
  118. package/esm/router/router.mjs.map +1 -1
  119. package/esm/router/types.d.mts +16 -1
  120. package/esm/router/types.d.mts.map +1 -1
  121. package/llms-full.txt +293 -5
  122. package/llms.txt +3 -2
  123. package/package.json +13 -13
  124. package/skills/README.md +3 -2
  125. package/skills/create-controller/SKILL.md +39 -2
  126. package/skills/define-resource/SKILL.md +100 -1
  127. package/skills/generate-openapi/SKILL.md +148 -0
  128. package/skills/index.md +3 -3
  129. package/skills/validate-input/SKILL.md +2 -2
@@ -0,0 +1,17 @@
1
+ import { OpenApiBuildResult, OpenApiContext, OpenApiRouteInput } from "./types.mjs";
2
+
3
+ //#region ../core/src/openapi/build-openapi-document.d.ts
4
+ /** `jsonSchemaDialect` of every document: OpenAPI 3.1 schemas are JSON Schema 2020-12. */
5
+ declare const OPENAPI_JSON_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
6
+ /**
7
+ * Build an OpenAPI 3.1 document from registered routes.
8
+ *
9
+ * Pure: it reads the routes it is given (`router.list()` in the app), converts Seal schemas
10
+ * with `toJsonSchema("draft-2020-12")` and returns plain JSON plus the gaps it found as
11
+ * `warnings`. It never throws for a single route it cannot document; that route is
12
+ * documented as far as possible and a warning explains what was left out.
13
+ */
14
+ declare function buildOpenApiDocument(routes: readonly OpenApiRouteInput[], context: OpenApiContext): OpenApiBuildResult;
15
+ //#endregion
16
+ export { OPENAPI_JSON_SCHEMA_DIALECT, buildOpenApiDocument };
17
+ //# sourceMappingURL=build-openapi-document.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-openapi-document.d.mts","names":[],"sources":["../../../../../../../core/src/openapi/build-openapi-document.ts"],"mappings":";;;;cA2Ba,2BAAA;AAAb;;;;AAAwC;AA8BxC;;;AA9BA,iBA8BgB,oBAAA,CACd,MAAA,WAAiB,iBAAA,IACjB,OAAA,EAAS,cAAA,GACR,kBAAA"}
@@ -0,0 +1,124 @@
1
+ import { buildDeclaredResponses } from "./response-schemas.mjs";
2
+ import { buildUnauthorizedResponse, buildValidationFailedResponse, resolveValidationResponse } from "./error-responses.mjs";
3
+ import { buildOperationId, expandMethods, firstSegmentTag, toOpenApiPath } from "./paths.mjs";
4
+ import { buildRequestParts } from "./request-schemas.mjs";
5
+ import { SchemaRegistry } from "./schema-registry.mjs";
6
+ import { SecuritySchemes, readRouteAuth } from "./security.mjs";
7
+
8
+ //#region ../core/src/openapi/build-openapi-document.ts
9
+ /** `jsonSchemaDialect` of every document: OpenAPI 3.1 schemas are JSON Schema 2020-12. */
10
+ const OPENAPI_JSON_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
11
+ const UNSAFE_METHODS = [
12
+ "post",
13
+ "put",
14
+ "patch",
15
+ "delete"
16
+ ];
17
+ function describeAuth(auth, method) {
18
+ const lines = [];
19
+ if (auth.userTypes.length > 0) lines.push(`Requires an authenticated user of type: ${auth.userTypes.join(", ")}.`);
20
+ if (auth.sources.some((source) => typeof source !== "string") && UNSAFE_METHODS.includes(method)) lines.push("When authenticating with the cookie, the request must also carry a same-origin Origin or Referer header (CSRF guard).");
21
+ return lines;
22
+ }
23
+ /**
24
+ * Build an OpenAPI 3.1 document from registered routes.
25
+ *
26
+ * Pure: it reads the routes it is given (`router.list()` in the app), converts Seal schemas
27
+ * with `toJsonSchema("draft-2020-12")` and returns plain JSON plus the gaps it found as
28
+ * `warnings`. It never throws for a single route it cannot document; that route is
29
+ * documented as far as possible and a warning explains what was left out.
30
+ */
31
+ function buildOpenApiDocument(routes, context) {
32
+ const warnings = [];
33
+ const registry = new SchemaRegistry(context.resolveResourceName, (message) => warnings.push(message));
34
+ const security = new SecuritySchemes();
35
+ const validationShape = resolveValidationResponse(context.validationResponse);
36
+ const paths = {};
37
+ const tags = /* @__PURE__ */ new Set();
38
+ const operationIds = /* @__PURE__ */ new Set();
39
+ for (const route of routes) {
40
+ if (route.isPage && !context.includePages) continue;
41
+ const methods = expandMethods(route.method);
42
+ const label = `${String(route.method).toUpperCase()} ${route.path}`;
43
+ const warn = (message) => warnings.push(`${label}: ${message}`);
44
+ if (methods.length === 0) {
45
+ warn(`unsupported method "${String(route.method)}", route skipped.`);
46
+ continue;
47
+ }
48
+ const handler = route.handler;
49
+ const validation = handler?.validation;
50
+ const auth = readRouteAuth(route.middleware, warn);
51
+ const tag = firstSegmentTag(route.path);
52
+ const pathKey = toOpenApiPath(route.path);
53
+ if (validation?.validate) warn("validation.validate is custom middleware and is not documented.");
54
+ for (const method of methods) {
55
+ paths[pathKey] ??= {};
56
+ if (paths[pathKey][method]) {
57
+ warn(`${method.toUpperCase()} ${pathKey} is already documented by an earlier route; this one is skipped.`);
58
+ continue;
59
+ }
60
+ let operationId = buildOperationId(route.name, method, route.path, methods.length > 1);
61
+ if (operationIds.has(operationId)) {
62
+ let suffix = 2;
63
+ while (operationIds.has(`${operationId}_${suffix}`)) suffix++;
64
+ warn(`operationId "${operationId}" is already used; renamed to "${operationId}_${suffix}".`);
65
+ operationId = `${operationId}_${suffix}`;
66
+ }
67
+ operationIds.add(operationId);
68
+ const description = [route.description ?? handler?.description, ...auth ? describeAuth(auth, method) : []].filter((line) => typeof line === "string" && line.length > 0);
69
+ const request = buildRequestParts({
70
+ method,
71
+ path: route.path,
72
+ validation,
73
+ warn
74
+ });
75
+ const responses = route.isPage ? { "200": {
76
+ description: "HTML page",
77
+ content: { "text/html": { schema: { type: "string" } } }
78
+ } } : buildDeclaredResponses(handler?.responseSchema, registry, warn);
79
+ if (!Object.keys(responses).some((status) => status.startsWith("2"))) responses["200"] = { description: "Successful response" };
80
+ if (validation?.schema) {
81
+ const failed = buildValidationFailedResponse(validationShape, registry);
82
+ responses[failed.status] ??= failed.response;
83
+ }
84
+ if (auth) responses["401"] ??= buildUnauthorizedResponse(registry);
85
+ if (tag) tags.add(tag);
86
+ const operation = {
87
+ operationId,
88
+ ...route.label ? { summary: route.label } : {},
89
+ ...description.length > 0 ? { description: description.join("\n\n") } : {},
90
+ ...tag ? { tags: [tag] } : {},
91
+ ...request.parameters.length > 0 ? { parameters: request.parameters } : {},
92
+ ...request.requestBody ? { requestBody: request.requestBody } : {},
93
+ responses: sortResponses(responses),
94
+ ...auth ? { security: security.requirementsFor(auth) } : {}
95
+ };
96
+ paths[pathKey][method] = operation;
97
+ }
98
+ }
99
+ const document = {
100
+ openapi: "3.1.0",
101
+ jsonSchemaDialect: OPENAPI_JSON_SCHEMA_DIALECT,
102
+ info: { ...context.info },
103
+ ...context.servers && context.servers.length > 0 ? { servers: context.servers.map((url) => ({ url })) } : {},
104
+ ...tags.size > 0 ? { tags: [...tags].sort().map((name) => ({ name })) } : {},
105
+ paths
106
+ };
107
+ const hasSchemas = Object.keys(registry.schemas).length > 0;
108
+ if (hasSchemas || security.hasSchemes()) {
109
+ document.components = {};
110
+ if (hasSchemas) document.components.schemas = registry.schemas;
111
+ if (security.hasSchemes()) document.components.securitySchemes = security.schemes;
112
+ }
113
+ return {
114
+ document,
115
+ warnings: [...new Set(warnings)]
116
+ };
117
+ }
118
+ function sortResponses(responses) {
119
+ return Object.fromEntries(Object.entries(responses).sort(([first], [second]) => first.localeCompare(second)));
120
+ }
121
+
122
+ //#endregion
123
+ export { OPENAPI_JSON_SCHEMA_DIALECT, buildOpenApiDocument };
124
+ //# sourceMappingURL=build-openapi-document.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-openapi-document.mjs","names":[],"sources":["../../../../../../../core/src/openapi/build-openapi-document.ts"],"sourcesContent":["import {\n buildUnauthorizedResponse,\n buildValidationFailedResponse,\n resolveValidationResponse,\n} from \"./error-responses\";\nimport {\n buildOperationId,\n expandMethods,\n firstSegmentTag,\n toOpenApiPath,\n type OpenApiMethod,\n} from \"./paths\";\nimport { buildRequestParts } from \"./request-schemas\";\nimport { buildDeclaredResponses } from \"./response-schemas\";\nimport { SchemaRegistry } from \"./schema-registry\";\nimport { readRouteAuth, SecuritySchemes } from \"./security\";\nimport type {\n AuthDescriptor,\n OpenApiBuildResult,\n OpenApiContext,\n OpenApiDocument,\n OpenApiOperation,\n OpenApiResponse,\n OpenApiRouteInput,\n} from \"./types\";\n\n/** `jsonSchemaDialect` of every document: OpenAPI 3.1 schemas are JSON Schema 2020-12. */\nexport const OPENAPI_JSON_SCHEMA_DIALECT = \"https://json-schema.org/draft/2020-12/schema\";\n\nconst UNSAFE_METHODS: readonly OpenApiMethod[] = [\"post\", \"put\", \"patch\", \"delete\"];\n\nfunction describeAuth(auth: AuthDescriptor, method: OpenApiMethod): string[] {\n const lines: string[] = [];\n\n if (auth.userTypes.length > 0) {\n lines.push(`Requires an authenticated user of type: ${auth.userTypes.join(\", \")}.`);\n }\n\n const usesCookie = auth.sources.some((source) => typeof source !== \"string\");\n\n if (usesCookie && UNSAFE_METHODS.includes(method)) {\n lines.push(\n \"When authenticating with the cookie, the request must also carry a same-origin Origin or Referer header (CSRF guard).\",\n );\n }\n\n return lines;\n}\n\n/**\n * Build an OpenAPI 3.1 document from registered routes.\n *\n * Pure: it reads the routes it is given (`router.list()` in the app), converts Seal schemas\n * with `toJsonSchema(\"draft-2020-12\")` and returns plain JSON plus the gaps it found as\n * `warnings`. It never throws for a single route it cannot document; that route is\n * documented as far as possible and a warning explains what was left out.\n */\nexport function buildOpenApiDocument(\n routes: readonly OpenApiRouteInput[],\n context: OpenApiContext,\n): OpenApiBuildResult {\n const warnings: string[] = [];\n const registry = new SchemaRegistry(context.resolveResourceName, (message) => warnings.push(message));\n const security = new SecuritySchemes();\n const validationShape = resolveValidationResponse(context.validationResponse);\n const paths: OpenApiDocument[\"paths\"] = {};\n const tags = new Set<string>();\n const operationIds = new Set<string>();\n\n for (const route of routes) {\n if (route.isPage && !context.includePages) {\n continue;\n }\n\n const methods = expandMethods(route.method);\n const label = `${String(route.method).toUpperCase()} ${route.path}`;\n const warn = (message: string) => warnings.push(`${label}: ${message}`);\n\n if (methods.length === 0) {\n warn(`unsupported method \"${String(route.method)}\", route skipped.`);\n\n continue;\n }\n\n const handler = route.handler as OpenApiRouteInput[\"handler\"] | undefined;\n const validation = handler?.validation;\n const auth = readRouteAuth(route.middleware, warn);\n const tag = firstSegmentTag(route.path);\n const pathKey = toOpenApiPath(route.path);\n\n if (validation?.validate) {\n warn(\"validation.validate is custom middleware and is not documented.\");\n }\n\n for (const method of methods) {\n paths[pathKey] ??= {};\n\n if (paths[pathKey][method]) {\n warn(`${method.toUpperCase()} ${pathKey} is already documented by an earlier route; this one is skipped.`);\n\n continue;\n }\n\n let operationId = buildOperationId(route.name, method, route.path, methods.length > 1);\n\n if (operationIds.has(operationId)) {\n let suffix = 2;\n\n while (operationIds.has(`${operationId}_${suffix}`)) {\n suffix++;\n }\n\n warn(`operationId \"${operationId}\" is already used; renamed to \"${operationId}_${suffix}\".`);\n operationId = `${operationId}_${suffix}`;\n }\n\n operationIds.add(operationId);\n\n const description = [\n route.description ?? handler?.description,\n ...(auth ? describeAuth(auth, method) : []),\n ].filter((line): line is string => typeof line === \"string\" && line.length > 0);\n\n const request = buildRequestParts({ method, path: route.path, validation, warn });\n\n const responses: Record<string, OpenApiResponse> = route.isPage\n ? { \"200\": { description: \"HTML page\", content: { \"text/html\": { schema: { type: \"string\" } } } } }\n : buildDeclaredResponses(handler?.responseSchema, registry, warn);\n\n if (!Object.keys(responses).some((status) => status.startsWith(\"2\"))) {\n responses[\"200\"] = { description: \"Successful response\" };\n }\n\n if (validation?.schema) {\n const failed = buildValidationFailedResponse(validationShape, registry);\n\n responses[failed.status] ??= failed.response;\n }\n\n if (auth) {\n responses[\"401\"] ??= buildUnauthorizedResponse(registry);\n }\n\n if (tag) {\n tags.add(tag);\n }\n\n // Built in reading order: identity, prose, inputs, outputs, security.\n const operation: OpenApiOperation = {\n operationId,\n ...(route.label ? { summary: route.label } : {}),\n ...(description.length > 0 ? { description: description.join(\"\\n\\n\") } : {}),\n ...(tag ? { tags: [tag] } : {}),\n ...(request.parameters.length > 0 ? { parameters: request.parameters } : {}),\n ...(request.requestBody ? { requestBody: request.requestBody } : {}),\n responses: sortResponses(responses),\n ...(auth ? { security: security.requirementsFor(auth) } : {}),\n };\n\n paths[pathKey][method] = operation;\n }\n }\n\n // Keys in reading order: header, servers, tags, paths, components.\n const document: OpenApiDocument = {\n openapi: \"3.1.0\",\n jsonSchemaDialect: OPENAPI_JSON_SCHEMA_DIALECT,\n info: { ...context.info },\n ...(context.servers && context.servers.length > 0\n ? { servers: context.servers.map((url) => ({ url })) }\n : {}),\n ...(tags.size > 0 ? { tags: [...tags].sort().map((name) => ({ name })) } : {}),\n paths,\n };\n\n const hasSchemas = Object.keys(registry.schemas).length > 0;\n\n if (hasSchemas || security.hasSchemes()) {\n document.components = {};\n\n if (hasSchemas) {\n document.components.schemas = registry.schemas;\n }\n\n if (security.hasSchemes()) {\n document.components.securitySchemes = security.schemes;\n }\n }\n\n return { document, warnings: [...new Set(warnings)] };\n}\n\nfunction sortResponses(responses: Record<string, OpenApiResponse>): Record<string, OpenApiResponse> {\n return Object.fromEntries(\n Object.entries(responses).sort(([first], [second]) => first.localeCompare(second)),\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ //#region ../core/src/openapi/cast-schema.ts
2
+ /**
3
+ * JSON Schema of the DEFAULT date output of a resource field (`iso`, `format`, `timestamp`
4
+ * and `humanTime` on; `timezone`, `locale` and `offset` off), matching `BaseCastOutput<"date">`.
5
+ */
6
+ const DEFAULT_DATE_SCHEMA = {
7
+ type: "object",
8
+ properties: {
9
+ iso: {
10
+ type: "string",
11
+ format: "date-time"
12
+ },
13
+ format: { type: "string" },
14
+ timestamp: { type: "number" },
15
+ humanTime: { type: "string" }
16
+ },
17
+ required: [
18
+ "iso",
19
+ "format",
20
+ "timestamp",
21
+ "humanTime"
22
+ ]
23
+ };
24
+ function baseCastSchema(base) {
25
+ switch (base) {
26
+ case "string":
27
+ case "localized": return { type: "string" };
28
+ case "url":
29
+ case "uploadsUrl":
30
+ case "storageUrl": return {
31
+ type: "string",
32
+ format: "uri"
33
+ };
34
+ case "number":
35
+ case "float": return { type: "number" };
36
+ case "int": return { type: "integer" };
37
+ case "boolean": return { type: "boolean" };
38
+ case "object": return { type: "object" };
39
+ case "array": return { type: "array" };
40
+ case "date": return structuredClone(DEFAULT_DATE_SCHEMA);
41
+ default: return;
42
+ }
43
+ }
44
+ /**
45
+ * Make a schema accept `null` too, the 3.1 way: `type: [T, "null"]`. A schema without a plain
46
+ * `type` (a `$ref`, `{}`) is wrapped in `anyOf` with `{ type: "null" }`.
47
+ */
48
+ function makeNullable(schema) {
49
+ if (typeof schema.type === "string") return {
50
+ ...schema,
51
+ type: [schema.type, "null"]
52
+ };
53
+ if (Array.isArray(schema.type)) return schema.type.includes("null") ? schema : {
54
+ ...schema,
55
+ type: [...schema.type, "null"]
56
+ };
57
+ return { anyOf: [schema, { type: "null" }] };
58
+ }
59
+ /**
60
+ * JSON Schema of a resource cast string, suffixes included (`"string"`, `"number?"`,
61
+ * `"string[]"`, `"date[]?"`). `?` means "always present, value or null"; `[]` wraps the base
62
+ * in an array, and with `?` it is the array that may be null.
63
+ *
64
+ * @returns `undefined` for a cast this mapper does not know.
65
+ */
66
+ function castToJsonSchema(cast) {
67
+ let base = cast;
68
+ let nullable = false;
69
+ let isArray = false;
70
+ if (base.endsWith("?")) {
71
+ nullable = true;
72
+ base = base.slice(0, -1);
73
+ }
74
+ if (base.endsWith("[]")) {
75
+ isArray = true;
76
+ base = base.slice(0, -2);
77
+ }
78
+ const baseSchema = baseCastSchema(base);
79
+ if (!baseSchema) return;
80
+ const shaped = isArray ? {
81
+ type: "array",
82
+ items: baseSchema
83
+ } : baseSchema;
84
+ return nullable ? makeNullable(shaped) : shaped;
85
+ }
86
+
87
+ //#endregion
88
+ export { castToJsonSchema };
89
+ //# sourceMappingURL=cast-schema.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cast-schema.mjs","names":[],"sources":["../../../../../../../core/src/openapi/cast-schema.ts"],"sourcesContent":["import type { OpenApiSchema } from \"./types\";\n\n/**\n * JSON Schema of the DEFAULT date output of a resource field (`iso`, `format`, `timestamp`\n * and `humanTime` on; `timezone`, `locale` and `offset` off), matching `BaseCastOutput<\"date\">`.\n */\nconst DEFAULT_DATE_SCHEMA: OpenApiSchema = {\n type: \"object\",\n properties: {\n iso: { type: \"string\", format: \"date-time\" },\n format: { type: \"string\" },\n timestamp: { type: \"number\" },\n humanTime: { type: \"string\" },\n },\n required: [\"iso\", \"format\", \"timestamp\", \"humanTime\"],\n};\n\nfunction baseCastSchema(base: string): OpenApiSchema | undefined {\n switch (base) {\n case \"string\":\n case \"localized\":\n return { type: \"string\" };\n case \"url\":\n case \"uploadsUrl\":\n case \"storageUrl\":\n return { type: \"string\", format: \"uri\" };\n case \"number\":\n case \"float\":\n return { type: \"number\" };\n case \"int\":\n return { type: \"integer\" };\n case \"boolean\":\n return { type: \"boolean\" };\n case \"object\":\n return { type: \"object\" };\n case \"array\":\n return { type: \"array\" };\n case \"date\":\n return structuredClone(DEFAULT_DATE_SCHEMA);\n default:\n return undefined;\n }\n}\n\n/**\n * Make a schema accept `null` too, the 3.1 way: `type: [T, \"null\"]`. A schema without a plain\n * `type` (a `$ref`, `{}`) is wrapped in `anyOf` with `{ type: \"null\" }`.\n */\nexport function makeNullable(schema: OpenApiSchema): OpenApiSchema {\n if (typeof schema.type === \"string\") {\n return { ...schema, type: [schema.type, \"null\"] };\n }\n\n if (Array.isArray(schema.type)) {\n return schema.type.includes(\"null\") ? schema : { ...schema, type: [...schema.type, \"null\"] };\n }\n\n return { anyOf: [schema, { type: \"null\" }] };\n}\n\n/**\n * JSON Schema of a resource cast string, suffixes included (`\"string\"`, `\"number?\"`,\n * `\"string[]\"`, `\"date[]?\"`). `?` means \"always present, value or null\"; `[]` wraps the base\n * in an array, and with `?` it is the array that may be null.\n *\n * @returns `undefined` for a cast this mapper does not know.\n */\nexport function castToJsonSchema(cast: string): OpenApiSchema | undefined {\n let base = cast;\n let nullable = false;\n let isArray = false;\n\n if (base.endsWith(\"?\")) {\n nullable = true;\n base = base.slice(0, -1);\n }\n\n if (base.endsWith(\"[]\")) {\n isArray = true;\n base = base.slice(0, -2);\n }\n\n const baseSchema = baseCastSchema(base);\n\n if (!baseSchema) {\n return undefined;\n }\n\n const shaped: OpenApiSchema = isArray ? { type: \"array\", items: baseSchema } : baseSchema;\n\n return nullable ? makeNullable(shaped) : shaped;\n}\n"],"mappings":";;;;;AAMA,MAAM,sBAAqC;CACzC,MAAM;CACN,YAAY;EACV,KAAK;GAAE,MAAM;GAAU,QAAQ;EAAY;EAC3C,QAAQ,EAAE,MAAM,SAAS;EACzB,WAAW,EAAE,MAAM,SAAS;EAC5B,WAAW,EAAE,MAAM,SAAS;CAC9B;CACA,UAAU;EAAC;EAAO;EAAU;EAAa;CAAW;AACtD;AAEA,SAAS,eAAe,MAAyC;CAC/D,QAAQ,MAAR;EACE,KAAK;EACL,KAAK,aACH,OAAO,EAAE,MAAM,SAAS;EAC1B,KAAK;EACL,KAAK;EACL,KAAK,cACH,OAAO;GAAE,MAAM;GAAU,QAAQ;EAAM;EACzC,KAAK;EACL,KAAK,SACH,OAAO,EAAE,MAAM,SAAS;EAC1B,KAAK,OACH,OAAO,EAAE,MAAM,UAAU;EAC3B,KAAK,WACH,OAAO,EAAE,MAAM,UAAU;EAC3B,KAAK,UACH,OAAO,EAAE,MAAM,SAAS;EAC1B,KAAK,SACH,OAAO,EAAE,MAAM,QAAQ;EACzB,KAAK,QACH,OAAO,gBAAgB,mBAAmB;EAC5C,SACE;CACJ;AACF;;;;;AAMA,SAAgB,aAAa,QAAsC;CACjE,IAAI,OAAO,OAAO,SAAS,UACzB,OAAO;EAAE,GAAG;EAAQ,MAAM,CAAC,OAAO,MAAM,MAAM;CAAE;CAGlD,IAAI,MAAM,QAAQ,OAAO,IAAI,GAC3B,OAAO,OAAO,KAAK,SAAS,MAAM,IAAI,SAAS;EAAE,GAAG;EAAQ,MAAM,CAAC,GAAG,OAAO,MAAM,MAAM;CAAE;CAG7F,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,EAAE;AAC7C;;;;;;;;AASA,SAAgB,iBAAiB,MAAyC;CACxE,IAAI,OAAO;CACX,IAAI,WAAW;CACf,IAAI,UAAU;CAEd,IAAI,KAAK,SAAS,GAAG,GAAG;EACtB,WAAW;EACX,OAAO,KAAK,MAAM,GAAG,EAAE;CACzB;CAEA,IAAI,KAAK,SAAS,IAAI,GAAG;EACvB,UAAU;EACV,OAAO,KAAK,MAAM,GAAG,EAAE;CACzB;CAEA,MAAM,aAAa,eAAe,IAAI;CAEtC,IAAI,CAAC,YACH;CAGF,MAAM,SAAwB,UAAU;EAAE,MAAM;EAAS,OAAO;CAAW,IAAI;CAE/E,OAAO,WAAW,aAAa,MAAM,IAAI;AAC3C"}
@@ -0,0 +1,40 @@
1
+ import { runRegistrationChild } from "../production/registration-child-runner.mjs";
2
+ import { OPENAPI_RESULT_MESSAGE, parseOpenApiChildResult } from "./openapi-protocol.mjs";
3
+
4
+ //#region ../core/src/openapi/collect-openapi-document.ts
5
+ /**
6
+ * Build the app's OpenAPI document in a fresh process.
7
+ *
8
+ * Uses the same registration child as `warlock build` (it imports the route modules without
9
+ * booting connectors) in OpenAPI mode: the document is built inside the child, where the Seal
10
+ * schemas and resource classes live, and only the finished JSON crosses IPC.
11
+ */
12
+ function collectOpenApiDocument(options = {}) {
13
+ const cwd = options.cwd ?? process.cwd();
14
+ const openapi = {
15
+ version: 1,
16
+ ...options.title !== void 0 ? { title: options.title } : {},
17
+ ...options.server !== void 0 ? { server: options.server } : {},
18
+ ...options.includePages !== void 0 ? { includePages: options.includePages } : {}
19
+ };
20
+ return runRegistrationChild({
21
+ cwd,
22
+ timeoutMs: options.timeoutMs,
23
+ childEntry: options.childEntry,
24
+ forkProcess: options.forkProcess,
25
+ request: {
26
+ version: 1,
27
+ cwd,
28
+ environment: options.environment,
29
+ runtimeStrategy: options.runtimeStrategy,
30
+ openapi
31
+ },
32
+ resultMessageType: OPENAPI_RESULT_MESSAGE,
33
+ resultNoun: "document",
34
+ readResult: (message) => parseOpenApiChildResult(message.result)
35
+ });
36
+ }
37
+
38
+ //#endregion
39
+ export { collectOpenApiDocument };
40
+ //# sourceMappingURL=collect-openapi-document.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-openapi-document.mjs","names":[],"sources":["../../../../../../../core/src/openapi/collect-openapi-document.ts"],"sourcesContent":["import {\n runRegistrationChild,\n type RegistrationChildForker,\n} from \"../production/registration-child-runner\";\nimport type { Environment, RuntimeStrategy } from \"../utils/environment\";\nimport {\n OPENAPI_PROTOCOL_VERSION,\n OPENAPI_RESULT_MESSAGE,\n parseOpenApiChildResult,\n type OpenApiChildRequestOptions,\n type OpenApiChildResult,\n} from \"./openapi-protocol\";\n\nexport type CollectOpenApiDocumentOptions = Readonly<{\n cwd?: string;\n environment?: Environment;\n runtimeStrategy?: RuntimeStrategy;\n timeoutMs?: number;\n childEntry?: string;\n forkProcess?: RegistrationChildForker;\n title?: string;\n server?: string;\n includePages?: boolean;\n}>;\n\n/**\n * Build the app's OpenAPI document in a fresh process.\n *\n * Uses the same registration child as `warlock build` (it imports the route modules without\n * booting connectors) in OpenAPI mode: the document is built inside the child, where the Seal\n * schemas and resource classes live, and only the finished JSON crosses IPC.\n */\nexport function collectOpenApiDocument(\n options: CollectOpenApiDocumentOptions = {},\n): Promise<OpenApiChildResult> {\n const cwd = options.cwd ?? process.cwd();\n const openapi: OpenApiChildRequestOptions = {\n version: OPENAPI_PROTOCOL_VERSION,\n ...(options.title !== undefined ? { title: options.title } : {}),\n ...(options.server !== undefined ? { server: options.server } : {}),\n ...(options.includePages !== undefined ? { includePages: options.includePages } : {}),\n };\n\n return runRegistrationChild<OpenApiChildResult>({\n cwd,\n timeoutMs: options.timeoutMs,\n childEntry: options.childEntry,\n forkProcess: options.forkProcess,\n request: {\n version: 1,\n cwd,\n environment: options.environment,\n runtimeStrategy: options.runtimeStrategy,\n openapi,\n },\n resultMessageType: OPENAPI_RESULT_MESSAGE,\n resultNoun: \"document\",\n readResult: (message) => parseOpenApiChildResult(message.result),\n });\n}\n"],"mappings":";;;;;;;;;;;AAgCA,SAAgB,uBACd,UAAyC,CAAC,GACb;CAC7B,MAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;CACvC,MAAM,UAAsC;EAC1C;EACA,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;EAC9D,GAAI,QAAQ,WAAW,SAAY,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;EACjE,GAAI,QAAQ,iBAAiB,SAAY,EAAE,cAAc,QAAQ,aAAa,IAAI,CAAC;CACrF;CAEA,OAAO,qBAAyC;EAC9C;EACA,WAAW,QAAQ;EACnB,YAAY,QAAQ;EACpB,aAAa,QAAQ;EACrB,SAAS;GACP,SAAS;GACT;GACA,aAAa,QAAQ;GACrB,iBAAiB,QAAQ;GACzB;EACF;EACA,mBAAmB;EACnB,YAAY;EACZ,aAAa,YAAY,wBAAwB,QAAQ,MAAM;CACjE,CAAC;AACH"}
@@ -0,0 +1,28 @@
1
+ import { PostmanCollection, PostmanCollectionOptions } from "./postman/postman-types.mjs";
2
+ import { OpenApiBuildResult } from "./types.mjs";
3
+
4
+ //#region ../core/src/openapi/development-openapi-provider.d.ts
5
+ type DevelopmentOpenApiProvider = () => Promise<OpenApiBuildResult>;
6
+ type DevelopmentPostmanResult = {
7
+ collection: PostmanCollection;
8
+ warnings: string[];
9
+ };
10
+ /**
11
+ * Called by the dev server at startup. The builder lives under `dev-server/`, so the
12
+ * production entry never reaches it; this registry is the only thing the root exports.
13
+ */
14
+ declare function registerDevelopmentOpenApiProvider(build: DevelopmentOpenApiProvider): void;
15
+ /**
16
+ * The OpenAPI document for the routes registered in this process, built by the dev
17
+ * server's provider. Throws outside `warlock dev`, where no provider is registered.
18
+ */
19
+ declare function getDevelopmentOpenApiDocument(): Promise<OpenApiBuildResult>;
20
+ /**
21
+ * The Postman collection (v2.1.0) for the routes registered in this process: the development
22
+ * OpenAPI document run through `openApiToPostmanCollection`, so both always agree. Throws
23
+ * outside `warlock dev`, like `getDevelopmentOpenApiDocument`.
24
+ */
25
+ declare function getDevelopmentPostmanCollection(options?: PostmanCollectionOptions): Promise<DevelopmentPostmanResult>;
26
+ //#endregion
27
+ export { DevelopmentPostmanResult, getDevelopmentOpenApiDocument, getDevelopmentPostmanCollection, registerDevelopmentOpenApiProvider };
28
+ //# sourceMappingURL=development-openapi-provider.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"development-openapi-provider.d.mts","names":[],"sources":["../../../../../../../core/src/openapi/development-openapi-provider.ts"],"mappings":";;;;KAIK,0BAAA,SAAmC,OAAO,CAAC,kBAAA;AAAA,KAEpC,wBAAA;EACV,UAAA,EAAY,iBAAiB;EAC7B,QAAA;AAAA;;AAJgE;AAElE;;iBAWgB,kCAAA,CAAmC,KAAiC,EAA1B,0BAA0B;;;;;iBAQ9D,6BAAA,IAAiC,OAAO,CAAC,kBAAA;AAjBrD;AASV;;;;AATU,iBAgCY,+BAAA,CACpB,OAAA,GAAS,wBAAA,GACR,OAAA,CAAQ,wBAAA"}
@@ -0,0 +1,35 @@
1
+ import { openApiToPostmanCollection } from "./postman/openapi-to-postman-collection.mjs";
2
+
3
+ //#region ../core/src/openapi/development-openapi-provider.ts
4
+ let provider;
5
+ /**
6
+ * Called by the dev server at startup. The builder lives under `dev-server/`, so the
7
+ * production entry never reaches it; this registry is the only thing the root exports.
8
+ */
9
+ function registerDevelopmentOpenApiProvider(build) {
10
+ provider = build;
11
+ }
12
+ /**
13
+ * The OpenAPI document for the routes registered in this process, built by the dev
14
+ * server's provider. Throws outside `warlock dev`, where no provider is registered.
15
+ */
16
+ async function getDevelopmentOpenApiDocument() {
17
+ if (!provider) throw new Error("No development OpenAPI provider is registered; the API docs are only available under `warlock dev`.");
18
+ return provider();
19
+ }
20
+ /**
21
+ * The Postman collection (v2.1.0) for the routes registered in this process: the development
22
+ * OpenAPI document run through `openApiToPostmanCollection`, so both always agree. Throws
23
+ * outside `warlock dev`, like `getDevelopmentOpenApiDocument`.
24
+ */
25
+ async function getDevelopmentPostmanCollection(options = {}) {
26
+ const { document, warnings } = await getDevelopmentOpenApiDocument();
27
+ return {
28
+ collection: openApiToPostmanCollection(document, options),
29
+ warnings
30
+ };
31
+ }
32
+
33
+ //#endregion
34
+ export { getDevelopmentOpenApiDocument, getDevelopmentPostmanCollection, registerDevelopmentOpenApiProvider };
35
+ //# sourceMappingURL=development-openapi-provider.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"development-openapi-provider.mjs","names":[],"sources":["../../../../../../../core/src/openapi/development-openapi-provider.ts"],"sourcesContent":["import { openApiToPostmanCollection } from \"./postman/openapi-to-postman-collection\";\nimport type { PostmanCollection, PostmanCollectionOptions } from \"./postman/postman-types\";\nimport type { OpenApiBuildResult } from \"./types\";\n\ntype DevelopmentOpenApiProvider = () => Promise<OpenApiBuildResult>;\n\nexport type DevelopmentPostmanResult = {\n collection: PostmanCollection;\n warnings: string[];\n};\n\nlet provider: DevelopmentOpenApiProvider | undefined;\n\n/**\n * Called by the dev server at startup. The builder lives under `dev-server/`, so the\n * production entry never reaches it; this registry is the only thing the root exports.\n */\nexport function registerDevelopmentOpenApiProvider(build: DevelopmentOpenApiProvider): void {\n provider = build;\n}\n\n/**\n * The OpenAPI document for the routes registered in this process, built by the dev\n * server's provider. Throws outside `warlock dev`, where no provider is registered.\n */\nexport async function getDevelopmentOpenApiDocument(): Promise<OpenApiBuildResult> {\n if (!provider) {\n throw new Error(\n \"No development OpenAPI provider is registered; the API docs are only available under `warlock dev`.\",\n );\n }\n\n return provider();\n}\n\n/**\n * The Postman collection (v2.1.0) for the routes registered in this process: the development\n * OpenAPI document run through `openApiToPostmanCollection`, so both always agree. Throws\n * outside `warlock dev`, like `getDevelopmentOpenApiDocument`.\n */\nexport async function getDevelopmentPostmanCollection(\n options: PostmanCollectionOptions = {},\n): Promise<DevelopmentPostmanResult> {\n const { document, warnings } = await getDevelopmentOpenApiDocument();\n\n return { collection: openApiToPostmanCollection(document, options), warnings };\n}\n"],"mappings":";;;AAWA,IAAI;;;;;AAMJ,SAAgB,mCAAmC,OAAyC;CAC1F,WAAW;AACb;;;;;AAMA,eAAsB,gCAA6D;CACjF,IAAI,CAAC,UACH,MAAM,IAAI,MACR,qGACF;CAGF,OAAO,SAAS;AAClB;;;;;;AAOA,eAAsB,gCACpB,UAAoC,CAAC,GACF;CACnC,MAAM,EAAE,UAAU,aAAa,MAAM,8BAA8B;CAEnE,OAAO;EAAE,YAAY,2BAA2B,UAAU,OAAO;EAAG;CAAS;AAC/E"}
@@ -0,0 +1,67 @@
1
+ import { describeStatus } from "./response-schemas.mjs";
2
+
3
+ //#region ../core/src/openapi/error-responses.ts
4
+ const DEFAULT_VALIDATION_RESPONSE = {
5
+ errors: "errors",
6
+ inputKey: "input",
7
+ inputError: "error",
8
+ status: 422
9
+ };
10
+ /**
11
+ * The `validation.response` config over its documented defaults, as `Response.failedSchema`
12
+ * reads it.
13
+ */
14
+ function resolveValidationResponse(config) {
15
+ const merged = { ...DEFAULT_VALIDATION_RESPONSE };
16
+ if (typeof config?.errors === "string" && config.errors) merged.errors = config.errors;
17
+ if (typeof config?.inputKey === "string" && config.inputKey) merged.inputKey = config.inputKey;
18
+ if (typeof config?.inputError === "string" && config.inputError) merged.inputError = config.inputError;
19
+ if (typeof config?.status === "number" && Number.isInteger(config.status)) merged.status = config.status;
20
+ return merged;
21
+ }
22
+ /**
23
+ * The failed-validation response: `{ [errors]: [{ [inputKey]: string, [inputError]: string }] }`
24
+ * under the configured status. The body is a shared component, emitted once.
25
+ */
26
+ function buildValidationFailedResponse(shape, registry) {
27
+ const ref = registry.shared("ValidationFailed", {
28
+ type: "object",
29
+ properties: { [shape.errors]: {
30
+ type: "array",
31
+ items: {
32
+ type: "object",
33
+ properties: {
34
+ [shape.inputKey]: { type: "string" },
35
+ [shape.inputError]: { type: "string" }
36
+ },
37
+ required: [shape.inputKey, shape.inputError]
38
+ }
39
+ } },
40
+ required: [shape.errors]
41
+ });
42
+ return {
43
+ status: String(shape.status),
44
+ response: {
45
+ description: describeStatus("422"),
46
+ content: { "application/json": { schema: ref } }
47
+ }
48
+ };
49
+ }
50
+ /**
51
+ * The 401 response of a guarded route: the conventional `{ error: string }` body.
52
+ */
53
+ function buildUnauthorizedResponse(registry) {
54
+ const ref = registry.shared("Unauthorized", {
55
+ type: "object",
56
+ properties: { error: { type: "string" } },
57
+ required: ["error"]
58
+ });
59
+ return {
60
+ description: describeStatus("401"),
61
+ content: { "application/json": { schema: ref } }
62
+ };
63
+ }
64
+
65
+ //#endregion
66
+ export { buildUnauthorizedResponse, buildValidationFailedResponse, resolveValidationResponse };
67
+ //# sourceMappingURL=error-responses.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-responses.mjs","names":[],"sources":["../../../../../../../core/src/openapi/error-responses.ts"],"sourcesContent":["import { describeStatus } from \"./response-schemas\";\nimport type { SchemaRegistry } from \"./schema-registry\";\nimport type { OpenApiResponse, OpenApiValidationResponseConfig } from \"./types\";\n\nconst DEFAULT_VALIDATION_RESPONSE = {\n errors: \"errors\",\n inputKey: \"input\",\n inputError: \"error\",\n status: 422,\n};\n\nexport type ValidationResponseShape = typeof DEFAULT_VALIDATION_RESPONSE;\n\n/**\n * The `validation.response` config over its documented defaults, as `Response.failedSchema`\n * reads it.\n */\nexport function resolveValidationResponse(\n config: OpenApiValidationResponseConfig | undefined,\n): ValidationResponseShape {\n const merged = { ...DEFAULT_VALIDATION_RESPONSE };\n\n if (typeof config?.errors === \"string\" && config.errors) {\n merged.errors = config.errors;\n }\n\n if (typeof config?.inputKey === \"string\" && config.inputKey) {\n merged.inputKey = config.inputKey;\n }\n\n if (typeof config?.inputError === \"string\" && config.inputError) {\n merged.inputError = config.inputError;\n }\n\n if (typeof config?.status === \"number\" && Number.isInteger(config.status)) {\n merged.status = config.status;\n }\n\n return merged;\n}\n\n/**\n * The failed-validation response: `{ [errors]: [{ [inputKey]: string, [inputError]: string }] }`\n * under the configured status. The body is a shared component, emitted once.\n */\nexport function buildValidationFailedResponse(\n shape: ValidationResponseShape,\n registry: SchemaRegistry,\n): { status: string; response: OpenApiResponse } {\n const ref = registry.shared(\"ValidationFailed\", {\n type: \"object\",\n properties: {\n [shape.errors]: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n [shape.inputKey]: { type: \"string\" },\n [shape.inputError]: { type: \"string\" },\n },\n required: [shape.inputKey, shape.inputError],\n },\n },\n },\n required: [shape.errors],\n });\n\n return {\n status: String(shape.status),\n response: {\n description: describeStatus(\"422\"),\n content: { \"application/json\": { schema: ref } },\n },\n };\n}\n\n/**\n * The 401 response of a guarded route: the conventional `{ error: string }` body.\n */\nexport function buildUnauthorizedResponse(registry: SchemaRegistry): OpenApiResponse {\n const ref = registry.shared(\"Unauthorized\", {\n type: \"object\",\n properties: { error: { type: \"string\" } },\n required: [\"error\"],\n });\n\n return {\n description: describeStatus(\"401\"),\n content: { \"application/json\": { schema: ref } },\n };\n}\n"],"mappings":";;;AAIA,MAAM,8BAA8B;CAClC,QAAQ;CACR,UAAU;CACV,YAAY;CACZ,QAAQ;AACV;;;;;AAQA,SAAgB,0BACd,QACyB;CACzB,MAAM,SAAS,EAAE,GAAG,4BAA4B;CAEhD,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,QAC/C,OAAO,SAAS,OAAO;CAGzB,IAAI,OAAO,QAAQ,aAAa,YAAY,OAAO,UACjD,OAAO,WAAW,OAAO;CAG3B,IAAI,OAAO,QAAQ,eAAe,YAAY,OAAO,YACnD,OAAO,aAAa,OAAO;CAG7B,IAAI,OAAO,QAAQ,WAAW,YAAY,OAAO,UAAU,OAAO,MAAM,GACtE,OAAO,SAAS,OAAO;CAGzB,OAAO;AACT;;;;;AAMA,SAAgB,8BACd,OACA,UAC+C;CAC/C,MAAM,MAAM,SAAS,OAAO,oBAAoB;EAC9C,MAAM;EACN,YAAY,GACT,MAAM,SAAS;GACd,MAAM;GACN,OAAO;IACL,MAAM;IACN,YAAY;MACT,MAAM,WAAW,EAAE,MAAM,SAAS;MAClC,MAAM,aAAa,EAAE,MAAM,SAAS;IACvC;IACA,UAAU,CAAC,MAAM,UAAU,MAAM,UAAU;GAC7C;EACF,EACF;EACA,UAAU,CAAC,MAAM,MAAM;CACzB,CAAC;CAED,OAAO;EACL,QAAQ,OAAO,MAAM,MAAM;EAC3B,UAAU;GACR,aAAa,eAAe,KAAK;GACjC,SAAS,EAAE,oBAAoB,EAAE,QAAQ,IAAI,EAAE;EACjD;CACF;AACF;;;;AAKA,SAAgB,0BAA0B,UAA2C;CACnF,MAAM,MAAM,SAAS,OAAO,gBAAgB;EAC1C,MAAM;EACN,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,EAAE;EACxC,UAAU,CAAC,OAAO;CACpB,CAAC;CAED,OAAO;EACL,aAAa,eAAe,KAAK;EACjC,SAAS,EAAE,oBAAoB,EAAE,QAAQ,IAAI,EAAE;CACjD;AACF"}
@@ -0,0 +1,6 @@
1
+ import { PostmanAuth, PostmanAuthEntry, PostmanBody, PostmanCollection, PostmanCollectionOptions, PostmanFolder, PostmanFormParam, PostmanHeader, PostmanItem, PostmanOriginalRequest, PostmanQueryParam, PostmanRequest, PostmanRequestItem, PostmanResponse, PostmanUrl, PostmanVariable } from "./postman/postman-types.mjs";
2
+ import { AuthDescriptor, OpenApiBuildResult, OpenApiComponents, OpenApiContext, OpenApiDocument, OpenApiOperation, OpenApiParameter, OpenApiRequestBody, OpenApiResponse, OpenApiRouteInput, OpenApiSchema, OpenApiSecurityRequirement, OpenApiSecurityScheme, OpenApiValidationResponseConfig, WarningSink } from "./types.mjs";
3
+ import { DevelopmentPostmanResult, getDevelopmentOpenApiDocument, getDevelopmentPostmanCollection, registerDevelopmentOpenApiProvider } from "./development-openapi-provider.mjs";
4
+ import { OPENAPI_JSON_SCHEMA_DIALECT, buildOpenApiDocument } from "./build-openapi-document.mjs";
5
+ import { exampleFromSchema } from "./postman/example-from-schema.mjs";
6
+ import { DEFAULT_POSTMAN_BASE_URL, POSTMAN_COLLECTION_SCHEMA, openApiToPostmanCollection } from "./postman/openapi-to-postman-collection.mjs";
@@ -0,0 +1,6 @@
1
+ import { exampleFromSchema } from "./postman/example-from-schema.mjs";
2
+ import { DEFAULT_POSTMAN_BASE_URL, POSTMAN_COLLECTION_SCHEMA, openApiToPostmanCollection } from "./postman/openapi-to-postman-collection.mjs";
3
+ import { getDevelopmentOpenApiDocument, getDevelopmentPostmanCollection, registerDevelopmentOpenApiProvider } from "./development-openapi-provider.mjs";
4
+ import { OPENAPI_JSON_SCHEMA_DIALECT, buildOpenApiDocument } from "./build-openapi-document.mjs";
5
+
6
+ export { };
@@ -0,0 +1,57 @@
1
+ import { httpConfig } from "../http/config.mjs";
2
+ import { router } from "../router/router.mjs";
3
+ import { buildOpenApiDocument } from "./build-openapi-document.mjs";
4
+ import { collectResourceExportMap } from "../router/named-api-routes-with-responses.mjs";
5
+ import config from "@mongez/config";
6
+ import path from "node:path";
7
+ import { readFile } from "node:fs/promises";
8
+
9
+ //#region ../core/src/openapi/openapi-child-output.ts
10
+ async function readAppPackage(cwd) {
11
+ try {
12
+ return JSON.parse(await readFile(path.join(cwd, "package.json"), "utf8"));
13
+ } catch {
14
+ return {};
15
+ }
16
+ }
17
+ function stringOr(value, fallback) {
18
+ return typeof value === "string" && value.length > 0 ? value : fallback;
19
+ }
20
+ /**
21
+ * `http://<host>:<port>` from the `http` config. A wildcard bind address is not a place a
22
+ * client can call, so it becomes `localhost`.
23
+ */
24
+ function resolveDefaultServer() {
25
+ const host = stringOr(httpConfig("host"), "localhost");
26
+ const port = httpConfig("port");
27
+ return `http://${host === "0.0.0.0" || host === "::" ? "localhost" : host}${typeof port === "number" || typeof port === "string" ? `:${port}` : ""}`;
28
+ }
29
+ /**
30
+ * Build the OpenAPI document from the routes registered in THIS process.
31
+ *
32
+ * Runs inside the registration child, after the app's route modules are loaded and config is
33
+ * available. Maps resource classes to their export names, reads `info` from the app
34
+ * `package.json`, the server from the `http` config and the 422 shape from
35
+ * `validation.response`.
36
+ */
37
+ async function buildOpenApiForChild(options) {
38
+ const routes = router.list();
39
+ const resourceMap = routes.some((route) => route.handler?.responseSchema) ? await collectResourceExportMap(options.files, options.onWarn) : void 0;
40
+ const pkg = await readAppPackage(options.cwd);
41
+ const validationResponse = config.get("validation.response", {});
42
+ return buildOpenApiDocument(routes, {
43
+ info: {
44
+ title: options.request.title ?? stringOr(pkg.name, "Warlock API"),
45
+ version: stringOr(pkg.version, "0.0.0"),
46
+ ...typeof pkg.description === "string" && pkg.description ? { description: pkg.description } : {}
47
+ },
48
+ servers: [options.request.server ?? resolveDefaultServer()],
49
+ includePages: options.request.includePages === true,
50
+ resolveResourceName: (resource) => resourceMap?.get(resource)?.exportName,
51
+ validationResponse
52
+ });
53
+ }
54
+
55
+ //#endregion
56
+ export { buildOpenApiForChild, resolveDefaultServer };
57
+ //# sourceMappingURL=openapi-child-output.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-child-output.mjs","names":[],"sources":["../../../../../../../core/src/openapi/openapi-child-output.ts"],"sourcesContent":["import config from \"@mongez/config\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { httpConfig } from \"../http/config\";\nimport {\n collectResourceExportMap,\n type ResourceSourceFile,\n} from \"../router/named-api-routes-with-responses\";\nimport { router } from \"../router/router\";\nimport { buildOpenApiDocument } from \"./build-openapi-document\";\nimport type { OpenApiChildRequestOptions } from \"./openapi-protocol\";\nimport type { OpenApiBuildResult, OpenApiValidationResponseConfig } from \"./types\";\n\nexport type BuildOpenApiForChildOptions = Readonly<{\n /** Application root (the child's cwd). */\n cwd: string;\n /** Every source file the orchestrator knows; `*.resource.ts(x)` ones are mapped to exports. */\n files: Iterable<ResourceSourceFile>;\n request: OpenApiChildRequestOptions;\n onWarn?: (message: string) => void;\n}>;\n\ntype AppPackage = { name?: unknown; version?: unknown; description?: unknown };\n\nasync function readAppPackage(cwd: string): Promise<AppPackage> {\n try {\n return JSON.parse(await readFile(path.join(cwd, \"package.json\"), \"utf8\")) as AppPackage;\n } catch {\n return {};\n }\n}\n\nfunction stringOr(value: unknown, fallback: string): string {\n return typeof value === \"string\" && value.length > 0 ? value : fallback;\n}\n\n/**\n * `http://<host>:<port>` from the `http` config. A wildcard bind address is not a place a\n * client can call, so it becomes `localhost`.\n */\nexport function resolveDefaultServer(): string {\n const host = stringOr(httpConfig(\"host\"), \"localhost\");\n const port = httpConfig(\"port\");\n const callable = host === \"0.0.0.0\" || host === \"::\" ? \"localhost\" : host;\n\n return `http://${callable}${typeof port === \"number\" || typeof port === \"string\" ? `:${port}` : \"\"}`;\n}\n\n/**\n * Build the OpenAPI document from the routes registered in THIS process.\n *\n * Runs inside the registration child, after the app's route modules are loaded and config is\n * available. Maps resource classes to their export names, reads `info` from the app\n * `package.json`, the server from the `http` config and the 422 shape from\n * `validation.response`.\n */\nexport async function buildOpenApiForChild(\n options: BuildOpenApiForChildOptions,\n): Promise<OpenApiBuildResult> {\n const routes = router.list();\n const declaresSchema = routes.some((route) => route.handler?.responseSchema);\n const resourceMap = declaresSchema\n ? await collectResourceExportMap(options.files, options.onWarn)\n : undefined;\n const pkg = await readAppPackage(options.cwd);\n const validationResponse = config.get(\n \"validation.response\",\n {},\n ) as OpenApiValidationResponseConfig;\n\n return buildOpenApiDocument(routes, {\n info: {\n title: options.request.title ?? stringOr(pkg.name, \"Warlock API\"),\n version: stringOr(pkg.version, \"0.0.0\"),\n ...(typeof pkg.description === \"string\" && pkg.description\n ? { description: pkg.description }\n : {}),\n },\n servers: [options.request.server ?? resolveDefaultServer()],\n includePages: options.request.includePages === true,\n resolveResourceName: (resource) => resourceMap?.get(resource)?.exportName,\n validationResponse,\n });\n}\n"],"mappings":";;;;;;;;;AAwBA,eAAe,eAAe,KAAkC;CAC9D,IAAI;EACF,OAAO,KAAK,MAAM,MAAM,SAAS,KAAK,KAAK,KAAK,cAAc,GAAG,MAAM,CAAC;CAC1E,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAS,SAAS,OAAgB,UAA0B;CAC1D,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;;;;;AAMA,SAAgB,uBAA+B;CAC7C,MAAM,OAAO,SAAS,WAAW,MAAM,GAAG,WAAW;CACrD,MAAM,OAAO,WAAW,MAAM;CAG9B,OAAO,UAFU,SAAS,aAAa,SAAS,OAAO,cAAc,OAEzC,OAAO,SAAS,YAAY,OAAO,SAAS,WAAW,IAAI,SAAS;AAClG;;;;;;;;;AAUA,eAAsB,qBACpB,SAC6B;CAC7B,MAAM,SAAS,OAAO,KAAK;CAE3B,MAAM,cADiB,OAAO,MAAM,UAAU,MAAM,SAAS,cAC5B,IAC7B,MAAM,yBAAyB,QAAQ,OAAO,QAAQ,MAAM,IAC5D;CACJ,MAAM,MAAM,MAAM,eAAe,QAAQ,GAAG;CAC5C,MAAM,qBAAqB,OAAO,IAChC,uBACA,CAAC,CACH;CAEA,OAAO,qBAAqB,QAAQ;EAClC,MAAM;GACJ,OAAO,QAAQ,QAAQ,SAAS,SAAS,IAAI,MAAM,aAAa;GAChE,SAAS,SAAS,IAAI,SAAS,OAAO;GACtC,GAAI,OAAO,IAAI,gBAAgB,YAAY,IAAI,cAC3C,EAAE,aAAa,IAAI,YAAY,IAC/B,CAAC;EACP;EACA,SAAS,CAAC,QAAQ,QAAQ,UAAU,qBAAqB,CAAC;EAC1D,cAAc,QAAQ,QAAQ,iBAAiB;EAC/C,sBAAsB,aAAa,aAAa,IAAI,QAAQ,CAAC,EAAE;EAC/D;CACF,CAAC;AACH"}
@@ -0,0 +1,30 @@
1
+ //#region ../core/src/openapi/openapi-protocol.ts
2
+ /** Version of the OpenAPI mode of the registration child protocol. */
3
+ const OPENAPI_PROTOCOL_VERSION = 1;
4
+ /** `type` of the one message the child answers with in OpenAPI mode. */
5
+ const OPENAPI_RESULT_MESSAGE = "openapi-registration:document";
6
+ function isRecord(value) {
7
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8
+ }
9
+ /**
10
+ * Validate the `openapi` request field inside the child. Throws on anything unexpected.
11
+ */
12
+ function parseOpenApiChildOptions(value) {
13
+ if (!isRecord(value) || value.version !== 1 || !(value.title === void 0 || typeof value.title === "string") || !(value.server === void 0 || typeof value.server === "string") || !(value.includePages === void 0 || typeof value.includePages === "boolean")) throw new Error("Route registration child received an invalid OpenAPI request.");
14
+ return value;
15
+ }
16
+ /**
17
+ * Validate the result message in the parent. Throws on anything unexpected.
18
+ */
19
+ function parseOpenApiChildResult(value) {
20
+ if (!isRecord(value) || value.version !== 1 || !isRecord(value.document) || typeof value.document.openapi !== "string" || !isRecord(value.document.paths) || !Array.isArray(value.warnings) || !value.warnings.every((warning) => typeof warning === "string")) throw new Error("Route registration child sent an invalid OpenAPI document payload.");
21
+ return Object.freeze({
22
+ version: 1,
23
+ document: value.document,
24
+ warnings: Object.freeze([...value.warnings])
25
+ });
26
+ }
27
+
28
+ //#endregion
29
+ export { OPENAPI_RESULT_MESSAGE, parseOpenApiChildOptions, parseOpenApiChildResult };
30
+ //# sourceMappingURL=openapi-protocol.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-protocol.mjs","names":[],"sources":["../../../../../../../core/src/openapi/openapi-protocol.ts"],"sourcesContent":["import type { OpenApiDocument } from \"./types\";\n\n/** Version of the OpenAPI mode of the registration child protocol. */\nexport const OPENAPI_PROTOCOL_VERSION = 1;\n\n/** `type` of the one message the child answers with in OpenAPI mode. */\nexport const OPENAPI_RESULT_MESSAGE = \"openapi-registration:document\";\n\n/**\n * The `openapi` field of the registration child request. Its presence switches the child\n * from \"send the route snapshot\" to \"build the OpenAPI document in this process\".\n */\nexport type OpenApiChildRequestOptions = Readonly<{\n version: typeof OPENAPI_PROTOCOL_VERSION;\n /** Overrides `info.title` (default: the app `package.json` name). */\n title?: string;\n /** Overrides `servers[0].url` (default: built from the `http` config). */\n server?: string;\n includePages?: boolean;\n}>;\n\n/** What the child sends back: plain JSON only, never a Seal instance. */\nexport type OpenApiChildResult = Readonly<{\n version: typeof OPENAPI_PROTOCOL_VERSION;\n document: OpenApiDocument;\n warnings: readonly string[];\n}>;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Validate the `openapi` request field inside the child. Throws on anything unexpected.\n */\nexport function parseOpenApiChildOptions(value: unknown): OpenApiChildRequestOptions {\n if (\n !isRecord(value) ||\n value.version !== OPENAPI_PROTOCOL_VERSION ||\n !(value.title === undefined || typeof value.title === \"string\") ||\n !(value.server === undefined || typeof value.server === \"string\") ||\n !(value.includePages === undefined || typeof value.includePages === \"boolean\")\n ) {\n throw new Error(\"Route registration child received an invalid OpenAPI request.\");\n }\n\n return value as OpenApiChildRequestOptions;\n}\n\n/**\n * Validate the result message in the parent. Throws on anything unexpected.\n */\nexport function parseOpenApiChildResult(value: unknown): OpenApiChildResult {\n if (\n !isRecord(value) ||\n value.version !== OPENAPI_PROTOCOL_VERSION ||\n !isRecord(value.document) ||\n typeof value.document.openapi !== \"string\" ||\n !isRecord(value.document.paths) ||\n !Array.isArray(value.warnings) ||\n !value.warnings.every((warning) => typeof warning === \"string\")\n ) {\n throw new Error(\"Route registration child sent an invalid OpenAPI document payload.\");\n }\n\n return Object.freeze({\n version: OPENAPI_PROTOCOL_VERSION,\n document: value.document as unknown as OpenApiDocument,\n warnings: Object.freeze([...(value.warnings as string[])]),\n });\n}\n"],"mappings":";;AAGA,MAAa,2BAA2B;;AAGxC,MAAa,yBAAyB;AAsBtC,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;;AAKA,SAAgB,yBAAyB,OAA4C;CACnF,IACE,CAAC,SAAS,KAAK,KACf,MAAM,iBACN,EAAE,MAAM,UAAU,UAAa,OAAO,MAAM,UAAU,aACtD,EAAE,MAAM,WAAW,UAAa,OAAO,MAAM,WAAW,aACxD,EAAE,MAAM,iBAAiB,UAAa,OAAO,MAAM,iBAAiB,YAEpE,MAAM,IAAI,MAAM,+DAA+D;CAGjF,OAAO;AACT;;;;AAKA,SAAgB,wBAAwB,OAAoC;CAC1E,IACE,CAAC,SAAS,KAAK,KACf,MAAM,iBACN,CAAC,SAAS,MAAM,QAAQ,KACxB,OAAO,MAAM,SAAS,YAAY,YAClC,CAAC,SAAS,MAAM,SAAS,KAAK,KAC9B,CAAC,MAAM,QAAQ,MAAM,QAAQ,KAC7B,CAAC,MAAM,SAAS,OAAO,YAAY,OAAO,YAAY,QAAQ,GAE9D,MAAM,IAAI,MAAM,oEAAoE;CAGtF,OAAO,OAAO,OAAO;EACnB;EACA,UAAU,MAAM;EAChB,UAAU,OAAO,OAAO,CAAC,GAAI,MAAM,QAAqB,CAAC;CAC3D,CAAC;AACH"}