Browse Source

Code formatting

main
Cédric Belin 1 month ago
parent
commit
03d048e6c2
31 changed files with 1421 additions and 1418 deletions
  1. +6
    -3
      .editorconfig
  2. +1
    -1
      .github/workflows/build.yaml
  3. +5
    -5
      .vscode/settings.json
  4. +29
    -29
      angular.json
  5. +0
    -4
      doc/about/see_also.md
  6. +2
    -2
      doc/index.md
  7. +8
    -8
      doc/installation.md
  8. +190
    -190
      doc/usage/api.md
  9. +45
    -45
      doc/usage/events.md
  10. +16
    -16
      doc/usage/iteration.md
  11. +32
    -32
      doc/usage/options.md
  12. +61
    -61
      etc/eslint.yaml
  13. +14
    -14
      etc/karma.cjs
  14. +9
    -5
      etc/mkdocs.yaml
  15. +54
    -54
      example/main.ts
  16. +7
    -7
      ng-package.json
  17. +80
    -80
      package.json
  18. +109
    -109
      src/cookie_options.ts
  19. +224
    -224
      src/cookies.ts
  20. +3
    -3
      src/index.ts
  21. +6
    -6
      src/json.ts
  22. +19
    -19
      src/tsconfig.json
  23. +109
    -109
      test/cookie_options_test.ts
  24. +358
    -358
      test/cookies_test.ts
  25. +5
    -5
      test/main.ts
  26. +5
    -5
      test/tsconfig.json
  27. +6
    -6
      tool/build.ps1
  28. +3
    -3
      tool/clean.ps1
  29. +0
    -4
      tool/fix.ps1
  30. +5
    -1
      tool/publish.ps1
  31. +10
    -10
      tool/watch.ps1

+ 6
- 3
.editorconfig View File

@@ -3,11 +3,14 @@ root = true

[*]
charset = utf-8
indent_size = 2
indent_style = space
indent_style = tab
insert_final_newline = true
tab_width = 2
trim_trailing_whitespace = true

[*.md]
indent_size = 4
trim_trailing_whitespace = false

[*.{yaml,yml}]
indent_size = 2
indent_style = space

+ 1
- 1
.github/workflows/build.yaml View File

@@ -3,7 +3,7 @@ on:
pull_request:
push:
schedule:
- cron: '0 0 1 * *'
- cron: "0 0 1 * *"
jobs:
test:
runs-on: ubuntu-latest


+ 5
- 5
.vscode/settings.json View File

@@ -1,7 +1,7 @@
{
"editor.insertSpaces": true,
"editor.tabSize": 2,
"files.encoding": "utf8",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true
"editor.insertSpaces": false,
"editor.tabSize": 2,
"files.encoding": "utf8",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true
}

+ 29
- 29
angular.json View File

@@ -1,31 +1,31 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"defaultProject": "ngx-cookies",
"version": 1,
"projects": {
"ngx-cookies": {
"prefix": "ngx",
"projectType": "library",
"root": "",
"sourceRoot": "src",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"project": "ng-package.json",
"tsConfig": "src/tsconfig.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"codeCoverage": true,
"main": "test/main.ts",
"karmaConfig": "etc/karma.cjs",
"tsConfig": "test/tsconfig.json"
}
}
}
}
}
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"defaultProject": "ngx-cookies",
"version": 1,
"projects": {
"ngx-cookies": {
"prefix": "ngx",
"projectType": "library",
"root": "",
"sourceRoot": "src",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"project": "ng-package.json",
"tsConfig": "src/tsconfig.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"codeCoverage": true,
"main": "test/main.ts",
"karmaConfig": "etc/karma.cjs",
"tsConfig": "test/tsconfig.json"
}
}
}
}
}
}

+ 0
- 4
doc/about/see_also.md View File

@@ -8,7 +8,3 @@
## Testing
- [Continuous integration](https://github.com/cedx/ngx-cookies/actions)
- [Code coverage](https://coveralls.io/github/cedx/ngx-cookies)

## Other implementations
- Dart: [Biscuits.dart](https://docs.belin.io/biscuits.dart)
- JavaScript: [Cookies for JS](https://docs.belin.io/cookies.js)

+ 2
- 2
doc/index.md View File

@@ -9,11 +9,11 @@ implemented in [TypeScript](https://www.typescriptlang.org).
## Quick start
Install the latest version of **Cookie Service for Angular** with [npm](https://www.npmjs.com):

```shell
``` shell
npm install @cedx/ngx-cookies
```

!!! info
This library is packaged as [ECMAScript modules](https://nodejs.org/api/esm.html).
This library is packaged as [ECMAScript modules](https://nodejs.org/api/esm.html).

For detailed instructions, see the [installation guide](installation.md).

+ 8
- 8
doc/installation.md View File

@@ -6,30 +6,30 @@ and [npm](https://www.npmjs.com), the Node.js package manager, up and running.

You can verify if you're already good to go with the following commands:

```shell
``` shell
node --version
# v14.2.0
# v14.3.0

npm --version
# 6.14.4
# 6.14.5
```

!!! info
If you plan to play with the package sources, you will also need
[PowerShell](https://docs.microsoft.com/en-us/powershell) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material).
If you plan to play with the package sources, you will also need
[PowerShell](https://docs.microsoft.com/en-us/powershell) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material).

## Installing with npm package manager

### 1. Install it
From a command prompt, run:

```shell
``` shell
npm install @cedx/ngx-cookies
```

### 2. Import it
Now in your [TypeScript](https://www.typescriptlang.org) code, you can use:

```typescript
import {Cookies, CookieOptions} from '@cedx/ngx-cookies';
``` typescript
import {Cookies, CookieOptions} from "@cedx/ngx-cookies";
```

+ 190
- 190
doc/usage/api.md View File

@@ -6,24 +6,24 @@ source: src/cookies.ts
# Programming interface
This package provides a service dedicated to the cookie management: the `Cookies` class.

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.set('foo', 'bar');
console.log(this._cookies.get('foo')); // "bar"
this._cookies.setObject('foo', {baz: 'qux'});
console.log(this._cookies.getObject('foo')); // {baz: "qux"}
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.set("foo", "bar");
console.log(this._cookies.get("foo")); // "bar"
this._cookies.setObject("foo", {baz: "qux"});
console.log(this._cookies.getObject("foo")); // {baz: "qux"}
}
}
```

@@ -32,122 +32,122 @@ The `Cookies` class has the following API:
## **defaults**: CookieOptions
Returns the default [options](options.md) to pass when setting cookies:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "", "expires": null, "path": "", "secure": false}
this._cookies.defaults.domain = 'domain.com';
this._cookies.defaults.path = '/www';
this._cookies.defaults.secure = true;
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "domain.com", "expires": null, "path": "/www", "secure": true}
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "", "expires": null, "path": "", "secure": false}
this._cookies.defaults.domain = "domain.com";
this._cookies.defaults.path = "/www";
this._cookies.defaults.secure = true;
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "domain.com", "expires": null, "path": "/www", "secure": true}
}
}
```

## **keys**: string[]
Returns the keys of the cookies associated with the current document:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.keys); // []
this._cookies.set('foo', 'bar');
console.log(this._cookies.keys); // ["foo"]
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.keys); // []
this._cookies.set("foo", "bar");
console.log(this._cookies.keys); // ["foo"]
}
}
```

## **length**: number
Returns the number of cookies associated with the current document:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.length); // 0
this._cookies.set('foo', 'bar');
console.log(this._cookies.length); // 1
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.length); // 0
this._cookies.set("foo", "bar");
console.log(this._cookies.length); // 1
}
}
```

## **clear**(): void
Removes all cookies associated with the current document:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.set('foo', 'bar');
console.log(this._cookies.length); // 1
this._cookies.clear();
console.log(this._cookies.length); // 0
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.set("foo", "bar");
console.log(this._cookies.length); // 1
this._cookies.clear();
console.log(this._cookies.length); // 0
}
}
```

## **get**(key: string, defaultValue?: string): string|undefined
Returns the value associated to the specified key:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.get('foo')); // undefined
console.log(this._cookies.get('foo', 'qux')); // "qux"
this._cookies.set('foo', 'bar');
console.log(this._cookies.get('foo')); // "bar"
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.get("foo")); // undefined
console.log(this._cookies.get("foo", "qux")); // "qux"
this._cookies.set("foo", "bar");
console.log(this._cookies.get("foo")); // "bar"
}
}
```

