soundcloud-api-ts 1.11.1 → 1.12.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/dist/index.js CHANGED
@@ -1,257 +1,269 @@
1
1
  'use strict';
2
2
 
3
- var chunkGTSVJU3P_js = require('./chunk-GTSVJU3P.js');
3
+ var chunkD7AF372V_js = require('./chunk-D7AF372V.js');
4
4
  var chunkMLVA534Z_js = require('./chunk-MLVA534Z.js');
5
5
 
6
6
 
7
7
 
8
+ Object.defineProperty(exports, "IMPLEMENTED_OPERATIONS", {
9
+ enumerable: true,
10
+ get: function () { return chunkD7AF372V_js.IMPLEMENTED_OPERATIONS; }
11
+ });
12
+ Object.defineProperty(exports, "InFlightDeduper", {
13
+ enumerable: true,
14
+ get: function () { return chunkD7AF372V_js.InFlightDeduper; }
15
+ });
16
+ Object.defineProperty(exports, "RawClient", {
17
+ enumerable: true,
18
+ get: function () { return chunkD7AF372V_js.RawClient; }
19
+ });
8
20
  Object.defineProperty(exports, "SoundCloudClient", {
9
21
  enumerable: true,
10
- get: function () { return chunkGTSVJU3P_js.SoundCloudClient; }
22
+ get: function () { return chunkD7AF372V_js.SoundCloudClient; }
11
23
  });
12
24
  Object.defineProperty(exports, "createPlaylist", {
13
25
  enumerable: true,
14
- get: function () { return chunkGTSVJU3P_js.createPlaylist; }
26
+ get: function () { return chunkD7AF372V_js.createPlaylist; }
15
27
  });
16
28
  Object.defineProperty(exports, "createTrackComment", {
17
29
  enumerable: true,
18
- get: function () { return chunkGTSVJU3P_js.createTrackComment; }
30
+ get: function () { return chunkD7AF372V_js.createTrackComment; }
19
31
  });
20
32
  Object.defineProperty(exports, "deletePlaylist", {
21
33
  enumerable: true,
22
- get: function () { return chunkGTSVJU3P_js.deletePlaylist; }
34
+ get: function () { return chunkD7AF372V_js.deletePlaylist; }
23
35
  });
24
36
  Object.defineProperty(exports, "deleteTrack", {
25
37
  enumerable: true,
26
- get: function () { return chunkGTSVJU3P_js.deleteTrack; }
38
+ get: function () { return chunkD7AF372V_js.deleteTrack; }
27
39
  });
28
40
  Object.defineProperty(exports, "fetchAll", {
29
41
  enumerable: true,
30
- get: function () { return chunkGTSVJU3P_js.fetchAll; }
42
+ get: function () { return chunkD7AF372V_js.fetchAll; }
31
43
  });
32
44
  Object.defineProperty(exports, "followUser", {
33
45
  enumerable: true,
34
- get: function () { return chunkGTSVJU3P_js.followUser; }
46
+ get: function () { return chunkD7AF372V_js.followUser; }
35
47
  });
36
48
  Object.defineProperty(exports, "generateCodeChallenge", {
37
49
  enumerable: true,
38
- get: function () { return chunkGTSVJU3P_js.generateCodeChallenge; }
50
+ get: function () { return chunkD7AF372V_js.generateCodeChallenge; }
39
51
  });
40
52
  Object.defineProperty(exports, "generateCodeVerifier", {
41
53
  enumerable: true,
42
- get: function () { return chunkGTSVJU3P_js.generateCodeVerifier; }
54
+ get: function () { return chunkD7AF372V_js.generateCodeVerifier; }
43
55
  });
44
56
  Object.defineProperty(exports, "getAuthorizationUrl", {
45
57
  enumerable: true,
46
- get: function () { return chunkGTSVJU3P_js.getAuthorizationUrl; }
58
+ get: function () { return chunkD7AF372V_js.getAuthorizationUrl; }
47
59
  });
48
60
  Object.defineProperty(exports, "getClientToken", {
49
61
  enumerable: true,
50
- get: function () { return chunkGTSVJU3P_js.getClientToken; }
62
+ get: function () { return chunkD7AF372V_js.getClientToken; }
51
63
  });
