@effect/platform 0.68.6 → 0.69.1

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 (229) hide show
  1. package/HttpApiMiddleware/package.json +6 -0
  2. package/README.md +306 -233
  3. package/dist/cjs/Headers.js +7 -2
  4. package/dist/cjs/Headers.js.map +1 -1
  5. package/dist/cjs/HttpApi.js +90 -78
  6. package/dist/cjs/HttpApi.js.map +1 -1
  7. package/dist/cjs/HttpApiBuilder.js +245 -255
  8. package/dist/cjs/HttpApiBuilder.js.map +1 -1
  9. package/dist/cjs/HttpApiClient.js +64 -59
  10. package/dist/cjs/HttpApiClient.js.map +1 -1
  11. package/dist/cjs/HttpApiEndpoint.js +74 -109
  12. package/dist/cjs/HttpApiEndpoint.js.map +1 -1
  13. package/dist/cjs/HttpApiError.js +3 -4
  14. package/dist/cjs/HttpApiError.js.map +1 -1
  15. package/dist/cjs/HttpApiGroup.js +103 -100
  16. package/dist/cjs/HttpApiGroup.js.map +1 -1
  17. package/dist/cjs/HttpApiMiddleware.js +67 -0
  18. package/dist/cjs/HttpApiMiddleware.js.map +1 -0
  19. package/dist/cjs/HttpApiSchema.js +33 -7
  20. package/dist/cjs/HttpApiSchema.js.map +1 -1
  21. package/dist/cjs/HttpApiSecurity.js +2 -2
  22. package/dist/cjs/HttpApiSecurity.js.map +1 -1
  23. package/dist/cjs/HttpApiSwagger.js +3 -1
  24. package/dist/cjs/HttpApiSwagger.js.map +1 -1
  25. package/dist/cjs/HttpBody.js.map +1 -1
  26. package/dist/cjs/HttpIncomingMessage.js +5 -1
  27. package/dist/cjs/HttpIncomingMessage.js.map +1 -1
  28. package/dist/cjs/HttpServer.js +12 -1
  29. package/dist/cjs/HttpServer.js.map +1 -1
  30. package/dist/cjs/HttpServerRespondable.js +1 -1
  31. package/dist/cjs/HttpServerRespondable.js.map +1 -1
  32. package/dist/cjs/OpenApi.js +102 -63
  33. package/dist/cjs/OpenApi.js.map +1 -1
  34. package/dist/cjs/OpenApiJsonSchema.js +58 -47
  35. package/dist/cjs/OpenApiJsonSchema.js.map +1 -1
  36. package/dist/cjs/Transferable.js +2 -2
  37. package/dist/cjs/Transferable.js.map +1 -1
  38. package/dist/cjs/UrlParams.js +5 -1
  39. package/dist/cjs/UrlParams.js.map +1 -1
  40. package/dist/cjs/Worker.js.map +1 -1
  41. package/dist/cjs/WorkerError.js +1 -5
  42. package/dist/cjs/WorkerError.js.map +1 -1
  43. package/dist/cjs/WorkerRunner.js.map +1 -1
  44. package/dist/cjs/index.js +3 -1
  45. package/dist/cjs/internal/httpBody.js +1 -1
  46. package/dist/cjs/internal/httpBody.js.map +1 -1
  47. package/dist/cjs/internal/httpClientRequest.js.map +1 -1
  48. package/dist/cjs/internal/httpClientResponse.js +1 -1
  49. package/dist/cjs/internal/httpClientResponse.js.map +1 -1
  50. package/dist/cjs/internal/httpRouter.js +1 -1
  51. package/dist/cjs/internal/httpRouter.js.map +1 -1
  52. package/dist/cjs/internal/httpServer.js +7 -1
  53. package/dist/cjs/internal/httpServer.js.map +1 -1
  54. package/dist/cjs/internal/httpServerRequest.js +1 -1
  55. package/dist/cjs/internal/httpServerRequest.js.map +1 -1
  56. package/dist/cjs/internal/httpServerResponse.js.map +1 -1
  57. package/dist/cjs/internal/keyValueStore.js +1 -1
  58. package/dist/cjs/internal/keyValueStore.js.map +1 -1
  59. package/dist/cjs/internal/multipart.js +1 -1
  60. package/dist/cjs/internal/multipart.js.map +1 -1
  61. package/dist/cjs/internal/worker.js +6 -7
  62. package/dist/cjs/internal/worker.js.map +1 -1
  63. package/dist/cjs/internal/workerRunner.js +3 -4
  64. package/dist/cjs/internal/workerRunner.js.map +1 -1
  65. package/dist/dts/Headers.d.ts +4 -6
  66. package/dist/dts/Headers.d.ts.map +1 -1
  67. package/dist/dts/HttpApi.d.ts +64 -140
  68. package/dist/dts/HttpApi.d.ts.map +1 -1
  69. package/dist/dts/HttpApiBuilder.d.ts +86 -167
  70. package/dist/dts/HttpApiBuilder.d.ts.map +1 -1
  71. package/dist/dts/HttpApiClient.d.ts +34 -11
  72. package/dist/dts/HttpApiClient.d.ts.map +1 -1
  73. package/dist/dts/HttpApiEndpoint.d.ts +119 -273
  74. package/dist/dts/HttpApiEndpoint.d.ts.map +1 -1
  75. package/dist/dts/HttpApiError.d.ts +5 -2
  76. package/dist/dts/HttpApiError.d.ts.map +1 -1
  77. package/dist/dts/HttpApiGroup.d.ts +96 -194
  78. package/dist/dts/HttpApiGroup.d.ts.map +1 -1
  79. package/dist/dts/HttpApiMiddleware.d.ts +228 -0
  80. package/dist/dts/HttpApiMiddleware.d.ts.map +1 -0
  81. package/dist/dts/HttpApiSchema.d.ts +6 -2
  82. package/dist/dts/HttpApiSchema.d.ts.map +1 -1
  83. package/dist/dts/HttpApiSecurity.d.ts +1 -1
  84. package/dist/dts/HttpApiSecurity.d.ts.map +1 -1
  85. package/dist/dts/HttpApiSwagger.d.ts +2 -2
  86. package/dist/dts/HttpApiSwagger.d.ts.map +1 -1
  87. package/dist/dts/HttpBody.d.ts +2 -2
  88. package/dist/dts/HttpBody.d.ts.map +1 -1
  89. package/dist/dts/HttpClientRequest.d.ts +2 -2
  90. package/dist/dts/HttpClientRequest.d.ts.map +1 -1
  91. package/dist/dts/HttpClientResponse.d.ts +3 -3
  92. package/dist/dts/HttpClientResponse.d.ts.map +1 -1
  93. package/dist/dts/HttpIncomingMessage.d.ts +3 -3
  94. package/dist/dts/HttpIncomingMessage.d.ts.map +1 -1
  95. package/dist/dts/HttpRouter.d.ts +3 -3
  96. package/dist/dts/HttpRouter.d.ts.map +1 -1
  97. package/dist/dts/HttpServer.d.ts +15 -0
  98. package/dist/dts/HttpServer.d.ts.map +1 -1
  99. package/dist/dts/HttpServerRequest.d.ts +3 -3
  100. package/dist/dts/HttpServerRequest.d.ts.map +1 -1
  101. package/dist/dts/HttpServerRespondable.d.ts.map +1 -1
  102. package/dist/dts/HttpServerResponse.d.ts +2 -2
  103. package/dist/dts/HttpServerResponse.d.ts.map +1 -1
  104. package/dist/dts/KeyValueStore.d.ts +2 -2
  105. package/dist/dts/KeyValueStore.d.ts.map +1 -1
  106. package/dist/dts/Multipart.d.ts +3 -3
  107. package/dist/dts/Multipart.d.ts.map +1 -1
  108. package/dist/dts/OpenApi.d.ts +17 -39
  109. package/dist/dts/OpenApi.d.ts.map +1 -1
  110. package/dist/dts/OpenApiJsonSchema.d.ts +10 -5
  111. package/dist/dts/OpenApiJsonSchema.d.ts.map +1 -1
  112. package/dist/dts/Transferable.d.ts +4 -1
  113. package/dist/dts/Transferable.d.ts.map +1 -1
  114. package/dist/dts/UrlParams.d.ts +3 -6
  115. package/dist/dts/UrlParams.d.ts.map +1 -1
  116. package/dist/dts/Worker.d.ts +7 -8
  117. package/dist/dts/Worker.d.ts.map +1 -1
  118. package/dist/dts/WorkerError.d.ts +1 -1
  119. package/dist/dts/WorkerError.d.ts.map +1 -1
  120. package/dist/dts/WorkerRunner.d.ts +2 -3
  121. package/dist/dts/WorkerRunner.d.ts.map +1 -1
  122. package/dist/dts/index.d.ts +4 -0
  123. package/dist/dts/index.d.ts.map +1 -1
  124. package/dist/dts/internal/httpRouter.d.ts.map +1 -1
  125. package/dist/esm/Headers.js +7 -2
  126. package/dist/esm/Headers.js.map +1 -1
  127. package/dist/esm/HttpApi.js +88 -77
  128. package/dist/esm/HttpApi.js.map +1 -1
  129. package/dist/esm/HttpApiBuilder.js +238 -244
  130. package/dist/esm/HttpApiBuilder.js.map +1 -1
  131. package/dist/esm/HttpApiClient.js +64 -59
  132. package/dist/esm/HttpApiClient.js.map +1 -1
  133. package/dist/esm/HttpApiEndpoint.js +73 -106
  134. package/dist/esm/HttpApiEndpoint.js.map +1 -1
  135. package/dist/esm/HttpApiError.js +3 -4
  136. package/dist/esm/HttpApiError.js.map +1 -1
  137. package/dist/esm/HttpApiGroup.js +102 -99
  138. package/dist/esm/HttpApiGroup.js.map +1 -1
  139. package/dist/esm/HttpApiMiddleware.js +56 -0
  140. package/dist/esm/HttpApiMiddleware.js.map +1 -0
  141. package/dist/esm/HttpApiSchema.js +31 -5
  142. package/dist/esm/HttpApiSchema.js.map +1 -1
  143. package/dist/esm/HttpApiSecurity.js +1 -1
  144. package/dist/esm/HttpApiSecurity.js.map +1 -1
  145. package/dist/esm/HttpApiSwagger.js +4 -2
  146. package/dist/esm/HttpApiSwagger.js.map +1 -1
  147. package/dist/esm/HttpBody.js.map +1 -1
  148. package/dist/esm/HttpIncomingMessage.js +4 -1
  149. package/dist/esm/HttpIncomingMessage.js.map +1 -1
  150. package/dist/esm/HttpServer.js +11 -0
  151. package/dist/esm/HttpServer.js.map +1 -1
  152. package/dist/esm/HttpServerRespondable.js +1 -1
  153. package/dist/esm/HttpServerRespondable.js.map +1 -1
  154. package/dist/esm/OpenApi.js +97 -59
  155. package/dist/esm/OpenApi.js.map +1 -1
  156. package/dist/esm/OpenApiJsonSchema.js +56 -46
  157. package/dist/esm/OpenApiJsonSchema.js.map +1 -1
  158. package/dist/esm/Transferable.js +2 -2
  159. package/dist/esm/Transferable.js.map +1 -1
  160. package/dist/esm/UrlParams.js +4 -1
  161. package/dist/esm/UrlParams.js.map +1 -1
  162. package/dist/esm/Worker.js.map +1 -1
  163. package/dist/esm/WorkerError.js +1 -4
  164. package/dist/esm/WorkerError.js.map +1 -1
  165. package/dist/esm/WorkerRunner.js.map +1 -1
  166. package/dist/esm/index.js +4 -0
  167. package/dist/esm/index.js.map +1 -1
  168. package/dist/esm/internal/httpBody.js +1 -1
  169. package/dist/esm/internal/httpBody.js.map +1 -1
  170. package/dist/esm/internal/httpClientRequest.js.map +1 -1
  171. package/dist/esm/internal/httpClientResponse.js +1 -1
  172. package/dist/esm/internal/httpClientResponse.js.map +1 -1
  173. package/dist/esm/internal/httpRouter.js +1 -1
  174. package/dist/esm/internal/httpRouter.js.map +1 -1
  175. package/dist/esm/internal/httpServer.js +6 -0
  176. package/dist/esm/internal/httpServer.js.map +1 -1
  177. package/dist/esm/internal/httpServerRequest.js +1 -1
  178. package/dist/esm/internal/httpServerRequest.js.map +1 -1
  179. package/dist/esm/internal/httpServerResponse.js.map +1 -1
  180. package/dist/esm/internal/keyValueStore.js +1 -1
  181. package/dist/esm/internal/keyValueStore.js.map +1 -1
  182. package/dist/esm/internal/multipart.js +1 -1
  183. package/dist/esm/internal/multipart.js.map +1 -1
  184. package/dist/esm/internal/worker.js +6 -7
  185. package/dist/esm/internal/worker.js.map +1 -1
  186. package/dist/esm/internal/workerRunner.js +3 -4
  187. package/dist/esm/internal/workerRunner.js.map +1 -1
  188. package/package.json +10 -3
  189. package/src/Headers.ts +12 -4
  190. package/src/HttpApi.ts +183 -258
  191. package/src/HttpApiBuilder.ts +534 -481
  192. package/src/HttpApiClient.ts +163 -112
  193. package/src/HttpApiEndpoint.ts +443 -564
  194. package/src/HttpApiError.ts +4 -6
  195. package/src/HttpApiGroup.ts +277 -325
  196. package/src/HttpApiMiddleware.ts +317 -0
  197. package/src/HttpApiSchema.ts +39 -2
  198. package/src/HttpApiSecurity.ts +1 -1
  199. package/src/HttpApiSwagger.ts +3 -3
  200. package/src/HttpBody.ts +2 -2
  201. package/src/HttpClientRequest.ts +2 -2
  202. package/src/HttpClientResponse.ts +3 -3
  203. package/src/HttpIncomingMessage.ts +3 -3
  204. package/src/HttpRouter.ts +3 -3
  205. package/src/HttpServer.ts +21 -0
  206. package/src/HttpServerRequest.ts +3 -3
  207. package/src/HttpServerRespondable.ts +1 -1
  208. package/src/HttpServerResponse.ts +2 -2
  209. package/src/KeyValueStore.ts +2 -2
  210. package/src/Multipart.ts +3 -3
  211. package/src/OpenApi.ts +113 -104
  212. package/src/OpenApiJsonSchema.ts +67 -53
  213. package/src/Transferable.ts +2 -2
  214. package/src/UrlParams.ts +3 -3
  215. package/src/Worker.ts +7 -8
  216. package/src/WorkerError.ts +1 -1
  217. package/src/WorkerRunner.ts +2 -3
  218. package/src/index.ts +5 -0
  219. package/src/internal/httpBody.ts +2 -2
  220. package/src/internal/httpClientRequest.ts +2 -2
  221. package/src/internal/httpClientResponse.ts +3 -3
  222. package/src/internal/httpRouter.ts +2 -2
  223. package/src/internal/httpServer.ts +13 -0
  224. package/src/internal/httpServerRequest.ts +3 -3
  225. package/src/internal/httpServerResponse.ts +2 -2
  226. package/src/internal/keyValueStore.ts +1 -1
  227. package/src/internal/multipart.ts +3 -3
  228. package/src/internal/worker.ts +6 -7
  229. package/src/internal/workerRunner.ts +3 -4