@@ -156,52 +156,52 @@ Returns `undefined` or the given default value if the key is not found.
## **getObject**(key: string, defaultValue?: any): any
Deserializes and returns the value associated to the specified key:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.getObject('foo')); // undefined
console.log(this._cookies.getObject('foo', 'qux')); // "qux"
this._cookies.setObject('foo', {bar: 'baz'});
console.log(this._cookies.getObject('foo')); // {bar: "baz"}
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.getObject("foo")); // undefined
console.log(this._cookies.getObject("foo", "qux")); // "qux"
this._cookies.setObject("foo", {bar: "baz"});
console.log(this._cookies.getObject("foo")); // {bar: "baz"}
}
}
```

!!! info
The value is deserialized using the [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) method.
The value is deserialized using the [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) method.

Returns `undefined` or the given default value if the key is not found.

## **has**(key: string): boolean
Returns a boolean value indicating whether the current document has a cookie with the specified key:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.has('foo')); // false
this._cookies.set('foo', 'bar');
console.log(this._cookies.has('foo')); // true
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.has("foo")); // false
this._cookies.set("foo", "bar");
console.log(this._cookies.has("foo")); // true
}
}
```

@@ -210,27 +210,27 @@ Looks up the cookie with the specified key, or add a new cookie if it isn't ther

Returns the value associated to the key, if there is one. Otherwise calls `ifAbsent` to get a new value, associates the key to that value, and then returns the new value:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.has('foo')); // false
let value = this._cookies.putIfAbsent('foo', () => 'bar');
console.log(this._cookies.has('foo')); // true
console.log(value); // "bar"
value = this._cookies.putIfAbsent('foo', () => 'qux');
console.log(value); // "bar"
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.has("foo")); // false
let value = this._cookies.putIfAbsent("foo", () => "bar");
console.log(this._cookies.has("foo")); // true
console.log(value); // "bar"
value = this._cookies.putIfAbsent("foo", () => "qux");
console.log(value); // "bar"
}
}
```

@@ -239,102 +239,102 @@ Looks up the cookie with the specified key, or add a new cookie if it isn't ther

Returns the deserialized value associated to the key, if there is one. Otherwise calls `ifAbsent` to get a new value, serializes and associates the key to that value, and then returns the new value:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.has('foo')); // false
let value = this._cookies.putObjectIfAbsent('foo', () => 123);
console.log(this._cookies.has('foo')); // true
console.log(value); // 123
value = this._cookies.putObjectIfAbsent('foo', () => 456);
console.log(value); // 123
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.has("foo")); // false
let value = this._cookies.putObjectIfAbsent("foo", () => 123);
console.log(this._cookies.has("foo")); // true
console.log(value); // 123
value = this._cookies.putObjectIfAbsent("foo", () => 456);
console.log(value); // 123
}
}
```

!!! info
The value is serialized using the [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) method, and deserialized using the [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) method.
The value is serialized using the [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) method, and deserialized using the [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) method.

## **remove**(key: string, options?: CookieOptions): string
Removes the value associated to the specified key:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.set('foo', 'bar');
console.log(this._cookies.has('foo')); // true
console.log(this._cookies.remove('foo')); // "bar"
console.log(this._cookies.has('foo')); // false
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.set("foo", "bar");
console.log(this._cookies.has("foo")); // true
console.log(this._cookies.remove("foo")); // "bar"
console.log(this._cookies.has("foo")); // false
}
}
```

## **set**(key: string, value: string, options?: CookieOptions): this
Associates a given value to the specified key:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.get('foo')); // undefined
this._cookies.set('foo', 'bar');
console.log(this._cookies.get('foo')); // "bar"
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.get("foo")); // undefined
this._cookies.set("foo", "bar");
console.log(this._cookies.get("foo")); // "bar"
}
}
```

## **setObject**(key: string, value: any, options?: CookieOptions): this
Serializes and associates a given value to the specified key:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.getObject('foo')); // undefined
this._cookies.setObject('foo', {bar: 'baz'});
console.log(this._cookies.getObject('foo')); // {bar: "baz"}
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(this._cookies.getObject("foo")); // undefined
this._cookies.setObject("foo", {bar: "baz"});
console.log(this._cookies.getObject("foo")); // {bar: "baz"}
}
}
```

!!! info
The value is serialized using the [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) method.
The value is serialized using the [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) method.

+ 45
- 45
doc/usage/events.md View File

@@ -3,76 +3,76 @@ Every time one or several values are changed (added, removed or updated) through

This event is exposed as an [Observable](https://angular.io/guide/observables), you can subscribe to it using the `onChanges` property:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.onChanges.subscribe(changes => {
for (const [key, value] of Object.entries(changes)) console.log(`${key}: ${value}`);
});
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.onChanges.subscribe(changes => {
for (const [key, value] of Object.entries(changes)) console.log(`${key}: ${value}`);
});
}
}
```

