@effect/platform 0.48.14 → 0.48.16
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/Http/Cookies/package.json +6 -0
- package/README.md +235 -46
- package/dist/cjs/Command.js +1 -1
- package/dist/cjs/Command.js.map +1 -1
- package/dist/cjs/CommandExecutor.js +1 -1
- package/dist/cjs/CommandExecutor.js.map +1 -1
- package/dist/cjs/Effectify.js +1 -1
- package/dist/cjs/Effectify.js.map +1 -1
- package/dist/cjs/Error.js +1 -1
- package/dist/cjs/Error.js.map +1 -1
- package/dist/cjs/FileSystem.js +1 -1
- package/dist/cjs/FileSystem.js.map +1 -1
- package/dist/cjs/Http/App.js +1 -1
- package/dist/cjs/Http/App.js.map +1 -1
- package/dist/cjs/Http/Body.js +1 -1
- package/dist/cjs/Http/Body.js.map +1 -1
- package/dist/cjs/Http/Client.js +7 -2
- package/dist/cjs/Http/Client.js.map +1 -1
- package/dist/cjs/Http/ClientError.js +1 -1
- package/dist/cjs/Http/ClientError.js.map +1 -1
- package/dist/cjs/Http/ClientRequest.js +1 -1
- package/dist/cjs/Http/ClientRequest.js.map +1 -1
- package/dist/cjs/Http/ClientResponse.js +1 -1
- package/dist/cjs/Http/ClientResponse.js.map +1 -1
- package/dist/cjs/Http/Cookies.js +543 -0
- package/dist/cjs/Http/Cookies.js.map +1 -0
- package/dist/cjs/Http/Etag.js +1 -1
- package/dist/cjs/Http/Etag.js.map +1 -1
- package/dist/cjs/Http/Headers.js +3 -3
- package/dist/cjs/Http/Headers.js.map +1 -1
- package/dist/cjs/Http/IncomingMessage.js +1 -1
- package/dist/cjs/Http/IncomingMessage.js.map +1 -1
- package/dist/cjs/Http/Method.js.map +1 -1
- package/dist/cjs/Http/Middleware.js +1 -1
- package/dist/cjs/Http/Middleware.js.map +1 -1
- package/dist/cjs/Http/Multipart.js +1 -1
- package/dist/cjs/Http/Multipart.js.map +1 -1
- package/dist/cjs/Http/Multiplex.js +1 -1
- package/dist/cjs/Http/Multiplex.js.map +1 -1
- package/dist/cjs/Http/Platform.js +1 -1
- package/dist/cjs/Http/Platform.js.map +1 -1
- package/dist/cjs/Http/Router.js +1 -1
- package/dist/cjs/Http/Router.js.map +1 -1
- package/dist/cjs/Http/Server.js +1 -1
- package/dist/cjs/Http/Server.js.map +1 -1
- package/dist/cjs/Http/ServerError.js +1 -1
- package/dist/cjs/Http/ServerError.js.map +1 -1
- package/dist/cjs/Http/ServerRequest.js +1 -1
- package/dist/cjs/Http/ServerRequest.js.map +1 -1
- package/dist/cjs/Http/ServerResponse.js +37 -2
- package/dist/cjs/Http/ServerResponse.js.map +1 -1
- package/dist/cjs/Http/UrlParams.js +1 -1
- package/dist/cjs/Http/UrlParams.js.map +1 -1
- package/dist/cjs/HttpClient.js +4 -2
- package/dist/cjs/HttpClient.js.map +1 -1
- package/dist/cjs/HttpServer.js +4 -2
- package/dist/cjs/HttpServer.js.map +1 -1
- package/dist/cjs/KeyValueStore.js +1 -1
- package/dist/cjs/KeyValueStore.js.map +1 -1
- package/dist/cjs/Path.js +1 -1
- package/dist/cjs/Path.js.map +1 -1
- package/dist/cjs/PlatformLogger.js +1 -1
- package/dist/cjs/PlatformLogger.js.map +1 -1
- package/dist/cjs/Runtime.js +1 -1
- package/dist/cjs/Runtime.js.map +1 -1
- package/dist/cjs/Socket.js +1 -1
- package/dist/cjs/Socket.js.map +1 -1
- package/dist/cjs/Template.js +1 -1
- package/dist/cjs/Template.js.map +1 -1
- package/dist/cjs/Terminal.js +1 -1
- package/dist/cjs/Terminal.js.map +1 -1
- package/dist/cjs/Transferable.js +1 -1
- package/dist/cjs/Transferable.js.map +1 -1
- package/dist/cjs/Worker.js +1 -1
- package/dist/cjs/Worker.js.map +1 -1
- package/dist/cjs/WorkerError.js +1 -1
- package/dist/cjs/WorkerError.js.map +1 -1
- package/dist/cjs/WorkerRunner.js +1 -1
- package/dist/cjs/WorkerRunner.js.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/command.js +1 -1
- package/dist/cjs/internal/command.js.map +1 -1
- package/dist/cjs/internal/commandExecutor.js +1 -1
- package/dist/cjs/internal/commandExecutor.js.map +1 -1
- package/dist/cjs/internal/effectify.js +1 -1
- package/dist/cjs/internal/effectify.js.map +1 -1
- package/dist/cjs/internal/error.js +1 -1
- package/dist/cjs/internal/error.js.map +1 -1
- package/dist/cjs/internal/fileSystem.js +1 -1
- package/dist/cjs/internal/fileSystem.js.map +1 -1
- package/dist/cjs/internal/http/body.js +1 -1
- package/dist/cjs/internal/http/body.js.map +1 -1
- package/dist/cjs/internal/http/client.js +6 -2
- package/dist/cjs/internal/http/client.js.map +1 -1
- package/dist/cjs/internal/http/clientError.js.map +1 -1
- package/dist/cjs/internal/http/clientRequest.js +1 -1
- package/dist/cjs/internal/http/clientRequest.js.map +1 -1
- package/dist/cjs/internal/http/clientResponse.js +9 -1
- package/dist/cjs/internal/http/clientResponse.js.map +1 -1
- package/dist/cjs/internal/http/etag.js +1 -1
- package/dist/cjs/internal/http/etag.js.map +1 -1
- package/dist/cjs/internal/http/middleware.js +1 -1
- package/dist/cjs/internal/http/middleware.js.map +1 -1
- package/dist/cjs/internal/http/multipart.js +1 -1
- package/dist/cjs/internal/http/multipart.js.map +1 -1
- package/dist/cjs/internal/http/multiplex.js +1 -1
- package/dist/cjs/internal/http/multiplex.js.map +1 -1
- package/dist/cjs/internal/http/platform.js +1 -1
- package/dist/cjs/internal/http/platform.js.map +1 -1
- package/dist/cjs/internal/http/router.js +1 -1
- package/dist/cjs/internal/http/router.js.map +1 -1
- package/dist/cjs/internal/http/server.js +1 -1
- package/dist/cjs/internal/http/server.js.map +1 -1
- package/dist/cjs/internal/http/serverError.js +1 -1
- package/dist/cjs/internal/http/serverError.js.map +1 -1
- package/dist/cjs/internal/http/serverRequest.js +9 -1
- package/dist/cjs/internal/http/serverRequest.js.map +1 -1
- package/dist/cjs/internal/http/serverResponse.js +34 -17
- package/dist/cjs/internal/http/serverResponse.js.map +1 -1
- package/dist/cjs/internal/keyValueStore.js +1 -1
- package/dist/cjs/internal/keyValueStore.js.map +1 -1
- package/dist/cjs/internal/path.js +1 -1
- package/dist/cjs/internal/path.js.map +1 -1
- package/dist/cjs/internal/platformLogger.js +1 -1
- package/dist/cjs/internal/platformLogger.js.map +1 -1
- package/dist/cjs/internal/terminal.js.map +1 -1
- package/dist/cjs/internal/worker.js +1 -1
- package/dist/cjs/internal/worker.js.map +1 -1
- package/dist/cjs/internal/workerError.js.map +1 -1
- package/dist/cjs/internal/workerRunner.js +1 -1
- package/dist/cjs/internal/workerRunner.js.map +1 -1
- package/dist/dts/Error.d.ts.map +1 -1
- package/dist/dts/Http/App.d.ts +1 -1
- package/dist/dts/Http/App.d.ts.map +1 -1
- package/dist/dts/Http/Client.d.ts +10 -0
- package/dist/dts/Http/Client.d.ts.map +1 -1
- package/dist/dts/Http/ClientResponse.d.ts +2 -0
- package/dist/dts/Http/ClientResponse.d.ts.map +1 -1
- package/dist/dts/Http/Cookies.d.ts +245 -0
- package/dist/dts/Http/Cookies.d.ts.map +1 -0
- package/dist/dts/Http/Headers.d.ts +2 -2
- package/dist/dts/Http/Headers.d.ts.map +1 -1
- package/dist/dts/Http/IncomingMessage.d.ts +2 -2
- package/dist/dts/Http/IncomingMessage.d.ts.map +1 -1
- package/dist/dts/Http/ServerRequest.d.ts +2 -0
- package/dist/dts/Http/ServerRequest.d.ts.map +1 -1
- package/dist/dts/Http/ServerResponse.d.ts +75 -0
- package/dist/dts/Http/ServerResponse.d.ts.map +1 -1
- package/dist/dts/Http/UrlParams.d.ts +1 -1
- package/dist/dts/Http/UrlParams.d.ts.map +1 -1
- package/dist/dts/HttpClient.d.ts +8 -0
- package/dist/dts/HttpClient.d.ts.map +1 -1
- package/dist/dts/HttpServer.d.ts +8 -0
- package/dist/dts/HttpServer.d.ts.map +1 -1
- package/dist/dts/Socket.d.ts +3 -3
- package/dist/dts/Socket.d.ts.map +1 -1
- package/dist/dts/internal/http/router.d.ts.map +1 -1
- package/dist/esm/Command.js.map +1 -1
- package/dist/esm/CommandExecutor.js.map +1 -1
- package/dist/esm/Effectify.js.map +1 -1
- package/dist/esm/Error.js.map +1 -1
- package/dist/esm/FileSystem.js.map +1 -1
- package/dist/esm/Http/App.js.map +1 -1
- package/dist/esm/Http/Body.js.map +1 -1
- package/dist/esm/Http/Client.js +5 -0
- package/dist/esm/Http/Client.js.map +1 -1
- package/dist/esm/Http/ClientError.js.map +1 -1
- package/dist/esm/Http/ClientRequest.js.map +1 -1
- package/dist/esm/Http/ClientResponse.js.map +1 -1
- package/dist/esm/Http/Cookies.js +496 -0
- package/dist/esm/Http/Cookies.js.map +1 -0
- package/dist/esm/Http/Etag.js.map +1 -1
- package/dist/esm/Http/Headers.js +2 -2
- package/dist/esm/Http/Headers.js.map +1 -1
- package/dist/esm/Http/IncomingMessage.js.map +1 -1
- package/dist/esm/Http/Method.js.map +1 -1
- package/dist/esm/Http/Middleware.js.map +1 -1
- package/dist/esm/Http/Multipart.js.map +1 -1
- package/dist/esm/Http/Multiplex.js.map +1 -1
- package/dist/esm/Http/Platform.js.map +1 -1
- package/dist/esm/Http/Router.js.map +1 -1
- package/dist/esm/Http/Server.js.map +1 -1
- package/dist/esm/Http/ServerError.js.map +1 -1
- package/dist/esm/Http/ServerRequest.js.map +1 -1
- package/dist/esm/Http/ServerResponse.js +35 -0
- package/dist/esm/Http/ServerResponse.js.map +1 -1
- package/dist/esm/Http/UrlParams.js.map +1 -1
- package/dist/esm/HttpClient.js +8 -0
- package/dist/esm/HttpClient.js.map +1 -1
- package/dist/esm/HttpServer.js +8 -0
- package/dist/esm/HttpServer.js.map +1 -1
- package/dist/esm/KeyValueStore.js.map +1 -1
- package/dist/esm/Path.js.map +1 -1
- package/dist/esm/PlatformLogger.js.map +1 -1
- package/dist/esm/Runtime.js.map +1 -1
- package/dist/esm/Socket.js.map +1 -1
- package/dist/esm/Template.js.map +1 -1
- package/dist/esm/Terminal.js.map +1 -1
- package/dist/esm/Transferable.js.map +1 -1
- package/dist/esm/Worker.js.map +1 -1
- package/dist/esm/WorkerError.js.map +1 -1
- package/dist/esm/WorkerRunner.js.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/command.js.map +1 -1
- package/dist/esm/internal/commandExecutor.js.map +1 -1
- package/dist/esm/internal/effectify.js.map +1 -1
- package/dist/esm/internal/error.js.map +1 -1
- package/dist/esm/internal/fileSystem.js.map +1 -1
- package/dist/esm/internal/http/body.js.map +1 -1
- package/dist/esm/internal/http/client.js +4 -0
- package/dist/esm/internal/http/client.js.map +1 -1
- package/dist/esm/internal/http/clientError.js.map +1 -1
- package/dist/esm/internal/http/clientRequest.js.map +1 -1
- package/dist/esm/internal/http/clientResponse.js +8 -0
- package/dist/esm/internal/http/clientResponse.js.map +1 -1
- package/dist/esm/internal/http/etag.js.map +1 -1
- package/dist/esm/internal/http/middleware.js.map +1 -1
- package/dist/esm/internal/http/multipart.js.map +1 -1
- package/dist/esm/internal/http/multiplex.js.map +1 -1
- package/dist/esm/internal/http/platform.js.map +1 -1
- package/dist/esm/internal/http/router.js.map +1 -1
- package/dist/esm/internal/http/server.js.map +1 -1
- package/dist/esm/internal/http/serverError.js.map +1 -1
- package/dist/esm/internal/http/serverRequest.js +8 -0
- package/dist/esm/internal/http/serverRequest.js.map +1 -1
- package/dist/esm/internal/http/serverResponse.js +32 -15
- package/dist/esm/internal/http/serverResponse.js.map +1 -1
- package/dist/esm/internal/keyValueStore.js.map +1 -1
- package/dist/esm/internal/path.js.map +1 -1
- package/dist/esm/internal/platformLogger.js.map +1 -1
- package/dist/esm/internal/terminal.js.map +1 -1
- package/dist/esm/internal/worker.js.map +1 -1
- package/dist/esm/internal/workerError.js.map +1 -1
- package/dist/esm/internal/workerRunner.js.map +1 -1
- package/package.json +12 -4
- package/src/Http/Client.ts +11 -0
- package/src/Http/ClientResponse.ts +2 -0
- package/src/Http/Cookies.ts +695 -0
- package/src/Http/Headers.ts +16 -8
- package/src/Http/ServerRequest.ts +2 -0
- package/src/Http/ServerResponse.ts +126 -0
- package/src/HttpClient.ts +8 -0
- package/src/HttpServer.ts +8 -0
- package/src/internal/http/client.ts +35 -0
- package/src/internal/http/clientResponse.ts +9 -0
- package/src/internal/http/serverRequest.ts +10 -0
- package/src/internal/http/serverResponse.ts +162 -0
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as Duration from "effect/Duration"
|
|
5
|
+
import * as Either from "effect/Either"
|
|
6
|
+
import { dual } from "effect/Function"
|
|
7
|
+
import * as Inspectable from "effect/Inspectable"
|
|
8
|
+
import * as Option from "effect/Option"
|
|
9
|
+
import { type Pipeable, pipeArguments } from "effect/Pipeable"
|
|
10
|
+
import * as Predicate from "effect/Predicate"
|
|
11
|
+
import * as ReadonlyArray from "effect/ReadonlyArray"
|
|
12
|
+
import type * as Types from "effect/Types"
|
|
13
|
+
import { TypeIdError } from "../Error.js"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
* @category type ids
|
|
18
|
+
*/
|
|
19
|
+
export const TypeId = Symbol.for("@effect/platform/Http/Cookies")
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
* @category type ids
|
|
24
|
+
*/
|
|
25
|
+
export type TypeId = typeof TypeId
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @since 1.0.0
|
|
29
|
+
* @category refinements
|
|
30
|
+
*/
|
|
31
|
+
export const isCookies = (u: unknown): u is Cookies => Predicate.hasProperty(u, TypeId)
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
* @category models
|
|
36
|
+
*/
|
|
37
|
+
export interface Cookies extends Pipeable, Inspectable.Inspectable {
|
|
38
|
+
readonly [TypeId]: TypeId
|
|
39
|
+
readonly cookies: ReadonlyArray<Cookie>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @since 1.0.0
|
|
44
|
+
* @category type ids
|
|
45
|
+
*/
|
|
46
|
+
export const CookieTypeId = Symbol.for("@effect/platform/Http/Cookies/Cookie")
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @since 1.0.0
|
|
50
|
+
* @category type ids
|
|
51
|
+
*/
|
|
52
|
+
export type CookieTypeId = typeof CookieTypeId
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @since 1.0.0
|
|
56
|
+
* @category cookie
|
|
57
|
+
*/
|
|
58
|
+
export interface Cookie extends Inspectable.Inspectable {
|
|
59
|
+
readonly [CookieTypeId]: CookieTypeId
|
|
60
|
+
readonly name: string
|
|
61
|
+
readonly value: string
|
|
62
|
+
readonly valueEncoded: string
|
|
63
|
+
readonly options?: {
|
|
64
|
+
readonly domain?: string | undefined
|
|
65
|
+
readonly expires?: Date | undefined
|
|
66
|
+
readonly maxAge?: Duration.DurationInput | undefined
|
|
67
|
+
readonly path?: string | undefined
|
|
68
|
+
readonly priority?: "low" | "medium" | "high" | undefined
|
|
69
|
+
readonly httpOnly?: boolean | undefined
|
|
70
|
+
readonly secure?: boolean | undefined
|
|
71
|
+
readonly partitioned?: boolean | undefined
|
|
72
|
+
readonly sameSite?: "lax" | "strict" | "none" | undefined
|
|
73
|
+
} | undefined
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @since 1.0.0
|
|
78
|
+
* @category type ids
|
|
79
|
+
*/
|
|
80
|
+
export const ErrorTypeId = Symbol.for("@effect/platform/Http/Cookies/CookieError")
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @since 1.0.0
|
|
84
|
+
* @category type ids
|
|
85
|
+
*/
|
|
86
|
+
export type ErrorTypeId = typeof ErrorTypeId
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @since 1.0.0
|
|
90
|
+
* @category errors
|
|
91
|
+
*/
|
|
92
|
+
export class CookiesError extends TypeIdError(ErrorTypeId, "CookieError")<{
|
|
93
|
+
readonly reason: "InvalidName" | "InvalidValue" | "InvalidDomain" | "InvalidPath" | "InfinityMaxAge"
|
|
94
|
+
}> {
|
|
95
|
+
get message() {
|
|
96
|
+
return this.reason
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const Proto: Omit<Cookies, "cookies"> = {
|
|
101
|
+
[TypeId]: TypeId,
|
|
102
|
+
...Inspectable.BaseProto,
|
|
103
|
+
toJSON(this: Cookies) {
|
|
104
|
+
return {
|
|
105
|
+
_id: "@effect/platform/Http/Cookies",
|
|
106
|
+
cookies: this.cookies.map((cookie) => cookie.toJSON())
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
pipe() {
|
|
110
|
+
return pipeArguments(this, arguments)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create a Cookies object from an Iterable
|
|
116
|
+
*
|
|
117
|
+
* @since 1.0.0
|
|
118
|
+
* @category constructors
|
|
119
|
+
*/
|
|
120
|
+
export const fromIterable = (cookies: Iterable<Cookie>): Cookies => {
|
|
121
|
+
const self = Object.create(Proto)
|
|
122
|
+
self.cookies = ReadonlyArray.fromIterable(cookies)
|
|
123
|
+
return self
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Create a Cookies object from a set of Set-Cookie headers
|
|
128
|
+
*
|
|
129
|
+
* @since 1.0.0
|
|
130
|
+
* @category constructors
|
|
131
|
+
*/
|
|
132
|
+
export const fromSetCookie = (headers: Iterable<string> | string): Cookies => {
|
|
133
|
+
const arrayHeaders = typeof headers === "string" ? [headers] : headers
|
|
134
|
+
const cookies: Array<Cookie> = []
|
|
135
|
+
for (const header of arrayHeaders) {
|
|
136
|
+
const cookie = parseSetCookie(header.trim())
|
|
137
|
+
if (Option.isSome(cookie)) {
|
|
138
|
+
cookies.push(cookie.value)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return fromIterable(cookies)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function parseSetCookie(header: string): Option.Option<Cookie> {
|
|
146
|
+
const parts = header.split(";").map((_) => _.trim()).filter((_) => _ !== "")
|
|
147
|
+
if (parts.length === 0) {
|
|
148
|
+
return Option.none()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const firstEqual = parts[0].indexOf("=")
|
|
152
|
+
if (firstEqual === -1) {
|
|
153
|
+
return Option.none()
|
|
154
|
+
}
|
|
155
|
+
const name = parts[0].slice(0, firstEqual)
|
|
156
|
+
if (!fieldContentRegExp.test(name)) {
|
|
157
|
+
return Option.none()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const valueEncoded = parts[0].slice(firstEqual + 1)
|
|
161
|
+
const value = tryDecodeURIComponent(valueEncoded)
|
|
162
|
+
|
|
163
|
+
if (parts.length === 1) {
|
|
164
|
+
return Option.some(Object.assign(Object.create(CookieProto), {
|
|
165
|
+
name,
|
|
166
|
+
value,
|
|
167
|
+
valueEncoded
|
|
168
|
+
}))
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const options: Types.Mutable<Cookie["options"]> = {}
|
|
172
|
+
|
|
173
|
+
for (let i = 1; i < parts.length; i++) {
|
|
174
|
+
const part = parts[i]
|
|
175
|
+
const equalIndex = part.indexOf("=")
|
|
176
|
+
const key = equalIndex === -1 ? part : part.slice(0, equalIndex).trim()
|
|
177
|
+
const value = equalIndex === -1 ? undefined : part.slice(equalIndex + 1).trim()
|
|
178
|
+
|
|
179
|
+
switch (key.toLowerCase()) {
|
|
180
|
+
case "domain": {
|
|
181
|
+
if (value === undefined) {
|
|
182
|
+
break
|
|
183
|
+
}
|
|
184
|
+
const domain = value.trim().replace(/^\./, "")
|
|
185
|
+
if (domain) {
|
|
186
|
+
options.domain = domain
|
|
187
|
+
}
|
|
188
|
+
break
|
|
189
|
+
}
|
|
190
|
+
case "expires": {
|
|
191
|
+
if (value === undefined) {
|
|
192
|
+
break
|
|
193
|
+
}
|
|
194
|
+
const date = new Date(value)
|
|
195
|
+
if (!isNaN(date.getTime())) {
|
|
196
|
+
options.expires = date
|
|
197
|
+
}
|
|
198
|
+
break
|
|
199
|
+
}
|
|
200
|
+
case "max-age": {
|
|
201
|
+
if (value === undefined) {
|
|
202
|
+
break
|
|
203
|
+
}
|
|
204
|
+
const maxAge = parseInt(value, 10)
|
|
205
|
+
if (!isNaN(maxAge)) {
|
|
206
|
+
options.maxAge = Duration.seconds(maxAge)
|
|
207
|
+
}
|
|
208
|
+
break
|
|
209
|
+
}
|
|
210
|
+
case "path": {
|
|
211
|
+
if (value === undefined) {
|
|
212
|
+
break
|
|
213
|
+
}
|
|
214
|
+
if (value[0] === "/") {
|
|
215
|
+
options.path = value
|
|
216
|
+
}
|
|
217
|
+
break
|
|
218
|
+
}
|
|
219
|
+
case "priority": {
|
|
220
|
+
if (value === undefined) {
|
|
221
|
+
break
|
|
222
|
+
}
|
|
223
|
+
switch (value.toLowerCase()) {
|
|
224
|
+
case "low":
|
|
225
|
+
options.priority = "low"
|
|
226
|
+
break
|
|
227
|
+
case "medium":
|
|
228
|
+
options.priority = "medium"
|
|
229
|
+
break
|
|
230
|
+
case "high":
|
|
231
|
+
options.priority = "high"
|
|
232
|
+
break
|
|
233
|
+
}
|
|
234
|
+
break
|
|
235
|
+
}
|
|
236
|
+
case "httponly": {
|
|
237
|
+
options.httpOnly = true
|
|
238
|
+
break
|
|
239
|
+
}
|
|
240
|
+
case "secure": {
|
|
241
|
+
options.secure = true
|
|
242
|
+
break
|
|
243
|
+
}
|
|
244
|
+
case "partitioned": {
|
|
245
|
+
options.partitioned = true
|
|
246
|
+
break
|
|
247
|
+
}
|
|
248
|
+
case "samesite": {
|
|
249
|
+
if (value === undefined) {
|
|
250
|
+
break
|
|
251
|
+
}
|
|
252
|
+
switch (value.toLowerCase()) {
|
|
253
|
+
case "lax":
|
|
254
|
+
options.sameSite = "lax"
|
|
255
|
+
break
|
|
256
|
+
case "strict":
|
|
257
|
+
options.sameSite = "strict"
|
|
258
|
+
break
|
|
259
|
+
case "none":
|
|
260
|
+
options.sameSite = "none"
|
|
261
|
+
break
|
|
262
|
+
}
|
|
263
|
+
break
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return Option.some(Object.assign(Object.create(CookieProto), {
|
|
269
|
+
name,
|
|
270
|
+
value,
|
|
271
|
+
valueEncoded,
|
|
272
|
+
options: Object.keys(options).length > 0 ? options : undefined
|
|
273
|
+
}))
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* An empty Cookies object
|
|
278
|
+
*
|
|
279
|
+
* @since 1.0.0
|
|
280
|
+
* @category constructors
|
|
281
|
+
*/
|
|
282
|
+
export const empty: Cookies = fromIterable([])
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @since 1.0.0
|
|
286
|
+
* @category refinements
|
|
287
|
+
*/
|
|
288
|
+
export const isEmpty = (self: Cookies): boolean => self.cookies.length === 0
|
|
289
|
+
|
|
290
|
+
// eslint-disable-next-line no-control-regex
|
|
291
|
+
const fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/
|
|
292
|
+
|
|
293
|
+
const CookieProto = {
|
|
294
|
+
[CookieTypeId]: CookieTypeId,
|
|
295
|
+
...Inspectable.BaseProto,
|
|
296
|
+
toJSON(this: Cookie) {
|
|
297
|
+
return {
|
|
298
|
+
_id: "@effect/platform/Http/Cookies/Cookie",
|
|
299
|
+
name: this.name,
|
|
300
|
+
value: this.value,
|
|
301
|
+
options: this.options
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Create a new cookie
|
|
308
|
+
*
|
|
309
|
+
* @since 1.0.0
|
|
310
|
+
* @category constructors
|
|
311
|
+
*/
|
|
312
|
+
export function makeCookie(
|
|
313
|
+
name: string,
|
|
314
|
+
value: string,
|
|
315
|
+
options?: Cookie["options"] | undefined
|
|
316
|
+
): Either.Either<Cookie, CookiesError> {
|
|
317
|
+
if (!fieldContentRegExp.test(name)) {
|
|
318
|
+
return Either.left(new CookiesError({ reason: "InvalidName" }))
|
|
319
|
+
}
|
|
320
|
+
const encodedValue = encodeURIComponent(value)
|
|
321
|
+
if (encodedValue && !fieldContentRegExp.test(encodedValue)) {
|
|
322
|
+
return Either.left(new CookiesError({ reason: "InvalidValue" }))
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (options !== undefined) {
|
|
326
|
+
if (options.domain !== undefined && !fieldContentRegExp.test(options.domain)) {
|
|
327
|
+
return Either.left(new CookiesError({ reason: "InvalidDomain" }))
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (options.path !== undefined && !fieldContentRegExp.test(options.path)) {
|
|
331
|
+
return Either.left(new CookiesError({ reason: "InvalidPath" }))
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (options.maxAge !== undefined && !Duration.isFinite(Duration.decode(options.maxAge))) {
|
|
335
|
+
return Either.left(new CookiesError({ reason: "InfinityMaxAge" }))
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return Either.right(Object.assign(Object.create(CookieProto), {
|
|
340
|
+
name,
|
|
341
|
+
value,
|
|
342
|
+
valueEncoded: encodedValue,
|
|
343
|
+
options
|
|
344
|
+
}))
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Create a new cookie, throwing an error if invalid
|
|
349
|
+
*
|
|
350
|
+
* @since 1.0.0
|
|
351
|
+
* @category constructors
|
|
352
|
+
*/
|
|
353
|
+
export const unsafeMakeCookie = (
|
|
354
|
+
name: string,
|
|
355
|
+
value: string,
|
|
356
|
+
options?: Cookie["options"] | undefined
|
|
357
|
+
): Cookie => Either.getOrThrow(makeCookie(name, value, options))
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Add a cookie to a Cookies object
|
|
361
|
+
*
|
|
362
|
+
* @since 1.0.0
|
|
363
|
+
* @category combinators
|
|
364
|
+
*/
|
|
365
|
+
export const append: {
|
|
366
|
+
(cookie: Cookie): (self: Cookies) => Cookies
|
|
367
|
+
(
|
|
368
|
+
self: Cookies,
|
|
369
|
+
cookie: Cookie
|
|
370
|
+
): Cookies
|
|
371
|
+
} = dual(
|
|
372
|
+
2,
|
|
373
|
+
(self: Cookies, cookie: Cookie) => fromIterable([...self.cookies, cookie])
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Add multiple cookies to a Cookies object
|
|
378
|
+
*
|
|
379
|
+
* @since 1.0.0
|
|
380
|
+
* @category combinators
|
|
381
|
+
*/
|
|
382
|
+
export const appendAll: {
|
|
383
|
+
(cookies: Iterable<Cookie>): (self: Cookies) => Cookies
|
|
384
|
+
(
|
|
385
|
+
self: Cookies,
|
|
386
|
+
cookies: Iterable<Cookie>
|
|
387
|
+
): Cookies
|
|
388
|
+
} = dual(2, (self: Cookies, cookies: Iterable<Cookie>) =>
|
|
389
|
+
fromIterable(
|
|
390
|
+
ReadonlyArray.appendAll(self.cookies, cookies)
|
|
391
|
+
))
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Combine two Cookies objects, removing duplicates from the first
|
|
395
|
+
*
|
|
396
|
+
* @since 1.0.0
|
|
397
|
+
* @category combinators
|
|
398
|
+
*/
|
|
399
|
+
export const merge: {
|
|
400
|
+
(that: Cookies): (self: Cookies) => Cookies
|
|
401
|
+
(
|
|
402
|
+
self: Cookies,
|
|
403
|
+
that: Cookies
|
|
404
|
+
): Cookies
|
|
405
|
+
} = dual(2, (self: Cookies, that: Cookies) => {
|
|
406
|
+
const cookies = self.cookies.filter((c) => !that.cookies.some((c2) => c2.name === c.name))
|
|
407
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
408
|
+
cookies.push(...that.cookies)
|
|
409
|
+
return fromIterable(cookies)
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Remove a cookie by name
|
|
414
|
+
*
|
|
415
|
+
* @since 1.0.0
|
|
416
|
+
* @category combinators
|
|
417
|
+
*/
|
|
418
|
+
export const remove: {
|
|
419
|
+
(name: string): (self: Cookies) => Cookies
|
|
420
|
+
(
|
|
421
|
+
self: Cookies,
|
|
422
|
+
name: string
|
|
423
|
+
): Cookies
|
|
424
|
+
} = dual(2, (self: Cookies, cookie: Cookie) =>
|
|
425
|
+
fromIterable(
|
|
426
|
+
self.cookies.filter((c) => c.name !== cookie.name)
|
|
427
|
+
))
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Add a cookie to a Cookies object
|
|
431
|
+
*
|
|
432
|
+
* @since 1.0.0
|
|
433
|
+
* @category combinators
|
|
434
|
+
*/
|
|
435
|
+
export const add: {
|
|
436
|
+
(
|
|
437
|
+
name: string,
|
|
438
|
+
value: string,
|
|
439
|
+
options?: Cookie["options"]
|
|
440
|
+
): (self: Cookies) => Either.Either<Cookies, CookiesError>
|
|
441
|
+
(
|
|
442
|
+
self: Cookies,
|
|
443
|
+
name: string,
|
|
444
|
+
value: string,
|
|
445
|
+
options?: Cookie["options"]
|
|
446
|
+
): Either.Either<Cookies, CookiesError>
|
|
447
|
+
} = dual(
|
|
448
|
+
(args) => isCookies(args[0]),
|
|
449
|
+
(self: Cookies, name: string, value: string, options?: Cookie["options"]) =>
|
|
450
|
+
Either.map(
|
|
451
|
+
makeCookie(name, value, options),
|
|
452
|
+
(cookie) => fromIterable([...self.cookies, cookie])
|
|
453
|
+
)
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Add a cookie to a Cookies object
|
|
458
|
+
*
|
|
459
|
+
* @since 1.0.0
|
|
460
|
+
* @category combinators
|
|
461
|
+
*/
|
|
462
|
+
export const unsafeAdd: {
|
|
463
|
+
(
|
|
464
|
+
name: string,
|
|
465
|
+
value: string,
|
|
466
|
+
options?: Cookie["options"]
|
|
467
|
+
): (self: Cookies) => Cookies
|
|
468
|
+
(
|
|
469
|
+
self: Cookies,
|
|
470
|
+
name: string,
|
|
471
|
+
value: string,
|
|
472
|
+
options?: Cookie["options"]
|
|
473
|
+
): Cookies
|
|
474
|
+
} = dual(
|
|
475
|
+
(args) => isCookies(args[0]),
|
|
476
|
+
(self: Cookies, name: string, value: string, options?: Cookie["options"]) =>
|
|
477
|
+
append(self, unsafeMakeCookie(name, value, options))
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Add multiple cookies to a Cookies object
|
|
482
|
+
*
|
|
483
|
+
* @since 1.0.0
|
|
484
|
+
* @category combinators
|
|
485
|
+
*/
|
|
486
|
+
export const addAll: {
|
|
487
|
+
(
|
|
488
|
+
cookies: Iterable<readonly [name: string, value: string, options?: Cookie["options"]]>
|
|
489
|
+
): (self: Cookies) => Either.Either<Cookies, CookiesError>
|
|
490
|
+
(
|
|
491
|
+
self: Cookies,
|
|
492
|
+
cookies: Iterable<readonly [name: string, value: string, options?: Cookie["options"]]>
|
|
493
|
+
): Either.Either<Cookies, CookiesError>
|
|
494
|
+
} = dual(
|
|
495
|
+
2,
|
|
496
|
+
(
|
|
497
|
+
self: Cookies,
|
|
498
|
+
cookies: Iterable<readonly [name: string, value: string, options?: Cookie["options"]]>
|
|
499
|
+
): Either.Either<Cookies, CookiesError> => {
|
|
500
|
+
const toAdd: Array<Cookie> = []
|
|
501
|
+
for (const [name, value, options] of cookies) {
|
|
502
|
+
const either = makeCookie(name, value, options)
|
|
503
|
+
if (Either.isLeft(either)) {
|
|
504
|
+
return either as Either.Left<CookiesError, never>
|
|
505
|
+
}
|
|
506
|
+
toAdd.push(either.right)
|
|
507
|
+
}
|
|
508
|
+
return Either.right(appendAll(self, toAdd))
|
|
509
|
+
}
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Add multiple cookies to a Cookies object, throwing an error if invalid
|
|
514
|
+
*
|
|
515
|
+
* @since 1.0.0
|
|
516
|
+
* @category combinators
|
|
517
|
+
*/
|
|
518
|
+
export const unsafeAddAll: {
|
|
519
|
+
(
|
|
520
|
+
cookies: Iterable<readonly [name: string, value: string, options?: Cookie["options"]]>
|
|
521
|
+
): (self: Cookies) => Cookies
|
|
522
|
+
(
|
|
523
|
+
self: Cookies,
|
|
524
|
+
cookies: Iterable<readonly [name: string, value: string, options?: Cookie["options"]]>
|
|
525
|
+
): Cookies
|
|
526
|
+
} = dual(
|
|
527
|
+
2,
|
|
528
|
+
(
|
|
529
|
+
self: Cookies,
|
|
530
|
+
cookies: Iterable<readonly [name: string, value: string, options?: Cookie["options"]]>
|
|
531
|
+
): Cookies => Either.getOrThrow(addAll(self, cookies))
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Serialize a cookie into a string
|
|
536
|
+
*
|
|
537
|
+
* Adapted from https://github.com/fastify/fastify-cookie under MIT License
|
|
538
|
+
*
|
|
539
|
+
* @since 1.0.0
|
|
540
|
+
* @category encoding
|
|
541
|
+
*/
|
|
542
|
+
export function serializeCookie(self: Cookie): string {
|
|
543
|
+
let str = self.name + "=" + self.valueEncoded
|
|
544
|
+
|
|
545
|
+
if (self.options === undefined) {
|
|
546
|
+
return str
|
|
547
|
+
}
|
|
548
|
+
const options = self.options
|
|
549
|
+
|
|
550
|
+
if (options.maxAge !== undefined) {
|
|
551
|
+
const maxAge = Duration.toSeconds(options.maxAge)
|
|
552
|
+
str += "; Max-Age=" + Math.trunc(maxAge)
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (options.domain !== undefined) {
|
|
556
|
+
str += "; Domain=" + options.domain
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (options.path !== undefined) {
|
|
560
|
+
str += "; Path=" + options.path
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (options.priority !== undefined) {
|
|
564
|
+
switch (options.priority) {
|
|
565
|
+
case "low":
|
|
566
|
+
str += "; Priority=Low"
|
|
567
|
+
break
|
|
568
|
+
case "medium":
|
|
569
|
+
str += "; Priority=Medium"
|
|
570
|
+
break
|
|
571
|
+
case "high":
|
|
572
|
+
str += "; Priority=High"
|
|
573
|
+
break
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (options.expires !== undefined) {
|
|
578
|
+
str += "; Expires=" + options.expires.toUTCString()
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (options.httpOnly) {
|
|
582
|
+
str += "; HttpOnly"
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (options.secure) {
|
|
586
|
+
str += "; Secure"
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Draft implementation to support Chrome from 2024-Q1 forward.
|
|
590
|
+
// See https://datatracker.ietf.org/doc/html/draft-cutler-httpbis-partitioned-cookies#section-2.1
|
|
591
|
+
if (options.partitioned) {
|
|
592
|
+
str += "; Partitioned"
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (options.sameSite !== undefined) {
|
|
596
|
+
switch (options.sameSite) {
|
|
597
|
+
case "lax":
|
|
598
|
+
str += "; SameSite=Lax"
|
|
599
|
+
break
|
|
600
|
+
case "strict":
|
|
601
|
+
str += "; SameSite=Strict"
|
|
602
|
+
break
|
|
603
|
+
case "none":
|
|
604
|
+
str += "; SameSite=None"
|
|
605
|
+
break
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return str
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Serialize a Cookies object into a Cookie header
|
|
614
|
+
*
|
|
615
|
+
* @since 1.0.0
|
|
616
|
+
* @category encoding
|
|
617
|
+
*/
|
|
618
|
+
export const toCookieHeader = (self: Cookies): string =>
|
|
619
|
+
self.cookies.map((cookie) => `${cookie.name}=${cookie.valueEncoded}`).join("; ")
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* To record
|
|
623
|
+
*
|
|
624
|
+
* @since 1.0.0
|
|
625
|
+
* @category encoding
|
|
626
|
+
*/
|
|
627
|
+
export const toRecord = (self: Cookies): Record<string, string> => {
|
|
628
|
+
const record: Record<string, string> = {}
|
|
629
|
+
for (let index = 0; index < self.cookies.length; index++) {
|
|
630
|
+
const cookie = self.cookies[index]
|
|
631
|
+
record[cookie.name] = cookie.value
|
|
632
|
+
}
|
|
633
|
+
return record
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Serialize a Cookies object into Headers object containing one or more Set-Cookie headers
|
|
638
|
+
*
|
|
639
|
+
* @since 1.0.0
|
|
640
|
+
* @category encoding
|
|
641
|
+
*/
|
|
642
|
+
export const toSetCookieHeaders = (self: Cookies): Array<string> => self.cookies.map(serializeCookie)
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Parse a cookie header into a record of key-value pairs
|
|
646
|
+
*
|
|
647
|
+
* Adapted from https://github.com/fastify/fastify-cookie under MIT License
|
|
648
|
+
*
|
|
649
|
+
* @since 1.0.0
|
|
650
|
+
* @category decoding
|
|
651
|
+
*/
|
|
652
|
+
export function parseHeader(header: string): Record<string, string> {
|
|
653
|
+
const result: Record<string, string> = {}
|
|
654
|
+
|
|
655
|
+
const strLen = header.length
|
|
656
|
+
let pos = 0
|
|
657
|
+
let terminatorPos = 0
|
|
658
|
+
// eslint-disable-next-line no-constant-condition
|
|
659
|
+
while (true) {
|
|
660
|
+
if (terminatorPos === strLen) break
|
|
661
|
+
terminatorPos = header.indexOf(";", pos)
|
|
662
|
+
if (terminatorPos === -1) terminatorPos = strLen // This is the last pair
|
|
663
|
+
|
|
664
|
+
let eqIdx = header.indexOf("=", pos)
|
|
665
|
+
if (eqIdx === -1) break // No key-value pairs left
|
|
666
|
+
if (eqIdx > terminatorPos) {
|
|
667
|
+
// Malformed key-value pair
|
|
668
|
+
pos = terminatorPos + 1
|
|
669
|
+
continue
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const key = header.substring(pos, eqIdx++).trim()
|
|
673
|
+
if (result[key] === undefined) {
|
|
674
|
+
const val = header.charCodeAt(eqIdx) === 0x22
|
|
675
|
+
? header.substring(eqIdx + 1, terminatorPos - 1).trim()
|
|
676
|
+
: header.substring(eqIdx, terminatorPos).trim()
|
|
677
|
+
|
|
678
|
+
result[key] = !(val.indexOf("%") === -1)
|
|
679
|
+
? tryDecodeURIComponent(val)
|
|
680
|
+
: val
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
pos = terminatorPos + 1
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return result
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const tryDecodeURIComponent = (str: string): string => {
|
|
690
|
+
try {
|
|
691
|
+
return decodeURIComponent(str)
|
|
692
|
+
} catch (_) {
|
|
693
|
+
return str
|
|
694
|
+
}
|
|
695
|
+
}
|