Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement CookieStore #1441

Closed
wants to merge 13 commits into from
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,57 @@ Gets the global dispatcher used by Common API Methods.

Returns: `Dispatcher`

### `undici.CookieStore`

Implements [CookieStore](https://wicg.github.io/cookie-store/)

* https://wicg.github.io/cookie-store/
* https://developer.mozilla.org/en-US/docs/Web/API/CookieStore

This is [experimental](https://nodejs.org/api/documentation.html#documentation_stability_index) and is not yet fully compliant with the CookieStore Standard.
We plan to ship breaking changes to this feature until it is out of experimental.

Basic usage example:

*Note*: You cannot directly construct a new CookieStore due to the spec.

```js
import { CookieStore, CookieStoreFrom } from 'undici'
import assert from 'node:assert'

const setCookies = [
'sessionId=abcd; Path=/account/login; Domain=example.com',
'key=value; Path=/admin; Expires=Sun, 15 May 2022 16:09:49 GMT'
]

const cookieStore = CookieStoreFrom(setCookies)

assert(cookieStore instanceof CookieStore)

const cookie = await cookieStore.get('sessionId')
const cookies = await cookieStore.getAll()
```

```js
import { CookieStoreFrom } from 'undici'

const cookieStore = CookieStoreFrom() // create an empty CookieStore
console.log(cookieStore) // CookieStore []
```

```js
import { CookieStoreFrom } from 'undici'

// create an empty cookie store where cookie domains must be 'example.com'
const cookieStore = CookieStoreFrom(undefined, 'https://example.com')

await cookieStore.set({ name: 'cookie', value: 'my-value', domain: 'example.org' }) // rejects
await cookieStore.set({ name: 'cookie', value: 'my-value', domain: 'example.com' }) // resolves
await cookieStore.set({ name: 'cookie', value: 'my-value' }) // resolves

// By excluding a second argument, all cookies will be considered valid.
```

### `UrlObject`

* **port** `string | number` (optional)
Expand Down
16 changes: 16 additions & 0 deletions lib/cookie-store/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict'

// https://wicg.github.io/cookie-store/#cookie-maximum-attribute-value-size
const maxAttributeValueSize = 1024

// https://wicg.github.io/cookie-store/#cookie-maximum-name-value-pair-size
const maxNameValuePairSize = 4096

// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-4.1.2.1
const maxExpiresMs = 34_560_000 * 1000

module.exports = {
maxAttributeValueSize,
maxNameValuePairSize,
maxExpiresMs
}
43 changes: 43 additions & 0 deletions lib/cookie-store/cookie-change-event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict'

const kChanged = Symbol('CookieChangeEvent changed')
const kDeleted = Symbol('CookieChangeEvent deleted')

// https://wicg.github.io/cookie-store/#CookieChangeEvent
class CookieChangeEvent extends Event {
/**
* @param {string} type
* @param {EventInit} eventInitDict
*/
constructor (type, eventInitDict = {}) {
super(type, eventInitDict)

this[kChanged] = []
this[kDeleted] = []

if (eventInitDict) {
if (eventInitDict.changed) {
this[kChanged].push(...eventInitDict.changed)
}

if (eventInitDict.deleted) {
this[kDeleted].push(...eventInitDict.deleted)
}
}

Object.freeze(this[kChanged])
Object.freeze(this[kDeleted])
}

get changed () {
return this[kChanged]
}

get deleted () {
return this[kDeleted]
}
}

module.exports = {
CookieChangeEvent
}
Loading