package/src/OpenApi.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import * as AST from "@effect/schema/AST"
5
- import * as Schema from "@effect/schema/Schema"
6
4
  import * as Context from "effect/Context"
7
- import { dual } from "effect/Function"
5
+ import * as HashSet from "effect/HashSet"
8
6
  import * as Option from "effect/Option"
9
7
  import type { ReadonlyRecord } from "effect/Record"
8
+ import * as Schema from "effect/Schema"
9
+ import * as AST from "effect/SchemaAST"
10
10
  import type { DeepMutable, Mutable } from "effect/Types"
11
11
  import * as HttpApi from "./HttpApi.js"
12
+ import * as HttpApiMiddleware from "./HttpApiMiddleware.js"
12
13
  import * as HttpApiSchema from "./HttpApiSchema.js"
13
14
  import type { HttpApiSecurity } from "./HttpApiSecurity.js"
14
15
  import * as HttpMethod from "./HttpMethod.js"
@@ -48,16 +49,24 @@ export class License extends Context.Tag("@effect/platform/OpenApi/License")<Lic
48
49
  * @since 1.0.0
49
50
  * @category annotations
50
51
  */
51
- export class Security extends Context.Tag("@effect/platform/OpenApi/Security")<Security, HttpApiSecurity>() {}
52
+ export class ExternalDocs
53
+ extends Context.Tag("@effect/platform/OpenApi/ExternalDocs")<ExternalDocs, OpenAPISpecExternalDocs>()
54
+ {}
52
55
 