52
64
  Object.defineProperty(exports, "getFollowers", {
53
65
  enumerable: true,
54
- get: function () { return chunkGTSVJU3P_js.getFollowers; }
66
+ get: function () { return chunkD7AF372V_js.getFollowers; }
55
67
  });
56
68
  Object.defineProperty(exports, "getFollowings", {
57
69
  enumerable: true,
58
- get: function () { return chunkGTSVJU3P_js.getFollowings; }
70
+ get: function () { return chunkD7AF372V_js.getFollowings; }
59
71
  });
60
72
  Object.defineProperty(exports, "getMe", {
61
73
  enumerable: true,
62
- get: function () { return chunkGTSVJU3P_js.getMe; }
74
+ get: function () { return chunkD7AF372V_js.getMe; }
63
75
  });
64
76
  Object.defineProperty(exports, "getMeActivities", {
65
77
  enumerable: true,
66
- get: function () { return chunkGTSVJU3P_js.getMeActivities; }
78
+ get: function () { return chunkD7AF372V_js.getMeActivities; }
67
79
  });
68
80
  Object.defineProperty(exports, "getMeActivitiesOwn", {
69
81
  enumerable: true,
70
- get: function () { return chunkGTSVJU3P_js.getMeActivitiesOwn; }
82
+ get: function () { return chunkD7AF372V_js.getMeActivitiesOwn; }
71
83
  });
72
84
  Object.defineProperty(exports, "getMeActivitiesTracks", {
73
85
  enumerable: true,
74
- get: function () { return chunkGTSVJU3P_js.getMeActivitiesTracks; }
86
+ get: function () { return chunkD7AF372V_js.getMeActivitiesTracks; }
75
87
  });
76
88
  Object.defineProperty(exports, "getMeFollowers", {
77
89
  enumerable: true,
78
- get: function () { return chunkGTSVJU3P_js.getMeFollowers; }
90
+ get: function () { return chunkD7AF372V_js.getMeFollowers; }
79
91
  });
80
92
  Object.defineProperty(exports, "getMeFollowings", {
81
93
  enumerable: true,
82
- get: function () { return chunkGTSVJU3P_js.getMeFollowings; }
94
+ get: function () { return chunkD7AF372V_js.getMeFollowings; }
83
95
  });
84
96
  Object.defineProperty(exports, "getMeFollowingsTracks", {
85
97
  enumerable: true,
86
- get: function () { return chunkGTSVJU3P_js.getMeFollowingsTracks; }
98
+ get: function () { return chunkD7AF372V_js.getMeFollowingsTracks; }
87
99
  });
88
100
  Object.defineProperty(exports, "getMeLikesPlaylists", {
89
101
  enumerable: true,
90
- get: function () { return chunkGTSVJU3P_js.getMeLikesPlaylists; }
102
+ get: function () { return chunkD7AF372V_js.getMeLikesPlaylists; }
91
103
  });
92
104
  Object.defineProperty(exports, "getMeLikesTracks", {
93
105
  enumerable: true,
94
- get: function () { return chunkGTSVJU3P_js.getMeLikesTracks; }
106
+ get: function () { return chunkD7AF372V_js.getMeLikesTracks; }
95
107
  });
96
108
  Object.defineProperty(exports, "getMePlaylists", {
97
109
  enumerable: true,
98
- get: function () { return chunkGTSVJU3P_js.getMePlaylists; }
110
+ get: function () { return chunkD7AF372V_js.getMePlaylists; }
99
111
  });
100
112
  Object.defineProperty(exports, "getMeTracks", {
101
113
  enumerable: true,
102
- get: function () { return chunkGTSVJU3P_js.getMeTracks; }
114
+ get: function () { return chunkD7AF372V_js.getMeTracks; }
103
115
  });
104
116
  Object.defineProperty(exports, "getPlaylist", {
105
117
  enumerable: true,
106
- get: function () { return chunkGTSVJU3P_js.getPlaylist; }
118
+ get: function () { return chunkD7AF372V_js.getPlaylist; }
107
119
  });
108
120
  Object.defineProperty(exports, "getPlaylistReposts", {
109
121
  enumerable: true,
110
- get: function () { return chunkGTSVJU3P_js.getPlaylistReposts; }
122
+ get: function () { return chunkD7AF372V_js.getPlaylistReposts; }
111
123
  });
