@walkeros/web-destination-posthog 4.1.0-next-1778668930820 → 4.1.0
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/CHANGELOG.md +122 -0
- package/README.md +27 -279
- package/dist/dev.js +1 -1
- package/dist/dev.js.map +1 -1
- package/dist/dev.mjs +1 -1
- package/dist/dev.mjs.map +1 -1
- package/dist/examples/index.js +11 -49
- package/dist/examples/index.mjs +11 -49
- package/dist/index.browser.js +1 -1
- package/dist/index.es5.js +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/walkerOS.json +13 -13
- package/package.json +6 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# @walkeros/web-destination-posthog
|
|
2
|
+
|
|
3
|
+
## 4.1.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [e155ff8]
|
|
8
|
+
- Updated dependencies [e800974]
|
|
9
|
+
- Updated dependencies [e155ff8]
|
|
10
|
+
- Updated dependencies [1a8f2d7]
|
|
11
|
+
- Updated dependencies [1a8f2d7]
|
|
12
|
+
- Updated dependencies [b276173]
|
|
13
|
+
- Updated dependencies [dd9f5ad]
|
|
14
|
+
- Updated dependencies [c60ef35]
|
|
15
|
+
- Updated dependencies [adeebea]
|
|
16
|
+
- Updated dependencies [13aaeaa]
|
|
17
|
+
- Updated dependencies [e800974]
|
|
18
|
+
- Updated dependencies [adeebea]
|
|
19
|
+
- Updated dependencies [e800974]
|
|
20
|
+
- Updated dependencies [e800974]
|
|
21
|
+
- Updated dependencies [058f7ed]
|
|
22
|
+
- Updated dependencies [28a8ac2]
|
|
23
|
+
- Updated dependencies [fd6076e]
|
|
24
|
+
- @walkeros/core@4.1.0
|
|
25
|
+
- @walkeros/web-core@4.1.0
|
|
26
|
+
|
|
27
|
+
## 4.0.2
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- @walkeros/web-core@4.0.2
|
|
32
|
+
|
|
33
|
+
## 4.0.1
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- @walkeros/web-core@4.0.1
|
|
38
|
+
|
|
39
|
+
## 4.0.0
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- Updated dependencies [93ea9c4]
|
|
44
|
+
- @walkeros/web-core@4.0.0
|
|
45
|
+
|
|
46
|
+
## 3.4.2
|
|
47
|
+
|
|
48
|
+
### Patch Changes
|
|
49
|
+
|
|
50
|
+
- @walkeros/web-core@3.4.2
|
|
51
|
+
|
|
52
|
+
## 3.4.1
|
|
53
|
+
|
|
54
|
+
### Patch Changes
|
|
55
|
+
|
|
56
|
+
- Updated dependencies [caea905]
|
|
57
|
+
- @walkeros/web-core@3.4.1
|
|
58
|
+
|
|
59
|
+
## 3.4.0
|
|
60
|
+
|
|
61
|
+
### Minor Changes
|
|
62
|
+
|
|
63
|
+
- 724f97e: Migrate every step example in every walkerOS package to the
|
|
64
|
+
standardized `[callable, ...args][]` shape introduced in `@walkeros/core`.
|
|
65
|
+
Every step example's `out` is now an array of effect tuples whose first
|
|
66
|
+
element is the callable's public SDK name (`'gtag'`, `'analytics.track'`,
|
|
67
|
+
`'fbq'`, `'dataLayer.push'`, `'sendServer'`, `'fetch'`, `'trackClient.track'`,
|
|
68
|
+
`'amplitude.track'`, `'fs.writeFile'`, `'producer.send'`, `'client.xadd'`,
|
|
69
|
+
`'client.send'`, `'dataset.table.insert'`, etc.). Source examples use `'elb'`
|
|
70
|
+
as the callable; transformer examples use the reserved `'return'` keyword;
|
|
71
|
+
store examples use store-operation callables (`'get'`, `'set'`). Tests capture
|
|
72
|
+
real calls on each component's spy and assert against `example.out` directly —
|
|
73
|
+
the hardcoded `PACKAGE_CALLS` registry in the app is no longer consulted
|
|
74
|
+
(emptied; plan #3 removes it structurally).
|
|
75
|
+
|
|
76
|
+
### Patch Changes
|
|
77
|
+
|
|
78
|
+
- @walkeros/web-core@3.4.0
|
|
79
|
+
|
|
80
|
+
## 3.3.1
|
|
81
|
+
|
|
82
|
+
### Patch Changes
|
|
83
|
+
|
|
84
|
+
- @walkeros/web-core@3.3.1
|
|
85
|
+
|
|
86
|
+
## 3.3.0
|
|
87
|
+
|
|
88
|
+
### Minor Changes
|
|
89
|
+
|
|
90
|
+
- 08c365a: Add PostHog web destination (`@walkeros/web-destination-posthog`) —
|
|
91
|
+
product analytics, identity with `$set`/`$set_once` person properties, groups,
|
|
92
|
+
logout via reset, consent opt-in/opt-out, plus passthrough for all built-in
|
|
93
|
+
PostHog features (session replay, feature flags, surveys, heatmaps, error
|
|
94
|
+
tracking) via the official `posthog-js` npm package.
|
|
95
|
+
- Default event forwarding: every walkerOS event becomes
|
|
96
|
+
`posthog.capture(name, properties)`
|
|
97
|
+
- Custom event properties: `settings.include` flattens walkerOS event sections
|
|
98
|
+
with prefix (`data_*`, `globals_*`, etc.)
|
|
99
|
+
- Identity: destination-level + per-event `settings.identify`, resolving to
|
|
100
|
+
`{ distinctId?, $set?, $set_once? }`. With `distinctId`:
|
|
101
|
+
`posthog.identify()`. Without `distinctId`: `posthog.setPersonProperties()`
|
|
102
|
+
(property-only updates). Runtime state diffing skips redundant `identify()`
|
|
103
|
+
calls when `distinctId` hasn't changed.
|
|
104
|
+
- Groups: `settings.group` for B2B workflows — destination-level or per-event,
|
|
105
|
+
tracked in runtime state
|
|
106
|
+
- Reset: `settings.reset: true` calls `posthog.reset()` on logout
|
|
107
|
+
- Consent: `on('consent')` handler derives the consent key from
|
|
108
|
+
`config.consent` and toggles
|
|
109
|
+
`posthog.opt_in_capturing()`/`opt_out_capturing()`
|
|
110
|
+
- Built-in features (config passthrough, no destination code): session
|
|
111
|
+
recording, feature flags, surveys, heatmaps, exceptions, cookieless mode,
|
|
112
|
+
bootstrap
|
|
113
|
+
- walkerOS defaults override PostHog defaults: `autocapture: false`,
|
|
114
|
+
`capture_pageview: false`, `capture_pageleave: false` — walkerOS sources
|
|
115
|
+
handle event capture
|
|
116
|
+
- SDK resolution follows the `env?.posthog ?? posthog` pattern (mirrors
|
|
117
|
+
`@walkeros/web-destination-clarity` and `@walkeros/server-destination-gcp`
|
|
118
|
+
BigQuery)
|
|
119
|
+
|
|
120
|
+
### Patch Changes
|
|
121
|
+
|
|
122
|
+
- @walkeros/web-core@3.3.0
|
package/README.md
CHANGED
|
@@ -4,298 +4,46 @@
|
|
|
4
4
|
</a>
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
#
|
|
7
|
+
# @walkeros/web-destination-posthog
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
•
|
|
11
|
-
[NPM Package](https://www.npmjs.com/package/@walkeros/web-destination-posthog)
|
|
12
|
-
• [Documentation](https://www.walkeros.io/docs/destinations/web/posthog)
|
|
13
|
-
|
|
14
|
-
This package forwards walkerOS events to [PostHog](https://posthog.com/) -
|
|
15
|
-
product analytics, session replay, feature flags, surveys, and heatmaps. Built
|
|
16
|
-
on the official [`posthog-js`](https://www.npmjs.com/package/posthog-js) SDK.
|
|
9
|
+
Product analytics, session replay, feature flags, and surveys.
|
|
17
10
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
heatmaps, exception capture) are available through SDK init passthrough - no
|
|
23
|
-
destination-specific plugins required.
|
|
24
|
-
|
|
25
|
-
## Features
|
|
26
|
-
|
|
27
|
-
- **Default event forwarding** - every walkerOS event becomes
|
|
28
|
-
`posthog.capture(event.name, properties)` with no additional config
|
|
29
|
-
- **Custom event properties** - flatten walkerOS event sections via
|
|
30
|
-
`settings.include` (prefixed as `data_*`, `globals_*`, etc.)
|
|
31
|
-
- **Identity** - destination-level and per-event identity mapping resolving to
|
|
32
|
-
`{ distinctId?, $set?, $set_once? }`. With `distinctId`: `posthog.identify()`.
|
|
33
|
-
Without `distinctId`: `posthog.setPersonProperties()` for pure person-property
|
|
34
|
-
updates. Runtime state diffing skips redundant identify calls when the
|
|
35
|
-
resolved values have not changed.
|
|
36
|
-
- **Groups** - B2B-style group analytics via `settings.group`; resolves to
|
|
37
|
-
`{ type, key, properties? }` and calls `posthog.group(...)`
|
|
38
|
-
- **Logout** - `reset: true` triggers `posthog.reset()` to clear the distinct ID
|
|
39
|
-
and regenerate an anonymous one
|
|
40
|
-
- **Consent** - declares required consent keys via `config.consent`; a
|
|
41
|
-
`walker consent` event with all required keys granted calls
|
|
42
|
-
`posthog.opt_in_capturing()`, otherwise `posthog.opt_out_capturing()`
|
|
43
|
-
- **Built-in PostHog features as config passthrough** - session replay
|
|
44
|
-
(`session_recording`), feature flags (`bootstrap`, `advanced_disable_flags`),
|
|
45
|
-
surveys (`disable_surveys`), heatmaps (`capture_heatmaps`), exception capture
|
|
46
|
-
(`capture_exceptions`), cookieless mode (`cookieless_mode`). All
|
|
47
|
-
`PostHogConfig` fields pass through unchanged.
|
|
11
|
+
[Documentation](https://www.walkeros.io/docs/destinations/web/posthog) •
|
|
12
|
+
[NPM Package](https://www.npmjs.com/package/@walkeros/web-destination-posthog)
|
|
13
|
+
•
|
|
14
|
+
[Source Code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/destinations/posthog)
|
|
48
15
|
|
|
49
16
|
## Installation
|
|
50
17
|
|
|
51
|
-
```
|
|
18
|
+
```bash
|
|
52
19
|
npm install @walkeros/web-destination-posthog
|
|
53
20
|
```
|
|
54
21
|
|
|
55
|
-
## Quick
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
import { startFlow } from '@walkeros/collector';
|
|
59
|
-
import { destinationPostHog } from '@walkeros/web-destination-posthog';
|
|
22
|
+
## Quick start
|
|
60
23
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
api_host: 'https://eu.i.posthog.com', // or us.i.posthog.com
|
|
69
|
-
},
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"version": 4,
|
|
27
|
+
"flows": {
|
|
28
|
+
"default": {
|
|
29
|
+
"config": {
|
|
30
|
+
"platform": "web"
|
|
70
31
|
},
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
| Name | Type | Description | Required |
|
|
81
|
-
| ---------- | --------------- | -------------------------------------------------------------------------------------------------- | -------- |
|
|
82
|
-
| `apiKey` | `string` | PostHog project API key (starts with `phc_`) | Yes |
|
|
83
|
-
| `api_host` | `string` | PostHog ingestion host. Default `https://us.i.posthog.com`. Use `https://eu.i.posthog.com` for EU. | No |
|
|
84
|
-
| `include` | `string[]` | walkerOS event sections to flatten into `capture()` properties (`data`, `globals`, `context`, …) | No |
|
|
85
|
-
| `identify` | `Mapping.Value` | Destination-level identity mapping; resolves to `{ distinctId?, $set?, $set_once? }` | No |
|
|
86
|
-
| `group` | `Mapping.Value` | Destination-level group assignment; resolves to `{ type, key, properties? }` | No |
|
|
87
|
-
|
|
88
|
-
All other [`PostHogConfig`](https://posthog.com/docs/libraries/js#config) fields
|
|
89
|
-
(e.g. `session_recording`, `capture_heatmaps`, `bootstrap`, `cookieless_mode`)
|
|
90
|
-
pass through unchanged. walkerOS sets three defaults that differ from PostHog's
|
|
91
|
-
built-ins: `autocapture: false`, `capture_pageview: false`,
|
|
92
|
-
`capture_pageleave: false` - because walkerOS sources handle event capture.
|
|
93
|
-
Override them explicitly in `settings` if you want PostHog's autocapture on.
|
|
94
|
-
|
|
95
|
-
### Mapping (`rule.settings`)
|
|
96
|
-
|
|
97
|
-
| Name | Type | Description |
|
|
98
|
-
| ---------- | -------------------------- | --------------------------------------------------------------------------------------- |
|
|
99
|
-
| `identify` | `Mapping.Value` | Per-event identity. Resolves to `{ distinctId?, $set?, $set_once? }` |
|
|
100
|
-
| `include` | `string[]` | Override destination-level `include` for this rule |
|
|
101
|
-
| `group` | `Mapping.Value` | Per-event group assignment. Resolves to `{ type, key, properties? }` |
|
|
102
|
-
| `reset` | `Mapping.Value \| boolean` | Logout trigger. Truthy value → `posthog.reset()`. Typically paired with `silent: true`. |
|
|
103
|
-
|
|
104
|
-
## Custom Event Properties
|
|
105
|
-
|
|
106
|
-
Two ways to attach properties to a PostHog capture:
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
// 1. Flatten walkerOS sections with settings.include
|
|
110
|
-
config: {
|
|
111
|
-
settings: {
|
|
112
|
-
apiKey: 'phc_...',
|
|
113
|
-
include: ['data', 'globals'], // → data_*, globals_* on every event
|
|
114
|
-
},
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// 2. Override per-rule via mapping.settings.include
|
|
118
|
-
mapping: {
|
|
119
|
-
order: {
|
|
120
|
-
complete: {
|
|
121
|
-
settings: {
|
|
122
|
-
include: ['data', 'globals'], // EUR currency, totals, pagegroup
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
},
|
|
32
|
+
"destinations": {
|
|
33
|
+
"posthog": {
|
|
34
|
+
"package": "@walkeros/web-destination-posthog",
|
|
35
|
+
"config": {}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
126
40
|
}
|
|
127
41
|
```
|
|
128
42
|
|
|
129
|
-
|
|
130
|
-
calls with the revenue properties in the payload. Use `include: ['data']` on an
|
|
131
|
-
`order complete` rule to forward `data_total`, `data_currency` (e.g. `"EUR"`),
|
|
132
|
-
`data_shipping`, etc.
|
|
133
|
-
|
|
134
|
-
## Identity
|
|
43
|
+
## Documentation
|
|
135
44
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
settings: {
|
|
141
|
-
apiKey: 'phc_...',
|
|
142
|
-
identify: {
|
|
143
|
-
map: {
|
|
144
|
-
distinctId: 'user.id',
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Per-event identify supports the full PostHog vocabulary - `distinctId`, `$set`
|
|
151
|
-
(person properties), `$set_once` (set-if-unset person properties). The
|
|
152
|
-
destination calls `posthog.identify(distinctId, $set, $set_once)`:
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
mapping: {
|
|
156
|
-
user: {
|
|
157
|
-
login: {
|
|
158
|
-
silent: true, // side-effect only, no capture() for user login
|
|
159
|
-
settings: {
|
|
160
|
-
identify: {
|
|
161
|
-
map: {
|
|
162
|
-
distinctId: 'data.user_id',
|
|
163
|
-
$set: {
|
|
164
|
-
map: {
|
|
165
|
-
email: 'data.email',
|
|
166
|
-
plan: 'data.plan',
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
$set_once: {
|
|
170
|
-
map: { first_login: 'timestamp' },
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
**Person properties without identity change.** When the resolved identify object
|
|
181
|
-
has no `distinctId`, the destination calls
|
|
182
|
-
`posthog.setPersonProperties($set, $set_once)` instead - useful for profile
|
|
183
|
-
updates that should not create a new identity:
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
mapping: {
|
|
187
|
-
profile: {
|
|
188
|
-
update: {
|
|
189
|
-
settings: {
|
|
190
|
-
identify: {
|
|
191
|
-
map: {
|
|
192
|
-
$set: { map: { name: 'data.name' } },
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
}
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
## Groups
|
|
202
|
-
|
|
203
|
-
PostHog's group analytics (Scale/Enterprise) aggregates events by company, team,
|
|
204
|
-
or any custom group type. Configure a group mapping at destination or rule level
|
|
205
|
-
|
|
206
|
-
- the destination calls `posthog.group(type, key, properties?)`:
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
mapping: {
|
|
210
|
-
company: {
|
|
211
|
-
update: {
|
|
212
|
-
silent: true,
|
|
213
|
-
settings: {
|
|
214
|
-
group: {
|
|
215
|
-
map: {
|
|
216
|
-
type: { value: 'company' },
|
|
217
|
-
key: 'data.company_id',
|
|
218
|
-
properties: {
|
|
219
|
-
map: {
|
|
220
|
-
name: 'data.company_name',
|
|
221
|
-
plan: 'data.plan',
|
|
222
|
-
},
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
## Logout
|
|
233
|
-
|
|
234
|
-
`reset: true` (or any truthy mapping value) calls `posthog.reset()`, clearing
|
|
235
|
-
the distinct ID and generating a new anonymous one. Pair with `silent: true` so
|
|
236
|
-
the rule runs as a pure side effect:
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
mapping: {
|
|
240
|
-
user: {
|
|
241
|
-
logout: {
|
|
242
|
-
silent: true,
|
|
243
|
-
settings: { reset: true },
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
}
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
## Consent
|
|
250
|
-
|
|
251
|
-
Declare the required consent keys on the destination via `config.consent`. A
|
|
252
|
-
`walker consent` event triggers the destination's `on('consent')` handler, which
|
|
253
|
-
checks every declared key against the event payload. If **all** required keys
|
|
254
|
-
are `true`, the destination calls `posthog.opt_in_capturing()`. Otherwise it
|
|
255
|
-
calls `posthog.opt_out_capturing()`, which stops capture, session replay, and
|
|
256
|
-
survey rendering.
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
destinations: {
|
|
260
|
-
posthog: {
|
|
261
|
-
code: destinationPostHog,
|
|
262
|
-
config: {
|
|
263
|
-
consent: { analytics: true }, // required keys
|
|
264
|
-
settings: { apiKey: 'phc_...' },
|
|
265
|
-
},
|
|
266
|
-
},
|
|
267
|
-
}
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
Without `config.consent` the destination takes no action on consent changes; the
|
|
271
|
-
walkerOS `config.consent` gate still blocks unconsented events from reaching the
|
|
272
|
-
destination in the first place.
|
|
273
|
-
|
|
274
|
-
## Built-in Features (Config Passthrough)
|
|
275
|
-
|
|
276
|
-
All these PostHog features work via standard `posthog-js` init options - no
|
|
277
|
-
destination wiring required:
|
|
278
|
-
|
|
279
|
-
- **Session replay** -
|
|
280
|
-
`settings.session_recording: { maskAllInputs: true, ... }`
|
|
281
|
-
- **Feature flags** - `settings.bootstrap: { featureFlags: {...} }` for SSR,
|
|
282
|
-
`settings.advanced_disable_flags: true` to disable entirely. Access flags
|
|
283
|
-
directly via the `posthog` singleton.
|
|
284
|
-
- **Surveys** - automatic via the SDK; `settings.disable_surveys: true` opts out
|
|
285
|
-
- **Heatmaps** - `settings.capture_heatmaps: true`
|
|
286
|
-
- **Exception capture** - `settings.capture_exceptions: true`
|
|
287
|
-
- **Cookieless mode** - `settings.cookieless_mode: 'always' | 'on_reject'`
|
|
288
|
-
- **Person profiles** - `settings.person_profiles: 'identified_only'` (PostHog
|
|
289
|
-
default, privacy-friendly) or `'always'`
|
|
290
|
-
|
|
291
|
-
For programmatic access to flags, surveys, or exception reporting, import the
|
|
292
|
-
SDK singleton directly:
|
|
293
|
-
|
|
294
|
-
```typescript
|
|
295
|
-
import posthog from 'posthog-js';
|
|
296
|
-
posthog.getFeatureFlag('my-flag');
|
|
297
|
-
posthog.captureException(error);
|
|
298
|
-
```
|
|
45
|
+
Full configuration, mapping, and examples live in the docs:
|
|
46
|
+
**https://www.walkeros.io/docs/destinations/web/posthog**
|
|
299
47
|
|
|
300
48
|
## Contribute
|
|
301
49
|
|
|
@@ -306,4 +54,4 @@ Feel free to contribute by submitting an
|
|
|
306
54
|
|
|
307
55
|
## License
|
|
308
56
|
|
|
309
|
-
|
|
57
|
+
MIT
|