The changes are expressed as a [`SimpleChanges`](https://angular.io/api/core/SimpleChanges) object.
The values of this object are [`SimpleChange`](https://angular.io/api/core/SimpleChange) instances, where an `undefined` property indicates an absence of value:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.onChanges.subscribe(changes => {
for (const [key, change] of Object.entries(changes)) console.log({
key,
current: change.currentValue,
previous: change.previousValue
});
});
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.onChanges.subscribe(changes => {
for (const [key, change] of Object.entries(changes)) console.log({
key,
current: change.currentValue,
previous: change.previousValue
});
});

this._cookies.set('foo', 'bar');
// Prints: {key: "foo", current: "bar", previous: undefined}
this._cookies.set("foo", "bar");
// Prints: {key: "foo", current: "bar", previous: undefined}

this._cookies.set('foo', 'baz');
// Prints: {key: "foo", current: "baz", previous: "bar"}
this._cookies.set("foo", "baz");
// Prints: {key: "foo", current: "baz", previous: "bar"}

this._cookies.remove('foo');
// Prints: {key: "foo", current: undefined, previous: "baz"}
}
this._cookies.remove("foo");
// Prints: {key: "foo", current: undefined, previous: "baz"}
}
}
```

The values contained in the `currentValue` and `previousValue` properties of the [`SimpleChange`](https://angular.io/api/core/SimpleChange) instances are the raw cookie values. If you use the `Cookies.setObject()` method to set a cookie, you will get the serialized string value, not the original value passed to the method:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.setObject('foo', {bar: 'baz'});
// Prints: {key: "foo", current: "{\"bar\": \"baz\"}", previous: undefined}
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.setObject("foo", {bar: "baz"});
// Prints: {key: "foo", current: "{\"bar\": \"baz\"}", previous: undefined}
}
}
```

+ 16
- 16
doc/usage/iteration.md View File

@@ -2,26 +2,26 @@
The [`Cookies`](api.md) class is iterable: you can go through all key/value pairs contained using a `for...of` loop.
Each entry is an array with two elements (i.e. the key and the value):

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.set('foo', 'bar');
this._cookies.set('anotherKey', 'anotherValue');
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.set("foo", "bar");
this._cookies.set("anotherKey", "anotherValue");

for (const entry of this._cookies) {
console.log(entry);
// Round 1: ["foo", "bar"]
// Round 2: ["anotherKey", "anotherValue"]
}
}
for (const entry of this._cookies) {
console.log(entry);
// Round 1: ["foo", "bar"]
// Round 2: ["anotherKey", "anotherValue"]
}
}
}
```

+ 32
- 32
doc/usage/options.md View File

@@ -14,55 +14,55 @@ These options are expressed using an instance of the `CookieOptions` class, whic
- **secure**: `boolean`: Value indicating whether to transmit the cookie over HTTPS only.

!!! info
The `maxAge` property has precedence over the `expires` one.
The `maxAge` property has precedence over the `expires` one.

For example:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies, CookieOptions} from '@cedx/cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies, CookieOptions} from "@cedx/cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.set('foo', 'bar', new CookieOptions({
domain: 'www.domain.com',
maxAge: 3600, // One hour.
path: '/'
}));
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
this._cookies.set("foo", "bar", new CookieOptions({
domain: "www.domain.com",
maxAge: 3600, // One hour.
path: "/"
}));
}
}
```
## Configuring defaults
It is possible to provide default values for the cookie options by using the [`Cookies.defaults`](api.md) property:

```typescript
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/cookies';
``` typescript
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/cookies";

@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "", "expires": null, "path": "", "secure": false}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "", "expires": null, "path": "", "secure": false}

this._cookies.defaults.domain = 'domain.com';
this._cookies.defaults.path = '/www';
this._cookies.defaults.secure = true;
this._cookies.defaults.domain = "domain.com";
this._cookies.defaults.path = "/www";
this._cookies.defaults.secure = true;