112
124
  Object.defineProperty(exports, "getPlaylistTracks", {
113
125
  enumerable: true,
114
- get: function () { return chunkGTSVJU3P_js.getPlaylistTracks; }
126
+ get: function () { return chunkD7AF372V_js.getPlaylistTracks; }
115
127
  });
116
128
  Object.defineProperty(exports, "getRelatedTracks", {
117
129
  enumerable: true,
118
- get: function () { return chunkGTSVJU3P_js.getRelatedTracks; }
130
+ get: function () { return chunkD7AF372V_js.getRelatedTracks; }
119
131
  });
120
132
  Object.defineProperty(exports, "getSoundCloudWidgetUrl", {
121
133
  enumerable: true,
122
- get: function () { return chunkGTSVJU3P_js.getSoundCloudWidgetUrl; }
134
+ get: function () { return chunkD7AF372V_js.getSoundCloudWidgetUrl; }
123
135
  });
124
136
  Object.defineProperty(exports, "getTrack", {
125
137
  enumerable: true,
126
- get: function () { return chunkGTSVJU3P_js.getTrack; }
138
+ get: function () { return chunkD7AF372V_js.getTrack; }
127
139
  });
128
140
  Object.defineProperty(exports, "getTrackComments", {
129
141
  enumerable: true,
130
- get: function () { return chunkGTSVJU3P_js.getTrackComments; }
142
+ get: function () { return chunkD7AF372V_js.getTrackComments; }
131
143
  });
132
144
  Object.defineProperty(exports, "getTrackLikes", {
133
145
  enumerable: true,
134
- get: function () { return chunkGTSVJU3P_js.getTrackLikes; }
146
+ get: function () { return chunkD7AF372V_js.getTrackLikes; }
135
147
  });
136
148
  Object.defineProperty(exports, "getTrackReposts", {
137
149
  enumerable: true,
138
- get: function () { return chunkGTSVJU3P_js.getTrackReposts; }
150
+ get: function () { return chunkD7AF372V_js.getTrackReposts; }
139
151
  });
140
152
  Object.defineProperty(exports, "getTrackStreams", {
141
153
  enumerable: true,
142
- get: function () { return chunkGTSVJU3P_js.getTrackStreams; }
154
+ get: function () { return chunkD7AF372V_js.getTrackStreams; }
143
155
  });
144
156
  Object.defineProperty(exports, "getUser", {
145
157
  enumerable: true,
146
- get: function () { return chunkGTSVJU3P_js.getUser; }
158
+ get: function () { return chunkD7AF372V_js.getUser; }
147
159
  });
148
160
  Object.defineProperty(exports, "getUserLikesPlaylists", {
149
161
  enumerable: true,
150
- get: function () { return chunkGTSVJU3P_js.getUserLikesPlaylists; }
162
+ get: function () { return chunkD7AF372V_js.getUserLikesPlaylists; }
151
163
  });
152
164
  Object.defineProperty(exports, "getUserLikesTracks", {
153
165
  enumerable: true,
154
- get: function () { return chunkGTSVJU3P_js.getUserLikesTracks; }
166
+ get: function () { return chunkD7AF372V_js.getUserLikesTracks; }
155
167
  });
156
168
  Object.defineProperty(exports, "getUserPlaylists", {
157
169
  enumerable: true,
158
- get: function () { return chunkGTSVJU3P_js.getUserPlaylists; }
170
+ get: function () { return chunkD7AF372V_js.getUserPlaylists; }
159
171
  });
160
172
  Object.defineProperty(exports, "getUserToken", {
161
173
  enumerable: true,
162
- get: function () { return chunkGTSVJU3P_js.getUserToken; }
174
+ get: function () { return chunkD7AF372V_js.getUserToken; }
163
175
  });
164
176
  Object.defineProperty(exports, "getUserTracks", {
165
177
  enumerable: true,
166
- get: function () { return chunkGTSVJU3P_js.getUserTracks; }
178
+ get: function () { return chunkD7AF372V_js.getUserTracks; }
167
179
  });
168
180
  Object.defineProperty(exports, "getUserWebProfiles", {
169
181
  enumerable: true,
170
- get: function () { return chunkGTSVJU3P_js.getUserWebProfiles; }
182
+ get: function () { return chunkD7AF372V_js.getUserWebProfiles; }
171
183
  });
