@purpleproser/soundboard-downloader-cli 1.5.0 → 1.6.1

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 CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  This changelog is automatically generated by [release-please](https://github.com/google-github-actions/release-please-action).
4
4
 
5
+ ## [1.6.1](https://github.com/blacksagres/soundboard-downloader-cli/compare/v1.6.0...v1.6.1) (2026-03-07)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * run npm audit ([7dfe691](https://github.com/blacksagres/soundboard-downloader-cli/commit/7dfe69192dfe2aac1f407b3eb731fe15531334bb))
11
+
12
+ ## [1.6.0](https://github.com/blacksagres/soundboard-downloader-cli/compare/v1.5.0...v1.6.0) (2026-03-01)
13
+
14
+
15
+ ### Features
16
+
17
+ * data validation with valibot ([1357286](https://github.com/blacksagres/soundboard-downloader-cli/commit/1357286c9c22153fccc6571f1b7dd1f554bbb057))
18
+ * paginate search list to avoid blastinhg the myinstants page with requests ([439751c](https://github.com/blacksagres/soundboard-downloader-cli/commit/439751cb70d3586aae8d026db902932d41fc2a68))
19
+ * restore single selection for sounds to keep UI clean ([c21895d](https://github.com/blacksagres/soundboard-downloader-cli/commit/c21895dcf373d55b1b87659371c5550d21773355))
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * use a mock api for integration tests ([9f410df](https://github.com/blacksagres/soundboard-downloader-cli/commit/9f410dfa796bafc11dc49177667388a84e638e41))
25
+
5
26
  ## [1.5.0](https://github.com/blacksagres/soundboard-downloader-cli/compare/v1.4.0...v1.5.0) (2026-02-26)
6
27
 
7
28
 
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * API Configuration - allows switching between real and mock API
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isMockMode = exports.getApiMode = exports.setApiMode = void 0;
7
+ // Default to real API, but can be overridden for testing
8
+ let currentApiMode = 'real';
9
+ /**
10
+ * Set the API mode
11
+ * @param mode - 'real' for production, 'mock' for testing
12
+ */
13
+ const setApiMode = (mode) => {
14
+ currentApiMode = mode;
15
+ };
16
+ exports.setApiMode = setApiMode;
17
+ /**
18
+ * Get the current API mode
19
+ */
20
+ const getApiMode = () => {
21
+ return currentApiMode;
22
+ };
23
+ exports.getApiMode = getApiMode;
24
+ /**
25
+ * Check if we're using mock API
26
+ */
27
+ const isMockMode = () => {
28
+ return currentApiMode === 'mock';
29
+ };
30
+ exports.isMockMode = isMockMode;
31
+ //# sourceMappingURL=api-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-config.js","sourceRoot":"","sources":["../../src/api/api-config.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAIH,yDAAyD;AACzD,IAAI,cAAc,GAAY,MAAM,CAAC;AAErC;;;GAGG;AACI,MAAM,UAAU,GAAG,CAAC,IAAa,EAAQ,EAAE;IAChD,cAAc,GAAG,IAAI,CAAC;AACxB,CAAC,CAAC;AAFW,QAAA,UAAU,cAErB;AAEF;;GAEG;AACI,MAAM,UAAU,GAAG,GAAY,EAAE;IACtC,OAAO,cAAc,CAAC;AACxB,CAAC,CAAC;AAFW,QAAA,UAAU,cAErB;AAEF;;GAEG;AACI,MAAM,UAAU,GAAG,GAAY,EAAE;IACtC,OAAO,cAAc,KAAK,MAAM,CAAC;AACnC,CAAC,CAAC;AAFW,QAAA,UAAU,cAErB"}
@@ -1,46 +1,106 @@
1
1
  "use strict";
2
2
  /**
3
- * Fetch the html from myinstants.com.
3
+ * Fetch the html from myinstants.com with server-side pagination support.
4
4
  *
5
- * 1. Query the html for the sound based on name
6
- * 2 .Then find the corresponding link to that sound
7
- * 3. Transform the found html nodes to an object with `{ label: string, download_url: string }`
8
- * 4. List them in the console - a user can pick one of the list (search in terminal)
9
- * 5. Download the sound (donwload folder from the user)
5
+ * This module provides functions to fetch sound nodes page by page,
6
+ * enabling efficient browsing of search results without loading everything at once.
7
+ *
8
+ * Can be switched to mock mode for testing via api-config.ts
10
9
  */
11
10
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.getNodeDownloadPage = exports.getSoundNodes = void 0;
13
- const getSoundNodes = async (searchString) => {
14
- let page = 1;
15
- const result = [];
11
+ exports.getNodeDownloadPage = exports.getAllSoundNodes = exports.hasNextPage = exports.getSoundNodesPage = void 0;
12
+ const api_config_1 = require("./api-config");
13
+ const MockApi = require("./my-instants.api.mock");
14
+ /**
15
+ * Fetch a single page of sound nodes from myinstants.com
16
+ * @param searchString The search term
17
+ * @param page The page number to fetch (default: 1)
18
+ * @returns HTML content of the requested page
19
+ * @throws Error if the page cannot be fetched
20
+ */
21
+ const getSoundNodesPage = async ({ searchString, page = 1 }) => {
22
+ // Use mock data if in mock mode
23
+ if ((0, api_config_1.isMockMode)()) {
24
+ return MockApi.getSoundNodesPage({ searchString, page });
25
+ }
16
26
  const escapedSearchParam = encodeURIComponent(searchString);
17
- /**
18
- * Yeah so that page does a little trick to make an infinite scroll.
19
- *
20
- * Whenever you hit the page in your browser, there's a script that will try to fetch the next page and
21
- * append the results to the current one, until it hits a 404 (ran out of pages). Here we try to do the same.
22
- *
23
- * One idea was to append this all into one string document but we just return 1 array with all
24
- * the pages we found. Then he service layer can crunch on this to determine what to pick.
25
- */
27
+ const url = `https://www.myinstants.com/en/search/?name=${escapedSearchParam}&page=${page}`;
28
+ const response = await fetch(url);
29
+ if (!response.ok) {
30
+ throw new Error(`Failed to fetch page ${page} for search: ${searchString}`);
31
+ }
32
+ return response.text();
33
+ };
34
+ exports.getSoundNodesPage = getSoundNodesPage;
35
+ /**
36
+ * Check if next page exists using HEAD request for efficiency
37
+ * @param searchString The search term
38
+ * @param page The current page number (default: 1)
39
+ * @returns true if next page exists, false otherwise
40
+ */
41
+ const hasNextPage = async ({ searchString, page = 1 }) => {
42
+ // Use mock data if in mock mode
43
+ if ((0, api_config_1.isMockMode)()) {
44
+ return MockApi.hasNextPage({ searchString, page });
45
+ }
46
+ const escapedSearchParam = encodeURIComponent(searchString);
47
+ const url = `https://www.myinstants.com/en/search/?name=${escapedSearchParam}&page=${page + 1}`;
48
+ try {
49
+ const response = await fetch(url, { method: 'HEAD' });
50
+ return response.ok;
51
+ }
52
+ catch (error) {
53
+ return false;
54
+ }
55
+ };
56
+ exports.hasNextPage = hasNextPage;
57
+ /**
58
+ * Fetch all pages (for --all flag)
59
+ * This maintains the original functionality but as an explicit option
60
+ * @param searchString The search term
61
+ * @returns Array of HTML content for all pages
62
+ */
63
+ const getAllSoundNodes = async ({ searchString }) => {
64
+ // Use mock data if in mock mode
65
+ if ((0, api_config_1.isMockMode)()) {
66
+ return MockApi.getAllSoundNodes({ searchString });
67
+ }
68
+ const result = [];
69
+ let page = 1;
26
70
  while (true) {
27
- const url = `https://www.myinstants.com/en/search/?name=${escapedSearchParam}&page=${page}`;
28
- const response = await fetch(url);
29
- if (response.ok) {
30
- const htmlResult = await response.text();
31
- result.push(htmlResult);
71
+ try {
72
+ const html = await (0, exports.getSoundNodesPage)({ searchString, page });
73
+ result.push(html);
32
74
  page++;
33
- continue;
75
+ // Check if next page exists
76
+ const nextExists = await (0, exports.hasNextPage)({ searchString, page });
77
+ if (!nextExists)
78
+ break;
79
+ }
80
+ catch (error) {
81
+ // If we get an error fetching a page, assume we've reached the end
82
+ break;
34
83
  }
35
- break;
36
84
  }
37
85
  return result;
38
86
  };
39
- exports.getSoundNodes = getSoundNodes;
87
+ exports.getAllSoundNodes = getAllSoundNodes;
88
+ /**
89
+ * Fetch the download page for a specific sound node
90
+ * @param soundNodeDetailsURL The relative URL of the sound detail page
91
+ * @returns HTML content of the sound detail page
92
+ */
40
93
  const getNodeDownloadPage = async (soundNodeDetailsURL) => {
94
+ // Use mock data if in mock mode
95
+ if ((0, api_config_1.isMockMode)()) {
96
+ return MockApi.getNodeDownloadPage(soundNodeDetailsURL);
97
+ }
41
98
  const root = `https://www.myinstants.com${soundNodeDetailsURL}`;
42
99
  const response = await fetch(root);
43
- return await response.text();
100
+ if (!response.ok) {
101
+ throw new Error(`Failed to fetch sound detail page: ${soundNodeDetailsURL}`);
102
+ }
103
+ return response.text();
44
104
  };
45
105
  exports.getNodeDownloadPage = getNodeDownloadPage;
46
106
  //# sourceMappingURL=my-instants.api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"my-instants.api.js","sourceRoot":"","sources":["../../src/api/my-instants.api.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEI,MAAM,aAAa,GAAG,KAAK,EAAE,YAAoB,EAAE,EAAE;IAC1D,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,MAAM,GAAkB,EAAE,CAAC;IAEjC,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAE5D;;;;;;;;OAQG;IAEH,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,8CAA8C,kBAAkB,SAAS,IAAI,EAAE,CAAC;QAC5F,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAElC,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;YAChB,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAExB,IAAI,EAAE,CAAC;YAEP,SAAS;QACX,CAAC;QAED,MAAM;IACR,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAjCW,QAAA,aAAa,iBAiCxB;AAEK,MAAM,mBAAmB,GAAG,KAAK,EAAE,mBAA2B,EAAE,EAAE;IACvE,MAAM,IAAI,GAAG,6BAA6B,mBAAmB,EAAE,CAAC;IAEhE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;IAEnC,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;AAC/B,CAAC,CAAC;AANW,QAAA,mBAAmB,uBAM9B"}
1
+ {"version":3,"file":"my-instants.api.js","sourceRoot":"","sources":["../../src/api/my-instants.api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /**
3
+ * Mock API layer for testing purposes
4
+ * Returns predictable data without making actual HTTP requests
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.getNodeDownloadPage = exports.getAllSoundNodes = exports.hasNextPage = exports.getSoundNodesPage = void 0;
8
+ // Mock data for testing
9
+ const mockSounds = [
10
+ {
11
+ label: "Wilhelm Scream",
12
+ detail_url: "/en/instant/wilhelm-scream/"
13
+ },
14
+ {
15
+ label: "THX Deep Note",
16
+ detail_url: "/en/instant/thx-deep-note/"
17
+ },
18
+ {
19
+ label: "Test Sound 1",
20
+ detail_url: "/en/instant/test-sound-1/"
21
+ },
22
+ {
23
+ label: "Test Sound 2",
24
+ detail_url: "/en/instant/test-sound-2/"
25
+ },
26
+ {
27
+ label: "Test Sound 3",
28
+ detail_url: "/en/instant/test-sound-3/"
29
+ },
30
+ {
31
+ label: "Test Sound 4",
32
+ detail_url: "/en/instant/test-sound-4/"
33
+ },
34
+ {
35
+ label: "Test Sound 5",
36
+ detail_url: "/en/instant/test-sound-5/"
37
+ },
38
+ {
39
+ label: "Test Sound 6",
40
+ detail_url: "/en/instant/test-sound-6/"
41
+ },
42
+ {
43
+ label: "Test Sound 7",
44
+ detail_url: "/en/instant/test-sound-7/"
45
+ },
46
+ {
47
+ label: "Test Sound 8",
48
+ detail_url: "/en/instant/test-sound-8/"
49
+ },
50
+ {
51
+ label: "Test Sound 9",
52
+ detail_url: "/en/instant/test-sound-9/"
53
+ },
54
+ {
55
+ label: "Test Sound 10",
56
+ detail_url: "/en/instant/test-sound-10/"
57
+ },
58
+ {
59
+ label: "Test Sound 11",
60
+ detail_url: "/en/instant/test-sound-11/"
61
+ },
62
+ {
63
+ label: "Test Sound 12",
64
+ detail_url: "/en/instant/test-sound-12/"
65
+ },
66
+ ];
67
+ // Mock HTML templates
68
+ const mockSearchPage = (sounds, page) => `
69
+ <!DOCTYPE html>
70
+ <html>
71
+ <head><title>MyInstants - Search Results</title></head>
72
+ <body>
73
+ <div class="instant-list">
74
+ ${sounds.map(sound => `
75
+ <div class="instant">
76
+ <a href="${sound.detail_url}" class="instant-link">${sound.label}</a>
77
+ </div>
78
+ `).join('')}
79
+ </div>
80
+ </body>
81
+ </html>
82
+ `;
83
+ const mockDetailPage = (downloadUrl) => `
84
+ <!DOCTYPE html>
85
+ <html>
86
+ <head><title>MyInstants - Sound Detail</title></head>
87
+ <body>
88
+ <a href="${downloadUrl}" download="sound.mp3">Download MP3</a>
89
+ </body>
90
+ </html>
91
+ `;
92
+ /**
93
+ * Mock implementation of getSoundNodesPage
94
+ */
95
+ const getSoundNodesPage = async ({ searchString, page = 1 }) => {
96
+ // Return mock data for any search string
97
+ const pageSize = 6;
98
+ const startIndex = (page - 1) * pageSize;
99
+ const pageSounds = mockSounds.slice(startIndex, startIndex + pageSize);
100
+ return mockSearchPage(pageSounds, page);
101
+ };
102
+ exports.getSoundNodesPage = getSoundNodesPage;
103
+ /**
104
+ * Mock implementation of hasNextPage
105
+ */
106
+ const hasNextPage = async ({ searchString, page = 1 }) => {
107
+ const pageSize = 6;
108
+ const startIndex = (page - 1) * pageSize;
109
+ return startIndex + pageSize < mockSounds.length;
110
+ };
111
+ exports.hasNextPage = hasNextPage;
112
+ /**
113
+ * Mock implementation of getAllSoundNodes
114
+ */
115
+ const getAllSoundNodes = async ({ searchString }) => {
116
+ const pageSize = 6;
117
+ const pages = [];
118
+ for (let page = 1;; page++) {
119
+ const startIndex = (page - 1) * pageSize;
120
+ const pageSounds = mockSounds.slice(startIndex, startIndex + pageSize);
121
+ if (pageSounds.length === 0)
122
+ break;
123
+ pages.push(mockSearchPage(pageSounds, page));
124
+ }
125
+ return pages;
126
+ };
127
+ exports.getAllSoundNodes = getAllSoundNodes;
128
+ /**
129
+ * Mock implementation of getNodeDownloadPage
130
+ */
131
+ const getNodeDownloadPage = async (soundNodeDetailsURL) => {
132
+ // Return a mock detail page with a valid download URL
133
+ return mockDetailPage("https://www.myinstants.com/media/sounds/test-sound.mp3");
134
+ };
135
+ exports.getNodeDownloadPage = getNodeDownloadPage;
136
+ //# sourceMappingURL=my-instants.api.mock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"my-instants.api.mock.js","sourceRoot":"","sources":["../../src/api/my-instants.api.mock.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAIH,wBAAwB;AACxB,MAAM,UAAU,GAAG;IACjB;QACE,KAAK,EAAE,gBAAgB;QACvB,UAAU,EAAE,6BAA6B;KAC1C;IACD;QACE,KAAK,EAAE,eAAe;QACtB,UAAU,EAAE,4BAA4B;KACzC;IACD;QACE,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,2BAA2B;KACxC;IACD;QACE,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,2BAA2B;KACxC;IACD;QACE,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,2BAA2B;KACxC;IACD;QACE,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,2BAA2B;KACxC;IACD;QACE,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,2BAA2B;KACxC;IACD;QACE,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,2BAA2B;KACxC;IACD;QACE,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,2BAA2B;KACxC;IACD;QACE,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,2BAA2B;KACxC;IACD;QACE,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,2BAA2B;KACxC;IACD;QACE,KAAK,EAAE,eAAe;QACtB,UAAU,EAAE,4BAA4B;KACzC;IACD;QACE,KAAK,EAAE,eAAe;QACtB,UAAU,EAAE,4BAA4B;KACzC;IACD;QACE,KAAK,EAAE,eAAe;QACtB,UAAU,EAAE,4BAA4B;KACzC;CACF,CAAC;AAEF,sBAAsB;AACtB,MAAM,cAAc,GAAG,CAAC,MAAyB,EAAE,IAAY,EAAE,EAAE,CAAC;;;;;;MAM9D,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;;mBAEP,KAAK,CAAC,UAAU,0BAA0B,KAAK,CAAC,KAAK;;KAEnE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;;;;CAId,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,WAAmB,EAAE,EAAE,CAAC;;;;;aAKnC,WAAW;;;CAGvB,CAAC;AAEF;;GAEG;AACI,MAAM,iBAAiB,GAAG,KAAK,EAAE,EAAE,YAAY,EAAE,IAAI,GAAG,CAAC,EAAS,EAAmB,EAAE;IAC5F,yCAAyC;IACzC,MAAM,QAAQ,GAAG,CAAC,CAAC;IACnB,MAAM,UAAU,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IACzC,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC;IAEvE,OAAO,cAAc,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AAC1C,CAAC,CAAC;AAPW,QAAA,iBAAiB,qBAO5B;AAEF;;GAEG;AACI,MAAM,WAAW,GAAG,KAAK,EAAE,EAAE,YAAY,EAAE,IAAI,GAAG,CAAC,EAAS,EAAoB,EAAE;IACvF,MAAM,QAAQ,GAAG,CAAC,CAAC;IACnB,MAAM,UAAU,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IACzC,OAAO,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC;AACnD,CAAC,CAAC;AAJW,QAAA,WAAW,eAItB;AAEF;;GAEG;AACI,MAAM,gBAAgB,GAAG,KAAK,EAAE,EAAE,YAAY,EAAS,EAAqB,EAAE;IACnF,MAAM,QAAQ,GAAG,CAAC,CAAC;IACnB,MAAM,KAAK,GAAG,EAAE,CAAC;IAEjB,KAAK,IAAI,IAAI,GAAG,CAAC,GAAI,IAAI,EAAE,EAAE,CAAC;QAC5B,MAAM,UAAU,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;QACzC,MAAM,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,UAAU,GAAG,QAAQ,CAAC,CAAC;QAEvE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM;QAEnC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAdW,QAAA,gBAAgB,oBAc3B;AAEF;;GAEG;AACI,MAAM,mBAAmB,GAAG,KAAK,EAAE,mBAA2B,EAAmB,EAAE;IACxF,sDAAsD;IACtD,OAAO,cAAc,CAAC,wDAAwD,CAAC,CAAC;AAClF,CAAC,CAAC;AAHW,QAAA,mBAAmB,uBAG9B"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ /**
3
+ * Valibot validation schemas for API responses
4
+ * Ensures both real and mock API responses match expected structure
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.validateSoundsArray = exports.safeValidateSound = exports.validateHtml = exports.validatePaginatedResults = exports.validateSound = void 0;
8
+ const valibot_1 = require("valibot");
9
+ // Schema for a single sound result
10
+ const SoundSchema = (0, valibot_1.object)({
11
+ label: (0, valibot_1.string)('Label must be a string'),
12
+ download_url: (0, valibot_1.string)('Download URL must be a string'),
13
+ });
14
+ // Schema for paginated results
15
+ const PaginatedResultsSchema = (0, valibot_1.object)({
16
+ results: (0, valibot_1.array)(SoundSchema, 'Results must be an array of sounds'),
17
+ hasNextPage: (0, valibot_1.boolean)('hasNextPage should be a boolean'),
18
+ hasPreviousPage: (0, valibot_1.boolean)('hasPreviousPage should be a boolean'),
19
+ currentPage: (0, valibot_1.number)('currentPage should be a number'),
20
+ });
21
+ // Schema for HTML response (used internally)
22
+ const HtmlSchema = (0, valibot_1.string)('HTML response must be a string');
23
+ /**
24
+ * Validate a single sound result
25
+ * @throws ValidationError if sound doesn't match schema
26
+ */
27
+ const validateSound = (sound) => {
28
+ return (0, valibot_1.parse)(SoundSchema, sound);
29
+ };
30
+ exports.validateSound = validateSound;
31
+ /**
32
+ * Validate paginated results
33
+ * @throws ValidationError if results don't match schema
34
+ */
35
+ const validatePaginatedResults = (results) => {
36
+ return (0, valibot_1.parse)(PaginatedResultsSchema, results);
37
+ };
38
+ exports.validatePaginatedResults = validatePaginatedResults;
39
+ /**
40
+ * Validate HTML response
41
+ * @throws ValidationError if HTML is not a string
42
+ */
43
+ const validateHtml = (html) => {
44
+ return (0, valibot_1.parse)(HtmlSchema, html);
45
+ };
46
+ exports.validateHtml = validateHtml;
47
+ /**
48
+ * Safe validation that returns null instead of throwing
49
+ */
50
+ const safeValidateSound = (sound) => {
51
+ try {
52
+ return (0, exports.validateSound)(sound);
53
+ }
54
+ catch (error) {
55
+ console.error('Validation failed:', error);
56
+ return null;
57
+ }
58
+ };
59
+ exports.safeValidateSound = safeValidateSound;
60
+ /**
61
+ * Validate an array of sounds
62
+ */
63
+ const validateSoundsArray = (sounds) => {
64
+ return sounds.map(sound => (0, exports.validateSound)(sound));
65
+ };
66
+ exports.validateSoundsArray = validateSoundsArray;
67
+ //# sourceMappingURL=validation-schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation-schemas.js","sourceRoot":"","sources":["../../src/api/validation-schemas.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,qCAA+F;AAE/F,mCAAmC;AACnC,MAAM,WAAW,GAAG,IAAA,gBAAM,EAAC;IACzB,KAAK,EAAE,IAAA,gBAAM,EAAC,wBAAwB,CAAC;IACvC,YAAY,EAAE,IAAA,gBAAM,EAAC,+BAA+B,CAAC;CACtD,CAAC,CAAC;AAIH,+BAA+B;AAC/B,MAAM,sBAAsB,GAAG,IAAA,gBAAM,EAAC;IACpC,OAAO,EAAE,IAAA,eAAK,EAAC,WAAW,EAAE,oCAAoC,CAAC;IACjE,WAAW,EAAE,IAAA,iBAAO,EAAC,iCAAiC,CAAC;IACvD,eAAe,EAAE,IAAA,iBAAO,EAAC,qCAAqC,CAAC;IAC/D,WAAW,EAAE,IAAA,gBAAM,EAAC,gCAAgC,CAAC;CACtD,CAAC,CAAC;AAIH,6CAA6C;AAC7C,MAAM,UAAU,GAAG,IAAA,gBAAM,EAAC,gCAAgC,CAAC,CAAC;AAG5D;;;GAGG;AACI,MAAM,aAAa,GAAG,CAAC,KAAc,EAAkB,EAAE;IAC9D,OAAO,IAAA,eAAK,EAAC,WAAW,EAAE,KAAK,CAAC,CAAC;AACnC,CAAC,CAAC;AAFW,QAAA,aAAa,iBAExB;AAEF;;;GAGG;AACI,MAAM,wBAAwB,GAAG,CAAC,OAAgB,EAA6B,EAAE;IACtF,OAAO,IAAA,eAAK,EAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC,CAAC;AAFW,QAAA,wBAAwB,4BAEnC;AAEF;;;GAGG;AACI,MAAM,YAAY,GAAG,CAAC,IAAa,EAAiB,EAAE;IAC3D,OAAO,IAAA,eAAK,EAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACjC,CAAC,CAAC;AAFW,QAAA,YAAY,gBAEvB;AAEF;;GAEG;AACI,MAAM,iBAAiB,GAAG,CAAC,KAAc,EAAyB,EAAE;IACzE,IAAI,CAAC;QACH,OAAO,IAAA,qBAAa,EAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAPW,QAAA,iBAAiB,qBAO5B;AAEF;;GAEG;AACI,MAAM,mBAAmB,GAAG,CAAC,MAAiB,EAAoB,EAAE;IACzE,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAA,qBAAa,EAAC,KAAK,CAAC,CAAC,CAAC;AACnD,CAAC,CAAC;AAFW,QAAA,mBAAmB,uBAE9B"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=query.type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.type.js","sourceRoot":"","sources":["../../../src/common/types/query.type.ts"],"names":[],"mappings":""}