@boldvideo/bold-js 0.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.
@@ -0,0 +1,8 @@
1
+ # Changesets
2
+
3
+ Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4
+ with multi-package repos, or single-package repos to help you version and publish your code. You can
5
+ find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6
+
7
+ We have a quick list of common questions to get you started engaging with this project in
8
+ [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
3
+ "changelog": "@changesets/cli/changelog",
4
+ "commit": false,
5
+ "fixed": [],
6
+ "linked": [],
7
+ "access": "restricted",
8
+ "baseBranch": "main",
9
+ "updateInternalDependencies": "patch",
10
+ "ignore": []
11
+ }
@@ -0,0 +1,21 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches:
5
+ - "**"
6
+
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v3
12
+ - uses: pnpm/action-setup@v2
13
+ with:
14
+ version: 7
15
+ - uses: actions/setup-node@v3
16
+ with:
17
+ node-version: 16.x
18
+ cache: "pnpm"
19
+
20
+ - run: pnpm install --frozen-lockfile
21
+ - run: pnpm run lint && pnpm run build
@@ -0,0 +1,29 @@
1
+ name: Publish
2
+ on:
3
+ push:
4
+ branches:
5
+ - "main"
6
+
7
+ concurrency: ${{ github.workflow }}-${{ github.ref }}
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v3
14
+ - uses: pnpm/action-setup@v2
15
+ with:
16
+ version: 7
17
+ - uses: actions/setup-node@v3
18
+ with:
19
+ node-version: 16.x
20
+ cache: "pnpm"
21
+
22
+ - run: pnpm install --frozen-lockfile
23
+ - name: Create Release Pull Request or Publish
24
+ id: changesets
25
+ uses: changesets/action@v1
26
+ with:
27
+ publish: pnpm run build
28
+ env:
29
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ <div align="center">
2
+ <a href="https://wearebold.af?utm_source=github.com&utm_medium=readme&utm_campaign=bold-js" align="center">
3
+ <img src="https://wearebold.af/bold-js-github-header.svg" alt="Bold Logo">
4
+ </a>
5
+ <h1 align="center rainbow">@boldvideo/bold-js</h1>
6
+ <p align="center">
7
+ The JavaScript SDK for interacting with the <a href="http://wearebold.af?utm_source=github.com&utm_medium=readme&utm_campaign=bold-js" target="_blank">Bold API</a>, to power your own business video platform.
8
+ </p>
9
+ </div>
10
+
11
+ <p align="center">
12
+ <a href="https://npmjs.com/package/@boldvideo/bold-js">
13
+ <img src="https://img.shields.io/npm/v/@boldvideo/bold-js/latest.svg?style=flat-square" alt="Bold JS" />
14
+ </a>
15
+ <a href="https://npmjs.com/package/@boldvideo/bold-js" rel="nofollow">
16
+ <img src="https://img.shields.io/npm/dt/@boldvideo/bold-js.svg?style=flat-square" alt="npm">
17
+ </a>
18
+ </p>
19
+
20
+ <p align="center">
21
+ <a href="https://twitter.com/intent/follow?screen_name=veryboldvideo">
22
+ <img src="https://img.shields.io/badge/Follow-%40veryboldvideo-09b3af?style=appveyor&logo=twitter" alt="Follow @veryboldvideo" />
23
+ </a>
24
+ <a href="https://https://app.boldvideo.io/register?utm_source=github.com&utm_medium=readme&utm_campaign=bold-js">
25
+ <img src="https://img.shields.io/badge/Try%20Bold-Free-09b3af?style=appveyor&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAmCAYAAADTGStiAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGFSURBVHgBxZg9SwNBEIZ34xUpVLCwEQQRtRARxV+g4M8QLO0sBPtgZS129gr+AbEWWyshOUSCkipBjB8cBHPrM4GVQ84qZuaFJTebj+feyczu3fmxEIIbXjnjjZEy7hm3feeunfdPf33B/xO4TBk/fMoZHXMCHU1wVBP3m8Cb2mDRI/AN4K9xouJ0NA9ovzih5Vj0jutZXHcl0HIsmkicW4uBJtiR2kUr8KQJGPVMwJ62sgJ//hxrtROQNsvnDO30JbGaY9xeROggVnLcY/FYAPwcJ7Qc7xahKmAAe33vz0vmRysK6rASQs2FUC3Oq1U1xZVSWVukvCWxWlXjbgnYFc6nVMEiXK+wQx0MjhX346gPWmtOe5MQjQPdsQBLylctUi3gholjnE6bgFHVCpxZgR+s/uOGVTvdWLTTCyvXurpj3J7IfbOqY0BpLrcx3mea22Id6LZAJdYA56T3COhy8dFE4kYkHN7xcgnwDGD79/sJH6i54SQ1ItfLXZx1GC2CehmsqG96m37o1gSKagAAAABJRU5ErkJggg==" alt="Try Bold Video" />
26
+ </a>
27
+ </p>
28
+
29
+ ## Usage
30
+
31
+ First, install the library:
32
+
33
+ ```sh
34
+ npm install @boldvideo/bold-js
35
+ ```
36
+
37
+ Next, instantiate the client to establish a connection to your Bold Channel:
38
+ ```js
39
+ import { createClient } from "@boldvideo/bold-js";
40
+
41
+ const bold = createClient('YOUR_API_KEY');
42
+ ```
43
+
44
+ Now you're able to query any of your resources from Bold. For exmaple:
45
+ ```js
46
+ // fetches your channel settings, menus and featured playlists
47
+ const settings = await bold.settings();
48
+
49
+ // fetches the latest videos
50
+ const videos = await bold.videos.list();
51
+
52
+ // fetches the latest playlists
53
+ const playlists = await bold.playlists.list();
54
+
55
+ ```
56
+
57
+ ## Related Links
58
+
59
+ - **[Bold API Documentation](https://docs.boldvideo.io/docs/api)**
60
+
61
+ ## More Resources
62
+
63
+ ### Support
64
+
65
+ - Bugs or Feature Requests? [Submit an issue](/../../issues/new).
66
+
67
+
package/dist/index.cjs ADDED
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __defProps = Object.defineProperties;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
9
+ var __getProtoOf = Object.getPrototypeOf;
10
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
11
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
12
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
13
+ var __spreadValues = (a, b) => {
14
+ for (var prop in b || (b = {}))
15
+ if (__hasOwnProp.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ if (__getOwnPropSymbols)
18
+ for (var prop of __getOwnPropSymbols(b)) {
19
+ if (__propIsEnum.call(b, prop))
20
+ __defNormalProp(a, prop, b[prop]);
21
+ }
22
+ return a;
23
+ };
24
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
25
+ var __export = (target, all) => {
26
+ for (var name in all)
27
+ __defProp(target, name, { get: all[name], enumerable: true });
28
+ };
29
+ var __copyProps = (to, from, except, desc) => {
30
+ if (from && typeof from === "object" || typeof from === "function") {
31
+ for (let key of __getOwnPropNames(from))
32
+ if (!__hasOwnProp.call(to, key) && key !== except)
33
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
34
+ }
35
+ return to;
36
+ };
37
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
38
+ // If the importer is in node compatibility mode or this is not an ESM
39
+ // file that has been converted to a CommonJS file using a Babel-
40
+ // compatible transform (i.e. "__esModule" has not been set), then set
41
+ // "default" to the CommonJS "module.exports" for node compatibility.
42
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
43
+ mod
44
+ ));
45
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
46
+ var __async = (__this, __arguments, generator) => {
47
+ return new Promise((resolve, reject) => {
48
+ var fulfilled = (value) => {
49
+ try {
50
+ step(generator.next(value));
51
+ } catch (e) {
52
+ reject(e);
53
+ }
54
+ };
55
+ var rejected = (value) => {
56
+ try {
57
+ step(generator.throw(value));
58
+ } catch (e) {
59
+ reject(e);
60
+ }
61
+ };
62
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
63
+ step((generator = generator.apply(__this, __arguments)).next());
64
+ });
65
+ };
66
+
67
+ // src/index.ts
68
+ var src_exports = {};
69
+ __export(src_exports, {
70
+ createClient: () => createClient
71
+ });
72
+ module.exports = __toCommonJS(src_exports);
73
+
74
+ // src/lib/client.ts
75
+ var import_axios = __toESM(require("axios"), 1);
76
+
77
+ // src/lib/fetchers.ts
78
+ function get(client, url) {
79
+ return __async(this, null, function* () {
80
+ const res = yield client.get(url);
81
+ return res.data;
82
+ });
83
+ }
84
+ function fetchSettings(client) {
85
+ return (videoLimit = 12) => __async(this, null, function* () {
86
+ return get(client, `settings?limit=${videoLimit}`);
87
+ });
88
+ }
89
+ function fetchVideos(client) {
90
+ return (videoLimit = 12) => __async(this, null, function* () {
91
+ return get(client, `videos/latest?limit=${videoLimit}`);
92
+ });
93
+ }
94
+ function fetchVideo(client) {
95
+ return (id) => __async(this, null, function* () {
96
+ return get(client, `videos/${id}`);
97
+ });
98
+ }
99
+ function fetchPlaylists(client) {
100
+ return () => __async(this, null, function* () {
101
+ return get(client, "playlists");
102
+ });
103
+ }
104
+ function fetchPlaylist(client) {
105
+ return (id) => __async(this, null, function* () {
106
+ return get(client, `playlists/${id}`);
107
+ });
108
+ }
109
+
110
+ // src/util/throttle.ts
111
+ var throttle = (fn, delay) => {
112
+ let wait = false;
113
+ let timeout;
114
+ let cancelled = false;
115
+ return [
116
+ (...args) => {
117
+ if (cancelled)
118
+ return void 0;
119
+ if (wait)
120
+ return void 0;
121
+ const val = fn(...args);
122
+ wait = true;
123
+ timeout = window.setTimeout(() => {
124
+ wait = false;
125
+ }, delay);
126
+ return val;
127
+ },
128
+ () => {
129
+ cancelled = true;
130
+ clearTimeout(timeout);
131
+ }
132
+ ];
133
+ };
134
+
135
+ // src/lib/tracking.ts
136
+ function sendEvent(client, eventName, data, debug) {
137
+ const payload = {
138
+ n: eventName,
139
+ u: data.url,
140
+ usr: data.userId,
141
+ d: data.domain,
142
+ ua: data.userAgent,
143
+ w: data.deviceWidth,
144
+ vid: (data == null ? void 0 : data.videoId) || void 0,
145
+ vt: data.title,
146
+ vdur: (data == null ? void 0 : data.videoDuration) || void 0,
147
+ time: (data == null ? void 0 : data.timeStamp) || void 0
148
+ };
149
+ if (debug)
150
+ console.log(`Bold SDK - Logging event '${eventName}'`, payload);
151
+ client.post("/event", payload);
152
+ }
153
+ var [throttledSendEvent] = throttle(sendEvent, 5e3);
154
+ function trackEvent(client, userId, options) {
155
+ return (video, event) => {
156
+ const eventDetails = __spreadProps(__spreadValues({}, basicInfos()), {
157
+ userId,
158
+ videoId: video.id,
159
+ title: video.title,
160
+ videoDuration: video.duration,
161
+ timeStamp: event.timeStamp
162
+ });
163
+ if (event.type == "timeupdate") {
164
+ throttledSendEvent(
165
+ client,
166
+ getEventName(event),
167
+ eventDetails,
168
+ options.debug
169
+ );
170
+ } else {
171
+ sendEvent(client, getEventName(event), eventDetails, options.debug);
172
+ }
173
+ };
174
+ }
175
+ function trackPageView(client, userId, options) {
176
+ return (title) => {
177
+ const eventDetails = __spreadProps(__spreadValues({}, basicInfos()), {
178
+ userId,
179
+ title
180
+ });
181
+ sendEvent(client, "page_view", eventDetails, options.debug);
182
+ };
183
+ }
184
+ function getEventName({ type }) {
185
+ switch (type) {
186
+ case "pause":
187
+ return "video_pause";
188
+ case "play":
189
+ return "video_resume";
190
+ case "loadedmetadata":
191
+ return "video_load";
192
+ case "timeupdate":
193
+ return "video_progress";
194
+ default:
195
+ return "unknown_event";
196
+ }
197
+ }
198
+ function basicInfos() {
199
+ return {
200
+ url: location.href,
201
+ domain: "localhost:3000",
202
+ referrer: document.referrer || null,
203
+ deviceWidth: window.innerWidth,
204
+ userAgent: navigator.userAgent
205
+ };
206
+ }
207
+
208
+ // src/lib/client.ts
209
+ function createClient(apiKey, options = { debug: false }) {
210
+ var _a;
211
+ const { debug } = options;
212
+ const apiClientOptions = {
213
+ baseURL: (_a = options.baseURL) != null ? _a : "https://app.boldvideo.io/api/v1/",
214
+ headers: {
215
+ Authorization: apiKey
216
+ }
217
+ };
218
+ const apiClient = import_axios.default.create(apiClientOptions);
219
+ const userId = [...Array(30)].map(() => Math.random().toString(36)[2]).join("");
220
+ return {
221
+ settings: fetchSettings(apiClient),
222
+ videos: {
223
+ list: fetchVideos(apiClient),
224
+ get: fetchVideo(apiClient)
225
+ },
226
+ playlists: {
227
+ list: fetchPlaylists(apiClient),
228
+ get: fetchPlaylist(apiClient)
229
+ },
230
+ trackEvent: trackEvent(apiClient, userId, { debug }),
231
+ trackPageView: trackPageView(apiClient, userId, { debug })
232
+ };
233
+ }
234
+ // Annotate the CommonJS export names for ESM import in node:
235
+ 0 && (module.exports = {
236
+ createClient
237
+ });
@@ -0,0 +1,75 @@
1
+ type Video = {
2
+ captions: string;
3
+ captions_label: string;
4
+ captions_lang: string;
5
+ description: string | null;
6
+ duration: number;
7
+ id: string;
8
+ imported_from: string | null;
9
+ legacy_video_url: null | null;
10
+ meta_data: [];
11
+ playback_id: string;
12
+ published_at: string;
13
+ stream_url: string;
14
+ teaser: string | null;
15
+ thumbnail: string;
16
+ title: string;
17
+ transcription: string;
18
+ type: string;
19
+ };
20
+ type Playlist = {
21
+ description?: string;
22
+ id: string;
23
+ is_private: boolean;
24
+ title: string;
25
+ type: string;
26
+ videos: Video[];
27
+ };
28
+ type MenuItem = {
29
+ icon: string;
30
+ is_ext: boolean;
31
+ label: string;
32
+ url: string;
33
+ };
34
+ type Settings = {
35
+ featured_playlists: Playlist[];
36
+ menu_items: MenuItem[];
37
+ meta_data: {
38
+ channel_name: string;
39
+ description: string;
40
+ image: string;
41
+ no_seo: boolean;
42
+ title: string;
43
+ title_suffix: string;
44
+ };
45
+ };
46
+
47
+ type ClientOptions = {
48
+ baseURL?: string;
49
+ debug: boolean;
50
+ };
51
+ declare function createClient(apiKey: string, options?: ClientOptions): {
52
+ settings: (videoLimit?: number) => Promise<{
53
+ data: Settings;
54
+ }>;
55
+ videos: {
56
+ list: (videoLimit?: number) => Promise<{
57
+ data: Video[];
58
+ }>;
59
+ get: (id: string) => Promise<{
60
+ data: Video;
61
+ }>;
62
+ };
63
+ playlists: {
64
+ list: () => Promise<{
65
+ data: Playlist[];
66
+ }>;
67
+ get: (id: string) => Promise<{
68
+ data: Playlist;
69
+ }>;
70
+ };
71
+ trackEvent: (video: any, event: Event) => void;
72
+ trackPageView: (title: string) => void;
73
+ };
74
+
75
+ export { createClient };
package/dist/index.js ADDED
@@ -0,0 +1,203 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __async = (__this, __arguments, generator) => {
21
+ return new Promise((resolve, reject) => {
22
+ var fulfilled = (value) => {
23
+ try {
24
+ step(generator.next(value));
25
+ } catch (e) {
26
+ reject(e);
27
+ }
28
+ };
29
+ var rejected = (value) => {
30
+ try {
31
+ step(generator.throw(value));
32
+ } catch (e) {
33
+ reject(e);
34
+ }
35
+ };
36
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
37
+ step((generator = generator.apply(__this, __arguments)).next());
38
+ });
39
+ };
40
+
41
+ // src/lib/client.ts
42
+ import axios from "axios";
43
+
44
+ // src/lib/fetchers.ts
45
+ function get(client, url) {
46
+ return __async(this, null, function* () {
47
+ const res = yield client.get(url);
48
+ return res.data;
49
+ });
50
+ }
51
+ function fetchSettings(client) {
52
+ return (videoLimit = 12) => __async(this, null, function* () {
53
+ return get(client, `settings?limit=${videoLimit}`);
54
+ });
55
+ }
56
+ function fetchVideos(client) {
57
+ return (videoLimit = 12) => __async(this, null, function* () {
58
+ return get(client, `videos/latest?limit=${videoLimit}`);
59
+ });
60
+ }
61
+ function fetchVideo(client) {
62
+ return (id) => __async(this, null, function* () {
63
+ return get(client, `videos/${id}`);
64
+ });
65
+ }
66
+ function fetchPlaylists(client) {
67
+ return () => __async(this, null, function* () {
68
+ return get(client, "playlists");
69
+ });
70
+ }
71
+ function fetchPlaylist(client) {
72
+ return (id) => __async(this, null, function* () {
73
+ return get(client, `playlists/${id}`);
74
+ });
75
+ }
76
+
77
+ // src/util/throttle.ts
78
+ var throttle = (fn, delay) => {
79
+ let wait = false;
80
+ let timeout;
81
+ let cancelled = false;
82
+ return [
83
+ (...args) => {
84
+ if (cancelled)
85
+ return void 0;
86
+ if (wait)
87
+ return void 0;
88
+ const val = fn(...args);
89
+ wait = true;
90
+ timeout = window.setTimeout(() => {
91
+ wait = false;
92
+ }, delay);
93
+ return val;
94
+ },
95
+ () => {
96
+ cancelled = true;
97
+ clearTimeout(timeout);
98
+ }
99
+ ];
100
+ };
101
+
102
+ // src/lib/tracking.ts
103
+ function sendEvent(client, eventName, data, debug) {
104
+ const payload = {
105
+ n: eventName,
106
+ u: data.url,
107
+ usr: data.userId,
108
+ d: data.domain,
109
+ ua: data.userAgent,
110
+ w: data.deviceWidth,
111
+ vid: (data == null ? void 0 : data.videoId) || void 0,
112
+ vt: data.title,
113
+ vdur: (data == null ? void 0 : data.videoDuration) || void 0,
114
+ time: (data == null ? void 0 : data.timeStamp) || void 0
115
+ };
116
+ if (debug)
117
+ console.log(`Bold SDK - Logging event '${eventName}'`, payload);
118
+ client.post("/event", payload);
119
+ }
120
+ var [throttledSendEvent] = throttle(sendEvent, 5e3);
121
+ function trackEvent(client, userId, options) {
122
+ return (video, event) => {
123
+ const eventDetails = __spreadProps(__spreadValues({}, basicInfos()), {
124
+ userId,
125
+ videoId: video.id,
126
+ title: video.title,
127
+ videoDuration: video.duration,
128
+ timeStamp: event.timeStamp
129
+ });
130
+ if (event.type == "timeupdate") {
131
+ throttledSendEvent(
132
+ client,
133
+ getEventName(event),
134
+ eventDetails,
135
+ options.debug
136
+ );
137
+ } else {
138
+ sendEvent(client, getEventName(event), eventDetails, options.debug);
139
+ }
140
+ };
141
+ }
142
+ function trackPageView(client, userId, options) {
143
+ return (title) => {
144
+ const eventDetails = __spreadProps(__spreadValues({}, basicInfos()), {
145
+ userId,
146
+ title
147
+ });
148
+ sendEvent(client, "page_view", eventDetails, options.debug);
149
+ };
150
+ }
151
+ function getEventName({ type }) {
152
+ switch (type) {
153
+ case "pause":
154
+ return "video_pause";
155
+ case "play":
156
+ return "video_resume";
157
+ case "loadedmetadata":
158
+ return "video_load";
159
+ case "timeupdate":
160
+ return "video_progress";
161
+ default:
162
+ return "unknown_event";
163
+ }
164
+ }
165
+ function basicInfos() {
166
+ return {
167
+ url: location.href,
168
+ domain: "localhost:3000",
169
+ referrer: document.referrer || null,
170
+ deviceWidth: window.innerWidth,
171
+ userAgent: navigator.userAgent
172
+ };
173
+ }
174
+
175
+ // src/lib/client.ts
176
+ function createClient(apiKey, options = { debug: false }) {
177
+ var _a;
178
+ const { debug } = options;
179
+ const apiClientOptions = {
180
+ baseURL: (_a = options.baseURL) != null ? _a : "https://app.boldvideo.io/api/v1/",
181
+ headers: {
182
+ Authorization: apiKey
183
+ }
184
+ };
185
+ const apiClient = axios.create(apiClientOptions);
186
+ const userId = [...Array(30)].map(() => Math.random().toString(36)[2]).join("");
187
+ return {
188
+ settings: fetchSettings(apiClient),
189
+ videos: {
190
+ list: fetchVideos(apiClient),
191
+ get: fetchVideo(apiClient)
192
+ },
193
+ playlists: {
194
+ list: fetchPlaylists(apiClient),
195
+ get: fetchPlaylist(apiClient)
196
+ },
197
+ trackEvent: trackEvent(apiClient, userId, { debug }),
198
+ trackPageView: trackPageView(apiClient, userId, { debug })
199
+ };
200
+ }
201
+ export {
202
+ createClient
203
+ };
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@boldvideo/bold-js",
3
+ "license": "MIT",
4
+ "version": "0.1.0",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "type": "module",
9
+ "scripts": {
10
+ "build": "tsup src/index.ts --format cjs,esm --dts",
11
+ "lint": "tsc"
12
+ },
13
+ "devDependencies": {
14
+ "@changesets/cli": "^2.26.0",
15
+ "tsup": "^6.6.3",
16
+ "typescript": "^4.9.5"
17
+ },
18
+ "dependencies": {
19
+ "axios": "^1.3.4"
20
+ }
21
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { createClient } from 'lib/client'
2
+
3
+
@@ -0,0 +1,41 @@
1
+ import axios, { AxiosInstance } from "axios";
2
+
3
+ import { fetchVideo, fetchVideos, fetchSettings, fetchPlaylist, fetchPlaylists } from './fetchers'
4
+ import { trackEvent, trackPageView } from './tracking'
5
+
6
+ type ClientOptions = {
7
+ baseURL?: string
8
+ debug: boolean
9
+ }
10
+
11
+ function createClient(apiKey: string, options: ClientOptions = {debug: false}) {
12
+ const { debug } = options;
13
+ const apiClientOptions = {
14
+ baseURL: options.baseURL ?? "https://app.boldvideo.io/api/v1/",
15
+ headers: {
16
+ Authorization: apiKey,
17
+ },
18
+ };
19
+
20
+ const apiClient: AxiosInstance = axios.create(apiClientOptions);
21
+
22
+ const userId = [...Array(30)]
23
+ .map(() => Math.random().toString(36)[2])
24
+ .join("");
25
+
26
+ return {
27
+ settings: fetchSettings(apiClient),
28
+ videos: {
29
+ list: fetchVideos(apiClient),
30
+ get: fetchVideo(apiClient),
31
+ },
32
+ playlists: {
33
+ list: fetchPlaylists(apiClient),
34
+ get: fetchPlaylist(apiClient),
35
+ },
36
+ trackEvent: trackEvent(apiClient, userId, { debug }),
37
+ trackPageView: trackPageView(apiClient, userId, { debug }),
38
+ };
39
+ }
40
+
41
+ export { createClient };
@@ -0,0 +1,33 @@
1
+ import { Video, Playlist, Settings } from './types'
2
+ import { AxiosInstance } from 'axios'
3
+
4
+ type Response<T> = {
5
+ data: T
6
+ }
7
+
8
+ type ApiClient = AxiosInstance;
9
+
10
+ async function get<TResponse>(client: ApiClient, url: string): Promise<TResponse> {
11
+ const res = await client.get(url);
12
+ return res.data as TResponse;
13
+ }
14
+
15
+ export function fetchSettings(client: ApiClient) {
16
+ return async (videoLimit = 12) => get<Response<Settings>>(client, `settings?limit=${videoLimit}`);
17
+ }
18
+
19
+ export function fetchVideos(client: ApiClient) {
20
+ return async (videoLimit = 12) => get<Response<Video[]>>(client, `videos/latest?limit=${videoLimit}`);
21
+ }
22
+
23
+ export function fetchVideo(client: ApiClient) {
24
+ return async (id: string) => get<Response<Video>>(client, `videos/${id}`);
25
+ }
26
+
27
+ export function fetchPlaylists(client: ApiClient) {
28
+ return async () => get<Response<Playlist[]>>(client, "playlists");
29
+ }
30
+
31
+ export function fetchPlaylist(client: ApiClient) {
32
+ return async (id: string) => get<Response<Playlist>>(client, `playlists/${id}`);
33
+ }
@@ -0,0 +1,115 @@
1
+ import { AxiosInstance } from "axios";
2
+ import { throttle } from "util/throttle";
3
+
4
+ type ApiClient = AxiosInstance;
5
+
6
+ type Options = {
7
+ debug: boolean;
8
+ };
9
+
10
+ type EventData = {
11
+ url: string;
12
+ userId: string;
13
+ domain: string;
14
+ userAgent: string;
15
+ deviceWidth: number;
16
+ videoId?: string;
17
+ title: string;
18
+ videoDuration?: number;
19
+ timeStamp?: number;
20
+ };
21
+
22
+ function sendEvent(
23
+ client: ApiClient,
24
+ eventName: string,
25
+ data: EventData,
26
+ debug: boolean
27
+ ) {
28
+ const payload = {
29
+ n: eventName,
30
+ u: data.url,
31
+ usr: data.userId,
32
+ d: data.domain,
33
+ ua: data.userAgent,
34
+ w: data.deviceWidth,
35
+ vid: data?.videoId || undefined,
36
+ vt: data.title,
37
+ vdur: data?.videoDuration || undefined,
38
+ time: data?.timeStamp || undefined,
39
+ };
40
+
41
+ if (debug) console.log(`Bold SDK - Logging event '${eventName}'`, payload);
42
+
43
+ client.post("/event", payload);
44
+ }
45
+
46
+ const [throttledSendEvent] = throttle(sendEvent, 5000);
47
+
48
+ export function trackEvent(
49
+ client: ApiClient,
50
+ userId: string,
51
+ options: Options
52
+ ) {
53
+ // TODO: verify event
54
+ return (video: any, event: Event) => {
55
+ const eventDetails = {
56
+ ...basicInfos(),
57
+ userId,
58
+ videoId: video.id,
59
+ title: video.title,
60
+ videoDuration: video.duration,
61
+ timeStamp: event.timeStamp,
62
+ };
63
+ // debounce fast hitting timeupdate event
64
+ if (event.type == "timeupdate") {
65
+ throttledSendEvent(
66
+ client,
67
+ getEventName(event),
68
+ eventDetails,
69
+ options.debug
70
+ );
71
+ } else {
72
+ sendEvent(client, getEventName(event), eventDetails, options.debug);
73
+ }
74
+ };
75
+ }
76
+
77
+ export function trackPageView(
78
+ client: ApiClient,
79
+ userId: string,
80
+ options: Options
81
+ ) {
82
+ return (title: string) => {
83
+ const eventDetails = {
84
+ ...basicInfos(),
85
+ userId,
86
+ title,
87
+ };
88
+ sendEvent(client, "page_view", eventDetails, options.debug);
89
+ };
90
+ }
91
+
92
+ function getEventName({ type }: { type: string }) {
93
+ switch (type) {
94
+ case "pause":
95
+ return "video_pause";
96
+ case "play":
97
+ return "video_resume";
98
+ case "loadedmetadata":
99
+ return "video_load";
100
+ case "timeupdate":
101
+ return "video_progress";
102
+ default:
103
+ return "unknown_event";
104
+ }
105
+ }
106
+
107
+ function basicInfos() {
108
+ return {
109
+ url: location.href,
110
+ domain: "localhost:3000",
111
+ referrer: document.referrer || null,
112
+ deviceWidth: window.innerWidth,
113
+ userAgent: navigator.userAgent,
114
+ };
115
+ }
@@ -0,0 +1,48 @@
1
+ export type Video = {
2
+ captions: string;
3
+ captions_label: string;
4
+ captions_lang: string;
5
+ description: string | null;
6
+ duration: number;
7
+ id: string;
8
+ imported_from: string | null;
9
+ legacy_video_url: null | null;
10
+ meta_data: [];
11
+ playback_id: string;
12
+ published_at: string;
13
+ stream_url: string;
14
+ teaser: string | null;
15
+ thumbnail: string;
16
+ title: string;
17
+ transcription: string;
18
+ type: string;
19
+ };
20
+
21
+ export type Playlist = {
22
+ description?: string;
23
+ id: string;
24
+ is_private: boolean;
25
+ title: string;
26
+ type: string;
27
+ videos: Video[];
28
+ };
29
+
30
+ export type MenuItem = {
31
+ icon: string;
32
+ is_ext: boolean;
33
+ label: string;
34
+ url: string;
35
+ };
36
+
37
+ export type Settings = {
38
+ featured_playlists: Playlist[];
39
+ menu_items: MenuItem[];
40
+ meta_data: {
41
+ channel_name: string;
42
+ description: string;
43
+ image: string;
44
+ no_seo: boolean;
45
+ title: string;
46
+ title_suffix: string;
47
+ };
48
+ };
@@ -0,0 +1,29 @@
1
+ export const throttle = <R, A extends any[]>(
2
+ fn: (...args: A) => R,
3
+ delay: number
4
+ ): [(...args: A) => R | undefined, () => void] => {
5
+ let wait = false;
6
+ let timeout: undefined | number;
7
+ let cancelled = false;
8
+
9
+ return [
10
+ (...args: A) => {
11
+ if (cancelled) return undefined;
12
+ if (wait) return undefined;
13
+
14
+ const val = fn(...args);
15
+
16
+ wait = true;
17
+
18
+ timeout = window.setTimeout(() => {
19
+ wait = false;
20
+ }, delay);
21
+
22
+ return val;
23
+ },
24
+ () => {
25
+ cancelled = true;
26
+ clearTimeout(timeout);
27
+ },
28
+ ];
29
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2016",
4
+ "module": "commonjs",
5
+ "esModuleInterop": true,
6
+ "forceConsistentCasingInFileNames": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "noUncheckedIndexedAccess": true,
10
+ "noEmit": true,
11
+ "baseUrl": "src"
12
+ }
13
+ }