172
184
  Object.defineProperty(exports, "likePlaylist", {
173
185
  enumerable: true,
174
- get: function () { return chunkGTSVJU3P_js.likePlaylist; }
186
+ get: function () { return chunkD7AF372V_js.likePlaylist; }
175
187
  });
176
188
  Object.defineProperty(exports, "likeTrack", {
177
189
  enumerable: true,
178
- get: function () { return chunkGTSVJU3P_js.likeTrack; }
190
+ get: function () { return chunkD7AF372V_js.likeTrack; }
179
191
  });
180
192
  Object.defineProperty(exports, "paginate", {
181
193
  enumerable: true,
182
- get: function () { return chunkGTSVJU3P_js.paginate; }
194
+ get: function () { return chunkD7AF372V_js.paginate; }
183
195
  });
184
196
  Object.defineProperty(exports, "paginateItems", {
185
197
  enumerable: true,
186
- get: function () { return chunkGTSVJU3P_js.paginateItems; }
198
+ get: function () { return chunkD7AF372V_js.paginateItems; }
187
199
  });
188
200
  Object.defineProperty(exports, "refreshUserToken", {
189
201
  enumerable: true,
190
- get: function () { return chunkGTSVJU3P_js.refreshUserToken; }
202
+ get: function () { return chunkD7AF372V_js.refreshUserToken; }
191
203
  });
192
204
  Object.defineProperty(exports, "repostPlaylist", {
193
205
  enumerable: true,
194
- get: function () { return chunkGTSVJU3P_js.repostPlaylist; }
206
+ get: function () { return chunkD7AF372V_js.repostPlaylist; }
195
207
  });
196
208
  Object.defineProperty(exports, "repostTrack", {
197
209
  enumerable: true,
198
- get: function () { return chunkGTSVJU3P_js.repostTrack; }
210
+ get: function () { return chunkD7AF372V_js.repostTrack; }
199
211
  });
200
212
  Object.defineProperty(exports, "resolveUrl", {
201
213
  enumerable: true,
202
- get: function () { return chunkGTSVJU3P_js.resolveUrl; }
214
+ get: function () { return chunkD7AF372V_js.resolveUrl; }
203
215
  });
204
216
  Object.defineProperty(exports, "scFetch", {
205
217
  enumerable: true,
206
- get: function () { return chunkGTSVJU3P_js.scFetch; }
218
+ get: function () { return chunkD7AF372V_js.scFetch; }
207
219
  });
208
220
  Object.defineProperty(exports, "scFetchUrl", {
209
221
  enumerable: true,
210
- get: function () { return chunkGTSVJU3P_js.scFetchUrl; }
222
+ get: function () { return chunkD7AF372V_js.scFetchUrl; }
211
223
  });
212
224
  Object.defineProperty(exports, "searchPlaylists", {
213
225
  enumerable: true,
214
- get: function () { return chunkGTSVJU3P_js.searchPlaylists; }
226
+ get: function () { return chunkD7AF372V_js.searchPlaylists; }
215
227
  });
216
228
  Object.defineProperty(exports, "searchTracks", {
217
229
  enumerable: true,
218
- get: function () { return chunkGTSVJU3P_js.searchTracks; }
230
+ get: function () { return chunkD7AF372V_js.searchTracks; }
219
231
  });
220
232
  Object.defineProperty(exports, "searchUsers", {
221
233
  enumerable: true,
222
- get: function () { return chunkGTSVJU3P_js.searchUsers; }
234
+ get: function () { return chunkD7AF372V_js.searchUsers; }
223
235
  });
224
236
  Object.defineProperty(exports, "signOut", {
225
237
  enumerable: true,
226
- get: function () { return chunkGTSVJU3P_js.signOut; }
238
+ get: function () { return chunkD7AF372V_js.signOut; }
227
239
  });
228
240
  Object.defineProperty(exports, "unfollowUser", {
229
241
  enumerable: true,
230
- get: function () { return chunkGTSVJU3P_js.unfollowUser; }
242
+ get: function () { return chunkD7AF372V_js.unfollowUser; }
231
243
  });
232
244
  Object.defineProperty(exports, "unlikePlaylist", {
233
245
  enumerable: true,
234
- get: function () { return chunkGTSVJU3P_js.unlikePlaylist; }
246
+ get: function () { return chunkD7AF372V_js.unlikePlaylist; }
235
247
  });
