@effect/platform 0.69.1 → 0.69.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/OpenApi.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  * @since 1.0.0
3
3
  */
4
4
  import * as Context from "effect/Context"
5
+ import { globalValue } from "effect/GlobalValue"
5
6
  import * as HashSet from "effect/HashSet"
6
7
  import * as Option from "effect/Option"
7
8
  import type { ReadonlyRecord } from "effect/Record"
@@ -58,70 +59,76 @@ export class ExternalDocs
58
59
  * @category annotations
59
60
  */
60
61
  export class Servers
61
- extends Context.Tag("@effect/platform/OpenApi/Servers")<ExternalDocs, ReadonlyArray<OpenAPISpecServer>>()
62
+ extends Context.Tag("@effect/platform/OpenApi/Servers")<Servers, ReadonlyArray<OpenAPISpecServer>>()
62
63
  {}
63
64
 
64
65
  /**
65
66
  * @since 1.0.0
66
67
  * @category annotations
67
68
  */
68
- export class Override extends Context.Tag("@effect/platform/OpenApi/Override")<Override, Record<string, unknown>>() {}
69
+ export class Format extends Context.Tag("@effect/platform/OpenApi/Format")<Format, string>() {}
69
70
 
70
71
  /**
71
72
  * @since 1.0.0
72
73
  * @category annotations
73
74
  */
74
- export const annotations = (annotations: {
75
- readonly identifier?: string | undefined
76
- readonly title?: string | undefined
77
- readonly description?: string | undefined
78
- readonly version?: string | undefined
79
- readonly license?: OpenAPISpecLicense | undefined
80
- readonly externalDocs?: OpenAPISpecExternalDocs | undefined
81
- readonly servers?: ReadonlyArray<OpenAPISpecServer> | undefined
82
- readonly override?: Record<string, unknown> | undefined
83
- }): Context.Context<never> => {
84
- let context = Context.empty()
85
- if (annotations.identifier !== undefined) {
86
- context = Context.add(context, Identifier, annotations.identifier)
87
- }
88
- if (annotations.title !== undefined) {
89
- context = Context.add(context, Title, annotations.title)
90
- }
91
- if (annotations.description !== undefined) {
92
- context = Context.add(context, Description, annotations.description)
93
- }
94
- if (annotations.version !== undefined) {
95
- context = Context.add(context, Version, annotations.version)
96
- }
97
- if (annotations.license !== undefined) {
98
- context = Context.add(context, License, annotations.license)
99
- }
100
- if (annotations.externalDocs !== undefined) {
101
- context = Context.add(context, ExternalDocs, annotations.externalDocs)
102
- }
103
- if (annotations.servers !== undefined) {
104
- context = Context.add(context, Servers, annotations.servers)
75
+ export class Override extends Context.Tag("@effect/platform/OpenApi/Override")<Override, Record<string, unknown>>() {}
76
+
77
+ const contextPartial = <Tags extends Record<string, Context.Tag<any, any>>>(tags: Tags): (
78
+ options: {
79
+ readonly [K in keyof Tags]?: Context.Tag.Service<Tags[K]> | undefined
105
80
  }
106
- if (annotations.override !== undefined) {
107
- context = Context.add(context, Override, annotations.override)
81
+ ) => Context.Context<never> => {
82
+ const entries = Object.entries(tags)
83
+ return (options) => {
84
+ let context = Context.empty()
85
+ for (const [key, tag] of entries) {
86
+ if (options[key] !== undefined) {
87
+ context = Context.add(context, tag, options[key]!)
88
+ }
89
+ }
90
+ return context
108
91
  }
109
- return context
110
92
  }
111
93
 
112
94
  /**
113
95
  * @since 1.0.0
114
96
  * @category annotations
115
97
  */
116
- export interface Annotatable {
117
- readonly annotations: Context.Context<never>
118
- }
98
+ export const annotations: (
99
+ options: {
100
+ readonly identifier?: string | undefined
101
+ readonly title?: string | undefined
102
+ readonly version?: string | undefined
103
+ readonly description?: string | undefined
104
+ readonly license?: OpenAPISpecLicense | undefined
105
+ readonly externalDocs?: OpenAPISpecExternalDocs | undefined
106
+ readonly servers?: ReadonlyArray<OpenAPISpecServer> | undefined
107
+ readonly format?: string | undefined
108
+ readonly override?: Record<string, unknown> | undefined
109
+ }
110
+ ) => Context.Context<never> = contextPartial({
111
+ identifier: Identifier,
112
+ title: Title,
113
+ version: Version,
114
+ description: Description,
115
+ license: License,
116
+ externalDocs: ExternalDocs,
117
+ servers: Servers,
118
+ format: Format,
119
+ override: Override
120
+ })
121
+
122
+ const apiCache = globalValue("@effect/platform/OpenApi/apiCache", () => new WeakMap<HttpApi.HttpApi.Any, OpenAPISpec>())
119
123
 
120
124
  /**
121
125
  * @category constructors
122
126
  * @since 1.0.0
123
127
  */
124
128
  export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec => {
129
+ if (apiCache.has(self)) {
130
+ return apiCache.get(self)!
131
+ }
125
132
  const api = self as unknown as HttpApi.HttpApi.AnyWithProps
126
133
  const jsonSchemaDefs: Record<string, JsonSchema.JsonSchema> = {}
127
134
  const spec: DeepMutable<OpenAPISpec> = {
@@ -145,17 +152,14 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec =>
145
152
  })
146
153
  }
147
154
  function registerSecurity(
148
- tag: HttpApiMiddleware.TagClassSecurityAny,
149
155
  name: string,
150
156
  security: HttpApiSecurity
151
- ): string {
152
- const id = `${tag.key}/${name}`
153
- if (spec.components!.securitySchemes![id]) {
154
- return id
157
+ ) {
158
+ if (spec.components!.securitySchemes![name]) {
159
+ return
155
160
  }
156
161
  const scheme = makeSecurityScheme(security)
157
- spec.components!.securitySchemes![id] = scheme
158
- return id
162
+ spec.components!.securitySchemes![name] = scheme
159
163
  }
160
164
  Option.map(Context.getOption(api.annotations, Description), (description) => {
161
165
  spec.info.description = description
@@ -174,10 +178,8 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec =>
174
178
  return
175
179
  }
176
180
  for (const [name, security] of Object.entries(middleware.security)) {
177
- const id = registerSecurity(middleware, name, security)
178
- spec.security!.push({
179
- [id]: []
180
- })
181
+ registerSecurity(name, security)
182
+ spec.security!.push({ [name]: [] })
181
183
  }
182
184
  })
183
185
  HttpApi.reflect(api as any, {
@@ -221,10 +223,8 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec =>
221
223
  return
222
224
  }
223
225
  for (const [name, security] of Object.entries(middleware.security)) {
224
- const id = registerSecurity(middleware, name, security)
225
- op.security!.push({
226
- [id]: []
227
- })
226
+ registerSecurity(name, security)
227
+ op.security!.push({ [name]: [] })
228
228
  }
229
229
  })
230
230
  endpoint.payloadSchema.pipe(
@@ -334,6 +334,8 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec =>
334
334
  }
335
335
  })
