Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"presets": [["env", {"targets": {"node": 4}}]],
"presets": ["es2015", "flow", "stage-3"],
"plugins": [
["transform-builtin-extend", {"globals": ["Error"]}],
"transform-runtime"
Expand Down
10 changes: 10 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[ignore]
.*/node_modules/documentation/.*
.*/test/.*

[include]

[libs]

[options]
unsafe.enable_getters_and_setters=true
232 changes: 145 additions & 87 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,137 +2,195 @@

[![npm](https://img.shields.io/npm/v/iab-vast-loader.svg)](https://www.npmjs.com/package/iab-vast-loader) [![Dependencies](https://img.shields.io/david/zentrick/iab-vast-loader.svg)](https://david-dm.org/zentrick/iab-vast-loader) [![Build Status](https://img.shields.io/circleci/project/github/zentrick/iab-vast-loader/master.svg)](https://circleci.com/gh/zentrick/iab-vast-loader) [![Coverage Status](https://img.shields.io/coveralls/zentrick/iab-vast-loader/master.svg)](https://coveralls.io/r/zentrick/iab-vast-loader) [![JavaScript Standard Style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)

Loads and parses IAB VAST tags, resolving wrapped tags along the way.
Loads IAB VAST tag trees using a preorder depth first strategy. The package is statically typed using [Flow](https://flow.org). [Observable streams](http://npmjs.com/package/rxjs) are used to update the consumer in time with new VAST documents.

This package exposes two functions: `loadVast()` and `vastToAd()`.

`loadVast()` is responsible for actually loading the VAST tree, it returns a stream of VAST objects, which allows you to react on failures and offers you the choice to continue listening for subsequent VAST documents in the tree. It also delivers you a new VAST document right away (preserving preorder depth first traversal semantics), instead of waiting for the whole tree to be fetched.

It also gives you a `vastToAd()` function to map the stream of VAST objects to a stream of Ad objects (both Wrapper and InLine), also in preorder depth first order. This gives you the right abstraction on which you can easily build further upon, using the RxJS `filter()` operator to for example only return `InLine` elements.

## Usage

```js
import VASTLoader from 'iab-vast-loader'
import { loadVast } from 'iab-vast-loader'

const tagUrl = 'https://example.com/vast.xml'

// Create the loader
const loader = new VASTLoader(tagUrl)
const vast$ = loadVast({
url: 'https://example.com/vast.xml'
})

// Load the tag chain and await the resulting Promise
loader.load()
.then((chain) => {
console.info('Loaded VAST tags:', chain)
// Load the VAST tree and log all the VAST tags in the tree.
vast$
.subscribe({
next: action => {
switch (action.type) {
case 'VAST_LOADED':
console.info('Loaded next VAST tag: ', action.vast)
break;
case 'VAST_LOADING_FAILED':
console.info('Loading next VAST tag failed: ', action.error, action.wrapper)
break;
}
},
complete: () => {
console.info('Finished loading the complete VAST tree')
}
})
.catch((err) => {
console.error('Error loading tag:', err)

// When interested in Ad events
const ad$ = vastToAd(vast$)

ad$
.subscribe({
next: action => {
switch (action.type) {
case 'AD_LOADED':
console.info('Loaded next Ad: ', action.ad)
break;
case 'AD_LOADING_FAILED'
console.info('Loading next Ad failed: ', action.error, action.wrapper)
break;
}
},
complete: () => {
console.info('Finished loading the complete VAST tree')
}
})
```

## API

### `#loadVast()`

```js
new VASTLoader(tagUrl[, options])
type LoadVast = (config: Config) => Observable<VastLoadAction>
```

Creates a VAST loader.
`loadVast` creates a stream of `VastLoadAction` objects. In a fully reactive codebase, this stream will be composed within another stream. If this library is used at the boundary, then you need to subscribe yourself like this:

```js
loader.load()
import { loadVast } from 'iab-vast-loader'
const vast$ = loadVast(config)

loadVast$.subscribe({
next: value => { },
complete: () => { }
})
```

Returns a `Promise` for an array of `VAST` instances. The `VAST` class is
provided by [iab-vast-model](https://www.npmjs.com/package/iab-vast-model).
#### Configuration

## Error Handling

In addition to the default export `VASTLoader`, the main module also exports
the `VASTLoaderError` class, which maps errors to the VAST specification:
`loadVast` accepts the following `Config` object:

```js
import { default as VASTLoader, VASTLoaderError } from 'iab-vast-loader'
type Config = {
url: string,
maxDepth?: number,
timeout?: number,
retryCount?: number,
credentials: Credentials
}
```

const loader = new VASTLoader(tagUrl)
An overview of its properties:

loader.load()
.catch((err) => {
if (err instanceof VASTLoaderError) {
console.error('VAST error: ' + err.code + ' ' + err.message)
} else {
console.error('Unknown error: ' + err)
}
})
```
- `url`: The url that points to the root VAST document of the VAST document tree that we need to fetch.
- `maxDepth`: The maximum number of VAST documents to load within one chain. The default is
`10`.
- `timeout`: The maximum number of milliseconds to spend per HTTP request. The default is
`10000`.
- `retryCount`: The amount of times it will retry fetching a VAST document in case of failure. The default is `0`.
- `credentials`: The credentials value defines if cookies will be sent with the request. You should pass a [`CredentialsType`](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials) string (`'omit'`, `'same-origin'` or `'include'`) or a function which calculcates the `CredentialsType` using the passed url. You can also pass an array of these values: in this case it will try the first credentials strategy, when this call fails it will go on with the next, until a strategy succeeds. If none of the credentials strategies succeed, it will result in a `VAST_LOADING_FAILED` action. Notice that passing an empty array doesn't make sense, because it will always result in a `VAST_LOADING_FAILED` action.

As with [iab-vast-model](https://www.npmjs.com/package/iab-vast-model), if
`instanceof` doesn't work for you, you may want to inspect `error.$type`
instead. This issue can occur if you load multiple versions of iab-vast-loader,
each with their own `VASTLoaderError` class.
With Flow, we can describe this more formally:

## Options
```js
type Credentials = CredentialsTypeOrFn | CredentialsTypeOrFn[]
type CredentialsTypeOrFn = CredentialsType | (url: string) => CredentialsType
type CredentialsType = 'omit' | 'same-origin' | 'include'
```

### `maxDepth`
You can use this option to control the behavior on a per-request basis. For example:

The maximum number of VAST documents to load within one chain. The default is
10.
```js
const loadVast$ = loadVast({
url: 'https://example.com/vast.xml',
credentials: [
url => uri.indexOf('.doubleclick.net/') !== 0 ? 'include' : 'omit'
]
})
```

### `timeout`
The default is `'omit'`.

The maximum number of milliseconds to spend per HTTP request. The default is
10,000.
#### Output

### `credentials`
The output of loadVast is a stream of `VastLoadAction` objects:

Controls [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)
behavior. You can either pass a string or a function.
```js
type VastLoadedAction = {
type: 'VAST_LOADED',
vast: VAST
}

type VastLoadingFailedAction = {
type: 'VAST_LOADING_FAILED',
error: VASTLoaderError,
wrapper: ?Wrapper
}

type VastLoadAction = VastLoadedAction | VastLoadingFailedAction
```

If you pass a string, it will be used as the value for the `credentials` option
to every request.
[Valid values](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)
are `'omit'` (the default), `'same-origin'` and `'include'`.
The `VAST` and `Wrapper` types are provided by [iab-vast-model](https://www.npmjs.com/package/iab-vast-model).

To control the behavior on a per-request basis, pass a function receiving the
request URL and returning one of the accepted values. For example:
### `#vastToAd()`

```js
const loader = new VASTLoader(wrapperUrl, {
credentials (uri) {
if (uri.indexOf('.doubleclick.net/') >= 0) {
return 'include'
} else {
return 'omit'
}
}
})
import { loadVast, vastToAd } from 'iab-vast-loader'
const vast$ = loadVast(config)
const ad$ = vastToAd(vast$)
```

## Events
`vastToAd` maps a stream of `VastLoadAction` objects to `AdLoadAction` objects. You can subscribe on this stream directly, or indirectly using RxJS operators, just like `loadVast`.

#### Output

A `VASTLoader` is an `EventEmitter`. To be notified about progress, you can
subscribe to the events `willFetch`, `didFetch`, `willParse`, and `didParse`
as follows:
The output of `vastToAd` is a stream of `AdLoadAction` objects:

```js
loader
.on('willFetch', ({ uri }) => {
console.info('Fetching', uri)
})
.on('didFetch', ({ uri, body }) => {
console.info('Fetched', body.length, 'bytes from', uri)
})
.on('willParse', ({ uri, body }) => {
console.info('Parsing', uri)
})
.on('didParse', ({ uri, body, vast }) => {
console.info('Parsed', uri)
})
.load()
.then((chain) => {
console.info('Loaded VAST tags:', chain)
})
.catch((err) => {
console.error('Error loading tag:', err)
})
type AdLoadedAction = {
type: 'AD_LOADED',
ad: Ad
}

type AdLoadingFailedAction = {
type: 'AD_LOADING_FAILED',
error: VASTLoaderError,
wrapper: ?Wrapper
}

type AdLoadAction = AdLoadedAction | AdLoadingFailedAction
```

## Maintainer
The `Ad` and `Wrapper` types are provided by [iab-vast-model](https://www.npmjs.com/package/iab-vast-model).

## Error Handling

In case the libary fails to load the next VAST document, it will emit a `VAST_LOADING_FAILED` action. You can react to this, by unsubscribing using the `takeUntil` operator, or you can continue listening for other values of another subtree of the VAST document tree. We don't push the stream into error state, because we want to enable the consumer to use subsequent VAST documents from the tree, after one subtree failed to fetch.

In addition to the default export `VASTLoader`, the main module also exports
the `VASTLoaderError` class, which maps errors to the VAST specification. You can get the VAST error code using its `code` property, and the cause using its `cause` property.

As with [iab-vast-model](https://www.npmjs.com/package/iab-vast-model), if
`instanceof` doesn't work for you, you may want to inspect `error.$type`
instead. This issue can occur if you load multiple versions of iab-vast-loader,
each with their own `VASTLoaderError` class.

## Maintainers

[Tim De Pauw](https://github.com/timdp)
- [Tim De Pauw](https://github.com/timdp)
- [Laurent De Smet](https://github.com/laurentdesmet)

## License

Expand Down
Loading