53
56
  /**
54
57
  * @since 1.0.0
55
58
  * @category annotations
56
59
  */
57
- export class ExternalDocs
58
- extends Context.Tag("@effect/platform/OpenApi/ExternalDocs")<ExternalDocs, OpenAPISpecExternalDocs>()
60
+ export class Servers
61
+ extends Context.Tag("@effect/platform/OpenApi/Servers")<ExternalDocs, ReadonlyArray<OpenAPISpecServer>>()
59
62
  {}
60
63
 
64
+ /**
65
+ * @since 1.0.0
66
+ * @category annotations
67
+ */
68
+ export class Override extends Context.Tag("@effect/platform/OpenApi/Override")<Override, Record<string, unknown>>() {}
69
+
61
70
  /**
62
71
  * @since 1.0.0
63
72
  * @category annotations
@@ -68,8 +77,9 @@ export const annotations = (annotations: {
68
77
  readonly description?: string | undefined
69
78
  readonly version?: string | undefined
70
79
  readonly license?: OpenAPISpecLicense | undefined
71
- readonly security?: HttpApiSecurity | undefined
72
80
  readonly externalDocs?: OpenAPISpecExternalDocs | undefined
81
+ readonly servers?: ReadonlyArray<OpenAPISpecServer> | undefined
82
+ readonly override?: Record<string, unknown> | undefined
73
83
  }): Context.Context<never> => {
74
84
  let context = Context.empty()
75
85
  if (annotations.identifier !== undefined) {
@@ -87,12 +97,15 @@ export const annotations = (annotations: {
87
97
  if (annotations.license !== undefined) {
88
98
  context = Context.add(context, License, annotations.license)
89
99
  }
90
- if (annotations.security !== undefined) {
91
- context = Context.add(context, Security, annotations.security)
92
- }
93
100
  if (annotations.externalDocs !== undefined) {
94
101
  context = Context.add(context, ExternalDocs, annotations.externalDocs)
95
102
  }
103
+ if (annotations.servers !== undefined) {
104
+ context = Context.add(context, Servers, annotations.servers)
105
+ }
106
+ if (annotations.override !== undefined) {
107
+ context = Context.add(context, Override, annotations.override)
108
+ }
96
109
  return context
97
110
  }
98
111
 
@@ -104,67 +117,13 @@ export interface Annotatable {
104
117
  readonly annotations: Context.Context<never>
105
118
  }
106
119
 
107
- /**
108
- * @since 1.0.0
109
- * @category annotations
110
- */
111
- export const annotate: {
112
- /**
113
- * @since 1.0.0
114
- * @category annotations
115
- */
116
- (
117
- annotations: {
118
- readonly identifier?: string | undefined
119
- readonly title?: string | undefined
120
- readonly description?: string | undefined
121
- readonly version?: string | undefined
122
- readonly license?: OpenAPISpecLicense | undefined
123
- readonly security?: HttpApiSecurity | undefined
124
- readonly externalDocs?: OpenAPISpecExternalDocs | undefined
125
- }
126
- ): <A extends Annotatable>(self: A) => A
127
- /**
128
- * @since 1.0.0
129
- * @category annotations
130
- */
131
- <A extends Annotatable>(
132
- self: A,
133
- annotations: {
134
- readonly identifier?: string | undefined
135
- readonly title?: string | undefined
136
- readonly description?: string | undefined
137
- readonly version?: string | undefined
138
- readonly license?: OpenAPISpecLicense | undefined
139
- readonly security?: HttpApiSecurity | undefined
140
- readonly externalDocs?: OpenAPISpecExternalDocs | undefined
141
- }
142
- ): A
143
- } = dual(2, <A extends Annotatable>(self: A, annotations_: {
144
- readonly identifier?: string | undefined
145
- readonly title?: string | undefined
146
- readonly description?: string | undefined
147
- readonly version?: string | undefined
148
- readonly license?: OpenAPISpecLicense | undefined
149
- readonly security?: HttpApiSecurity | undefined
150
- readonly externalDocs?: OpenAPISpecExternalDocs | undefined
151
- }): A => {
152
- const base = typeof self === "function" ? function() {} : self
153
- Object.setPrototypeOf(base, Object.getPrototypeOf(self))
154
- const context = Context.merge(
155
- self.annotations,
156
- annotations(annotations_)
157
- )
158
- return Object.assign(base, self, {
159
- annotations: context
160
- })
161
- })
162
-
163
120
  /**
164
121
  * @category constructors
165
122
  * @since 1.0.0
166
123
  */