console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "domain.com", "expires": null, "path": "/www", "secure": true}
}
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "domain.com", "expires": null, "path": "/www", "secure": true}
}
}
```

+ 61
- 61
etc/eslint.yaml View File

@@ -1,23 +1,23 @@
root: true
env:
browser: true
es2020: true
node: true
extends:
- eslint:recommended
- plugin:@typescript-eslint/eslint-recommended
- plugin:@typescript-eslint/recommended
- plugin:@typescript-eslint/recommended-requiring-type-checking
parser: '@typescript-eslint/parser'
parser: "@typescript-eslint/parser"
parserOptions:
project: src/tsconfig.json
sourceType: module
warnOnUnsupportedTypeScriptVersion: false
plugins: ['@typescript-eslint']
plugins: ["@typescript-eslint"]

rules:
# Possible errors.
no-console: [error, {allow: [error, warn]}]
no-extra-semi: 'off'
no-extra-semi: "off"
no-template-curly-in-string: error
require-atomic-updates: error

@@ -31,7 +31,7 @@ rules:
grouped-accessor-pairs: error
no-alert: error
no-caller: error
no-case-declarations: 'off'
no-case-declarations: "off"
no-constructor-return: error
no-else-return: error
no-eval: error
@@ -74,7 +74,7 @@ rules:
no-label-var: error
no-shadow: error
no-undef-init: error
no-unused-vars: 'off'
no-unused-vars: "off"

# Stylistic issues.
array-bracket-newline: [error, consistent]
@@ -99,7 +99,7 @@ rules:
no-mixed-operators: error
no-multiple-empty-lines: error
no-new-object: error
no-tabs: error
no-tabs: [error, {allowIndentationTabs: true}]
no-trailing-spaces: error
no-unneeded-ternary: error
no-whitespace-before-property: error
@@ -126,10 +126,10 @@ rules:
arrow-parens: [error, as-needed]
arrow-spacing: error
generator-star-spacing: error
no-dupe-class-members: 'off'
no-dupe-class-members: "off"
no-duplicate-imports: error
no-useless-computed-key: error
no-useless-constructor: 'off'
no-useless-constructor: "off"
no-useless-rename: error
no-var: error
object-shorthand: error
@@ -146,57 +146,57 @@ rules:
yield-star-spacing: error

# TypeScript: supported rules.
'@typescript-eslint/array-type': [error, {default: array-simple}]
'@typescript-eslint/camelcase': 'off'
'@typescript-eslint/class-literal-property-style': error
'@typescript-eslint/explicit-function-return-type': [error, {allowExpressions: true}]
'@typescript-eslint/explicit-member-accessibility': [error, {accessibility: no-public}]
'@typescript-eslint/member-naming': [error, {private: '^_', protected: '^_'}]
'@typescript-eslint/member-ordering': [error , {default: [public-field, protected-field, private-field, constructor, public-method, protected-method, private-method]}]
'@typescript-eslint/method-signature-style': error
'@typescript-eslint/no-dynamic-delete': error
'@typescript-eslint/no-extraneous-class': error
'@typescript-eslint/no-explicit-any': 'off'
'@typescript-eslint/no-floating-promises': error
'@typescript-eslint/no-implied-eval': error
'@typescript-eslint/no-inferrable-types': [error, {ignoreParameters: true, ignoreProperties: true}]
'@typescript-eslint/no-invalid-void-type': error
'@typescript-eslint/no-non-null-assertion': 'off'
'@typescript-eslint/no-require-imports': error
'@typescript-eslint/no-throw-literal': error
'@typescript-eslint/no-unnecessary-boolean-literal-compare': error
'@typescript-eslint/no-unnecessary-condition': error
'@typescript-eslint/no-unnecessary-qualifier': error
'@typescript-eslint/no-unnecessary-type-arguments': error
'@typescript-eslint/no-unused-vars-experimental': error
'@typescript-eslint/prefer-as-const': error
'@typescript-eslint/prefer-for-of': error
'@typescript-eslint/prefer-function-type': error
'@typescript-eslint/prefer-nullish-coalescing': error
'@typescript-eslint/prefer-optional-chain': error
'@typescript-eslint/prefer-readonly': error
'@typescript-eslint/prefer-reduce-type-parameter': error
'@typescript-eslint/require-array-sort-compare': error
'@typescript-eslint/restrict-plus-operands': error
'@typescript-eslint/switch-exhaustiveness-check': error
'@typescript-eslint/unbound-method': [error, {ignoreStatic: true}]
'@typescript-eslint/unified-signatures': error
"@typescript-eslint/array-type": [error, {default: array-simple}]
"@typescript-eslint/camelcase": "off"
"@typescript-eslint/class-literal-property-style": error
"@typescript-eslint/explicit-function-return-type": [error, {allowExpressions: true}]
"@typescript-eslint/explicit-member-accessibility": [error, {accessibility: no-public}]
"@typescript-eslint/member-ordering": [error , {default: [public-field, protected-field, private-field, constructor, public-method, protected-method, private-method]}]
"@typescript-eslint/method-signature-style": error
"@typescript-eslint/naming-convention": error
"@typescript-eslint/no-dynamic-delete": error
"@typescript-eslint/no-extraneous-class": error
"@typescript-eslint/no-explicit-any": "off"
"@typescript-eslint/no-floating-promises": error
"@typescript-eslint/no-implied-eval": error
"@typescript-eslint/no-inferrable-types": [error, {ignoreParameters: true, ignoreProperties: true}]
"@typescript-eslint/no-invalid-void-type": error
"@typescript-eslint/no-non-null-assertion": "off"
"@typescript-eslint/no-require-imports": error
"@typescript-eslint/no-throw-literal": error
"@typescript-eslint/no-unnecessary-boolean-literal-compare": error
"@typescript-eslint/no-unnecessary-condition": error
"@typescript-eslint/no-unnecessary-qualifier": error
"@typescript-eslint/no-unnecessary-type-arguments": error
"@typescript-eslint/no-unused-vars-experimental": error
"@typescript-eslint/prefer-as-const": error
"@typescript-eslint/prefer-for-of": error
"@typescript-eslint/prefer-function-type": error
"@typescript-eslint/prefer-nullish-coalescing": error
"@typescript-eslint/prefer-optional-chain": error
"@typescript-eslint/prefer-readonly": error
"@typescript-eslint/prefer-reduce-type-parameter": error
"@typescript-eslint/require-array-sort-compare": error
"@typescript-eslint/restrict-plus-operands": error
"@typescript-eslint/switch-exhaustiveness-check": error
"@typescript-eslint/unbound-method": [error, {ignoreStatic: true}]
"@typescript-eslint/unified-signatures": error

# TypeScript: extension rules.
'@typescript-eslint/brace-style': [error, stroustrup, {allowSingleLine: true}]
'@typescript-eslint/comma-spacing': error
'@typescript-eslint/default-param-last': error
'@typescript-eslint/dot-notation': error
'@typescript-eslint/func-call-spacing': error
'@typescript-eslint/indent': [error, 2, {SwitchCase: 1}]
'@typescript-eslint/keyword-spacing': error
'@typescript-eslint/lines-between-class-members': error
'@typescript-eslint/no-dupe-class-members': error
'@typescript-eslint/no-extra-semi': error
'@typescript-eslint/no-invalid-this': error
'@typescript-eslint/no-use-before-define': [error, nofunc]
'@typescript-eslint/no-useless-constructor': error
'@typescript-eslint/quotes': [error, single, {avoidEscape: true}]
'@typescript-eslint/return-await': error
'@typescript-eslint/semi': error
'@typescript-eslint/space-before-function-paren': [error, {anonymous: never, asyncArrow: always, named: never}]
"@typescript-eslint/brace-style": [error, stroustrup, {allowSingleLine: true}]
"@typescript-eslint/comma-spacing": error
"@typescript-eslint/default-param-last": error
"@typescript-eslint/dot-notation": error
"@typescript-eslint/func-call-spacing": error
"@typescript-eslint/indent": [error, tab, {SwitchCase: 1}]
"@typescript-eslint/keyword-spacing": error
"@typescript-eslint/lines-between-class-members": error
"@typescript-eslint/no-dupe-class-members": error
"@typescript-eslint/no-extra-semi": error
"@typescript-eslint/no-invalid-this": error
"@typescript-eslint/no-use-before-define": [error, nofunc]
"@typescript-eslint/no-useless-constructor": error
"@typescript-eslint/quotes": [error, double, {avoidEscape: true}]
"@typescript-eslint/return-await": error
"@typescript-eslint/semi": error
"@typescript-eslint/space-before-function-paren": [error, {anonymous: never, asyncArrow: always, named: never}]

+ 14
- 14
etc/karma.cjs View File

@@ -1,16 +1,16 @@
module.exports = config => config.set({
basePath: '..',
browsers: ['FirefoxHeadless'],
client: {
clearContext: false
},
coverageIstanbulReporter: {
dir: 'var',
fixWebpackSourcePaths: true,
reports: ['lcovonly']
},
frameworks: ['mocha', 'chai', '@angular-devkit/build-angular'],
plugins: ['karma-*', '@angular-devkit/build-angular/plugins/karma'],
reporters: ['progress'],
singleRun: true
basePath: "..",
browsers: ["FirefoxHeadless"],
client: {
clearContext: false
},
coverageIstanbulReporter: {
dir: "var",
fixWebpackSourcePaths: true,
reports: ["lcovonly"]
},
frameworks: ["mocha", "chai", "@angular-devkit/build-angular"],
plugins: ["karma-*", "@angular-devkit/build-angular/plugins/karma"],
reporters: ["progress"],
singleRun: true
});

+ 9
- 5
etc/mkdocs.yaml View File

@@ -8,19 +8,23 @@ site_dir: ../www

repo_name: git.belin.io
repo_url: https://git.belin.io/cedx/ngx-cookies
edit_uri: ''
edit_uri: ""

copyright: Copyright © 2016 - 2020 Cédric Belin
extra:
social:
- icon: fontawesome/solid/globe
link: 'https://belin.io'
link: "https://belin.io"
name: Belin.io
- icon: fontawesome/brands/github
link: 'https://github.com/cedx'
link: "https://github.com/cedx"
name: GitHub
- icon: fontawesome/brands/twitter
link: 'https://twitter.com/cedxbelin'
link: "https://twitter.com/cedxbelin"
name: Twitter
- icon: fontawesome/brands/linkedin
link: 'https://linkedin.com/in/cedxbelin'
link: "https://linkedin.com/in/cedxbelin"
name: LinkedIn

markdown_extensions:
- admonition


+ 54
- 54
example/main.ts View File

@@ -1,60 +1,60 @@
import {Component, OnInit} from '@angular/core';
import {Cookies} from '@cedx/ngx-cookies';
import {Component, OnInit} from "@angular/core";
import {Cookies} from "@cedx/ngx-cookies";

/** A component that demonstrates the usage of the `Cookies` service. */
@Component({
selector: 'my-component',
templateUrl: './my-component.html'
selector: "my-component",
templateUrl: "./my-component.html"
})
export class MyComponent implements OnInit {
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
// The defaut options used when a cookie is created or removed.
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "", "expires": null, "path": "", "secure": false}
this._cookies.defaults.domain = 'domain.com';
this._cookies.defaults.path = '/www';
this._cookies.defaults.secure = true;
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "domain.com", "expires": null, "path": "/www", "secure": true}
// Query the cookies.
console.log(this._cookies.has('foo')); // false
console.log(this._cookies.has('baz')); // false
console.log(this._cookies.length); // 0
console.log(this._cookies.keys); // []
// Write the cookies.
this._cookies.set('foo', 'bar');
console.log(this._cookies.has('foo')); // true
console.log(this._cookies.length); // 1
console.log(this._cookies.keys); // ["foo"]
this._cookies.setObject('baz', {qux: 123});
console.log(this._cookies.has('baz')); // true
console.log(this._cookies.length); // 2
console.log(this._cookies.keys); // ["foo", "baz"]
// Read the cookies.
console.log(this._cookies.get('foo').constructor.name); // "String"
console.log(this._cookies.get('foo')); // "bar"
console.log(this._cookies.getObject('baz').constructor.name); // "Object"
console.log(this._cookies.getObject('baz')); // {qux: 123}
console.log(this._cookies.getObject('baz').qux); // 123
// Delete the cookies.
this._cookies.remove('foo');
console.log(this._cookies.has('foo')); // false
console.log(this._cookies.length); // 1
console.log(this._cookies.keys); // ["baz"]
this._cookies.clear();
console.log(this._cookies.has('baz')); // false
console.log(this._cookies.length); // 0
console.log(this._cookies.keys); // []
}
constructor(private _cookies: Cookies) {}
ngOnInit(): void {
// The defaut options used when a cookie is created or removed.
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "", "expires": null, "path": "", "secure": false}
this._cookies.defaults.domain = "domain.com";
this._cookies.defaults.path = "/www";
this._cookies.defaults.secure = true;
console.log(JSON.stringify(this._cookies.defaults));
// {"domain": "domain.com", "expires": null, "path": "/www", "secure": true}
// Query the cookies.
console.log(this._cookies.has("foo")); // false
console.log(this._cookies.has("baz")); // false
console.log(this._cookies.length); // 0
console.log(this._cookies.keys); // []
// Write the cookies.
this._cookies.set("foo", "bar");
console.log(this._cookies.has("foo")); // true
console.log(this._cookies.length); // 1
console.log(this._cookies.keys); // ["foo"]
this._cookies.setObject("baz", {qux: 123});
console.log(this._cookies.has("baz")); // true
console.log(this._cookies.length); // 2
console.log(this._cookies.keys); // ["foo", "baz"]
// Read the cookies.
console.log(this._cookies.get("foo").constructor.name); // "String"
console.log(this._cookies.get("foo")); // "bar"
console.log(this._cookies.getObject("baz").constructor.name); // "Object"
console.log(this._cookies.getObject("baz")); // {qux: 123}
console.log(this._cookies.getObject("baz").qux); // 123
// Delete the cookies.
this._cookies.remove("foo");
console.log(this._cookies.has("foo")); // false
console.log(this._cookies.length); // 1
console.log(this._cookies.keys); // ["baz"]
this._cookies.clear();
console.log(this._cookies.has("baz")); // false
console.log(this._cookies.length); // 0
console.log(this._cookies.keys); // []
}
}

+ 7
- 7
ng-package.json View File

@@ -1,9 +1,9 @@
{
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
"dest": "build",
"lib": {
"entryFile": "src/index.ts",
"flatModuleFile": "ngx-cookies",
"umdId": "ngx.cookies"
}
"$schema": "./node_modules/ng-packagr/ng-package.schema.json",
"dest": "build",
"lib": {
"entryFile": "src/index.ts",
"flatModuleFile": "ngx-cookies",
"umdId": "ngx.cookies"
}
}

+ 80
- 80
package.json View File

@@ -1,82 +1,82 @@
{
"bugs": "https://git.belin.io/cedx/ngx-cookies/issues",
"description": "Angular service for interacting with the HTTP cookies.",
"homepage": "https://docs.belin.io/ngx-cookies",
"license": "MIT",
"main": "./lib/index.js",
"name": "@cedx/ngx-cookies",
"type": "module",
"types": "./lib/index.d.ts",
"version": "3.1.0",
"author": {
"email": "cedric@belin.io",
"name": "Cédric Belin",
"url": "https://belin.io"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.901.7",
"@angular-devkit/build-ng-packagr": "^0.901.7",
"@angular/cli": "^9.1.7",
"@angular/common": "^9.1.9",
"@angular/compiler": "^9.1.9",
"@angular/compiler-cli": "^9.1.9",
"@angular/core": "^9.1.9",
"@angular/language-service": "^9.1.9",
"@angular/platform-browser": "^9.1.9",
"@angular/platform-browser-dynamic": "^9.1.9",
"@cedx/coveralls": "^10.1.0",
"@compodoc/compodoc": "^1.1.11",
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
"@types/node": "^14.0.9",
"@typescript-eslint/eslint-plugin": "^3.1.0",
"@typescript-eslint/parser": "^3.1.0",
"chai": "^4.2.0",
"eslint": "^7.1.0",
"karma": "^5.0.9",
"karma-chai": "^0.1.0",
"karma-coverage-istanbul-reporter": "^3.0.3",
"karma-firefox-launcher": "^1.3.0",
"karma-mocha": "^2.0.1",
"mocha": "^7.2.0",
"ng-packagr": "^9.1.5",
"rxjs": "^6.5.5",
"tslib": "^2.0.0",
"typescript": "^3.9.3",
"zone.js": "^0.10.3"
},
"engines": {
"node": ">=14.3.0"
},
"files": [
"lib/"
],
"funding": {
"type": "patreon",
"url": "https://www.patreon.com/cedx"
},
"keywords": [
"angular",
"cookie",
"ngx",
"observable",
"rxjs",
"service"
],
"peerDependencies": {
"@angular/common": ">=9.1.0",
"@angular/core": ">=9.1.0",
"rxjs": ">=6.5.0",
"tslib": ">=1.11.0",
"zone.js": ">=0.10.0"
},
"repository": {
"type": "git",
"url": "https://git.belin.io/cedx/ngx-cookies.git"
},
"scripts": {
"coverage": "coveralls var/lcov.info",
"prepack": "pwsh tool/build.ps1",
"test": "ng test"
}
"bugs": "https://git.belin.io/cedx/ngx-cookies/issues",
"description": "Angular service for interacting with the HTTP cookies.",
"homepage": "https://docs.belin.io/ngx-cookies",
"license": "MIT",
"main": "./lib/index.js",
"name": "@cedx/ngx-cookies",
"type": "module",
"types": "./lib/index.d.ts",
"version": "3.1.0",
"author": {
"email": "cedric@belin.io",
"name": "Cédric Belin",
"url": "https://belin.io"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.901.7",
"@angular-devkit/build-ng-packagr": "^0.901.7",
"@angular/cli": "^9.1.7",
"@angular/common": "^9.1.9",
"@angular/compiler": "^9.1.9",
"@angular/compiler-cli": "^9.1.9",
"@angular/core": "^9.1.9",
"@angular/language-service": "^9.1.9",
"@angular/platform-browser": "^9.1.9",
"@angular/platform-browser-dynamic": "^9.1.9",
"@cedx/coveralls": "^10.1.0",
"@compodoc/compodoc": "^1.1.11",
"@types/chai": "^4.2.11",
"@types/mocha": "^7.0.2",
"@types/node": "^14.0.9",
"@typescript-eslint/eslint-plugin": "^3.1.0",
"@typescript-eslint/parser": "^3.1.0",
"chai": "^4.2.0",
"eslint": "^7.1.0",
"karma": "^5.0.9",
"karma-chai": "^0.1.0",
"karma-coverage-istanbul-reporter": "^3.0.3",
"karma-firefox-launcher": "^1.3.0",
"karma-mocha": "^2.0.1",
"mocha": "^7.2.0",
"ng-packagr": "^9.1.5",
"rxjs": "^6.5.5",
"tslib": "^2.0.0",
"typescript": "^3.9.3",
"zone.js": "^0.10.3"
},
"engines": {
"node": ">=14.3.0"
},
"files": [
"lib/"
],
"funding": {
"type": "patreon",
"url": "https://www.patreon.com/cedx"
},
"keywords": [
"angular",
"cookie",
"ngx",
"observable",
"rxjs",
"service"
],
"peerDependencies": {
"@angular/common": ">=9.1.0",
"@angular/core": ">=9.1.0",
"rxjs": ">=6.5.0",
"tslib": ">=1.11.0",
"zone.js": ">=0.10.0"
},
"repository": {
"type": "git",
"url": "https://git.belin.io/cedx/ngx-cookies.git"
},
"scripts": {
"coverage": "coveralls var/lcov.info",
"prepack": "pwsh tool/build.ps1",
"test": "ng test"
}
}

+ 109
- 109
src/cookie_options.ts View File

@@ -1,123 +1,123 @@
import {JsonObject} from './json';
import {JsonObject} from "./json";

/** Defines the attributes of a HTTP cookie. */
export class CookieOptions {

/** The domain for which the cookie is valid. */
domain: string;
/** The expiration date and time for the cookie. An `undefined` value indicates a session cookie. */
expires?: Date;
/** The path to which the cookie applies. */
path: string;
/** Value indicating whether to transmit the cookie over HTTPS only. */
secure: boolean;
/**
* Creates new cookie options.
* @param options An object specifying values used to initialize this instance.
*/
constructor(options: Partial<CookieOptionsParams> = {}) {
const {domain = '', expires, maxAge = -1, path = '', secure = false} = options;
this.domain = domain;
this.expires = expires;
this.path = path;
this.secure = secure;
if (maxAge >= 0) this.maxAge = maxAge;
}
/** The maximum duration, in seconds, until the cookie expires. A negative value indicates a session cookie. */
get maxAge(): number {
if (!this.expires) return -1;
const now = new Date;
return this.expires > now ? Math.ceil((this.expires.getTime() - now.getTime()) / 1000) : 0;
}
set maxAge(value: number) {
this.expires = value < 0 ? undefined : new Date(Date.now() + (value * 1000));
}
/**
* Creates new cookie options from the specified JSON object.
* @param map A JSON object representing cookie options.
* @return The instance corresponding to the specified JSON object.
*/
static fromJson(map: JsonObject): CookieOptions {
return new CookieOptions({
domain: typeof map.domain == 'string' ? map.domain : '',
expires: typeof map.expires == 'string' ? new Date(map.expires) : undefined,
maxAge: typeof map.maxAge == 'number' && Number.isInteger(map.maxAge) ? map.maxAge : -1,
path: typeof map.path == 'string' ? map.path : '',
secure: typeof map.secure == 'boolean' ? map.secure : false
});
}
/**
* Creates new options from the specified cookie string.
* @param value A string representing a cookie.
* @return The instance corresponding to the specified cookie string.
*/
static fromString(value: string): CookieOptions {
const attributes = ['domain', 'expires', 'max-age', 'path', 'secure'];
const map = new Map<string, string>();
for (const [optionName, optionValue] of value.split('; ').slice(1).map(part => part.split('='))) {
const attribute = optionName.toLowerCase();
if (attributes.includes(attribute)) map.set(attribute, optionValue);
}
return new CookieOptions({
domain: map.has('domain') ? map.get('domain') : '',
expires: map.has('expires') ? new Date(map.get('expires')!) : undefined,
maxAge: map.has('max-age') ? Number.parseInt(map.get('max-age')!, 10) : -1,
path: map.has('path') ? map.get('path') : '',
secure: map.has('secure')
});
}
/**
* Converts this object to a map in JSON format.
* @return The map in JSON format corresponding to this object.
*/
toJSON(): JsonObject {
return {
domain: this.domain,
expires: this.expires ? this.expires.toJSON() : null,
path: this.path,
secure: this.secure
};
}
/**
* Returns a string representation of this object.
* @return The string representation of this object.
*/
toString(): string {
const value = [];
if (this.expires) value.push(`expires=${this.expires.toUTCString()}`);
if (this.domain.length) value.push(`domain=${this.domain}`);
if (this.path.length) value.push(`path=${this.path}`);
if (this.secure) value.push('secure');
return value.join('; ');
}
/** The domain for which the cookie is valid. */
domain: string;
/** The expiration date and time for the cookie. An `undefined` value indicates a session cookie. */
expires?: Date;
/** The path to which the cookie applies. */
path: string;
/** Value indicating whether to transmit the cookie over HTTPS only. */
secure: boolean;
/**
* Creates new cookie options.
* @param options An object specifying values used to initialize this instance.
*/
constructor(options: Partial<CookieOptionsParams> = {}) {
const {domain = "", expires, maxAge = -1, path = "", secure = false} = options;
this.domain = domain;
this.expires = expires;
this.path = path;
this.secure = secure;
if (maxAge >= 0) this.maxAge = maxAge;
}
/** The maximum duration, in seconds, until the cookie expires. A negative value indicates a session cookie. */
get maxAge(): number {
if (!this.expires) return -1;
const now = new Date;
return this.expires > now ? Math.ceil((this.expires.getTime() - now.getTime()) / 1000) : 0;
}
set maxAge(value: number) {
this.expires = value < 0 ? undefined : new Date(Date.now() + (value * 1000));
}
/**
* Creates new cookie options from the specified JSON object.
* @param map A JSON object representing cookie options.
* @return The instance corresponding to the specified JSON object.
*/
static fromJson(map: JsonObject): CookieOptions {
return new CookieOptions({
domain: typeof map.domain == "string" ? map.domain : "",
expires: typeof map.expires == "string" ? new Date(map.expires) : undefined,
maxAge: typeof map.maxAge == "number" && Number.isInteger(map.maxAge) ? map.maxAge : -1,
path: typeof map.path == "string" ? map.path : "",
secure: typeof map.secure == "boolean" ? map.secure : false
});
}
/**
* Creates new options from the specified cookie string.
* @param value A string representing a cookie.
* @return The instance corresponding to the specified cookie string.
*/
static fromString(value: string): CookieOptions {
const attributes = ["domain", "expires", "max-age", "path", "secure"];
const map = new Map<string, string>();
for (const [optionName, optionValue] of value.split("; ").slice(1).map(part => part.split("="))) {
const attribute = optionName.toLowerCase();
if (attributes.includes(attribute)) map.set(attribute, optionValue);
}
return new CookieOptions({
domain: map.has("domain") ? map.get("domain") : "",
expires: map.has("expires") ? new Date(map.get("expires")!) : undefined,
maxAge: map.has("max-age") ? Number.parseInt(map.get("max-age")!, 10) : -1,
path: map.has("path") ? map.get("path") : "",
secure: map.has("secure")
});
}
/**
* Converts this object to a map in JSON format.
* @return The map in JSON format corresponding to this object.
*/
toJSON(): JsonObject {
return {
domain: this.domain,
expires: this.expires ? this.expires.toJSON() : null,
path: this.path,
secure: this.secure
};
}
/**
* Returns a string representation of this object.
* @return The string representation of this object.
*/
toString(): string {
const value = [];
if (this.expires) value.push(`expires=${this.expires.toUTCString()}`);
if (this.domain.length) value.push(`domain=${this.domain}`);
if (this.path.length) value.push(`path=${this.path}`);
if (this.secure) value.push("secure");
return value.join("; ");
}
}

/** Defines the parameters of a `CookieOptions` instance. */
export interface CookieOptionsParams {

/** The domain for which the cookie is valid. */
domain: string;
/** The domain for which the cookie is valid. */
domain: string;

/** The expiration date and time for the cookie. An `undefined` value indicates a session cookie. */
expires: Date;
/** The expiration date and time for the cookie. An `undefined` value indicates a session cookie. */
expires: Date;

/** The maximum duration, in seconds, until the cookie expires. A negative value indicates a session cookie. */
maxAge: number;
/** The maximum duration, in seconds, until the cookie expires. A negative value indicates a session cookie. */
maxAge: number;

/** The path to which the cookie applies. */
path: string;
/** The path to which the cookie applies. */
path: string;

/** Value indicating whether to transmit the cookie over HTTPS only. */
secure: boolean;
/** Value indicating whether to transmit the cookie over HTTPS only. */
secure: boolean;
}

+ 224
- 224
src/cookies.ts View File

@@ -1,229 +1,229 @@
import {Injectable, OnDestroy, SimpleChange, SimpleChanges} from '@angular/core';
import {Observable, Subject} from 'rxjs';
import {CookieOptions} from './cookie_options';
import {JsonObject} from './json';
import {Injectable, OnDestroy, SimpleChange, SimpleChanges} from "@angular/core";
import {Observable, Subject} from "rxjs";
import {CookieOptions} from "./cookie_options";
import {JsonObject} from "./json";

/** Provides access to the [HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies). */
@Injectable({providedIn: 'root'})
@Injectable({providedIn: "root"})
export class Cookies implements Iterable<[string, string|undefined]>, OnDestroy {

/** The default cookie options. */
readonly defaults: CookieOptions = new CookieOptions;
/** The handler of "changes" events. */
private readonly _onChanges: Subject<SimpleChanges> = new Subject<SimpleChanges>();
/** The keys of the cookies associated with the current document. */
get keys(): string[] {
const keys = document.cookie.replace(/((?:^|\s*;)[^=]+)(?=;|$)|^\s*|\s*(?:=[^;]*)?(?:\1|$)/g, '');
return keys.length ? keys.split(/\s*(?:=[^;]*)?;\s*/).map(decodeURIComponent) : [];
}
/** The number of cookies associated with the current document. */
get length(): number {
return this.keys.length;
}
/** The stream of "changes" events. */
get onChanges(): Observable<SimpleChanges> {
return this._onChanges.asObservable();
}
/**
* Returns a new iterator that allows iterating the cookies associated with the current document.
* @return An iterator for the cookies of the current document.
*/
*[Symbol.iterator](): IterableIterator<[string, string|undefined]> {
for (const key of this.keys) yield [key, this.get(key)];
}
/** Removes all cookies associated with the current document. */
clear(): void {
const changes: SimpleChanges = {};
for (const [key, value] of this) {
changes[key] = new SimpleChange(value, undefined, false);
this._removeItem(key);
}
this._onChanges.next(changes);
}
/**
* Gets the value associated to the specified key.
* @param key The cookie name.
* @param defaultValue The value to return if the cookie does not exist.
* @return The cookie value, or the default value if the cookie is not found.
*/
get(key: string, defaultValue?: string): string|undefined {
if (!this.has(key)) return defaultValue;
try {
const token = encodeURIComponent(key).replace(/[-.+*]/g, String.raw`\$&`);
const scanner = new RegExp(String.raw`(?:(?:^|.*;)\s*${token}\s*=\s*([^;]*).*$)|^.*$`);
return decodeURIComponent(document.cookie.replace(scanner, '$1'));
}
catch {
return defaultValue;
}
}
/**
* Gets the deserialized value associated to the specified key.
* @param key The cookie name.
* @param defaultValue The value to return if the cookie does not exist.
* @return The deserialized cookie value, or the default value if the cookie is not found.
*/
getObject(key: string, defaultValue?: any): any {
try {
const value = this.get(key);
return value != undefined ? JSON.parse(value) : defaultValue;
}
catch {
return defaultValue;
}
}
/**
* Gets a value indicating whether the current document has a cookie with the specified key.
* @param key The cookie name.
* @return `true` if the current document has a cookie with the specified key, otherwise `false`.
*/
has(key: string): boolean {
const token = encodeURIComponent(key).replace(/[-.+*]/g, String.raw`\$&`);
return new RegExp(String.raw`(?:^|;\s*)${token}\s*=`).test(document.cookie);
}
/** Method invoked before the service is destroyed. */
ngOnDestroy(): void {
this._onChanges.complete();
}
/**
* Looks up the cookie with the specified key, or add a new cookie if it isn't there.
*
* Returns the value associated to `key`, if there is one. Otherwise calls `ifAbsent` to get a new value,
* associates `key` to that value, and then returns the new value.
*
* @param key The key to seek for.
* @param ifAbsent The function called to get a new value.
* @param options The cookie options.
* @return The value associated with the specified key.
*/
putIfAbsent(key: string, ifAbsent: () => string, options?: CookieOptions): string {
if (!this.has(key)) this.set(key, ifAbsent(), options);
return this.get(key)!;
}
/**
* Looks up the cookie with the specified key, or add a new cookie if it isn't there.
*
* Returns the deserialized value associated to `key`, if there is one. Otherwise calls `ifAbsent` to get a new value,
* serializes and associates `key` to that value, and then returns the new value.
*
* @param key The key to seek for.
* @param ifAbsent The function called to get a new value.
* @param options The cookie options.
* @return The deserialized value associated with the specified key.
*/
putObjectIfAbsent(key: string, ifAbsent: () => any, options?: CookieOptions): any {
if (!this.has(key)) this.setObject(key, ifAbsent(), options);
return this.getObject(key);
}
/**
* Removes the cookie with the specified key and its associated value.
* @param key The cookie name.
* @param options The cookie options.
* @return The value associated with the specified key before it was removed.
*/
remove(key: string, options?: CookieOptions): string|undefined {
const previousValue = this.get(key);
this._removeItem(key, options);
this._onChanges.next({
[key]: new SimpleChange(previousValue, undefined, false)
});
return previousValue;
}
/**
* Associates a given value to the specified key.
* @param key The cookie name.
* @param value The cookie value.
* @param options The cookie options.
* @return This instance.
* @throws `TypeError` The specified key is invalid.
*/
set(key: string, value: string, options?: CookieOptions): this {
if (!key.length) throw new TypeError('Invalid cookie name.');
const cookieOptions = this._getOptions(options).toString();
let cookieValue = `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
if (cookieOptions.length) cookieValue += `; ${cookieOptions}`;
const previousValue = this.get(key);
document.cookie = cookieValue;
this._onChanges.next({
[key]: new SimpleChange(previousValue, value, false)
});
return this;
}
/**
* Serializes and associates a given value to the specified key.
* @param key The cookie name.
* @param value The cookie value.
* @param options The cookie options.
* @return This instance.
*/
setObject(key: string, value: any, options?: CookieOptions): this {
return this.set(key, JSON.stringify(value), options);
}
/**
* Converts this object to a map in JSON format.
* @return The map in JSON format corresponding to this object.
*/
toJSON(): JsonObject {
const map: JsonObject = {};
for (const [key, value] of this) map[key] = value ?? null;
return map;
}
/**
* Returns a string representation of this object.
* @return The string representation of this object.
*/
toString(): string {
return document.cookie;
}
/**
* Merges the default cookie options with the specified ones.
* @param options The options to merge with the defaults.
* @return The resulting cookie options.
*/
private _getOptions(options: CookieOptions = new CookieOptions): CookieOptions {
return new CookieOptions({
domain: options.domain.length ? options.domain : this.defaults.domain,
expires: options.expires ? options.expires : this.defaults.expires,
path: options.path.length ? options.path : this.defaults.path,
secure: options.secure ? options.secure : this.defaults.secure
});
}
/**
* Removes the value associated to the specified key.
* @param key The cookie name.
* @param options The cookie options.
*/
private _removeItem(key: string, options?: CookieOptions): void {
if (!this.has(key)) return;