@brigadasos/nadeshiko-sdk 1.5.1-dev.e889ab8 → 2.0.0-dev.384ecdc
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/README.md +144 -89
- package/dist/errors.d.ts +48 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.cjs +214 -82
- package/dist/index.cjs.map +7 -4
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +214 -82
- package/dist/index.js.map +7 -4
- package/dist/nadeshiko.gen.d.ts +1430 -80
- package/dist/nadeshiko.gen.d.ts.map +1 -1
- package/dist/paginate.d.ts +28 -0
- package/dist/paginate.d.ts.map +1 -0
- package/dist/retry.d.ts +17 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/types.gen.d.ts +16 -0
- package/dist/types.gen.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,41 +5,54 @@ TypeScript SDK for the [Nadeshiko API](https://nadeshiko.co).
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
+
# npm / pnpm / bun
|
|
9
|
+
npm add @brigadasos/nadeshiko-sdk
|
|
10
|
+
pnpm add @brigadasos/nadeshiko-sdk
|
|
8
11
|
bun add @brigadasos/nadeshiko-sdk
|
|
9
12
|
```
|
|
10
13
|
|
|
11
|
-
Install the internal build (includes
|
|
14
|
+
Install the internal build (includes session-authenticated endpoints) via the `internal` dist-tag:
|
|
12
15
|
|
|
13
16
|
```bash
|
|
14
17
|
bun add @brigadasos/nadeshiko-sdk@internal
|
|
15
18
|
```
|
|
16
19
|
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { createNadeshikoClient } from '@brigadasos/nadeshiko-sdk';
|
|
24
|
+
|
|
25
|
+
const client = createNadeshikoClient({
|
|
26
|
+
apiKey: process.env.NADESHIKO_API_KEY!,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const { data } = await client.search({ body: { query: { search: '彼女' } } });
|
|
30
|
+
console.log(data.segments);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Errors throw by default. Wrap calls you want to handle explicitly in `try/catch`.
|
|
34
|
+
|
|
17
35
|
## Authentication
|
|
18
36
|
|
|
19
37
|
### API key (server-to-server)
|
|
20
38
|
|
|
21
|
-
Use an API key for endpoints
|
|
39
|
+
Use an API key for public endpoints. The key is sent as `Authorization: Bearer <apiKey>`.
|
|
22
40
|
|
|
23
41
|
```typescript
|
|
24
|
-
import { createNadeshikoClient
|
|
42
|
+
import { createNadeshikoClient } from '@brigadasos/nadeshiko-sdk';
|
|
25
43
|
|
|
26
44
|
const client = createNadeshikoClient({
|
|
27
45
|
apiKey: process.env.NADESHIKO_API_KEY!,
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const result = await search({
|
|
32
|
-
client,
|
|
33
|
-
body: { query: { search: '彼女' } },
|
|
46
|
+
baseURL: 'PRODUCTION', // 'LOCAL' | 'DEVELOPMENT' | 'PRODUCTION' | custom URL
|
|
34
47
|
});
|
|
35
48
|
```
|
|
36
49
|
|
|
37
50
|
### Session token (user-authenticated endpoints, internal build only)
|
|
38
51
|
|
|
39
|
-
The
|
|
40
|
-
For session-authenticated endpoints (
|
|
52
|
+
The public package exposes only API-key-capable endpoints.
|
|
53
|
+
For session-authenticated endpoints (`/v1/user/*`, `/v1/collections/*`), use the internal build.
|
|
41
54
|
|
|
42
|
-
|
|
55
|
+
Pass a `sessionToken` getter that returns the value of the `nadeshiko.session_token` cookie — called fresh on every request.
|
|
43
56
|
|
|
44
57
|
**Nuxt / Nitro server routes:**
|
|
45
58
|
|
|
@@ -63,100 +76,142 @@ export default defineEventHandler(async (event) => {
|
|
|
63
76
|
});
|
|
64
77
|
```
|
|
65
78
|
|
|
66
|
-
**Browser note:** if your session cookie is `HttpOnly`, use same-origin proxy routes and let the browser attach cookies automatically.
|
|
79
|
+
**Browser note:** if your session cookie is `HttpOnly`, use same-origin proxy routes and let the browser attach cookies automatically.
|
|
67
80
|
|
|
68
|
-
|
|
81
|
+
## Error handling
|
|
69
82
|
|
|
70
|
-
|
|
83
|
+
Errors throw a `NadeshikoError` — a proper `Error` subclass with all RFC 7807 Problem Details fields available directly.
|
|
71
84
|
|
|
72
85
|
```typescript
|
|
73
|
-
import { createNadeshikoClient,
|
|
86
|
+
import { createNadeshikoClient, NadeshikoError } from '@brigadasos/nadeshiko-sdk';
|
|
74
87
|
|
|
75
|
-
const client = createNadeshikoClient({
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
});
|
|
88
|
+
const client = createNadeshikoClient({ apiKey: process.env.NADESHIKO_API_KEY! });
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const { data } = await client.search({ body: { query: { search: '食べる' } } });
|
|
92
|
+
console.log(data.segments);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
if (err instanceof NadeshikoError) {
|
|
95
|
+
switch (err.code) {
|
|
96
|
+
// 400 — Bad Request
|
|
97
|
+
case 'VALIDATION_FAILED':
|
|
98
|
+
console.error('Validation failed:', err.detail);
|
|
99
|
+
for (const [field, msg] of Object.entries(err.errors ?? {})) {
|
|
100
|
+
console.error(` ${field}: ${msg}`);
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
case 'INVALID_JSON':
|
|
104
|
+
case 'INVALID_REQUEST':
|
|
105
|
+
console.error('Bad request:', err.detail);
|
|
106
|
+
break;
|
|
107
|
+
|
|
108
|
+
// 401 — Unauthorized
|
|
109
|
+
case 'AUTH_CREDENTIALS_REQUIRED':
|
|
110
|
+
console.error('Missing API key');
|
|
111
|
+
break;
|
|
112
|
+
case 'AUTH_CREDENTIALS_INVALID':
|
|
113
|
+
console.error('API key is invalid');
|
|
114
|
+
break;
|
|
115
|
+
case 'AUTH_CREDENTIALS_EXPIRED':
|
|
116
|
+
console.error('Token has expired, re-authenticate');
|
|
117
|
+
break;
|
|
118
|
+
|
|
119
|
+
// 403 — Forbidden
|
|
120
|
+
case 'ACCESS_DENIED':
|
|
121
|
+
case 'INSUFFICIENT_PERMISSIONS':
|
|
122
|
+
console.error('Access denied');
|
|
123
|
+
break;
|
|
124
|
+
|
|
125
|
+
// 429 — Too Many Requests
|
|
126
|
+
case 'RATE_LIMIT_EXCEEDED':
|
|
127
|
+
console.error('Rate limit hit, slow down');
|
|
128
|
+
break;
|
|
129
|
+
case 'QUOTA_EXCEEDED':
|
|
130
|
+
console.error('Monthly quota exhausted');
|
|
131
|
+
break;
|
|
132
|
+
|
|
133
|
+
// 500 — Internal Server Error
|
|
134
|
+
case 'INTERNAL_SERVER_EXCEPTION':
|
|
135
|
+
// err.traceId is the instance field — include when reporting issues
|
|
136
|
+
console.error('Server error, trace ID:', err.traceId);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
79
142
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
143
|
+
**`NadeshikoError` fields:**
|
|
144
|
+
|
|
145
|
+
| Field | Type | Description |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| `code` | `string` | Machine-readable error code |
|
|
148
|
+
| `title` | `string` | Short summary |
|
|
149
|
+
| `detail` | `string` | Human-readable explanation |
|
|
150
|
+
| `status` | `number` | HTTP status code |
|
|
151
|
+
| `traceId` | `string \| undefined` | Trace ID for this error — include when reporting issues |
|
|
152
|
+
| `errors` | `Record<string, string> \| undefined` | Per-field messages (`VALIDATION_FAILED` only) |
|
|
153
|
+
|
|
154
|
+
### Opt out of throwing per-call
|
|
155
|
+
|
|
156
|
+
If you need the old `{ data, error }` return shape for a specific call, pass `throwOnError: false`:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const result = await client.search({
|
|
160
|
+
throwOnError: false,
|
|
161
|
+
body: { query: { search: '猫' } },
|
|
83
162
|
});
|
|
84
163
|
|
|
85
164
|
if (result.error) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
console.error('Validation failed:', result.error.detail);
|
|
90
|
-
for (const [field, msg] of Object.entries(result.error.errors ?? {})) {
|
|
91
|
-
console.error(` ${field}: ${msg}`);
|
|
92
|
-
}
|
|
93
|
-
break;
|
|
94
|
-
case 'INVALID_JSON':
|
|
95
|
-
console.error('Malformed JSON body:', result.error.detail);
|
|
96
|
-
break;
|
|
97
|
-
case 'INVALID_REQUEST':
|
|
98
|
-
console.error('Invalid request:', result.error.detail);
|
|
99
|
-
break;
|
|
100
|
-
|
|
101
|
-
// 401 — Unauthorized
|
|
102
|
-
case 'AUTH_CREDENTIALS_REQUIRED':
|
|
103
|
-
console.error('Missing API key or session token');
|
|
104
|
-
break;
|
|
105
|
-
case 'AUTH_CREDENTIALS_INVALID':
|
|
106
|
-
console.error('API key is invalid');
|
|
107
|
-
break;
|
|
108
|
-
case 'AUTH_CREDENTIALS_EXPIRED':
|
|
109
|
-
console.error('Token has expired, re-authenticate');
|
|
110
|
-
break;
|
|
111
|
-
case 'EMAIL_NOT_VERIFIED':
|
|
112
|
-
console.error('Email verification required');
|
|
113
|
-
break;
|
|
114
|
-
|
|
115
|
-
// 403 — Forbidden
|
|
116
|
-
case 'ACCESS_DENIED':
|
|
117
|
-
console.error('Access denied');
|
|
118
|
-
break;
|
|
119
|
-
case 'INSUFFICIENT_PERMISSIONS':
|
|
120
|
-
console.error('API key lacks the required scope');
|
|
121
|
-
break;
|
|
122
|
-
|
|
123
|
-
// 429 — Too Many Requests
|
|
124
|
-
case 'RATE_LIMIT_EXCEEDED':
|
|
125
|
-
console.error('Rate limit hit, slow down');
|
|
126
|
-
break;
|
|
127
|
-
case 'QUOTA_EXCEEDED':
|
|
128
|
-
console.error('Monthly quota exhausted');
|
|
129
|
-
break;
|
|
130
|
-
|
|
131
|
-
// 500 — Internal Server Error
|
|
132
|
-
case 'INTERNAL_SERVER_EXCEPTION':
|
|
133
|
-
console.error('Server error, trace ID:', result.error.instance);
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
return;
|
|
165
|
+
console.error(result.error);
|
|
166
|
+
} else {
|
|
167
|
+
console.log(result.data.segments);
|
|
137
168
|
}
|
|
169
|
+
```
|
|
138
170
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
171
|
+
## Retry and timeout
|
|
172
|
+
|
|
173
|
+
The client retries automatically on network errors and `408 / 429 / 500 / 502 / 503 / 504` responses. `429` responses with a `Retry-After` header are respected. Configure via `retryOptions`:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const client = createNadeshikoClient({
|
|
177
|
+
apiKey: process.env.NADESHIKO_API_KEY!,
|
|
178
|
+
retryOptions: {
|
|
179
|
+
maxRetries: 3, // default: 2
|
|
180
|
+
initialDelayMs: 1000, // default: 500 — doubles with each attempt
|
|
181
|
+
maxDelayMs: 30_000, // default: 30_000
|
|
182
|
+
timeout: 10_000, // per-attempt timeout in ms (default: none)
|
|
183
|
+
},
|
|
184
|
+
});
|
|
143
185
|
```
|
|
144
186
|
|
|
145
|
-
|
|
187
|
+
## Pagination
|
|
146
188
|
|
|
147
|
-
|
|
189
|
+
Use `paginate()` to iterate through all pages without manual cursor tracking:
|
|
148
190
|
|
|
149
191
|
```typescript
|
|
150
|
-
|
|
151
|
-
const { data } = await search({
|
|
152
|
-
client,
|
|
153
|
-
throwOnError: true,
|
|
154
|
-
body: { query: { search: '彼女' } },
|
|
155
|
-
});
|
|
192
|
+
import { createNadeshikoClient, paginate } from '@brigadasos/nadeshiko-sdk';
|
|
156
193
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
194
|
+
const client = createNadeshikoClient({ apiKey: process.env.NADESHIKO_API_KEY! });
|
|
195
|
+
|
|
196
|
+
for await (const segment of paginate(
|
|
197
|
+
(opts) => client.search(opts),
|
|
198
|
+
{ body: { query: { search: '猫' } } },
|
|
199
|
+
(data) => ({ items: data.segments, pagination: data.pagination }),
|
|
200
|
+
)) {
|
|
201
|
+
console.log(segment.textJa.content);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`paginate()` works with any endpoint that returns `{ pagination: { hasMore, cursor } }`:
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Browse all media
|
|
209
|
+
for await (const media of paginate(
|
|
210
|
+
(opts) => client.listMedia(opts),
|
|
211
|
+
{},
|
|
212
|
+
(data) => ({ items: data.media, pagination: data.pagination }),
|
|
213
|
+
)) {
|
|
214
|
+
console.log(media.nameEn);
|
|
160
215
|
}
|
|
161
216
|
```
|
|
162
217
|
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Error400, Error401, Error403, Error429, Error500, Error409, Error404 } from './types.gen';
|
|
2
|
+
/** Union of all known API error codes. */
|
|
3
|
+
export type NadeshikoErrorCode = Error400['code'] | Error401['code'] | Error403['code'] | Error429['code'] | Error500['code'] | Error409['code'] | Error404['code'];
|
|
4
|
+
export interface NadeshikoProblemDetails {
|
|
5
|
+
code: NadeshikoErrorCode;
|
|
6
|
+
title: string;
|
|
7
|
+
detail: string;
|
|
8
|
+
type?: string;
|
|
9
|
+
/** Trace ID for this specific error occurrence — include when reporting issues */
|
|
10
|
+
instance?: string;
|
|
11
|
+
status: number;
|
|
12
|
+
/** Per-field validation messages, present when `code` is `'VALIDATION_FAILED'` */
|
|
13
|
+
errors?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Thrown by the SDK when the API returns a non-2xx response.
|
|
17
|
+
*
|
|
18
|
+
* All fields from the RFC 7807 Problem Details response body are
|
|
19
|
+
* available directly on the error instance.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { NadeshikoError } from '@brigadasos/nadeshiko-sdk';
|
|
24
|
+
*
|
|
25
|
+
* try {
|
|
26
|
+
* const { data } = await client.search({ body: { query: { search: '猫' } } });
|
|
27
|
+
* } catch (err) {
|
|
28
|
+
* if (err instanceof NadeshikoError) {
|
|
29
|
+
* console.error(err.code); // 'RATE_LIMIT_EXCEEDED'
|
|
30
|
+
* console.error(err.status); // 429
|
|
31
|
+
* console.error(err.traceId); // trace ID for support
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare class NadeshikoError extends Error {
|
|
37
|
+
readonly code: NadeshikoErrorCode;
|
|
38
|
+
readonly title: string;
|
|
39
|
+
readonly detail: string;
|
|
40
|
+
readonly type?: string;
|
|
41
|
+
readonly status: number;
|
|
42
|
+
/** Trace ID from `instance` field — include when reporting issues */
|
|
43
|
+
readonly traceId?: string;
|
|
44
|
+
/** Per-field validation messages, present when `code === 'VALIDATION_FAILED'` */
|
|
45
|
+
readonly errors?: Record<string, string>;
|
|
46
|
+
constructor(body: NadeshikoProblemDetails);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../generated/dev/errors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAExG,0CAA0C;AAC1C,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;AAEpK,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAE7B,IAAI,EAAE,uBAAuB;CAW1C"}
|