336
336
 
337
+ apiCache.set(self, spec)
338
+
337
339
  return spec
338
340
  }
339
341
 
@@ -351,10 +353,15 @@ const makeSecurityScheme = (security: HttpApiSecurity): OpenAPISecurityScheme =>
351
353
  }
352
354
  }
353
355
  case "Bearer": {
356
+ const format = Context.getOption(security.annotations, Format).pipe(
357
+ Option.map((format) => ({ bearerFormat: format })),
358
+ Option.getOrUndefined
359
+ )
354
360
  return {
355
361
  ...meta,
356
362
  type: "http",
357
- scheme: "bearer"
363
+ scheme: "bearer",
364
+ ...format
358
365
  }
359
366
  }
360
367
  case "ApiKey": {
@@ -76,8 +76,7 @@ export const withTracerDisabledForUrls = dual<
76
76
  export const logger = make((httpApp) => {
77
77
  let counter = 0
78
78
  return Effect.withFiberRuntime((fiber) => {
79
- const context = fiber.getFiberRef(FiberRef.currentContext)
80
- const request = Context.unsafeGet(context, ServerRequest.HttpServerRequest)
79
+ const request = Context.unsafeGet(fiber.currentContext, ServerRequest.HttpServerRequest)
81
80
  return Effect.withLogSpan(
82
81
  Effect.flatMap(Effect.exit(httpApp), (exit) => {
83
82
  if (fiber.getFiberRef(loggerDisabled)) {
@@ -110,8 +109,7 @@ export const logger = make((httpApp) => {
110
109
  /** @internal */
111
110
  export const tracer = make((httpApp) =>
112
111
  Effect.withFiberRuntime((fiber) => {
113
- const context = fiber.getFiberRef(FiberRef.currentContext)
114
- const request = Context.unsafeGet(context, ServerRequest.HttpServerRequest)
112
+ const request = Context.unsafeGet(fiber.currentContext, ServerRequest.HttpServerRequest)
115
113
  const disabled = fiber.getFiberRef(currentTracerDisabledWhen)(request)
116
114
  if (disabled) {
117
115
  return httpApp
@@ -303,8 +301,7 @@ export const cors = (options?: {
303
301
 
304
302
  return <E, R>(httpApp: App.Default<E, R>): App.Default<E, R> =>
305
303
  Effect.withFiberRuntime((fiber) => {
306
- const context = fiber.getFiberRef(FiberRef.currentContext)
307
- const request = Context.unsafeGet(context, ServerRequest.HttpServerRequest)
304
+ const request = Context.unsafeGet(fiber.currentContext, ServerRequest.HttpServerRequest)
308
305
  if (request.method === "OPTIONS") {
309
306
  return Effect.succeed(ServerResponse.empty({
310
307
  status: 204,