@boldvideo/bold-js 0.6.0 → 0.7.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 +20 -0
- package/dist/index.cjs +299 -0
- package/dist/index.d.ts +197 -0
- package/dist/index.js +265 -0
- package/package.json +1 -1
- package/thoughts/shared/plans/2025-10-22-BOLD-759-portal-color-scheme.md +272 -0
- package/thoughts/shared/research/2025-10-22-BOLD-759-portal-color-scheme-settings.md +215 -0
- package/.changeset/README.md +0 -8
- package/.changeset/config.json +0 -11
- package/.github/dependabot.yml +0 -11
- package/.github/workflow/main.yml +0 -21
- package/.github/workflow/publish.yml +0 -29
- package/.github/workflows/changeset-release.yml +0 -51
- package/.github/workflows/ci.yml +0 -90
- package/.github/workflows/release.yml +0 -83
- package/CLAUDE.md +0 -76
- package/CONTRIBUTING.md +0 -103
- package/SECURITY.md +0 -66
- package/src/index.ts +0 -22
- package/src/lib/client.ts +0 -53
- package/src/lib/fetchers.ts +0 -96
- package/src/lib/tracking.ts +0 -117
- package/src/lib/types.ts +0 -217
- package/src/util/throttle.ts +0 -29
- package/tsconfig.json +0 -14
package/CONTRIBUTING.md
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
# Contributing to @boldvideo/bold-js
|
|
2
|
-
|
|
3
|
-
First off, thank you for considering contributing to Bold JS! It's people like you that make Bold JS such a great tool.
|
|
4
|
-
|
|
5
|
-
## Code of Conduct
|
|
6
|
-
|
|
7
|
-
By participating in this project, you are expected to uphold our Code of Conduct:
|
|
8
|
-
- Be respectful and inclusive
|
|
9
|
-
- Welcome newcomers and help them get started
|
|
10
|
-
- Focus on what is best for the community
|
|
11
|
-
- Show empathy towards other community members
|
|
12
|
-
|
|
13
|
-
## How Can I Contribute?
|
|
14
|
-
|
|
15
|
-
### Reporting Bugs
|
|
16
|
-
|
|
17
|
-
Before creating bug reports, please check existing issues to avoid duplicates. When you create a bug report, include as many details as possible:
|
|
18
|
-
|
|
19
|
-
- Use a clear and descriptive title
|
|
20
|
-
- Describe the exact steps to reproduce the problem
|
|
21
|
-
- Provide specific examples to demonstrate the steps
|
|
22
|
-
- Describe the behavior you observed and what behavior you expected
|
|
23
|
-
- Include your environment details (Node.js version, OS, etc.)
|
|
24
|
-
|
|
25
|
-
### Suggesting Enhancements
|
|
26
|
-
|
|
27
|
-
Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion:
|
|
28
|
-
|
|
29
|
-
- Use a clear and descriptive title
|
|
30
|
-
- Provide a step-by-step description of the suggested enhancement
|
|
31
|
-
- Provide specific examples to demonstrate the use case
|
|
32
|
-
- Explain why this enhancement would be useful to most users
|
|
33
|
-
|
|
34
|
-
### Pull Requests
|
|
35
|
-
|
|
36
|
-
1. Fork the repo and create your branch from `main`
|
|
37
|
-
2. If you've added code that should be tested, add tests
|
|
38
|
-
3. Ensure the test suite passes (`pnpm run lint && pnpm run build`)
|
|
39
|
-
4. Make sure your code follows the existing code style
|
|
40
|
-
5. Create a changeset for your changes:
|
|
41
|
-
```bash
|
|
42
|
-
pnpm changeset
|
|
43
|
-
```
|
|
44
|
-
6. Push your branch and submit a pull request
|
|
45
|
-
|
|
46
|
-
## Development Setup
|
|
47
|
-
|
|
48
|
-
1. Fork and clone the repo:
|
|
49
|
-
```bash
|
|
50
|
-
git clone https://github.com/YOUR_USERNAME/bold-js.git
|
|
51
|
-
cd bold-js
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
2. Install dependencies:
|
|
55
|
-
```bash
|
|
56
|
-
pnpm install
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
3. Make your changes and run the build:
|
|
60
|
-
```bash
|
|
61
|
-
pnpm run build
|
|
62
|
-
pnpm run lint
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Changesets
|
|
66
|
-
|
|
67
|
-
We use [changesets](https://github.com/changesets/changesets) to manage versions and changelogs. When you make a change:
|
|
68
|
-
|
|
69
|
-
1. Run `pnpm changeset`
|
|
70
|
-
2. Select the type of change (patch/minor/major)
|
|
71
|
-
3. Describe your changes for the changelog
|
|
72
|
-
4. Commit the generated changeset file along with your changes
|
|
73
|
-
|
|
74
|
-
## Style Guide
|
|
75
|
-
|
|
76
|
-
- Use TypeScript for all new code
|
|
77
|
-
- Follow the existing code style (enforced by TypeScript compiler)
|
|
78
|
-
- Write clear, self-documenting code
|
|
79
|
-
- Add comments only when necessary to explain "why" not "what"
|
|
80
|
-
- Keep functions small and focused
|
|
81
|
-
- Use meaningful variable and function names
|
|
82
|
-
|
|
83
|
-
## Commit Messages
|
|
84
|
-
|
|
85
|
-
- Use the present tense ("Add feature" not "Added feature")
|
|
86
|
-
- Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
|
|
87
|
-
- Limit the first line to 72 characters or less
|
|
88
|
-
- Reference issues and pull requests liberally after the first line
|
|
89
|
-
|
|
90
|
-
## Release Process (Maintainers Only)
|
|
91
|
-
|
|
92
|
-
Releases are automated through GitHub Actions:
|
|
93
|
-
|
|
94
|
-
1. When PRs with changesets are merged to main, a "Version Packages" PR is created
|
|
95
|
-
2. Review and merge the version PR to trigger a release
|
|
96
|
-
3. The workflow will automatically:
|
|
97
|
-
- Publish to npm
|
|
98
|
-
- Create a GitHub release
|
|
99
|
-
- Update the changelog
|
|
100
|
-
|
|
101
|
-
## Questions?
|
|
102
|
-
|
|
103
|
-
Feel free to open an issue with your question or reach out to the maintainers directly.
|
package/SECURITY.md
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# Security Policy
|
|
2
|
-
|
|
3
|
-
## Supported Versions
|
|
4
|
-
|
|
5
|
-
We release patches for security vulnerabilities for the following versions:
|
|
6
|
-
|
|
7
|
-
| Version | Supported |
|
|
8
|
-
| ------- | ------------------ |
|
|
9
|
-
| 0.x.x | :white_check_mark: |
|
|
10
|
-
|
|
11
|
-
## Reporting a Vulnerability
|
|
12
|
-
|
|
13
|
-
If you discover a security vulnerability within Bold JS, please send an email to security@boldvideo.com. All security vulnerabilities will be promptly addressed.
|
|
14
|
-
|
|
15
|
-
Please do not report security vulnerabilities through public GitHub issues.
|
|
16
|
-
|
|
17
|
-
When reporting a vulnerability, please include:
|
|
18
|
-
|
|
19
|
-
- The version of @boldvideo/bold-js you're using
|
|
20
|
-
- A description of the vulnerability
|
|
21
|
-
- Steps to reproduce the issue
|
|
22
|
-
- Potential impact of the vulnerability
|
|
23
|
-
- Any suggested fixes (if applicable)
|
|
24
|
-
|
|
25
|
-
## Security Measures
|
|
26
|
-
|
|
27
|
-
### NPM Publishing
|
|
28
|
-
|
|
29
|
-
- Releases are automated through GitHub Actions
|
|
30
|
-
- NPM tokens are stored as encrypted GitHub secrets
|
|
31
|
-
- Only maintainers with write access can trigger releases
|
|
32
|
-
- All releases include npm provenance for supply chain security
|
|
33
|
-
|
|
34
|
-
### Dependencies
|
|
35
|
-
|
|
36
|
-
- Dependencies are regularly updated via Dependabot
|
|
37
|
-
- Security advisories are monitored through GitHub's security features
|
|
38
|
-
- Minimal dependencies to reduce attack surface (only axios as runtime dependency)
|
|
39
|
-
|
|
40
|
-
### Code Review
|
|
41
|
-
|
|
42
|
-
- All changes require pull request reviews
|
|
43
|
-
- CI checks must pass before merging
|
|
44
|
-
- Type safety enforced through TypeScript
|
|
45
|
-
|
|
46
|
-
## Best Practices for Users
|
|
47
|
-
|
|
48
|
-
When using @boldvideo/bold-js:
|
|
49
|
-
|
|
50
|
-
1. **Keep your API keys secure**
|
|
51
|
-
- Never commit API keys to version control
|
|
52
|
-
- Use environment variables for API keys
|
|
53
|
-
- Rotate keys regularly
|
|
54
|
-
|
|
55
|
-
2. **Stay updated**
|
|
56
|
-
- Regularly update to the latest version
|
|
57
|
-
- Monitor security advisories
|
|
58
|
-
- Review the changelog for security updates
|
|
59
|
-
|
|
60
|
-
3. **Validate input**
|
|
61
|
-
- Always validate and sanitize user input before passing to the SDK
|
|
62
|
-
- Be cautious with data from external sources
|
|
63
|
-
|
|
64
|
-
## Acknowledgments
|
|
65
|
-
|
|
66
|
-
We appreciate responsible disclosure of security vulnerabilities and will acknowledge researchers who report issues (with their permission).
|
package/src/index.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export { createClient } from "lib/client";
|
|
2
|
-
export type {
|
|
3
|
-
Video,
|
|
4
|
-
VideoAttachment,
|
|
5
|
-
VideoDownloadUrls,
|
|
6
|
-
VideoSubtitles,
|
|
7
|
-
VideoTranscript,
|
|
8
|
-
VideoMetadata,
|
|
9
|
-
Playlist,
|
|
10
|
-
MenuItem,
|
|
11
|
-
Settings,
|
|
12
|
-
Portal,
|
|
13
|
-
PortalDisplay,
|
|
14
|
-
PortalLayout,
|
|
15
|
-
PortalNavigation,
|
|
16
|
-
PortalTheme,
|
|
17
|
-
AssistantConfig,
|
|
18
|
-
ThemeConfig,
|
|
19
|
-
ThemeColors,
|
|
20
|
-
Account,
|
|
21
|
-
AccountAI,
|
|
22
|
-
} from "./lib/types";
|
package/src/lib/client.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import axios, { AxiosInstance } from "axios";
|
|
2
|
-
|
|
3
|
-
import { fetchVideo, fetchVideos, searchVideos, 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
|
-
if (!apiKey || typeof apiKey !== 'string') {
|
|
13
|
-
throw new Error('API key is missing or invalid');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const { debug } = options;
|
|
17
|
-
const apiClientOptions = {
|
|
18
|
-
baseURL: options.baseURL ?? "https://app.boldvideo.io/api/v1/",
|
|
19
|
-
headers: {
|
|
20
|
-
Authorization: apiKey,
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
let apiClient: AxiosInstance;
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
apiClient = axios.create(apiClientOptions);
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.error("Error creating API client", error);
|
|
30
|
-
throw error;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const userId = [...Array(30)]
|
|
34
|
-
.map(() => Math.random().toString(36)[2])
|
|
35
|
-
.join("");
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
settings: fetchSettings(apiClient),
|
|
39
|
-
videos: {
|
|
40
|
-
list: fetchVideos(apiClient),
|
|
41
|
-
get: fetchVideo(apiClient),
|
|
42
|
-
search: searchVideos(apiClient),
|
|
43
|
-
},
|
|
44
|
-
playlists: {
|
|
45
|
-
list: fetchPlaylists(apiClient),
|
|
46
|
-
get: fetchPlaylist(apiClient),
|
|
47
|
-
},
|
|
48
|
-
trackEvent: trackEvent(apiClient, userId, { debug }),
|
|
49
|
-
trackPageView: trackPageView(apiClient, userId, { debug }),
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export { createClient };
|
package/src/lib/fetchers.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
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>(
|
|
11
|
-
client: ApiClient,
|
|
12
|
-
url: string
|
|
13
|
-
): Promise<TResponse> {
|
|
14
|
-
try {
|
|
15
|
-
const res = await client.get(url);
|
|
16
|
-
if (res.status !== 200) {
|
|
17
|
-
throw new Error(`Unexpected response status: ${res.status}`);
|
|
18
|
-
}
|
|
19
|
-
return res.data as TResponse;
|
|
20
|
-
} catch (error) {
|
|
21
|
-
console.error(`Error fetching data from URL: ${url}`, error);
|
|
22
|
-
throw error;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function fetchSettings(client: ApiClient) {
|
|
27
|
-
return async (videoLimit = 12) => {
|
|
28
|
-
try {
|
|
29
|
-
return await get<Response<Settings>>(
|
|
30
|
-
client,
|
|
31
|
-
`settings?limit=${videoLimit}`
|
|
32
|
-
);
|
|
33
|
-
} catch (error) {
|
|
34
|
-
console.error(`Error fetching settings with limit: ${videoLimit}`, error);
|
|
35
|
-
throw error;
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function fetchVideos(client: ApiClient) {
|
|
41
|
-
return async (videoLimit = 12) => {
|
|
42
|
-
try {
|
|
43
|
-
return await get<Response<Video[]>>(
|
|
44
|
-
client,
|
|
45
|
-
`videos/latest?limit=${videoLimit}`
|
|
46
|
-
);
|
|
47
|
-
} catch (error) {
|
|
48
|
-
console.error(`Error fetching videos with limit: ${videoLimit}`, error);
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function searchVideos(client: ApiClient) {
|
|
55
|
-
return async (term: string) => {
|
|
56
|
-
try {
|
|
57
|
-
return await get<Response<Video[]>>(client, `videos?query=${term}`);
|
|
58
|
-
} catch (error) {
|
|
59
|
-
console.error(`Error searching for videos with term: ${term}`, error);
|
|
60
|
-
throw error;
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function fetchVideo(client: ApiClient) {
|
|
66
|
-
return async (id: string) => {
|
|
67
|
-
try {
|
|
68
|
-
return await get<Response<Video>>(client, `videos/${id}`);
|
|
69
|
-
} catch (error) {
|
|
70
|
-
console.error(`Error fetching video with ID: ${id}`, error);
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function fetchPlaylists(client: ApiClient) {
|
|
77
|
-
return async () => {
|
|
78
|
-
try {
|
|
79
|
-
return await get<Response<Playlist[]>>(client, "playlists");
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error("Error fetching playlists", error);
|
|
82
|
-
throw error;
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function fetchPlaylist(client: ApiClient) {
|
|
88
|
-
return async (id: string) => {
|
|
89
|
-
try {
|
|
90
|
-
return await get<Response<Playlist>>(client, `playlists/${id}`);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
console.error(`Error fetching playlist with ID: ${id}`, error);
|
|
93
|
-
throw error;
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
}
|
package/src/lib/tracking.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
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
|
-
currentTime?: 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?.currentTime || 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
|
-
currentTime: (event.target as HTMLMediaElement)?.currentTime || 0,
|
|
62
|
-
};
|
|
63
|
-
// debounce fast hitting timeupdate event
|
|
64
|
-
if (event.type == "timeupdate" || event.type == "time-update") {
|
|
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(event: { type: string }) {
|
|
93
|
-
switch (event.type) {
|
|
94
|
-
case "pause":
|
|
95
|
-
return "video_pause";
|
|
96
|
-
case "play":
|
|
97
|
-
return "video_resume";
|
|
98
|
-
case "loadedmetadata":
|
|
99
|
-
case "loaded-metadata":
|
|
100
|
-
return "video_load";
|
|
101
|
-
case "time-update":
|
|
102
|
-
case "timeupdate":
|
|
103
|
-
return "video_progress";
|
|
104
|
-
default:
|
|
105
|
-
return "unknown_event";
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function basicInfos() {
|
|
110
|
-
return {
|
|
111
|
-
url: location.href,
|
|
112
|
-
domain: location.hostname,
|
|
113
|
-
referrer: document.referrer || null,
|
|
114
|
-
deviceWidth: window.innerWidth,
|
|
115
|
-
userAgent: navigator.userAgent,
|
|
116
|
-
};
|
|
117
|
-
}
|
package/src/lib/types.ts
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
export type VideoAttachment = {
|
|
2
|
-
id: string;
|
|
3
|
-
title: string;
|
|
4
|
-
file_url: string;
|
|
5
|
-
file_size?: number;
|
|
6
|
-
file_type?: string;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export type VideoDownloadUrls = {
|
|
10
|
-
mp4?: string;
|
|
11
|
-
audio?: string;
|
|
12
|
-
legacy_mp4?: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type VideoSubtitles = {
|
|
16
|
-
label: string;
|
|
17
|
-
url: string;
|
|
18
|
-
engine?: string;
|
|
19
|
-
language: string;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type VideoTranscript = {
|
|
23
|
-
text: string;
|
|
24
|
-
json: any;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export type VideoMetadata = {
|
|
28
|
-
description: string;
|
|
29
|
-
title: string;
|
|
30
|
-
image: string | null;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export type Video = {
|
|
34
|
-
// Existing fields (kept as-is)
|
|
35
|
-
captions: string;
|
|
36
|
-
captions_label: string;
|
|
37
|
-
captions_lang: string;
|
|
38
|
-
description: string | null;
|
|
39
|
-
duration: number;
|
|
40
|
-
id: string;
|
|
41
|
-
imported_from: string | null;
|
|
42
|
-
legacy_video_url: string | null;
|
|
43
|
-
playback_id: string;
|
|
44
|
-
published_at: string;
|
|
45
|
-
stream_url: string;
|
|
46
|
-
teaser: string | null;
|
|
47
|
-
thumbnail: string;
|
|
48
|
-
title: string;
|
|
49
|
-
type: string;
|
|
50
|
-
|
|
51
|
-
// Fixed: meta_data should be an object, not array
|
|
52
|
-
meta_data: VideoMetadata;
|
|
53
|
-
|
|
54
|
-
// New: Chapters in WEBVTT format
|
|
55
|
-
chapters?: string;
|
|
56
|
-
|
|
57
|
-
// New: Attachments array
|
|
58
|
-
attachments?: VideoAttachment[];
|
|
59
|
-
|
|
60
|
-
// New: Call-to-action (can be null)
|
|
61
|
-
cta?: any | null;
|
|
62
|
-
|
|
63
|
-
// New: Download URLs object
|
|
64
|
-
download_urls?: VideoDownloadUrls;
|
|
65
|
-
|
|
66
|
-
// New: Internal ID
|
|
67
|
-
internal_id?: string;
|
|
68
|
-
|
|
69
|
-
// New: Playback speed
|
|
70
|
-
playback_speed?: number;
|
|
71
|
-
|
|
72
|
-
// New: Subtitles object
|
|
73
|
-
subtitles?: VideoSubtitles;
|
|
74
|
-
|
|
75
|
-
// New: Tags array
|
|
76
|
-
tags?: string[];
|
|
77
|
-
|
|
78
|
-
// New: Transcript object (replaces transcription)
|
|
79
|
-
transcript?: VideoTranscript;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
export type Playlist = {
|
|
83
|
-
description?: string;
|
|
84
|
-
id: string;
|
|
85
|
-
is_private: boolean;
|
|
86
|
-
title: string;
|
|
87
|
-
type: string;
|
|
88
|
-
videos: Video[];
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
export type MenuItem = {
|
|
92
|
-
icon: string;
|
|
93
|
-
is_ext: boolean;
|
|
94
|
-
label: string;
|
|
95
|
-
url: string;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
export type PortalDisplay = {
|
|
99
|
-
show_chapters: boolean;
|
|
100
|
-
show_transcripts: boolean;
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
export type AssistantConfig = {
|
|
104
|
-
headline: string;
|
|
105
|
-
subheadline: string;
|
|
106
|
-
suggestions: string[];
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export type PortalLayout = {
|
|
110
|
-
assistant_config: AssistantConfig | null;
|
|
111
|
-
show_playlists: boolean;
|
|
112
|
-
type: string;
|
|
113
|
-
videos_limit: number;
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
export type PortalNavigation = {
|
|
117
|
-
show_ai_search: boolean;
|
|
118
|
-
show_header: boolean;
|
|
119
|
-
show_search: boolean;
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
export type PortalTheme = {
|
|
123
|
-
background: string;
|
|
124
|
-
font_body: string;
|
|
125
|
-
font_header: string;
|
|
126
|
-
foreground: string;
|
|
127
|
-
logo_height: number;
|
|
128
|
-
logo_url: string;
|
|
129
|
-
logo_width: number;
|
|
130
|
-
primary: string;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
export type Portal = {
|
|
134
|
-
display: PortalDisplay;
|
|
135
|
-
layout: PortalLayout;
|
|
136
|
-
navigation: PortalNavigation;
|
|
137
|
-
theme: PortalTheme;
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
export type ThemeColors = {
|
|
141
|
-
background: string;
|
|
142
|
-
border: string;
|
|
143
|
-
card: string;
|
|
144
|
-
"card-foreground": string;
|
|
145
|
-
destructive: string;
|
|
146
|
-
"destructive-foreground": string;
|
|
147
|
-
foreground: string;
|
|
148
|
-
input: string;
|
|
149
|
-
muted: string;
|
|
150
|
-
"muted-foreground": string;
|
|
151
|
-
popover: string;
|
|
152
|
-
"popover-foreground": string;
|
|
153
|
-
primary: string;
|
|
154
|
-
"primary-foreground": string;
|
|
155
|
-
ring: string;
|
|
156
|
-
secondary: string;
|
|
157
|
-
"secondary-foreground": string;
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
export type ThemeConfig = {
|
|
161
|
-
dark: ThemeColors;
|
|
162
|
-
light: ThemeColors;
|
|
163
|
-
radius: string;
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
export type AccountAI = {
|
|
167
|
-
avatar_url: string;
|
|
168
|
-
enabled: boolean;
|
|
169
|
-
greeting: string;
|
|
170
|
-
name: string;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
export type Account = {
|
|
174
|
-
ai: AccountAI;
|
|
175
|
-
name: string;
|
|
176
|
-
slug: string;
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
export type Settings = {
|
|
180
|
-
// Existing top-level arrays
|
|
181
|
-
featured_playlists: Playlist[];
|
|
182
|
-
menu_items: MenuItem[];
|
|
183
|
-
|
|
184
|
-
// Existing flat AI fields (kept for backward compatibility)
|
|
185
|
-
ai_avatar: string;
|
|
186
|
-
ai_name: string;
|
|
187
|
-
ai_greeting?: string;
|
|
188
|
-
has_ai: boolean;
|
|
189
|
-
|
|
190
|
-
// New: Account object with nested AI config
|
|
191
|
-
account: Account;
|
|
192
|
-
|
|
193
|
-
// New: Top-level URL fields
|
|
194
|
-
favicon_url?: string;
|
|
195
|
-
logo_dark_url?: string;
|
|
196
|
-
logo_url?: string;
|
|
197
|
-
|
|
198
|
-
// Updated: meta_data with additional fields
|
|
199
|
-
meta_data: {
|
|
200
|
-
channel_name: string;
|
|
201
|
-
description: string;
|
|
202
|
-
image: string | null;
|
|
203
|
-
no_seo: boolean;
|
|
204
|
-
social_graph_image_url?: string;
|
|
205
|
-
title: string;
|
|
206
|
-
title_suffix: string;
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
// New: Portal object with all nested structures
|
|
210
|
-
portal: Portal;
|
|
211
|
-
|
|
212
|
-
// New: Theme configuration
|
|
213
|
-
theme_config: ThemeConfig;
|
|
214
|
-
|
|
215
|
-
// New: API version
|
|
216
|
-
version: string;
|
|
217
|
-
};
|
package/src/util/throttle.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
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
|
-
};
|