167
- export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
124
+ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec => {
125
+ const api = self as unknown as HttpApi.HttpApi.AnyWithProps
126
+ const jsonSchemaDefs: Record<string, JsonSchema.JsonSchema> = {}
168
127
  const spec: DeepMutable<OpenAPISpec> = {
169
128
  openapi: "3.0.3",
170
129
  info: {
@@ -174,22 +133,28 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
174
133
  paths: {},
175
134
  tags: [],
176
135
  components: {
177
- schemas: {},
136
+ schemas: jsonSchemaDefs,
178
137
  securitySchemes: {}
179
138
  },
180
139
  security: []
181
140
  }
182
- const securityMap = new Map<HttpApiSecurity, string>()
183
- let securityCount = 0
184
- function registerSecurity(security: HttpApiSecurity): string {
185
- if (securityMap.has(security)) {
186
- return securityMap.get(security)!
141
+ function makeJsonSchemaOrRef(schema: Schema.Schema.All): JsonSchema.JsonSchema {
142
+ return JsonSchema.makeWithDefs(schema as any, {
143
+ defs: jsonSchemaDefs,
144
+ defsPath: "#/components/schemas/"
145
+ })
146
+ }
147
+ function registerSecurity(
148
+ tag: HttpApiMiddleware.TagClassSecurityAny,
149
+ name: string,
150
+ security: HttpApiSecurity
151
+ ): string {
152
+ const id = `${tag.key}/${name}`
153
+ if (spec.components!.securitySchemes![id]) {
154
+ return id
187
155
  }
188
- const count = securityCount++
189
- const id = `${security._tag}${count === 0 ? "" : count}`
190
156
  const scheme = makeSecurityScheme(security)
191
157
  spec.components!.securitySchemes![id] = scheme
192
- securityMap.set(security, id)
193
158
  return id
194
159
  }
195
160
  Option.map(Context.getOption(api.annotations, Description), (description) => {
@@ -198,10 +163,22 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
198
163
  Option.map(Context.getOption(api.annotations, License), (license) => {
199
164
  spec.info.license = license
200
165
  })
201
- Option.map(Context.getOption(api.annotations, Security), (apiSecurity) => {
202
- spec.security!.push({
203
- [registerSecurity(apiSecurity)]: []
204
- })
166
+ Option.map(Context.getOption(api.annotations, Servers), (servers) => {
167
+ spec.servers = servers as any
168
+ })
169
+ Option.map(Context.getOption(api.annotations, Override), (override) => {
170
+ Object.assign(spec, override)
171
+ })
172
+ HashSet.forEach(api.middlewares, (middleware) => {
173
+ if (!HttpApiMiddleware.isSecurity(middleware)) {
174
+ return
175
+ }
176
+ for (const [name, security] of Object.entries(middleware.security)) {
177
+ const id = registerSecurity(middleware, name, security)
178
+ spec.security!.push({
179
+ [id]: []
180
+ })
181
+ }
205
182
  })
206
183
  HttpApi.reflect(api as any, {
207
184
  onGroup({ group }) {
@@ -214,21 +191,24 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
214
191
  Option.map(Context.getOption(group.annotations, ExternalDocs), (externalDocs) => {
215
192
  tag.externalDocs = externalDocs
216
193
  })
194
+ Option.map(Context.getOption(group.annotations, Override), (override) => {
195
+ Object.assign(tag, override)
196
+ })
217
197
  spec.tags!.push(tag)
218
198
  },
219
- onEndpoint({ endpoint, errors, group, mergedAnnotations, successAST, successEncoding, successStatus }) {
199
+ onEndpoint({ endpoint, errors, group, middleware, successes }) {
220
200
  const path = endpoint.path.replace(/:(\w+)[^/]*/g, "{$1}")
221
201
  const method = endpoint.method.toLowerCase() as OpenAPISpecMethodName
222
202
  const op: DeepMutable<OpenAPISpecOperation> = {
223
203
  tags: [Context.getOrElse(group.annotations, Title, () => group.identifier)],
224
- operationId: Context.getOrElse(endpoint.annotations, Identifier, () => `${group.identifier}.${endpoint.name}`),
204
+ operationId: Context.getOrElse(
205
+ endpoint.annotations,
206
+ Identifier,
207
+ () => group.topLevel ? endpoint.name : `${group.identifier}.${endpoint.name}`
208
+ ),
225
209
  parameters: [],
226
210
  security: [],
227
- responses: {
228
- [successStatus]: {
229
- description: Option.getOrElse(getDescriptionOrIdentifier(successAST), () => "Success")
230
- }
231
- }
211
+ responses: {}
232
212
  }
233
213
  Option.map(Context.getOption(endpoint.annotations, Description), (description) => {
234
214
  op.description = description
@@ -236,10 +216,16 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
236
216
  Option.map(Context.getOption(endpoint.annotations, ExternalDocs), (externalDocs) => {
237
217
  op.externalDocs = externalDocs
238
218
  })
239
- Option.map(Context.getOption(mergedAnnotations, Security), (apiSecurity) => {
240
- op.security!.push({
241
- [registerSecurity(apiSecurity)]: []
242
- })
219
+ HashSet.forEach(middleware, (middleware) => {
220
+ if (!HttpApiMiddleware.isSecurity(middleware)) {
221
+ return
222
+ }
223
+ for (const [name, security] of Object.entries(middleware.security)) {
224
+ const id = registerSecurity(middleware, name, security)
225
+ op.security!.push({
226
+ [id]: []
227
+ })
228
+ }
243
229
  })
244
230
  endpoint.payloadSchema.pipe(
245
231
  Option.filter(() => HttpMethod.hasBody(endpoint.method)),
@@ -247,24 +233,31 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
247
233
  op.requestBody = {
248
234
  content: {
249
235
  [HttpApiSchema.getMultipart(schema.ast) ? "multipart/form-data" : "application/json"]: {
250
- schema: JsonSchema.make(schema)
236
+ schema: makeJsonSchemaOrRef(schema)
251
237
  }
252
238
  },
253
239
  required: true
254
240
  }
255
241
  })
256
242
  )
257
- successAST.pipe(
258
- Option.map((ast) => {
259
- op.responses![successStatus].content = {
260
- [successEncoding.contentType]: {
261
- schema: JsonSchema.make(Schema.make(ast))
243
+ for (const [status, ast] of successes) {
244
+ if (op.responses![status]) continue
245
+ op.responses![status] = {
246
+ description: Option.getOrElse(getDescriptionOrIdentifier(ast), () => "Success")
247
+ }
248
+ ast.pipe(
249
+ Option.filter((ast) => !HttpApiSchema.getEmptyDecodeable(ast)),
250
+ Option.map((ast) => {
251
+ op.responses![status].content = {
252
+ "application/json": {
253
+ schema: makeJsonSchemaOrRef(Schema.make(ast))
254
+ }
262
255
  }
263
- }
264
- })
265
- )
256
+ })
257
+ )
258
+ }
266
259
  if (Option.isSome(endpoint.pathSchema)) {
267
- const schema = JsonSchema.make(endpoint.pathSchema.value) as JsonSchema.Object
260
+ const schema = makeJsonSchemaOrRef(endpoint.pathSchema.value) as JsonSchema.Object
268
261
  if ("properties" in schema) {
269
262
  Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
270
263
  op.parameters!.push({
@@ -277,7 +270,7 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
277
270
  }
278
271
  }
279
272
  if (!HttpMethod.hasBody(endpoint.method) && Option.isSome(endpoint.payloadSchema)) {
280
- const schema = JsonSchema.make(endpoint.payloadSchema.value) as JsonSchema.Object
273
+ const schema = makeJsonSchemaOrRef(endpoint.payloadSchema.value) as JsonSchema.Object
281
274
  if ("properties" in schema) {
282
275
  Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
283
276
  op.parameters!.push({
@@ -290,7 +283,7 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
290
283
  }
291
284
  }
292
285
  if (Option.isSome(endpoint.headersSchema)) {
293
- const schema = JsonSchema.make(endpoint.headersSchema.value) as JsonSchema.Object
286
+ const schema = makeJsonSchemaOrRef(endpoint.headersSchema.value) as JsonSchema.Object
294
287
  if ("properties" in schema) {
295
288
  Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
296
289
  op.parameters!.push({
@@ -302,6 +295,19 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
302
295
  })
303
296
  }
304
297
  }
298
+ if (Option.isSome(endpoint.urlParamsSchema)) {
299
+ const schema = makeJsonSchemaOrRef(endpoint.urlParamsSchema.value) as JsonSchema.Object
300
+ if ("properties" in schema) {
301
+ Object.entries(schema.properties).forEach(([name, jsonSchema]) => {
302
+ op.parameters!.push({
303
+ name,
304
+ in: "query",
305
+ schema: jsonSchema,
306
+ required: schema.required.includes(name)
307
+ })
308
+ })
309
+ }
310
+ }
305
311
  for (const [status, ast] of errors) {
306
312
  if (op.responses![status]) continue
307
313
  op.responses![status] = {
@@ -312,7 +318,7 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
312
318
  Option.map((ast) => {
313
319
  op.responses![status].content = {
314
320
  "application/json": {
315
- schema: JsonSchema.make(Schema.make(ast))
321
+ schema: makeJsonSchemaOrRef(Schema.make(ast))
316
322
  }
317
323
  }
318
324
  })
@@ -321,6 +327,9 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(api: A): OpenAPISpec => {
321
327
  if (!spec.paths[path]) {
322
328
  spec.paths[path] = {}
323
329
  }
330
+ Option.map(Context.getOption(endpoint.annotations, Override), (override) => {
331
+ Object.assign(op, override)
332
+ })
324
333
  spec.paths[path][method] = op
325
334
  }
326
335
  })
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import * as AST from "@effect/schema/AST"
5
- import type * as ParseResult from "@effect/schema/ParseResult"
6
- import type * as Schema from "@effect/schema/Schema"
7
4
  import * as Arr from "effect/Array"
8
5
  import * as Option from "effect/Option"
6
+ import type * as ParseResult from "effect/ParseResult"
9
7
  import * as Predicate from "effect/Predicate"
10
8
  import * as Record from "effect/Record"
9
+ import type * as Schema from "effect/Schema"
10
+ import * as AST from "effect/SchemaAST"
11
11
 
12
12
  /**
13
13
  * @category model
@@ -213,19 +213,32 @@ export type Root = JsonSchema & {
213
213
  */
214
214
  export const make = <A, I, R>(schema: Schema.Schema<A, I, R>): Root => {
215
215
  const $defs: Record<string, any> = {}
216
- const out = go(schema.ast, $defs, true, []) as Root
217
- // clean up self-referencing entries
218
- for (const id in $defs) {
219
- if ($defs[id]["$ref"] === get$ref(id)) {
220
- delete $defs[id]
221
- }
222
- }
216
+ const out = makeWithDefs(schema, { defs: $defs })
223
217
  if (!Record.isEmptyRecord($defs)) {
224
218
  out.$defs = $defs
225
219
  }
226
220
  return out
227
221
  }
228
222
 
223
+ /**
224
+ * @category encoding
225
+ * @since 1.0.0
226
+ */
227
+ export const makeWithDefs = <A, I, R>(schema: Schema.Schema<A, I, R>, options: {
228
+ readonly defs: Record<string, any>
229
+ readonly defsPath?: string
230
+ }): Root => {
231
+ const defsPath = options.defsPath ?? "#/$defs/"
232
+ const getRef = (id: string) => `${defsPath}${id}`
233
+ const out = go(schema.ast, options.defs, true, [], { getRef }) as Root
234
+ for (const id in options.defs) {
235
+ if (options.defs[id]["$ref"] === getRef(id)) {
236
+ delete options.defs[id]
237
+ }
238
+ }
239
+ return out
240
+ }
241
+
229
242
  const constAny: JsonSchema = { $id: "/schemas/any" }
230
243
 
231
244
  const constUnknown: JsonSchema = { $id: "/schemas/unknown" }
@@ -293,10 +306,6 @@ const pruneUndefinedKeyword = (ps: AST.PropertySignature): AST.AST | undefined =
293
306
  }
294
307
  }
295
308
 
296
- const DEFINITION_PREFIX = "#/$defs/"
297
-
298
- const get$ref = (id: string): string => `${DEFINITION_PREFIX}${id}`
299
-
300
309
  const getRefinementInnerTransformation = (ast: AST.Refinement): AST.AST | undefined => {
301
310
  switch (ast.from._tag) {
302
311
  case "Transformation":
@@ -312,7 +321,9 @@ const getRefinementInnerTransformation = (ast: AST.Refinement): AST.AST | undefi
312
321
  }
313
322
  }
314
323
 
315
- const isParseJsonTransformation = (ast: AST.AST): boolean => ast.annotations[AST.TypeAnnotationId] === ParseJsonTypeId
324
+ const isParseJsonTransformation = (ast: AST.AST): boolean =>
325
+ ast._tag === "Transformation" &&
326
+ ast.from.annotations[AST.SchemaIdAnnotationId] === AST.ParseJsonSchemaId
316
327
 
317
328
  const isOverrideAnnotation = (jsonSchema: JsonSchema): boolean => {
318
329
  return ("type" in jsonSchema) || ("oneOf" in jsonSchema) || ("anyOf" in jsonSchema) || ("const" in jsonSchema) ||
@@ -323,7 +334,10 @@ const go = (
323
334
  ast: AST.AST,
324
335
  $defs: Record<string, JsonSchema>,
325
336
  handleIdentifier: boolean,
326
- path: ReadonlyArray<PropertyKey>
337
+ path: ReadonlyArray<PropertyKey>,
338
+ options: {
339
+ readonly getRef: (id: string) => string
340
+ }
327
341
  ): JsonSchema => {
328
342
  const hook = AST.getJSONSchemaAnnotation(ast)
329
343
  if (Option.isSome(hook)) {
@@ -333,7 +347,7 @@ const go = (
333
347
  if (t === undefined) {
334
348
  try {
335
349
  return {
336
- ...go(ast.from, $defs, true, path),
350
+ ...go(ast.from, $defs, true, path, options),
337
351
  ...getJsonSchemaAnnotations(ast),
338
352
  ...handler
339
353
  }
@@ -345,33 +359,42 @@ const go = (
345
359
  }
346
360
  } else if (!isOverrideAnnotation(handler)) {
347
361
  return {
348
- ...go(t, $defs, true, path),
362
+ ...go(t, $defs, true, path, options),
349
363
  ...getJsonSchemaAnnotations(ast)
350
364
  }
351
365
  }
352
366
  }
353
367
  return handler
354
368
  }
355
- const surrogate = getSurrogateAnnotation(ast)
356
- if (Option.isSome(surrogate)) {
357
- return {
358
- ...(ast._tag === "Transformation" ? getJsonSchemaAnnotations(ast.to) : {}),
359
- ...go(surrogate.value, $defs, handleIdentifier, path),
360
- ...getJsonSchemaAnnotations(ast)
361
- }
362
- }
363
- if (handleIdentifier && !AST.isTransformation(ast) && !AST.isRefinement(ast)) {
364
- const identifier = getJSONIdentifier(ast)
369
+ const surrogate = AST.getSurrogateAnnotation(ast)
370
+ if (handleIdentifier && !AST.isRefinement(ast)) {
371
+ const identifier = AST.getJSONIdentifier(
372
+ Option.isSome(surrogate) ?
373
+ {
374
+ annotations: {
375
+ ...(ast._tag === "Transformation" ? ast.to.annotations : {}),
376
+ ...ast.annotations
377
+ }
378
+ } :
379
+ ast
380
+ )
365
381
  if (Option.isSome(identifier)) {
366
382
  const id = identifier.value
367
- const out = { $ref: get$ref(id) }
383
+ const out = { $ref: options.getRef(id) }
368
384
  if (!Record.has($defs, id)) {
369
385
  $defs[id] = out
370
- $defs[id] = go(ast, $defs, false, path)
386
+ $defs[id] = go(ast, $defs, false, path, options)
371
387
  }
372
388
  return out
373
389
  }
374
390
  }
391
+ if (Option.isSome(surrogate)) {
392
+ return {
393
+ ...(ast._tag === "Transformation" ? getJsonSchemaAnnotations(ast.to) : {}),
394
+ ...go(surrogate.value, $defs, handleIdentifier, path, options),
395
+ ...getJsonSchemaAnnotations(ast)
396
+ }
397
+ }
375
398
  switch (ast._tag) {
376
399
  case "Declaration":
377
400
  throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
@@ -429,11 +452,11 @@ const go = (
429
452
  throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
430
453
  case "TupleType": {
431
454
  const elements = ast.elements.map((e, i) => ({
432
- ...go(e.type, $defs, true, path.concat(i)),
455
+ ...go(e.type, $defs, true, path.concat(i), options),
433
456
  ...getJsonSchemaAnnotations(e)
434
457
  }))
435
458
  const rest = ast.rest.map((annotatedAST) => ({
436
- ...go(annotatedAST.type, $defs, true, path),
459
+ ...go(annotatedAST.type, $defs, true, path, options),
437
460
  ...getJsonSchemaAnnotations(annotatedAST)
438
461
  }))
439
462
  const output: Array = { type: "array" }
@@ -490,11 +513,11 @@ const go = (
490
513
  const parameter = is.parameter
491
514
  switch (parameter._tag) {
492
515
  case "StringKeyword": {
493
- patternProperties = go(is.type, $defs, true, path)
516
+ patternProperties = go(is.type, $defs, true, path, options)
494
517
  break
495
518
  }
496
519
  case "TemplateLiteral": {
497
- patternProperties = go(is.type, $defs, true, path)
520
+ patternProperties = go(is.type, $defs, true, path, options)
498
521
  propertyNames = {
499
522
  type: "string",
500
523
  pattern: AST.getTemplateLiteralRegExp(parameter).source
@@ -502,8 +525,8 @@ const go = (
502
525
  break
503
526
  }
504
527
  case "Refinement": {
505
- patternProperties = go(is.type, $defs, true, path)
506
- propertyNames = go(parameter, $defs, true, path)
528
+ patternProperties = go(is.type, $defs, true, path, options)
529
+ propertyNames = go(parameter, $defs, true, path, options)
507
530
  break
508
531
  }
509
532
  case "SymbolKeyword":
@@ -525,7 +548,7 @@ const go = (
525
548
  if (Predicate.isString(name)) {
526
549
  const pruned = pruneUndefinedKeyword(ps)
527
550
  output.properties[name] = {
528
- ...go(pruned ? pruned : ps.type, $defs, true, path.concat(ps.name)),
551
+ ...go(pruned ? pruned : ps.type, $defs, true, path.concat(ps.name), options),
529
552
  ...getJsonSchemaAnnotations(ps)
530
553
  }
531
554
  // ---------------------------------------------
@@ -558,7 +581,7 @@ const go = (
558
581
  const enums: globalThis.Array<AST.LiteralValue> = []
559
582
  const anyOf: globalThis.Array<JsonSchema> = []
560
583
  for (const type of ast.types) {
561
- const schema = go(type, $defs, true, path)
584
+ const schema = go(type, $defs, true, path, options)
562
585
  if ("enum" in schema) {
563
586
  if (Object.keys(schema).length > 1) {
564
587
  anyOf.push(schema)
@@ -591,7 +614,7 @@ const go = (
591
614
  if (AST.encodedBoundAST(ast) === ast) {
592
615
  throw new Error(getJSONSchemaMissingAnnotationErrorMessage(path, ast))
593
616
  }
594
- return go(ast.from, $defs, true, path)
617
+ return go(ast.from, $defs, true, path, options)
595
618
  }
596
619
  case "TemplateLiteral": {
597
620
  const regex = AST.getTemplateLiteralRegExp(ast)
@@ -603,12 +626,12 @@ const go = (
603
626
  }
604
627
  }
605
628
  case "Suspend": {
606
- const identifier = Option.orElse(getJSONIdentifier(ast), () => getJSONIdentifier(ast.f()))
629
+ const identifier = Option.orElse(AST.getJSONIdentifier(ast), () => AST.getJSONIdentifier(ast.f()))
607
630
  if (Option.isNone(identifier)) {
608
631
  throw new Error(getJSONSchemaMissingIdentifierAnnotationErrorMessage(path, ast))
609
632
  }
610
633
  return {
611
- ...go(ast.f(), $defs, true, path),
634
+ ...go(ast.f(), $defs, true, path, options),
612
635
  ...getJsonSchemaAnnotations(ast)
613
636
  }
614
637
  }
@@ -617,17 +640,17 @@ const go = (
617
640
  // the 'to' side of the AST. This approach prevents the generation of useless schemas
618
641
  // derived from the 'from' side (type: string), ensuring the output matches the intended
619
642
  // complex schema type.
620
- if (isParseJsonTransformation(ast.from)) {
643
+ if (isParseJsonTransformation(ast)) {
621
644
  return {
622
645
  type: "string",
623
646
  contentMediaType: "application/json",
624
- contentSchema: go(ast.to, $defs, true, path),
647
+ contentSchema: go(ast.to, $defs, true, path, options),
625
648
  ...getJsonSchemaAnnotations(ast)
626
649
  }
627
650
  }
628
651
  return {
629
652
  ...getASTJsonSchemaAnnotations(ast.to),
630
- ...go(ast.from, $defs, true, path),
653
+ ...go(ast.from, $defs, true, path, options),
631
654
  ...getJsonSchemaAnnotations(ast)
632
655
  }
633
656
  }
@@ -703,12 +726,3 @@ const formatPath = (path: ParseResult.Path): string =>
703
726
  const isNonEmpty = <A>(x: ParseResult.SingleOrNonEmpty<A>): x is Arr.NonEmptyReadonlyArray<A> => Array.isArray(x)
704
727
 
705
728
  const formatPropertyKey = (name: PropertyKey): string => typeof name === "string" ? JSON.stringify(name) : String(name)
706
-
707
- const ParseJsonTypeId: unique symbol = Symbol.for("@effect/schema/TypeId/ParseJson")
708
- const SurrogateAnnotationId = Symbol.for("@effect/schema/annotation/Surrogate")
709
- const JSONIdentifierAnnotationId = Symbol.for("@effect/schema/annotation/JSONIdentifier")
710
-
711
- const getSurrogateAnnotation = AST.getAnnotation<AST.AST>(SurrogateAnnotationId)
712
- const getJSONIdentifierAnnotation = AST.getAnnotation<string>(JSONIdentifierAnnotationId)
713
- const getJSONIdentifier = (annotated: AST.Annotated) =>
714
- Option.orElse(getJSONIdentifierAnnotation(annotated), () => AST.getIdentifierAnnotation(annotated))
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import * as ParseResult from "@effect/schema/ParseResult"
5
- import * as Schema from "@effect/schema/Schema"
6
4
  import * as Context from "effect/Context"
7
5
  import * as Effect from "effect/Effect"
8
6
  import { dual } from "effect/Function"
9
7
  import * as Option from "effect/Option"
8
+ import * as ParseResult from "effect/ParseResult"
9
+ import * as Schema from "effect/Schema"
10
10
 
11
11
  /**
12
12
  * @since 1.0.0
package/src/UrlParams.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import type { ParseOptions } from "@effect/schema/AST"
5
- import type * as ParseResult from "@effect/schema/ParseResult"
6
- import * as Schema from "@effect/schema/Schema"
7
4
  import * as Arr from "effect/Array"
8
5
  import type * as Effect from "effect/Effect"
9
6
  import * as Either from "effect/Either"
10
7
  import { dual } from "effect/Function"
11
8
  import * as Option from "effect/Option"
9
+ import type * as ParseResult from "effect/ParseResult"
10
+ import * as Schema from "effect/Schema"
11
+ import type { ParseOptions } from "effect/SchemaAST"
12
12
 
13
13
  /**
14
14
  * @since 1.0.0