@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/dist/index.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
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
|
+
try {
|
|
48
|
+
const res = yield client.get(url);
|
|
49
|
+
if (res.status !== 200) {
|
|
50
|
+
throw new Error(`Unexpected response status: ${res.status}`);
|
|
51
|
+
}
|
|
52
|
+
return res.data;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(`Error fetching data from URL: ${url}`, error);
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function fetchSettings(client) {
|
|
60
|
+
return (videoLimit = 12) => __async(this, null, function* () {
|
|
61
|
+
try {
|
|
62
|
+
return yield get(
|
|
63
|
+
client,
|
|
64
|
+
`settings?limit=${videoLimit}`
|
|
65
|
+
);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error(`Error fetching settings with limit: ${videoLimit}`, error);
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function fetchVideos(client) {
|
|
73
|
+
return (videoLimit = 12) => __async(this, null, function* () {
|
|
74
|
+
try {
|
|
75
|
+
return yield get(
|
|
76
|
+
client,
|
|
77
|
+
`videos/latest?limit=${videoLimit}`
|
|
78
|
+
);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error(`Error fetching videos with limit: ${videoLimit}`, error);
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function searchVideos(client) {
|
|
86
|
+
return (term) => __async(this, null, function* () {
|
|
87
|
+
try {
|
|
88
|
+
return yield get(client, `videos?query=${term}`);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error(`Error searching for videos with term: ${term}`, error);
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function fetchVideo(client) {
|
|
96
|
+
return (id) => __async(this, null, function* () {
|
|
97
|
+
try {
|
|
98
|
+
return yield get(client, `videos/${id}`);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error(`Error fetching video with ID: ${id}`, error);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function fetchPlaylists(client) {
|
|
106
|
+
return () => __async(this, null, function* () {
|
|
107
|
+
try {
|
|
108
|
+
return yield get(client, "playlists");
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error("Error fetching playlists", error);
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function fetchPlaylist(client) {
|
|
116
|
+
return (id) => __async(this, null, function* () {
|
|
117
|
+
try {
|
|
118
|
+
return yield get(client, `playlists/${id}`);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error(`Error fetching playlist with ID: ${id}`, error);
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/util/throttle.ts
|
|
127
|
+
var throttle = (fn, delay) => {
|
|
128
|
+
let wait = false;
|
|
129
|
+
let timeout;
|
|
130
|
+
let cancelled = false;
|
|
131
|
+
return [
|
|
132
|
+
(...args) => {
|
|
133
|
+
if (cancelled)
|
|
134
|
+
return void 0;
|
|
135
|
+
if (wait)
|
|
136
|
+
return void 0;
|
|
137
|
+
const val = fn(...args);
|
|
138
|
+
wait = true;
|
|
139
|
+
timeout = window.setTimeout(() => {
|
|
140
|
+
wait = false;
|
|
141
|
+
}, delay);
|
|
142
|
+
return val;
|
|
143
|
+
},
|
|
144
|
+
() => {
|
|
145
|
+
cancelled = true;
|
|
146
|
+
clearTimeout(timeout);
|
|
147
|
+
}
|
|
148
|
+
];
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// src/lib/tracking.ts
|
|
152
|
+
function sendEvent(client, eventName, data, debug) {
|
|
153
|
+
const payload = {
|
|
154
|
+
n: eventName,
|
|
155
|
+
u: data.url,
|
|
156
|
+
usr: data.userId,
|
|
157
|
+
d: data.domain,
|
|
158
|
+
ua: data.userAgent,
|
|
159
|
+
w: data.deviceWidth,
|
|
160
|
+
vid: (data == null ? void 0 : data.videoId) || void 0,
|
|
161
|
+
vt: data.title,
|
|
162
|
+
vdur: (data == null ? void 0 : data.videoDuration) || void 0,
|
|
163
|
+
time: (data == null ? void 0 : data.currentTime) || void 0
|
|
164
|
+
};
|
|
165
|
+
if (debug)
|
|
166
|
+
console.log(`Bold SDK - Logging event '${eventName}'`, payload);
|
|
167
|
+
client.post("/event", payload);
|
|
168
|
+
}
|
|
169
|
+
var [throttledSendEvent] = throttle(sendEvent, 5e3);
|
|
170
|
+
function trackEvent(client, userId, options) {
|
|
171
|
+
return (video, event) => {
|
|
172
|
+
var _a;
|
|
173
|
+
const eventDetails = __spreadProps(__spreadValues({}, basicInfos()), {
|
|
174
|
+
userId,
|
|
175
|
+
videoId: video.id,
|
|
176
|
+
title: video.title,
|
|
177
|
+
videoDuration: video.duration,
|
|
178
|
+
currentTime: ((_a = event.target) == null ? void 0 : _a.currentTime) || 0
|
|
179
|
+
});
|
|
180
|
+
if (event.type == "timeupdate" || event.type == "time-update") {
|
|
181
|
+
throttledSendEvent(
|
|
182
|
+
client,
|
|
183
|
+
getEventName(event),
|
|
184
|
+
eventDetails,
|
|
185
|
+
options.debug
|
|
186
|
+
);
|
|
187
|
+
} else {
|
|
188
|
+
sendEvent(client, getEventName(event), eventDetails, options.debug);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function trackPageView(client, userId, options) {
|
|
193
|
+
return (title) => {
|
|
194
|
+
const eventDetails = __spreadProps(__spreadValues({}, basicInfos()), {
|
|
195
|
+
userId,
|
|
196
|
+
title
|
|
197
|
+
});
|
|
198
|
+
sendEvent(client, "page_view", eventDetails, options.debug);
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function getEventName(event) {
|
|
202
|
+
switch (event.type) {
|
|
203
|
+
case "pause":
|
|
204
|
+
return "video_pause";
|
|
205
|
+
case "play":
|
|
206
|
+
return "video_resume";
|
|
207
|
+
case "loadedmetadata":
|
|
208
|
+
case "loaded-metadata":
|
|
209
|
+
return "video_load";
|
|
210
|
+
case "time-update":
|
|
211
|
+
case "timeupdate":
|
|
212
|
+
return "video_progress";
|
|
213
|
+
default:
|
|
214
|
+
return "unknown_event";
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function basicInfos() {
|
|
218
|
+
return {
|
|
219
|
+
url: location.href,
|
|
220
|
+
domain: location.hostname,
|
|
221
|
+
referrer: document.referrer || null,
|
|
222
|
+
deviceWidth: window.innerWidth,
|
|
223
|
+
userAgent: navigator.userAgent
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/lib/client.ts
|
|
228
|
+
function createClient(apiKey, options = { debug: false }) {
|
|
229
|
+
var _a;
|
|
230
|
+
if (!apiKey || typeof apiKey !== "string") {
|
|
231
|
+
throw new Error("API key is missing or invalid");
|
|
232
|
+
}
|
|
233
|
+
const { debug } = options;
|
|
234
|
+
const apiClientOptions = {
|
|
235
|
+
baseURL: (_a = options.baseURL) != null ? _a : "https://app.boldvideo.io/api/v1/",
|
|
236
|
+
headers: {
|
|
237
|
+
Authorization: apiKey
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
let apiClient;
|
|
241
|
+
try {
|
|
242
|
+
apiClient = axios.create(apiClientOptions);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error("Error creating API client", error);
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
const userId = [...Array(30)].map(() => Math.random().toString(36)[2]).join("");
|
|
248
|
+
return {
|
|
249
|
+
settings: fetchSettings(apiClient),
|
|
250
|
+
videos: {
|
|
251
|
+
list: fetchVideos(apiClient),
|
|
252
|
+
get: fetchVideo(apiClient),
|
|
253
|
+
search: searchVideos(apiClient)
|
|
254
|
+
},
|
|
255
|
+
playlists: {
|
|
256
|
+
list: fetchPlaylists(apiClient),
|
|
257
|
+
get: fetchPlaylist(apiClient)
|
|
258
|
+
},
|
|
259
|
+
trackEvent: trackEvent(apiClient, userId, { debug }),
|
|
260
|
+
trackPageView: trackPageView(apiClient, userId, { debug })
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
export {
|
|
264
|
+
createClient
|
|
265
|
+
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Add portal.color_scheme to Settings Type Implementation Plan
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Add the `color_scheme` field to the `Portal` type in the bold-js SDK to support the Next.js starter's theme configuration feature. The API already returns this field in production; we need to update the TypeScript types to reflect this.
|
|
6
|
+
|
|
7
|
+
## Current State Analysis
|
|
8
|
+
|
|
9
|
+
**What exists:**
|
|
10
|
+
- Portal type defined at `src/lib/types.ts:133-138` with four nested objects (display, layout, navigation, theme)
|
|
11
|
+
- Portal type properly exported through `src/index.ts:12`
|
|
12
|
+
- Settings type at `src/lib/types.ts:210` includes the Portal object
|
|
13
|
+
- API already returning `portal.color_scheme` field (live in production)
|
|
14
|
+
- Next.js starter consuming this field but TypeScript doesn't know about it
|
|
15
|
+
|
|
16
|
+
**What's missing:**
|
|
17
|
+
- `color_scheme` field in the Portal type definition
|
|
18
|
+
- Type definition doesn't match the API response
|
|
19
|
+
- Consumers can't benefit from TypeScript autocomplete/validation for this field
|
|
20
|
+
|
|
21
|
+
**Key constraints:**
|
|
22
|
+
- Must maintain backward compatibility (field should be optional)
|
|
23
|
+
- Must follow existing optional field pattern (using `?` suffix)
|
|
24
|
+
- This will be the first string literal union type in Portal/Settings types
|
|
25
|
+
- Project uses changesets for version management
|
|
26
|
+
- No existing test suite to update
|
|
27
|
+
|
|
28
|
+
## Desired End State
|
|
29
|
+
|
|
30
|
+
After implementation:
|
|
31
|
+
1. Portal type includes `color_scheme?: 'toggle' | 'light' | 'dark'` field
|
|
32
|
+
2. TypeScript consumers get autocomplete for the three valid values
|
|
33
|
+
3. Type declarations are rebuilt and available in `dist/index.d.ts`
|
|
34
|
+
4. Version bumped to 0.7.0 with appropriate changelog entry
|
|
35
|
+
5. Changes published to npm
|
|
36
|
+
|
|
37
|
+
**Verification:**
|
|
38
|
+
- Portal type includes the new field with correct union type
|
|
39
|
+
- Build succeeds without errors: `pnpm run build`
|
|
40
|
+
- Type checking passes: `pnpm run lint`
|
|
41
|
+
- Compiled type declarations in `dist/index.d.ts` include the field
|
|
42
|
+
- Changeset created for version 0.7.0
|
|
43
|
+
|
|
44
|
+
## What We're NOT Doing
|
|
45
|
+
|
|
46
|
+
- Not adding runtime validation for the color_scheme values
|
|
47
|
+
- Not adding tests (no test framework exists in this project)
|
|
48
|
+
- Not modifying the API or fetcher functions (they already work correctly)
|
|
49
|
+
- Not changing any other types or fields
|
|
50
|
+
- Not adding documentation beyond the changelog entry
|
|
51
|
+
- Not updating example code or README (out of scope for this ticket)
|
|
52
|
+
|
|
53
|
+
## Implementation Approach
|
|
54
|
+
|
|
55
|
+
This is a single-phase implementation because:
|
|
56
|
+
1. It's a one-line type change
|
|
57
|
+
2. No runtime code changes required
|
|
58
|
+
3. No migration needed (field is optional)
|
|
59
|
+
4. Build and publish can happen together
|
|
60
|
+
|
|
61
|
+
The approach:
|
|
62
|
+
1. Add the `color_scheme` field to Portal type
|
|
63
|
+
2. Create changeset for version bump
|
|
64
|
+
3. Build to verify TypeScript compilation
|
|
65
|
+
4. Commit changes following git conventions
|
|
66
|
+
|
|
67
|
+
## Phase 1: Add color_scheme Field
|
|
68
|
+
|
|
69
|
+
### Overview
|
|
70
|
+
|
|
71
|
+
Add the `color_scheme` field to the Portal type definition following the SDK's established patterns for optional fields.
|
|
72
|
+
|
|
73
|
+
### Changes Required
|
|
74
|
+
|
|
75
|
+
#### 1. Update Portal Type Definition
|
|
76
|
+
|
|
77
|
+
**File**: `src/lib/types.ts`
|
|
78
|
+
**Line**: 133-138
|
|
79
|
+
**Changes**: Add `color_scheme` as the first field in the Portal type
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
export type Portal = {
|
|
83
|
+
color_scheme?: 'toggle' | 'light' | 'dark';
|
|
84
|
+
display: PortalDisplay;
|
|
85
|
+
layout: PortalLayout;
|
|
86
|
+
navigation: PortalNavigation;
|
|
87
|
+
theme: PortalTheme;
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Rationale:**
|
|
92
|
+
- Optional field (`?`) maintains backward compatibility
|
|
93
|
+
- String literal union type provides type safety and autocomplete
|
|
94
|
+
- Positioned first in the type as it's a top-level configuration (similar to how top-level fields appear before nested objects in Settings)
|
|
95
|
+
- Follows TypeScript best practices for union types
|
|
96
|
+
|
|
97
|
+
#### 2. Create Changeset
|
|
98
|
+
|
|
99
|
+
**Command**: `pnpm changeset`
|
|
100
|
+
|
|
101
|
+
**Changeset content:**
|
|
102
|
+
```markdown
|
|
103
|
+
---
|
|
104
|
+
"@boldvideo/bold-js": minor
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
Add `color_scheme` field to Portal type (BOLD-759)
|
|
108
|
+
|
|
109
|
+
Added optional `color_scheme` field to the Portal type to support theme configuration:
|
|
110
|
+
- `'toggle'`: Show theme toggle, allow user to switch between light/dark
|
|
111
|
+
- `'light'`: Force light mode, hide theme toggle
|
|
112
|
+
- `'dark'`: Force dark mode, hide theme toggle
|
|
113
|
+
|
|
114
|
+
This field is already returned by the API and consumed by the Next.js starter. The type definition now matches the API response.
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Why minor version:**
|
|
118
|
+
- Adds new functionality (new field) to the public API
|
|
119
|
+
- Follows semantic versioning: backward compatible API addition
|
|
120
|
+
- Matches pattern from CHANGELOG.md (v0.5.0 added `ai_greeting`, v0.6.0 added portal object)
|
|
121
|
+
|
|
122
|
+
### Success Criteria
|
|
123
|
+
|
|
124
|
+
#### Automated Verification:
|
|
125
|
+
|
|
126
|
+
- [x] Portal type includes `color_scheme?: 'toggle' | 'light' | 'dark'` field in correct position
|
|
127
|
+
- [x] Build completes successfully: `pnpm run build`
|
|
128
|
+
- [x] Type checking passes: `pnpm run lint` (runs `tsc`)
|
|
129
|
+
- [x] Compiled declarations in `dist/index.d.ts` include the new field
|
|
130
|
+
- [x] Git status shows only expected changes: `src/lib/types.ts` and changeset file
|
|
131
|
+
|
|
132
|
+
#### Manual Verification:
|
|
133
|
+
|
|
134
|
+
- [x] Type change makes semantic sense (three valid theme modes)
|
|
135
|
+
- [x] Field placement is logical within the Portal type structure
|
|
136
|
+
- [x] Changeset message accurately describes the change
|
|
137
|
+
- [x] No unintended changes to other types
|
|
138
|
+
|
|
139
|
+
## Phase 2: Version and Publish
|
|
140
|
+
|
|
141
|
+
### Overview
|
|
142
|
+
|
|
143
|
+
Use changesets to version the package and publish to npm.
|
|
144
|
+
|
|
145
|
+
### Changes Required
|
|
146
|
+
|
|
147
|
+
#### 1. Version the Package
|
|
148
|
+
|
|
149
|
+
**Command**: `pnpm changeset version`
|
|
150
|
+
|
|
151
|
+
**Expected changes:**
|
|
152
|
+
- `package.json` version bumped from `0.6.1` to `0.7.0`
|
|
153
|
+
- `CHANGELOG.md` updated with changeset content under `## 0.7.0`
|
|
154
|
+
- Changeset file consumed and removed
|
|
155
|
+
|
|
156
|
+
#### 2. Build for Distribution
|
|
157
|
+
|
|
158
|
+
**Command**: `pnpm run build`
|
|
159
|
+
|
|
160
|
+
**Expected output:**
|
|
161
|
+
- `dist/index.js` (ESM)
|
|
162
|
+
- `dist/index.cjs` (CommonJS)
|
|
163
|
+
- `dist/index.d.ts` (TypeScript declarations)
|
|
164
|
+
|
|
165
|
+
The build must succeed and include the new `color_scheme` field in type declarations.
|
|
166
|
+
|
|
167
|
+
#### 3. Publish to npm
|
|
168
|
+
|
|
169
|
+
**Command**: `pnpm changeset publish`
|
|
170
|
+
|
|
171
|
+
**Expected behavior:**
|
|
172
|
+
- Package tagged as `v0.7.0`
|
|
173
|
+
- Published to npm registry with public access
|
|
174
|
+
- Git tag created for the release
|
|
175
|
+
|
|
176
|
+
### Success Criteria
|
|
177
|
+
|
|
178
|
+
#### Automated Verification:
|
|
179
|
+
|
|
180
|
+
- [x] `package.json` shows version `0.7.0`
|
|
181
|
+
- [x] `CHANGELOG.md` includes entry for version `0.7.0` with BOLD-759 reference
|
|
182
|
+
- [x] Build completes without errors: `pnpm run build`
|
|
183
|
+
- [x] Type declarations in `dist/index.d.ts` include `color_scheme` field
|
|
184
|
+
- [ ] Package published successfully to npm (requires manual 2FA)
|
|
185
|
+
- [ ] Git tag `v0.7.0` created (created by publish command)
|
|
186
|
+
|
|
187
|
+
#### Manual Verification:
|
|
188
|
+
|
|
189
|
+
- [ ] Published package on npm shows correct version (pending publish)
|
|
190
|
+
- [ ] Type declarations are available to consumers (pending publish)
|
|
191
|
+
- [x] CHANGELOG.md entry is accurate and well-formatted
|
|
192
|
+
- [x] All files committed with appropriate git message (following project conventions)
|
|
193
|
+
|
|
194
|
+
## Testing Strategy
|
|
195
|
+
|
|
196
|
+
### Unit Tests
|
|
197
|
+
|
|
198
|
+
Not applicable - this project has no test suite.
|
|
199
|
+
|
|
200
|
+
### Integration Tests
|
|
201
|
+
|
|
202
|
+
Not applicable - no test framework configured.
|
|
203
|
+
|
|
204
|
+
### Manual Testing Steps
|
|
205
|
+
|
|
206
|
+
Since this is a type-only change, manual testing focuses on verification that TypeScript types work correctly:
|
|
207
|
+
|
|
208
|
+
1. **Verify type compilation:**
|
|
209
|
+
```bash
|
|
210
|
+
pnpm run lint # Runs tsc to check types
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
2. **Verify build output:**
|
|
214
|
+
```bash
|
|
215
|
+
pnpm run build
|
|
216
|
+
# Check that dist/index.d.ts contains the new field
|
|
217
|
+
cat dist/index.d.ts | grep -A 5 "type Portal"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
3. **Verify in a consumer project (optional):**
|
|
221
|
+
- Link the package locally: `pnpm link @boldvideo/bold-js`
|
|
222
|
+
- In a TypeScript project, import and use the type:
|
|
223
|
+
```typescript
|
|
224
|
+
import { createClient } from '@boldvideo/bold-js';
|
|
225
|
+
|
|
226
|
+
const bold = createClient('api-key');
|
|
227
|
+
const settings = await bold.settings();
|
|
228
|
+
|
|
229
|
+
// TypeScript should autocomplete 'toggle' | 'light' | 'dark'
|
|
230
|
+
const colorScheme = settings.data.portal.color_scheme;
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Performance Considerations
|
|
234
|
+
|
|
235
|
+
None - this is a compile-time only change. No runtime performance impact.
|
|
236
|
+
|
|
237
|
+
## Migration Notes
|
|
238
|
+
|
|
239
|
+
No migration required. The field is optional and backward compatible:
|
|
240
|
+
|
|
241
|
+
**For existing consumers:**
|
|
242
|
+
- Upgrading to 0.7.0 requires no code changes
|
|
243
|
+
- The `color_scheme` field will be `undefined` if not returned by the API
|
|
244
|
+
- Type checking will help consumers handle the field correctly if they choose to use it
|
|
245
|
+
|
|
246
|
+
**API compatibility:**
|
|
247
|
+
- API already returns this field (in production since commits b31396f, b42c9fe, b9d8ee8)
|
|
248
|
+
- Older SDK versions will ignore the field (no breaking changes)
|
|
249
|
+
- Newer SDK versions provide type safety for the field
|
|
250
|
+
|
|
251
|
+
## References
|
|
252
|
+
|
|
253
|
+
- Original ticket: [BOLD-759](https://linear.app/boldvideo/issue/BOLD-759/add-portalcolor-scheme-to-settings-type-in-bold-js-sdk)
|
|
254
|
+
- Research document: `thoughts/shared/research/2025-10-22-BOLD-759-portal-color-scheme-settings.md`
|
|
255
|
+
- Portal type definition: `src/lib/types.ts:133-138`
|
|
256
|
+
- Settings type definition: `src/lib/types.ts:179-217`
|
|
257
|
+
- Type exports: `src/index.ts:12`
|
|
258
|
+
|
|
259
|
+
## Implementation Notes
|
|
260
|
+
|
|
261
|
+
**Field positioning rationale:**
|
|
262
|
+
The `color_scheme` field should be placed first in the Portal type because:
|
|
263
|
+
1. It's a direct configuration value (not a nested object like display/layout/navigation/theme)
|
|
264
|
+
2. Follows the pattern in Settings type where direct values come before nested objects
|
|
265
|
+
3. Makes the type more readable - simple values first, complex nested types after
|
|
266
|
+
|
|
267
|
+
**Union type introduction:**
|
|
268
|
+
This is the first string literal union type in Portal/Settings types. While the existing types use simple primitives (`string`, `boolean`, `number`), using a union type here provides:
|
|
269
|
+
1. Type safety - prevents invalid values
|
|
270
|
+
2. Better developer experience - autocomplete in IDEs
|
|
271
|
+
3. Self-documenting code - valid values are in the type definition
|
|
272
|
+
4. Follows TypeScript best practices for known string values
|