@effect/platform 0.67.1 → 0.68.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.
- package/README.md +66 -52
- package/dist/cjs/FetchHttpClient.js.map +1 -1
- package/dist/cjs/HttpClient.js +43 -31
- package/dist/cjs/HttpClient.js.map +1 -1
- package/dist/cjs/HttpClientRequest.js.map +1 -1
- package/dist/cjs/PlatformConfigProvider.js +24 -1
- package/dist/cjs/PlatformConfigProvider.js.map +1 -1
- package/dist/cjs/internal/fetchHttpClient.js +1 -1
- package/dist/cjs/internal/fetchHttpClient.js.map +1 -1
- package/dist/cjs/internal/httpClient.js +41 -38
- package/dist/cjs/internal/httpClient.js.map +1 -1
- package/dist/cjs/internal/httpClientRequest.js +5 -9
- package/dist/cjs/internal/httpClientRequest.js.map +1 -1
- package/dist/cjs/internal/platformConfigProvider.js +117 -0
- package/dist/cjs/internal/platformConfigProvider.js.map +1 -0
- package/dist/dts/FetchHttpClient.d.ts +1 -1
- package/dist/dts/FetchHttpClient.d.ts.map +1 -1
- package/dist/dts/HttpApiClient.d.ts +2 -2
- package/dist/dts/HttpApiClient.d.ts.map +1 -1
- package/dist/dts/HttpClient.d.ts +118 -186
- package/dist/dts/HttpClient.d.ts.map +1 -1
- package/dist/dts/HttpClientRequest.d.ts +2 -5
- package/dist/dts/HttpClientRequest.d.ts.map +1 -1
- package/dist/dts/HttpServer.d.ts +1 -1
- package/dist/dts/HttpServer.d.ts.map +1 -1
- package/dist/dts/PlatformConfigProvider.d.ts +23 -0
- package/dist/dts/PlatformConfigProvider.d.ts.map +1 -1
- package/dist/dts/internal/httpClient.d.ts +23 -1
- package/dist/dts/internal/httpClient.d.ts.map +1 -1
- package/dist/dts/internal/platformConfigProvider.d.ts +2 -0
- package/dist/dts/internal/platformConfigProvider.d.ts.map +1 -0
- package/dist/esm/FetchHttpClient.js.map +1 -1
- package/dist/esm/HttpClient.js +42 -30
- package/dist/esm/HttpClient.js.map +1 -1
- package/dist/esm/HttpClientRequest.js.map +1 -1
- package/dist/esm/PlatformConfigProvider.js +23 -0
- package/dist/esm/PlatformConfigProvider.js.map +1 -1
- package/dist/esm/internal/fetchHttpClient.js +1 -1
- package/dist/esm/internal/fetchHttpClient.js.map +1 -1
- package/dist/esm/internal/httpClient.js +29 -33
- package/dist/esm/internal/httpClient.js.map +1 -1
- package/dist/esm/internal/httpClientRequest.js +4 -8
- package/dist/esm/internal/httpClientRequest.js.map +1 -1
- package/dist/esm/internal/platformConfigProvider.js +106 -0
- package/dist/esm/internal/platformConfigProvider.js.map +1 -0
- package/package.json +2 -2
- package/src/FetchHttpClient.ts +1 -1
- package/src/HttpApiClient.ts +2 -2
- package/src/HttpClient.ts +258 -279
- package/src/HttpClientRequest.ts +2 -7
- package/src/HttpServer.ts +1 -1
- package/src/PlatformConfigProvider.ts +30 -0
- package/src/internal/fetchHttpClient.ts +1 -1
- package/src/internal/httpClient.ts +191 -303
- package/src/internal/httpClientRequest.ts +4 -10
- package/src/internal/platformConfigProvider.ts +148 -0
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import type { ParseOptions } from "@effect/schema/AST"
|
|
2
2
|
import type * as Schema from "@effect/schema/Schema"
|
|
3
|
-
import * as Context from "effect/Context"
|
|
4
3
|
import * as Effect from "effect/Effect"
|
|
5
|
-
import * as Effectable from "effect/Effectable"
|
|
6
4
|
import { dual } from "effect/Function"
|
|
7
5
|
import * as Inspectable from "effect/Inspectable"
|
|
8
6
|
import * as Option from "effect/Option"
|
|
7
|
+
import { pipeArguments } from "effect/Pipeable"
|
|
9
8
|
import * as Redacted from "effect/Redacted"
|
|
10
9
|
import type * as Stream from "effect/Stream"
|
|
11
10
|
import type * as PlatformError from "../Error.js"
|
|
12
11
|
import type * as FileSystem from "../FileSystem.js"
|
|
13
12
|
import * as Headers from "../Headers.js"
|
|
14
13
|
import type * as Body from "../HttpBody.js"
|
|
15
|
-
import type { HttpClient } from "../HttpClient.js"
|
|
16
14
|
import type * as ClientRequest from "../HttpClientRequest.js"
|
|
17
15
|
import type { HttpMethod } from "../HttpMethod.js"
|
|
18
16
|
import * as UrlParams from "../UrlParams.js"
|
|
@@ -21,16 +19,9 @@ import * as internalBody from "./httpBody.js"
|
|
|
21
19
|
/** @internal */
|
|
22
20
|
export const TypeId: ClientRequest.TypeId = Symbol.for("@effect/platform/HttpClientRequest") as ClientRequest.TypeId
|
|
23
21
|
|
|
24
|
-
/** @internal */
|
|
25
|
-
export const clientTag = Context.GenericTag<HttpClient.Service>("@effect/platform/HttpClient")
|
|
26
|
-
|
|
27
22
|
const Proto = {
|
|
28
23
|
[TypeId]: TypeId,
|
|
29
|
-
...Effectable.CommitPrototype,
|
|
30
24
|
...Inspectable.BaseProto,
|
|
31
|
-
commit(this: ClientRequest.HttpClientRequest) {
|
|
32
|
-
return Effect.flatMap(clientTag, (client) => client.execute(this))
|
|
33
|
-
},
|
|
34
25
|
toJSON(this: ClientRequest.HttpClientRequest): unknown {
|
|
35
26
|
return {
|
|
36
27
|
_id: "@effect/platform/HttpClientRequest",
|
|
@@ -41,6 +32,9 @@ const Proto = {
|
|
|
41
32
|
headers: this.headers,
|
|
42
33
|
body: this.body.toJSON()
|
|
43
34
|
}
|
|
35
|
+
},
|
|
36
|
+
pipe() {
|
|
37
|
+
return pipeArguments(this, arguments)
|
|
44
38
|
}
|
|
45
39
|
}
|
|
46
40
|
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { PlatformError } from "@effect/platform/Error"
|
|
2
|
+
import * as FileSystem from "@effect/platform/FileSystem"
|
|
3
|
+
import * as ConfigProvider from "effect/ConfigProvider"
|
|
4
|
+
import * as Context from "effect/Context"
|
|
5
|
+
import * as DefaultServices from "effect/DefaultServices"
|
|
6
|
+
import * as Effect from "effect/Effect"
|
|
7
|
+
import * as FiberRef from "effect/FiberRef"
|
|
8
|
+
import * as Layer from "effect/Layer"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* dot env ConfigProvider
|
|
12
|
+
*
|
|
13
|
+
* Based on
|
|
14
|
+
* - https://github.com/motdotla/dotenv
|
|
15
|
+
* - https://github.com/motdotla/dotenv-expand
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** @internal */
|
|
19
|
+
export const fromDotEnv = (
|
|
20
|
+
path: string
|
|
21
|
+
): Effect.Effect<ConfigProvider.ConfigProvider, PlatformError, FileSystem.FileSystem> =>
|
|
22
|
+
Effect.gen(function*(_) {
|
|
23
|
+
const fs = yield* FileSystem.FileSystem
|
|
24
|
+
const content = yield* fs.readFileString(path)
|
|
25
|
+
const obj = parseDotEnv(content)
|
|
26
|
+
return ConfigProvider.fromJson(obj)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
/** @internal */
|
|
30
|
+
export const layerDotEnv = (path: string): Layer.Layer<never, PlatformError, FileSystem.FileSystem> =>
|
|
31
|
+
fromDotEnv(path).pipe(
|
|
32
|
+
Effect.map(Layer.setConfigProvider),
|
|
33
|
+
Layer.unwrapEffect
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
/** @internal */
|
|
37
|
+
export const layerDotEnvAdd = (path: string): Layer.Layer<never, never, FileSystem.FileSystem> =>
|
|
38
|
+
Effect.gen(function*(_) {
|
|
39
|
+
const dotEnvConfigProvider = yield* Effect.orElseSucceed(fromDotEnv(path), () => null)
|
|
40
|
+
|
|
41
|
+
if (dotEnvConfigProvider === null) {
|
|
42
|
+
yield* Effect.logDebug(`File '${path}' not found, skipping dotenv ConfigProvider.`)
|
|
43
|
+
return Layer.empty
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const currentConfigProvider = yield* FiberRef.get(DefaultServices.currentServices).pipe(
|
|
47
|
+
Effect.map((services) => Context.get(services, ConfigProvider.ConfigProvider))
|
|
48
|
+
)
|
|
49
|
+
const configProvider = ConfigProvider.orElse(currentConfigProvider, () => dotEnvConfigProvider)
|
|
50
|
+
return Layer.setConfigProvider(configProvider)
|
|
51
|
+
}).pipe(Layer.unwrapEffect)
|
|
52
|
+
|
|
53
|
+
/** @internal */
|
|
54
|
+
const DOT_ENV_LINE =
|
|
55
|
+
/(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
|
|
56
|
+
|
|
57
|
+
/** @internal */
|
|
58
|
+
const parseDotEnv = (lines: string): Record<string, string> => {
|
|
59
|
+
const obj: Record<string, string> = {}
|
|
60
|
+
|
|
61
|
+
// Convert line breaks to same format
|
|
62
|
+
lines = lines.replace(/\r\n?/gm, "\n")
|
|
63
|
+
|
|
64
|
+
let match: RegExpExecArray | null
|
|
65
|
+
while ((match = DOT_ENV_LINE.exec(lines)) != null) {
|
|
66
|
+
const key = match[1]
|
|
67
|
+
|
|
68
|
+
// Default undefined or null to empty string
|
|
69
|
+
let value = match[2] || ""
|
|
70
|
+
|
|
71
|
+
// Remove whitespace
|
|
72
|
+
value = value.trim()
|
|
73
|
+
|
|
74
|
+
// Check if double quoted
|
|
75
|
+
const maybeQuote = value[0]
|
|
76
|
+
|
|
77
|
+
// Remove surrounding quotes
|
|
78
|
+
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2")
|
|
79
|
+
|
|
80
|
+
// Expand newlines if double quoted
|
|
81
|
+
if (maybeQuote === "\"") {
|
|
82
|
+
value = value.replace(/\\n/g, "\n")
|
|
83
|
+
value = value.replace(/\\r/g, "\r")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add to object
|
|
87
|
+
obj[key] = value
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return expand(obj)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** @internal */
|
|
94
|
+
const expand = (parsed: Record<string, string>) => {
|
|
95
|
+
const newParsed: Record<string, string> = {}
|
|
96
|
+
|
|
97
|
+
for (const configKey in parsed) {
|
|
98
|
+
// resolve escape sequences
|
|
99
|
+
newParsed[configKey] = interpolate(parsed[configKey], parsed).replace(/\\\$/g, "$")
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return newParsed
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** @internal */
|
|
106
|
+
const interpolate = (envValue: string, parsed: Record<string, string>) => {
|
|
107
|
+
// find the last unescaped dollar sign in the
|
|
108
|
+
// value so that we can evaluate it
|
|
109
|
+
const lastUnescapedDollarSignIndex = searchLast(envValue, /(?!(?<=\\))\$/g)
|
|
110
|
+
|
|
111
|
+
// If we couldn't match any unescaped dollar sign
|
|
112
|
+
// let's return the string as is
|
|
113
|
+
if (lastUnescapedDollarSignIndex === -1) return envValue
|
|
114
|
+
|
|
115
|
+
// This is the right-most group of variables in the string
|
|
116
|
+
const rightMostGroup = envValue.slice(lastUnescapedDollarSignIndex)
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* This finds the inner most variable/group divided
|
|
120
|
+
* by variable name and default value (if present)
|
|
121
|
+
* (
|
|
122
|
+
* (?!(?<=\\))\$ // only match dollar signs that are not escaped
|
|
123
|
+
* {? // optional opening curly brace
|
|
124
|
+
* ([\w]+) // match the variable name
|
|
125
|
+
* (?::-([^}\\]*))? // match an optional default value
|
|
126
|
+
* }? // optional closing curly brace
|
|
127
|
+
* )
|
|
128
|
+
*/
|
|
129
|
+
const matchGroup = /((?!(?<=\\))\${?([\w]+)(?::-([^}\\]*))?}?)/
|
|
130
|
+
const match = rightMostGroup.match(matchGroup)
|
|
131
|
+
|
|
132
|
+
if (match !== null) {
|
|
133
|
+
const [_, group, variableName, defaultValue] = match
|
|
134
|
+
|
|
135
|
+
return interpolate(
|
|
136
|
+
envValue.replace(group, defaultValue || parsed[variableName] || ""),
|
|
137
|
+
parsed
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return envValue
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** @internal */
|
|
145
|
+
const searchLast = (str: string, rgx: RegExp) => {
|
|
146
|
+
const matches = Array.from(str.matchAll(rgx))
|
|
147
|
+
return matches.length > 0 ? matches.slice(-1)[0].index : -1
|
|
148
|
+
}
|