@ucdjs/client 0.1.1-beta.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-PRESENT Lucas Nørgård
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @ucdjs/client
2
+
3
+ [![npm version][npm-version-src]][npm-version-href]
4
+ [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
+ [![codecov][codecov-src]][codecov-href]
6
+
7
+ A TypeScript-first HTTP client for interacting with the UCD.js API, providing type-safe methods for fetching Unicode character data.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @ucdjs/client
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ### Basic Usage
18
+
19
+ ```typescript
20
+ import { client } from "@ucdjs/client";
21
+
22
+ // Get Unicode versions
23
+ const { data: versions, error } = await client.GET("/api/v1/unicode-versions");
24
+ if (error) {
25
+ console.error("Error:", error.message);
26
+ } else {
27
+ console.log("Available versions:", versions);
28
+ }
29
+
30
+ // Access Unicode data files via proxy
31
+ const { data: fileInfo } = await client.GET("/api/v1/unicode-proxy/{wildcard}", {
32
+ params: {
33
+ path: { wildcard: "latest/ucd.all.json" }
34
+ }
35
+ });
36
+ console.log("File info:", fileInfo);
37
+ ```
38
+
39
+ ### Custom Client Configuration
40
+
41
+ ```typescript
42
+ import { createClient } from "@ucdjs/client";
43
+
44
+ // Create client with custom UCD.js API instance
45
+ const customClient = createClient("https://preview.api.ucdjs.dev");
46
+
47
+ // Use the custom client
48
+ const { data, error } = await customClient.GET("/api/v1/unicode-versions");
49
+ if (data) {
50
+ console.log("Unicode versions from preview API:", data);
51
+ }
52
+ ```
53
+
54
+ ### Working with Binary Data
55
+
56
+ ```typescript
57
+ import { client } from "@ucdjs/client";
58
+
59
+ // Fetch binary Unicode data file
60
+ const { data: binaryData } = await client.GET("/api/v1/unicode-proxy/{wildcard}", {
61
+ params: {
62
+ path: { wildcard: "latest/UnicodeData.txt" }
63
+ },
64
+ parseAs: "arrayBuffer"
65
+ });
66
+
67
+ if (binaryData) {
68
+ // eslint-disable-next-line node/prefer-global/buffer
69
+ const text = Buffer.from(binaryData).toString("utf-8");
70
+ console.log("Unicode data:", `${text.substring(0, 100)}...`);
71
+ }
72
+ ```
73
+
74
+ ## Documentation
75
+
76
+ For comprehensive documentation, examples, and API reference, visit the [Client Documentation](https://ucdjs.dev/docs/core/client).
77
+
78
+ ## 📄 License
79
+
80
+ Published under [MIT License](./LICENSE).
81
+
82
+ [npm-version-src]: https://img.shields.io/npm/v/@ucdjs/client?style=flat&colorA=18181B&colorB=4169E1
83
+ [npm-version-href]: https://npmjs.com/package/@ucdjs/client
84
+ [npm-downloads-src]: https://img.shields.io/npm/dm/@ucdjs/client?style=flat&colorA=18181B&colorB=4169E1
85
+ [npm-downloads-href]: https://npmjs.com/package/@ucdjs/client
86
+ [codecov-src]: https://img.shields.io/codecov/c/gh/ucdjs/ucd?style=flat&colorA=18181B&colorB=4169E1
87
+ [codecov-href]: https://codecov.io/gh/ucdjs/ucd
@@ -0,0 +1,837 @@
1
+ import { SafeFetchResponse } from "@ucdjs-internal/shared";
2
+ import { UCDStoreVersionManifest, UCDWellKnownConfig } from "@ucdjs/schemas";
3
+
4
+ //#region src/resources/config.d.ts
5
+ interface ConfigResource {
6
+ /**
7
+ * Get the UCD configuration including endpoints and available versions
8
+ * @returns {Promise<SafeFetchResponse<UCDWellKnownConfig>>} The UCD configuration
9
+ */
10
+ get: () => Promise<SafeFetchResponse<UCDWellKnownConfig>>;
11
+ }
12
+ //#endregion
13
+ //#region src/.generated/api.d.ts
14
+ /**
15
+ * This file was auto-generated by openapi-typescript.
16
+ * Do not make direct changes to the file.
17
+ */
18
+ interface paths {
19
+ "/api/v1/versions": {
20
+ parameters: {
21
+ query?: never;
22
+ header?: never;
23
+ path?: never;
24
+ cookie?: never;
25
+ };
26
+ /**
27
+ * @description ## List All Unicode Versions
28
+ *
29
+ * This endpoint retrieves a comprehensive list of all Unicode versions, including metadata and support status.
30
+ *
31
+ * - Provides **version metadata** such as documentation URLs and public URLs
32
+ * - Includes **draft versions** if available
33
+ * - Supports **caching** for performance optimization
34
+ */
35
+ get: {
36
+ parameters: {
37
+ query?: never;
38
+ header?: never;
39
+ path?: never;
40
+ cookie?: never;
41
+ };
42
+ requestBody?: never;
43
+ responses: {
44
+ /** @description List of Unicode Versions */200: {
45
+ headers: {
46
+ [name: string]: unknown;
47
+ };
48
+ content: {
49
+ "application/json": components["schemas"]["UnicodeVersionList"];
50
+ };
51
+ };
52
+ 404: components["responses"]["NotFoundError"];
53
+ 429: components["responses"]["TooManyRequestsError"];
54
+ 500: components["responses"]["InternalServerError"];
55
+ 502: components["responses"]["BadGatewayError"];
56
+ };
57
+ };
58
+ put?: never;
59
+ post?: never;
60
+ delete?: never;
61
+ options?: never;
62
+ head?: never;
63
+ patch?: never;
64
+ trace?: never;
65
+ };
66
+ "/api/v1/versions/{version}": {
67
+ parameters: {
68
+ query?: never;
69
+ header?: never;
70
+ path?: never;
71
+ cookie?: never;
72
+ };
73
+ /**
74
+ * @description ## Get Unicode Version Details
75
+ *
76
+ * This endpoint retrieves detailed information about a specific Unicode version.
77
+ *
78
+ * - Provides **version metadata** such as version name, documentation URL, release date, and type (stable/draft)
79
+ * - Includes **location information** (UCD URL and mapped version)
80
+ * - Returns **statistics** about characters, blocks, and scripts (if available)
81
+ * - Supports **caching** for performance optimization
82
+ */
83
+ get: {
84
+ parameters: {
85
+ query?: never;
86
+ header?: never;
87
+ path: {
88
+ /** @description A Unicode Version */version: string;
89
+ };
90
+ cookie?: never;
91
+ };
92
+ requestBody?: never;
93
+ responses: {
94
+ /** @description Detailed information about a Unicode version */200: {
95
+ headers: {
96
+ [name: string]: unknown;
97
+ };
98
+ content: {
99
+ "application/json": components["schemas"]["UnicodeVersionDetails"];
100
+ };
101
+ };
102
+ 400: components["responses"]["BadRequestError"];
103
+ 404: components["responses"]["NotFoundError"];
104
+ 429: components["responses"]["TooManyRequestsError"];
105
+ 500: components["responses"]["InternalServerError"];
106
+ 502: components["responses"]["BadGatewayError"];
107
+ };
108
+ };
109
+ put?: never;
110
+ post?: never;
111
+ delete?: never;
112
+ options?: never;
113
+ head?: never;
114
+ patch?: never;
115
+ trace?: never;
116
+ };
117
+ "/api/v1/versions/{version}/file-tree": {
118
+ parameters: {
119
+ query?: never;
120
+ header?: never;
121
+ path?: never;
122
+ cookie?: never;
123
+ };
124
+ /**
125
+ * @description This endpoint provides a **structured list of all files** inside the [`ucd folder`](https://unicode.org/Public/UCD/latest/ucd) associated with a specific Unicode version.
126
+ *
127
+ * For older versions, the files are retrieved without the `/ucd` prefix, while for the latest version, the `/ucd` prefix is included.
128
+ */
129
+ get: {
130
+ parameters: {
131
+ query?: never;
132
+ header?: never;
133
+ path: {
134
+ /** @description A Unicode Version */version: string;
135
+ };
136
+ cookie?: never;
137
+ };
138
+ requestBody?: never;
139
+ responses: {
140
+ /** @description Structured list of files for a Unicode version */200: {
141
+ headers: {
142
+ [name: string]: unknown;
143
+ };
144
+ content: {
145
+ "application/json": components["schemas"]["UnicodeFileTree"];
146
+ };
147
+ };
148
+ 400: components["responses"]["BadRequestError"];
149
+ 429: components["responses"]["TooManyRequestsError"];
150
+ 500: components["responses"]["InternalServerError"];
151
+ 502: components["responses"]["BadGatewayError"];
152
+ };
153
+ };
154
+ put?: never;
155
+ post?: never;
156
+ delete?: never;
157
+ options?: never;
158
+ head?: never;
159
+ patch?: never;
160
+ trace?: never;
161
+ };
162
+ "/api/v1/files/{wildcard}": {
163
+ parameters: {
164
+ query?: never;
165
+ header?: never;
166
+ path?: never;
167
+ cookie?: never;
168
+ };
169
+ /**
170
+ * @description This endpoint proxies requests to Unicode.org's Public directory, streaming files directly while transforming directory listings into structured JSON.
171
+ *
172
+ * All paths are relative to `/api/v1/files` — for example, requesting `/api/v1/files/15.1.0/ucd/emoji/emoji-data.txt` fetches the emoji data file from Unicode version 15.1.0.
173
+ *
174
+ * > [!IMPORTANT]
175
+ * > The `{wildcard}` parameter accepts any valid path, including deeply nested ones like `15.1.0/ucd/emoji/emoji-data.txt`. In directory listing responses, paths for directories include a trailing slash (e.g., `/15.1.0/ucd/charts/`), while file paths do not.
176
+ *
177
+ * > [!NOTE]
178
+ * > To retrieve only metadata without downloading content, use a `HEAD` request instead. See [here](#tag/files/head/api/v1/files/{wildcard})
179
+ *
180
+ * ### Directory Listing Features
181
+ *
182
+ * When accessing a directory, you can filter and sort entries using these query parameters:
183
+ *
184
+ * - `query` - Prefix-based search (case-insensitive) on entry names
185
+ * - `pattern` - Glob pattern matching for filtering
186
+ * - `type` - Filter by entry type: `all` (default), `files`, or `directories`
187
+ * - `sort` - Sort by `name` (default) or `lastModified`
188
+ * - `order` - Sort order: `asc` (default) or `desc`
189
+ *
190
+ * ### Modifications
191
+ *
192
+ * Directory responses are automatically transformed into JSON arrays containing file and directory entries. Files are streamed directly from Unicode.org with appropriate content types.
193
+ */
194
+ get: {
195
+ parameters: {
196
+ query?: {
197
+ /**
198
+ * @description A glob pattern to filter directory listing results by filename. Only applies when the response is a directory listing.
199
+ * The matching is **case-insensitive**.
200
+ *
201
+ * ## Supported Glob Syntax
202
+ *
203
+ * | Pattern | Description | Example |
204
+ * |-----------|-----------------------------------------------|------------------------------------------------------|
205
+ * | `*` | Match any characters (except path separators) | `*.txt` matches `file.txt` |
206
+ * | `?` | Match a single character | `file?.txt` matches `file1.txt` |
207
+ * | `{a,b}` | Match any of the patterns | `*.{txt,xml}` matches `file.txt` or `file.xml` |
208
+ * | `[abc]` | Match any character in the set | `file[123].txt` matches `file1.txt` |
209
+ *
210
+ * ## Examples
211
+ *
212
+ * - `*.txt` - Match all text files
213
+ * - `Uni*` - Match files starting with "Uni" (e.g., UnicodeData.txt)
214
+ * - `*Data*` - Match files containing "Data"
215
+ * - `*.{txt,xml}` - Match text or XML files
216
+ */
217
+ pattern?: string;
218
+ /**
219
+ * @description A search query to filter directory listing results. Entries are matched if their name **starts with** this value (case-insensitive).
220
+ * This is useful for quick prefix-based searching within a directory.
221
+ *
222
+ * ## Examples
223
+ *
224
+ * - `Uni` - Match entries starting with "Uni" (e.g., UnicodeData.txt)
225
+ * - `15` - Match version directories starting with "15"
226
+ */
227
+ query?: string;
228
+ /**
229
+ * @description Filter directory listing results by entry type.
230
+ *
231
+ * - `all` (default) - Return both files and directories
232
+ * - `files` - Return only files
233
+ * - `directories` - Return only directories
234
+ */
235
+ type?: "all" | "files" | "directories";
236
+ /**
237
+ * @description The field to sort directory listing results by.
238
+ *
239
+ * - `name` (default) - Sort alphabetically by entry name
240
+ * - `lastModified` - Sort by last modification timestamp
241
+ */
242
+ sort?: "name" | "lastModified";
243
+ /**
244
+ * @description The sort order for directory listing results.
245
+ *
246
+ * - `asc` (default) - Ascending order (A-Z, oldest first)
247
+ * - `desc` - Descending order (Z-A, newest first)
248
+ */
249
+ order?: "asc" | "desc";
250
+ };
251
+ header?: never;
252
+ path: {
253
+ /**
254
+ * @description The path to the Unicode data resource you want to access. This can be any valid path from the official Unicode Public directory structure.
255
+ *
256
+ * ## Path Format Options
257
+ *
258
+ * | Pattern | Description | Example |
259
+ * |--------------------------------|--------------------------------|-------------------------------------|
260
+ * | `{version}/ucd/{filename}` | UCD files for specific version | `15.1.0/ucd/UnicodeData.txt` |
261
+ * | `{version}/ucd/{sub}/{file}` | Files in subdirectories | `15.1.0/ucd/emoji/emoji-data.txt` |
262
+ * | `{version}` | List files for version | `15.1.0` |
263
+ * | `latest/ucd/{filename}` | Latest version of file | `latest/ucd/PropList.txt` |
264
+ */
265
+ wildcard: string;
266
+ };
267
+ cookie?: never;
268
+ };
269
+ requestBody?: never;
270
+ responses: {
271
+ /** @description Response from Unicode.org */200: {
272
+ headers: {
273
+ /** @description The type of the file or directory */"X-UCD-Stat-Type": "file" | "directory"; /** @description The size of the file in bytes (only for files) */
274
+ "X-UCD-Stat-Size"?: string; /** @description Number of children (only for directories) */
275
+ "X-UCD-Stat-Children"?: string; /** @description Number of child files (only for directories) */
276
+ "X-UCD-Stat-Children-Files"?: string; /** @description Number of child directories (only for directories) */
277
+ "X-UCD-Stat-Children-Dirs"?: string;
278
+ [name: string]: unknown;
279
+ };
280
+ content: {
281
+ "application/json": components["schemas"]["FileEntryList"];
282
+ "application/xml": string;
283
+ "text/plain": string;
284
+ "text/html": string;
285
+ "application/pdf": string;
286
+ "application/octet-stream": string;
287
+ };
288
+ };
289
+ 400: components["responses"]["BadRequestError"];
290
+ 404: components["responses"]["NotFoundError"];
291
+ 500: components["responses"]["InternalServerError"];
292
+ 502: components["responses"]["BadGatewayError"];
293
+ };
294
+ };
295
+ put?: never;
296
+ post?: never;
297
+ delete?: never;
298
+ options?: never;
299
+ /**
300
+ * @description Retrieve metadata about a file or directory without downloading the content. Useful for checking existence, file size, and other metadata.
301
+ *
302
+ * All paths are relative to `/api/v1/files`. Directory paths always include a trailing slash (e.g., `/15.1.0/ucd/charts/`), while file paths do not.
303
+ *
304
+ * > [!NOTE]
305
+ * > This endpoint returns the same headers as the `GET` request (file size, directory entry counts, last modified timestamps, content type) without the response body.
306
+ */
307
+ head: {
308
+ parameters: {
309
+ query?: {
310
+ /**
311
+ * @description A glob pattern to filter directory listing results by filename. Only applies when the response is a directory listing.
312
+ * The matching is **case-insensitive**.
313
+ *
314
+ * ## Supported Glob Syntax
315
+ *
316
+ * | Pattern | Description | Example |
317
+ * |-----------|-----------------------------------------------|------------------------------------------------------|
318
+ * | `*` | Match any characters (except path separators) | `*.txt` matches `file.txt` |
319
+ * | `?` | Match a single character | `file?.txt` matches `file1.txt` |
320
+ * | `{a,b}` | Match any of the patterns | `*.{txt,xml}` matches `file.txt` or `file.xml` |
321
+ * | `[abc]` | Match any character in the set | `file[123].txt` matches `file1.txt` |
322
+ *
323
+ * ## Examples
324
+ *
325
+ * - `*.txt` - Match all text files
326
+ * - `Uni*` - Match files starting with "Uni" (e.g., UnicodeData.txt)
327
+ * - `*Data*` - Match files containing "Data"
328
+ * - `*.{txt,xml}` - Match text or XML files
329
+ */
330
+ pattern?: string;
331
+ /**
332
+ * @description A search query to filter directory listing results. Entries are matched if their name **starts with** this value (case-insensitive).
333
+ * This is useful for quick prefix-based searching within a directory.
334
+ *
335
+ * ## Examples
336
+ *
337
+ * - `Uni` - Match entries starting with "Uni" (e.g., UnicodeData.txt)
338
+ * - `15` - Match version directories starting with "15"
339
+ */
340
+ query?: string;
341
+ /**
342
+ * @description Filter directory listing results by entry type.
343
+ *
344
+ * - `all` (default) - Return both files and directories
345
+ * - `files` - Return only files
346
+ * - `directories` - Return only directories
347
+ */
348
+ type?: "all" | "files" | "directories";
349
+ /**
350
+ * @description The field to sort directory listing results by.
351
+ *
352
+ * - `name` (default) - Sort alphabetically by entry name
353
+ * - `lastModified` - Sort by last modification timestamp
354
+ */
355
+ sort?: "name" | "lastModified";
356
+ /**
357
+ * @description The sort order for directory listing results.
358
+ *
359
+ * - `asc` (default) - Ascending order (A-Z, oldest first)
360
+ * - `desc` - Descending order (Z-A, newest first)
361
+ */
362
+ order?: "asc" | "desc";
363
+ };
364
+ header?: never;
365
+ path: {
366
+ /**
367
+ * @description The path to the Unicode data resource you want to access. This can be any valid path from the official Unicode Public directory structure.
368
+ *
369
+ * ## Path Format Options
370
+ *
371
+ * | Pattern | Description | Example |
372
+ * |--------------------------------|--------------------------------|-------------------------------------|
373
+ * | `{version}/ucd/{filename}` | UCD files for specific version | `15.1.0/ucd/UnicodeData.txt` |
374
+ * | `{version}/ucd/{sub}/{file}` | Files in subdirectories | `15.1.0/ucd/emoji/emoji-data.txt` |
375
+ * | `{version}` | List files for version | `15.1.0` |
376
+ * | `latest/ucd/{filename}` | Latest version of file | `latest/ucd/PropList.txt` |
377
+ */
378
+ wildcard: string;
379
+ };
380
+ cookie?: never;
381
+ };
382
+ requestBody?: never;
383
+ responses: {
384
+ /** @description Response from Unicode.org */200: {
385
+ headers: {
386
+ /** @description The type of the file or directory */"X-UCD-Stat-Type": "file" | "directory"; /** @description The size of the file in bytes (only for files) */
387
+ "X-UCD-Stat-Size": string; /** @description Number of children (only for directories) */
388
+ "X-UCD-Stat-Children"?: string; /** @description Number of child files (only for directories) */
389
+ "X-UCD-Stat-Children-Files"?: string; /** @description Number of child directories (only for directories) */
390
+ "X-UCD-Stat-Children-Dirs"?: string; /** @description The content type of the file */
391
+ "Content-Type": string; /** @description Last modification time from upstream */
392
+ "Last-Modified"?: string; /** @description Byte length when applicable */
393
+ "Content-Length": string;
394
+ [name: string]: unknown;
395
+ };
396
+ content?: never;
397
+ };
398
+ };
399
+ };
400
+ patch?: never;
401
+ trace?: never;
402
+ };
403
+ "/.well-known/ucd-config.json": {
404
+ parameters: {
405
+ query?: never;
406
+ header?: never;
407
+ path?: never;
408
+ cookie?: never;
409
+ };
410
+ /**
411
+ * @description ## UCD Configuration
412
+ *
413
+ * This endpoint retrieves the UCD configuration, including available API endpoints for accessing Unicode data resources.
414
+ *
415
+ * > [!NOTE]
416
+ * > The configuration follows the [UCD.js Well-Known Configuration](https://ucdjs.dev/docs/usage/well-known) specification.
417
+ */
418
+ get: {
419
+ parameters: {
420
+ query?: never;
421
+ header?: never;
422
+ path?: never;
423
+ cookie?: never;
424
+ };
425
+ requestBody?: never;
426
+ responses: {
427
+ /** @description Retrieves the UCD configuration */200: {
428
+ headers: {
429
+ [name: string]: unknown;
430
+ };
431
+ content: {
432
+ "application/json": components["schemas"]["UCDWellKnownConfig"];
433
+ };
434
+ };
435
+ 502: components["responses"]["BadGatewayError"];
436
+ };
437
+ };
438
+ put?: never;
439
+ post?: never;
440
+ delete?: never;
441
+ options?: never;
442
+ head?: never;
443
+ patch?: never;
444
+ trace?: never;
445
+ };
446
+ "/.well-known/ucd-store/{version}.json": {
447
+ parameters: {
448
+ query?: never;
449
+ header?: never;
450
+ path?: never;
451
+ cookie?: never;
452
+ };
453
+ /**
454
+ * @description ## UCD Store Manifest (Per Version)
455
+ *
456
+ * This endpoint retrieves the UCD Store manifest for a specific Unicode version, containing metadata about expected files for that version.
457
+ *
458
+ * This is the recommended endpoint for fetching manifest data, as it provides:
459
+ * - Smaller payloads (only the requested version)
460
+ * - Better caching (version-specific cache invalidation)
461
+ * - Reduced server load
462
+ *
463
+ * Each file entry includes:
464
+ * - `name`: The filename only
465
+ * - `path`: Path for the /api/v1/files endpoint (includes /ucd/ for versions >= 4.1.0)
466
+ * - `storePath`: Path for the store subdomain (ucd-store.ucdjs.dev)
467
+ *
468
+ * > [!NOTE]
469
+ * > The monolithic endpoint `/.well-known/ucd-store.json` is deprecated. Use this per-version endpoint instead.
470
+ */
471
+ get: {
472
+ parameters: {
473
+ query?: never;
474
+ header?: never;
475
+ path: {
476
+ /** @description Unicode version (e.g., '16.0.0') */version: string;
477
+ };
478
+ cookie?: never;
479
+ };
480
+ requestBody?: never;
481
+ responses: {
482
+ /** @description The UCD Store manifest for the specified version */200: {
483
+ headers: {
484
+ [name: string]: unknown;
485
+ };
486
+ content: {
487
+ "application/json": components["schemas"]["UCDStoreVersionManifest"];
488
+ };
489
+ };
490
+ 404: components["responses"]["NotFoundError"];
491
+ 429: components["responses"]["TooManyRequestsError"];
492
+ 500: components["responses"]["InternalServerError"];
493
+ 502: components["responses"]["BadGatewayError"];
494
+ };
495
+ };
496
+ put?: never;
497
+ post?: never;
498
+ delete?: never;
499
+ options?: never;
500
+ head?: never;
501
+ patch?: never;
502
+ trace?: never;
503
+ };
504
+ }
505
+ interface components {
506
+ schemas: {
507
+ /**
508
+ * @description Standard error response format used consistently across all API endpoints.
509
+ *
510
+ * Contains essential information for debugging and user feedback. The specific error scenarios and status codes are documented in the individual endpoint response definitions.
511
+ */
512
+ ApiError: {
513
+ /** @description Human-readable error message describing what went wrong */message: string; /** @description HTTP status code matching the response status */
514
+ status: number; /** @description ISO 8601 timestamp when the error occurred */
515
+ timestamp: string;
516
+ }; /** @description A list of Unicode versions with their metadata and support status. */
517
+ UnicodeVersionList: components["schemas"]["UnicodeVersion"][];
518
+ /**
519
+ * @description Represents a Unicode version with its metadata and support status.
520
+ * @example {
521
+ * "version": "17.0.0",
522
+ * "documentationUrl": "https://www.unicode.org/versions/Unicode17.0.0/",
523
+ * "date": null,
524
+ * "url": "https://www.unicode.org/Public/17.0.0",
525
+ * "mappedUcdVersion": null,
526
+ * "type": "draft"
527
+ * }
528
+ * @example {
529
+ * "version": "16.0.0",
530
+ * "documentationUrl": "https://www.unicode.org/versions/Unicode16.0.0/",
531
+ * "date": "2024",
532
+ * "url": "https://www.unicode.org/Public/16.0.0",
533
+ * "mappedUcdVersion": null,
534
+ * "type": "stable"
535
+ * }
536
+ * @example {
537
+ * "version": "15.1.0",
538
+ * "documentationUrl": "https://www.unicode.org/versions/Unicode15.1.0/",
539
+ * "date": "2023",
540
+ * "url": "https://www.unicode.org/Public/15.1.0",
541
+ * "mappedUcdVersion": null,
542
+ * "type": "stable"
543
+ * }
544
+ */
545
+ UnicodeVersion: {
546
+ /** @description The version of the Unicode standard. */version: string;
547
+ /**
548
+ * Format: uri
549
+ * @description The URL to the Unicode version documentation.
550
+ */
551
+ documentationUrl: string; /** @description The year of the Unicode version. */
552
+ date: string | null;
553
+ /**
554
+ * Format: uri
555
+ * @description The URL to the Unicode Character Database (UCD) for this version.
556
+ */
557
+ url: string; /** @description The corresponding UCD version mapping for this Unicode version. Null if same as version. */
558
+ mappedUcdVersion: string | null;
559
+ /**
560
+ * @description The status of the Unicode version. 'unsupported' means the version exists but is not yet supported by the API.
561
+ * @enum {string}
562
+ */
563
+ type: "draft" | "stable" | "unsupported";
564
+ };
565
+ /**
566
+ * @description Detailed information about a Unicode version, including metadata and statistics.
567
+ * @example {
568
+ * "version": "16.0.0",
569
+ * "documentationUrl": "https://www.unicode.org/versions/Unicode16.0.0/",
570
+ * "date": "2024",
571
+ * "url": "https://www.unicode.org/Public/16.0.0",
572
+ * "mappedUcdVersion": null,
573
+ * "type": "stable",
574
+ * "statistics": {
575
+ * "totalCharacters": 149813,
576
+ * "newCharacters": 5185,
577
+ * "totalBlocks": 331,
578
+ * "newBlocks": 4,
579
+ * "totalScripts": 165,
580
+ * "newScripts": 2
581
+ * }
582
+ * }
583
+ */
584
+ UnicodeVersionDetails: {
585
+ /** @description The version of the Unicode standard. */version: string;
586
+ /**
587
+ * Format: uri
588
+ * @description The URL to the Unicode version documentation.
589
+ */
590
+ documentationUrl: string; /** @description The year of the Unicode version. */
591
+ date: string | null;
592
+ /**
593
+ * Format: uri
594
+ * @description The URL to the Unicode Character Database (UCD) for this version.
595
+ */
596
+ url: string; /** @description The corresponding UCD version mapping for this Unicode version. Null if same as version. */
597
+ mappedUcdVersion: string | null;
598
+ /**
599
+ * @description The status of the Unicode version. 'unsupported' means the version exists but is not yet supported by the API.
600
+ * @enum {string}
601
+ */
602
+ type: "draft" | "stable" | "unsupported";
603
+ /**
604
+ * @description Statistics about this Unicode version. May be null if statistics are not available.
605
+ * @default {
606
+ * "newBlocks": 0,
607
+ * "newCharacters": 0,
608
+ * "newScripts": 0,
609
+ * "totalBlocks": 0,
610
+ * "totalCharacters": 0,
611
+ * "totalScripts": 0
612
+ * }
613
+ */
614
+ statistics: {
615
+ /** @description Total number of characters in this Unicode version. */totalCharacters: number; /** @description Number of new characters added in this version. */
616
+ newCharacters: number; /** @description Total number of blocks in this Unicode version. */
617
+ totalBlocks: number; /** @description Number of new blocks added in this version. */
618
+ newBlocks: number; /** @description Total number of scripts in this Unicode version. */
619
+ totalScripts: number; /** @description Number of new scripts added in this version. */
620
+ newScripts: number;
621
+ };
622
+ }; /** @description A recursive file tree structure rooted at an array of entries. */
623
+ UnicodeFileTree: components["schemas"]["UnicodeFileTreeNode"][]; /** @description A recursive file tree node; directories include children, files do not. */
624
+ UnicodeFileTreeNode: components["schemas"]["UnicodeFileTreeDirectory"] | components["schemas"]["UnicodeFileTreeFile"]; /** @description A directory node in the Unicode file tree, containing child nodes. */
625
+ UnicodeFileTreeDirectory: {
626
+ name: string;
627
+ path: string;
628
+ lastModified: number | null; /** @enum {string} */
629
+ type: "directory";
630
+ children: components["schemas"]["UnicodeFileTreeNode"][];
631
+ }; /** @description A file node in the Unicode file tree. */
632
+ UnicodeFileTreeFile: {
633
+ name: string;
634
+ path: string;
635
+ lastModified: number | null; /** @enum {string} */
636
+ type: "file";
637
+ }; /** @description An array of file entries, each representing either a file or a directory. */
638
+ FileEntryList: ({
639
+ name: string;
640
+ path: string;
641
+ lastModified: number | null; /** @enum {string} */
642
+ type: "directory";
643
+ } | {
644
+ name: string;
645
+ path: string;
646
+ lastModified: number | null; /** @enum {string} */
647
+ type: "file";
648
+ })[];
649
+ /**
650
+ * @description Configuration schema for the .well-known/ucd-config.json endpoint.
651
+ *
652
+ * This configuration provides clients with the necessary information to interact with the UCD API server, including endpoint paths and optional metadata about the server itself.
653
+ *
654
+ * The `manifest` endpoint is deprecated. Use the per-version endpoint `/.well-known/ucd-store/{version}.json` instead for better performance and caching.
655
+ */
656
+ UCDWellKnownConfig: {
657
+ /** @default 1.0 */version: string;
658
+ endpoints: {
659
+ files: string;
660
+ manifest: string;
661
+ versions: string;
662
+ }; /** @default [] */
663
+ versions: string[];
664
+ };
665
+ /**
666
+ * @description Response schema for per-version manifest endpoint.
667
+ * Matches the schema from /.well-known/ucd-store/{version}.json
668
+ */
669
+ UCDStoreVersionManifest: {
670
+ /** @description List of expected files for this version with their paths */expectedFiles: components["schemas"]["ExpectedFile"][];
671
+ }; /** @description A file expected to be present in a UCD version */
672
+ ExpectedFile: {
673
+ /** @description Filename only */name: string; /** @description Path relative to /api/v1/files endpoint (includes /ucd/ for versions >= 4.1.0) */
674
+ path: string; /** @description Path for store subdomain (without /ucd/ prefix) */
675
+ storePath: string;
676
+ };
677
+ };
678
+ responses: {
679
+ /** @description Bad request error */BadRequestError: {
680
+ headers: {
681
+ [name: string]: unknown;
682
+ };
683
+ content: {
684
+ "application/json": components["schemas"]["ApiError"];
685
+ };
686
+ }; /** @description Resource not found */
687
+ NotFoundError: {
688
+ headers: {
689
+ [name: string]: unknown;
690
+ };
691
+ content: {
692
+ "application/json": components["schemas"]["ApiError"];
693
+ };
694
+ }; /** @description Rate limit exceeded */
695
+ TooManyRequestsError: {
696
+ headers: {
697
+ [name: string]: unknown;
698
+ };
699
+ content: {
700
+ "application/json": components["schemas"]["ApiError"];
701
+ };
702
+ }; /** @description Internal server error */
703
+ InternalServerError: {
704
+ headers: {
705
+ [name: string]: unknown;
706
+ };
707
+ content: {
708
+ "application/json": components["schemas"]["ApiError"];
709
+ };
710
+ }; /** @description Bad gateway - upstream service failed */
711
+ BadGatewayError: {
712
+ headers: {
713
+ [name: string]: unknown;
714
+ };
715
+ content: {
716
+ "application/json": components["schemas"]["ApiError"];
717
+ };
718
+ };
719
+ };
720
+ parameters: never;
721
+ requestBodies: never;
722
+ headers: never;
723
+ pathItems: never;
724
+ }
725
+ //#endregion
726
+ //#region src/resources/files.d.ts
727
+ type FileResponse = paths["/api/v1/files/{wildcard}"]["get"]["responses"][200]["content"];
728
+ interface FilesResource {
729
+ /**
730
+ * Get a file or directory listing from the Unicode data
731
+ *
732
+ * @param {string} path - The path to the file (e.g., "16.0.0/ucd/UnicodeData.txt")
733
+ * @returns {Promise<SafeFetchResponse<FileResponse[keyof FileResponse]>>} File content as text, JSON, or other format depending on the file type
734
+ */
735
+ get: (path: string) => Promise<SafeFetchResponse<FileResponse[keyof FileResponse]>>;
736
+ }
737
+ //#endregion
738
+ //#region src/resources/manifest.d.ts
739
+ interface ManifestResource {
740
+ /**
741
+ * Get the manifest for a specific Unicode version
742
+ * @param {string} version - The Unicode version (e.g., "16.0.0")
743
+ * @returns {Promise<SafeFetchResponse<UCDStoreVersionManifest>>} The manifest containing expectedFiles
744
+ */
745
+ get: (version: string) => Promise<SafeFetchResponse<UCDStoreVersionManifest>>;
746
+ }
747
+ //#endregion
748
+ //#region src/resources/versions.d.ts
749
+ type VersionsListResponse = paths["/api/v1/versions"]["get"]["responses"][200]["content"]["application/json"];
750
+ type FileTreeResponse = paths["/api/v1/versions/{version}/file-tree"]["get"]["responses"][200]["content"]["application/json"];
751
+ interface VersionsResource {
752
+ /**
753
+ * List all available Unicode versions
754
+ * @return {Promise<SafeFetchResponse<VersionsListResponse>>} An array of available Unicode version strings
755
+ */
756
+ list: () => Promise<SafeFetchResponse<VersionsListResponse>>;
757
+ /**
758
+ * Get the file tree for a specific Unicode version
759
+ *
760
+ * @param {string} version - The Unicode version (e.g., "16.0.0")
761
+ * @returns {Promise<SafeFetchResponse<FileTreeResponse>>} The file tree structure for the specified version
762
+ */
763
+ getFileTree: (version: string) => Promise<SafeFetchResponse<FileTreeResponse>>;
764
+ }
765
+ //#endregion
766
+ //#region src/index.d.ts
767
+ interface UCDClient {
768
+ /**
769
+ * Access file-related endpoints
770
+ */
771
+ files: FilesResource;
772
+ /**
773
+ * Access version-related endpoints
774
+ */
775
+ versions: VersionsResource;
776
+ /**
777
+ * Access configuration endpoints
778
+ */
779
+ config: ConfigResource;
780
+ /**
781
+ * Access manifest endpoints
782
+ */
783
+ manifest: ManifestResource;
784
+ }
785
+ /**
786
+ * Creates a UCD client that automatically discovers endpoint paths
787
+ * via the well-known configuration endpoint
788
+ *
789
+ * @param {string} baseUrl - The base URL of the UCD API server (e.g., "https://api.ucdjs.dev")
790
+ * @returns {Promise<UCDClient>} A configured UCD client with resource namespaces
791
+ *
792
+ * @example
793
+ * ```ts
794
+ * const client = await createUCDClient('https://api.ucdjs.dev');
795
+ *
796
+ * // List all versions
797
+ * const versions = await client.versions.list();
798
+ *
799
+ * // Get a file
800
+ * const file = await client.files.get('16.0.0/ucd/UnicodeData.txt');
801
+ *
802
+ * // Get configuration
803
+ * const config = await client.config.get();
804
+ *
805
+ * // Get manifest for a version
806
+ * const manifest = await client.manifest.get('16.0.0');
807
+ * ```
808
+ */
809
+ declare function createUCDClient(baseUrl: string): Promise<UCDClient>;
810
+ /**
811
+ * Creates a UCD client with a synchronous configuration
812
+ *
813
+ * @param {string} baseUrl - The base URL of the UCD API server (e.g., "https://api.ucdjs.dev")
814
+ * @param {UCDWellKnownConfig} endpointConfig - The well-known configuration for endpoints
815
+ * @returns {UCDClient} A configured UCD client with resource namespaces
816
+ *
817
+ * @example
818
+ * ```ts
819
+ * const client = createUCDClientWithConfig('https://api.ucdjs.dev', {
820
+ * version: '1.0',
821
+ * endpoints: {
822
+ * files: '/files',
823
+ * manifest: '/files/.ucd-store.json',
824
+ * versions: '/versions',
825
+ * },
826
+ * });
827
+ *
828
+ * // List all versions
829
+ * const versions = await client.versions.list();
830
+ *
831
+ * // Get a file
832
+ * const file = await client.files.get('16.0.0/ucd/UnicodeData.txt');
833
+ * ```
834
+ */
835
+ declare function createUCDClientWithConfig(baseUrl: string, endpointConfig: UCDWellKnownConfig): UCDClient;
836
+ //#endregion
837
+ export { UCDClient, createUCDClient, createUCDClientWithConfig };
package/dist/index.mjs ADDED
@@ -0,0 +1,155 @@
1
+ import { customFetch, discoverEndpointsFromConfig, tryOr } from "@ucdjs-internal/shared";
2
+ import { UCDStoreVersionManifestSchema, UCDWellKnownConfigSchema, UnicodeFileTreeSchema, UnicodeVersionListSchema } from "@ucdjs/schemas";
3
+ import { PathTraversalError, resolveSafePath } from "@ucdjs/path-utils";
4
+
5
+ //#region src/resources/config.ts
6
+ function createConfigResource(options) {
7
+ const { baseUrl } = options;
8
+ return { async get() {
9
+ const url = new URL("/.well-known/ucd-config.json", baseUrl);
10
+ return customFetch.safe(url.toString(), {
11
+ parseAs: "json",
12
+ schema: UCDWellKnownConfigSchema
13
+ });
14
+ } };
15
+ }
16
+
17
+ //#endregion
18
+ //#region src/resources/files.ts
19
+ function createFilesResource(options) {
20
+ const { baseUrl, endpoints } = options;
21
+ return { async get(path) {
22
+ const resolvedPathOrError = tryOr({
23
+ try: () => resolveSafePath(endpoints.files, path),
24
+ err: (err) => {
25
+ if (err instanceof PathTraversalError) return {
26
+ data: null,
27
+ error: err
28
+ };
29
+ throw err;
30
+ }
31
+ });
32
+ if (typeof resolvedPathOrError !== "string") return resolvedPathOrError;
33
+ const url = new URL(resolvedPathOrError, baseUrl);
34
+ return customFetch.safe(url.toString());
35
+ } };
36
+ }
37
+
38
+ //#endregion
39
+ //#region src/resources/manifest.ts
40
+ /**
41
+ * Regex pattern for validating Unicode version format (X.Y.Z)
42
+ * Compiled once at module load for better performance
43
+ */
44
+ const VERSION_FORMAT_REGEX = /^\d+\.\d+\.\d+$/;
45
+ function createManifestResource(options) {
46
+ const { baseUrl } = options;
47
+ return { async get(version) {
48
+ if (!VERSION_FORMAT_REGEX.test(version)) return {
49
+ error: /* @__PURE__ */ new Error(`Invalid version format: ${version}. Expected X.Y.Z format.`),
50
+ data: null
51
+ };
52
+ const url = new URL(`/.well-known/ucd-store/${version}.json`, baseUrl);
53
+ return customFetch.safe(url.toString(), {
54
+ parseAs: "json",
55
+ schema: UCDStoreVersionManifestSchema
56
+ });
57
+ } };
58
+ }
59
+
60
+ //#endregion
61
+ //#region src/resources/versions.ts
62
+ function createVersionsResource(options) {
63
+ const { baseUrl, endpoints } = options;
64
+ return {
65
+ async list() {
66
+ const url = new URL(endpoints.versions, baseUrl);
67
+ return customFetch.safe(url.toString(), {
68
+ parseAs: "json",
69
+ schema: UnicodeVersionListSchema
70
+ });
71
+ },
72
+ async getFileTree(version) {
73
+ const url = new URL(`${endpoints.versions}/${version}/file-tree`, baseUrl);
74
+ return customFetch.safe(url.toString(), {
75
+ parseAs: "json",
76
+ schema: UnicodeFileTreeSchema
77
+ });
78
+ }
79
+ };
80
+ }
81
+
82
+ //#endregion
83
+ //#region src/index.ts
84
+ function createResources(baseUrl, endpointConfig) {
85
+ return {
86
+ files: createFilesResource({
87
+ baseUrl,
88
+ endpoints: endpointConfig
89
+ }),
90
+ versions: createVersionsResource({
91
+ baseUrl,
92
+ endpoints: endpointConfig
93
+ }),
94
+ config: createConfigResource({ baseUrl }),
95
+ manifest: createManifestResource({ baseUrl })
96
+ };
97
+ }
98
+ /**
99
+ * Creates a UCD client that automatically discovers endpoint paths
100
+ * via the well-known configuration endpoint
101
+ *
102
+ * @param {string} baseUrl - The base URL of the UCD API server (e.g., "https://api.ucdjs.dev")
103
+ * @returns {Promise<UCDClient>} A configured UCD client with resource namespaces
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * const client = await createUCDClient('https://api.ucdjs.dev');
108
+ *
109
+ * // List all versions
110
+ * const versions = await client.versions.list();
111
+ *
112
+ * // Get a file
113
+ * const file = await client.files.get('16.0.0/ucd/UnicodeData.txt');
114
+ *
115
+ * // Get configuration
116
+ * const config = await client.config.get();
117
+ *
118
+ * // Get manifest for a version
119
+ * const manifest = await client.manifest.get('16.0.0');
120
+ * ```
121
+ */
122
+ async function createUCDClient(baseUrl) {
123
+ return createResources(baseUrl, (await discoverEndpointsFromConfig(baseUrl)).endpoints);
124
+ }
125
+ /**
126
+ * Creates a UCD client with a synchronous configuration
127
+ *
128
+ * @param {string} baseUrl - The base URL of the UCD API server (e.g., "https://api.ucdjs.dev")
129
+ * @param {UCDWellKnownConfig} endpointConfig - The well-known configuration for endpoints
130
+ * @returns {UCDClient} A configured UCD client with resource namespaces
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * const client = createUCDClientWithConfig('https://api.ucdjs.dev', {
135
+ * version: '1.0',
136
+ * endpoints: {
137
+ * files: '/files',
138
+ * manifest: '/files/.ucd-store.json',
139
+ * versions: '/versions',
140
+ * },
141
+ * });
142
+ *
143
+ * // List all versions
144
+ * const versions = await client.versions.list();
145
+ *
146
+ * // Get a file
147
+ * const file = await client.files.get('16.0.0/ucd/UnicodeData.txt');
148
+ * ```
149
+ */
150
+ function createUCDClientWithConfig(baseUrl, endpointConfig) {
151
+ return createResources(baseUrl, endpointConfig.endpoints);
152
+ }
153
+
154
+ //#endregion
155
+ export { createUCDClient, createUCDClientWithConfig };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@ucdjs/client",
3
+ "version": "0.1.1-beta.1",
4
+ "type": "module",
5
+ "author": {
6
+ "name": "Lucas Nørgård",
7
+ "email": "lucasnrgaard@gmail.com",
8
+ "url": "https://luxass.dev"
9
+ },
10
+ "license": "MIT",
11
+ "homepage": "https://github.com/ucdjs/ucd",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/ucdjs/ucd.git",
15
+ "directory": "packages/client"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/ucdjs/ucd/issues"
19
+ },
20
+ "exports": {
21
+ ".": "./dist/index.mjs",
22
+ "./package.json": "./package.json"
23
+ },
24
+ "types": "./dist/index.d.mts",
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "engines": {
29
+ "node": ">=22.18"
30
+ },
31
+ "dependencies": {
32
+ "@ucdjs/schemas": "0.1.1-beta.1",
33
+ "@ucdjs/path-utils": "0.1.1-beta.1",
34
+ "@ucdjs-internal/shared": "0.1.1-beta.1",
35
+ "@ucdjs/env": "0.1.1-beta.1"
36
+ },
37
+ "devDependencies": {
38
+ "@luxass/eslint-config": "7.2.0",
39
+ "@types/picomatch": "4.0.2",
40
+ "eslint": "10.0.0",
41
+ "openapi-typescript": "7.13.0",
42
+ "publint": "0.3.17",
43
+ "tsdown": "0.20.3",
44
+ "tsx": "4.21.0",
45
+ "typescript": "5.9.3",
46
+ "vitest-testdirs": "4.4.2",
47
+ "@ucdjs-tooling/tsdown-config": "1.0.0",
48
+ "@ucdjs-tooling/tsconfig": "1.0.0"
49
+ },
50
+ "publishConfig": {
51
+ "access": "public"
52
+ },
53
+ "scripts": {
54
+ "build": "tsdown --tsconfig=./tsconfig.build.json",
55
+ "dev": "tsdown --watch",
56
+ "clean": "git clean -xdf dist node_modules",
57
+ "lint": "eslint .",
58
+ "typecheck": "tsc --noEmit -p tsconfig.build.json",
59
+ "generate:client": "openapi-typescript ../../ucd-generated/api/openapi.json -o ./src/.generated/api.d.ts",
60
+ "generate:client:local": "openapi-typescript http://localhost:8787/openapi.json -o ./src/.generated/api.d.ts"
61
+ }
62
+ }