236
248
  Object.defineProperty(exports, "unlikeTrack", {
237
249
  enumerable: true,
238
- get: function () { return chunkGTSVJU3P_js.unlikeTrack; }
250
+ get: function () { return chunkD7AF372V_js.unlikeTrack; }
239
251
  });
240
252
  Object.defineProperty(exports, "unrepostPlaylist", {
241
253
  enumerable: true,
242
- get: function () { return chunkGTSVJU3P_js.unrepostPlaylist; }
254
+ get: function () { return chunkD7AF372V_js.unrepostPlaylist; }
243
255
  });
244
256
  Object.defineProperty(exports, "unrepostTrack", {
245
257
  enumerable: true,
246
- get: function () { return chunkGTSVJU3P_js.unrepostTrack; }
258
+ get: function () { return chunkD7AF372V_js.unrepostTrack; }
247
259
  });
248
260
  Object.defineProperty(exports, "updatePlaylist", {
249
261
  enumerable: true,
250
- get: function () { return chunkGTSVJU3P_js.updatePlaylist; }
262
+ get: function () { return chunkD7AF372V_js.updatePlaylist; }
251
263
  });
252
264
  Object.defineProperty(exports, "updateTrack", {
253
265
  enumerable: true,
254
- get: function () { return chunkGTSVJU3P_js.updateTrack; }
266
+ get: function () { return chunkD7AF372V_js.updateTrack; }
255
267
  });
256
268
  Object.defineProperty(exports, "SoundCloudError", {
257
269
  enumerable: true,
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { SoundCloudClient, createPlaylist, createTrackComment, deletePlaylist, deleteTrack, fetchAll, followUser, generateCodeChallenge, generateCodeVerifier, getAuthorizationUrl, getClientToken, getFollowers, getFollowings, getMe, getMeActivities, getMeActivitiesOwn, getMeActivitiesTracks, getMeFollowers, getMeFollowings, getMeFollowingsTracks, getMeLikesPlaylists, getMeLikesTracks, getMePlaylists, getMeTracks, getPlaylist, getPlaylistReposts, getPlaylistTracks, getRelatedTracks, getSoundCloudWidgetUrl, getTrack, getTrackComments, getTrackLikes, getTrackReposts, getTrackStreams, getUser, getUserLikesPlaylists, getUserLikesTracks, getUserPlaylists, getUserToken, getUserTracks, getUserWebProfiles, likePlaylist, likeTrack, paginate, paginateItems, refreshUserToken, repostPlaylist, repostTrack, resolveUrl, scFetch, scFetchUrl, searchPlaylists, searchTracks, searchUsers, signOut, unfollowUser, unlikePlaylist, unlikeTrack, unrepostPlaylist, unrepostTrack, updatePlaylist, updateTrack } from './chunk-DGZEPXIQ.mjs';
1
+ export { IMPLEMENTED_OPERATIONS, InFlightDeduper, RawClient, SoundCloudClient, createPlaylist, createTrackComment, deletePlaylist, deleteTrack, fetchAll, followUser, generateCodeChallenge, generateCodeVerifier, getAuthorizationUrl, getClientToken, getFollowers, getFollowings, getMe, getMeActivities, getMeActivitiesOwn, getMeActivitiesTracks, getMeFollowers, getMeFollowings, getMeFollowingsTracks, getMeLikesPlaylists, getMeLikesTracks, getMePlaylists, getMeTracks, getPlaylist, getPlaylistReposts, getPlaylistTracks, getRelatedTracks, getSoundCloudWidgetUrl, getTrack, getTrackComments, getTrackLikes, getTrackReposts, getTrackStreams, getUser, getUserLikesPlaylists, getUserLikesTracks, getUserPlaylists, getUserToken, getUserTracks, getUserWebProfiles, likePlaylist, likeTrack, paginate, paginateItems, refreshUserToken, repostPlaylist, repostTrack, resolveUrl, scFetch, scFetchUrl, searchPlaylists, searchTracks, searchUsers, signOut, unfollowUser, unlikePlaylist, unlikeTrack, unrepostPlaylist, unrepostTrack, updatePlaylist, updateTrack } from './chunk-JLRQJWU5.mjs';
2
2
  export { SoundCloudError } from './chunk-QYYEWUIJ.mjs';
3
3
  //# sourceMappingURL=index.mjs.map
4
4
  //# sourceMappingURL=index.mjs.map
package/llms.txt CHANGED
@@ -200,6 +200,51 @@ const sc = new SoundCloudClient({
200
200
 
201
201
  `SCRequestTelemetry`: `{ method, path, durationMs, status, retryCount, error? }` — fires after every request including pagination and retries.
202
202
 
203
+ ## Raw API
204
+ ```ts
205
+ // Call any endpoint — path templating, returns { data, status, headers }
206
+ const res = await sc.raw.get('/tracks/{id}', { id: 123456 });
207
+ await sc.raw.post('/tracks/{id}/comments', { id: 123456, body: { body: 'great track' } });
208
+ await sc.raw.request({ method: 'DELETE', path: '/tracks/{id}', query: { id: 123456 } });
209
+ ```
210
+ `RawResponse<T>`: `{ data: T, status: number, headers: Record<string, string> }` — does not throw on non-2xx.
211
+
212
+ ## In-Flight Deduplication & Cache
213
+ ```ts
214
+ const sc = new SoundCloudClient({
215
+ clientId: '...', clientSecret: '...',
216
+ dedupe: true, // default: true — concurrent identical GETs share one promise
217
+ cache: myCache, // optional: SoundCloudCache interface (get/set/delete)
218
+ cacheTtlMs: 60000, // default: 60s per cached GET response
219
+ });
220
+ ```
221
+ `SoundCloudCache`: `{ get<T>(key): T|undefined; set<T>(key, value, {ttlMs}): void; delete(key): void }`
222
+
223
+ ## Fetch Injection & Runtime Portability
224
+ ```ts
225
+ const sc = new SoundCloudClient({
226
+ clientId: '...', clientSecret: '...',
227
+ fetch: customFetch, // custom fetch implementation
228
+ AbortController: customAbort, // custom AbortController
229
+ });
230
+ ```
231
+ No Node-only APIs. Works in Cloudflare Workers, Bun, Deno, Edge runtimes.
232
+
233
+ ## Retry Hook
234
+ ```ts
235
+ const sc = new SoundCloudClient({
236
+ clientId: '...', clientSecret: '...',
237
+ onRetry: (info) => console.warn(`retry #${info.attempt} — ${info.reason} delay=${info.delayMs}ms`),
238
+ });
239
+ ```
240
+ `RetryInfo`: `{ attempt, delayMs, reason, status?, url }` — fires on every retry. 429 `Retry-After` header honored (capped 60s).
241
+
242
+ ## OpenAPI Tooling
243
+ ```bash
244
+ pnpm openapi:sync # fetch SoundCloud OpenAPI spec → tools/openapi.json + openapi-operations.json
245
+ pnpm openapi:coverage # compare spec operations to IMPLEMENTED_OPERATIONS in src/client/registry.ts
246
+ ```
247
+
203
248
  ## Full Documentation
204
249
 
205
250
  https://twin-paws.github.io/soundcloud-api-ts/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soundcloud-api-ts",
3
- "version": "1.11.1",
3
+ "version": "1.12.0",
4
4
  "description": "TypeScript-first SoundCloud API client for accessing tracks, users, playlists, and search endpoints. Zero dependencies, native fetch, full OAuth 2.1 + PKCE support.",
5
5
  "bin": {
6
6
  "sc-cli": "./dist/cli.js",
@@ -34,7 +34,9 @@
34
34
  "test": "vitest run",
35
35
  "test:watch": "vitest",
36
36
  "test:coverage": "vitest run --coverage",
37
- "docs": "typedoc"
37
+ "docs": "typedoc",
38
+ "openapi:sync": "npx tsx tools/openapi-sync.ts",
39
+ "openapi:coverage": "node -e \"const { IMPLEMENTED_OPERATIONS } = require('./dist/index.cjs'); const ops = require('./tools/openapi-operations.json'); const total = ops.length; const covered = ops.filter(o => IMPLEMENTED_OPERATIONS.includes(o.operationId)).length; console.log('Coverage: ' + covered + '/' + total + ' operations (' + Math.round(covered/total*100) + '%)');\" 2>/dev/null || echo 'Run pnpm build and pnpm openapi:sync first'"
38
40
  },
39
41
  "repository": {
40
42
  "type": "git",