@pfdev/api-types 3.0.1 → 3.0.2

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.
Files changed (4) hide show
  1. package/README.md +95 -59
  2. package/index.ts +1 -1
  3. package/package.json +1 -1
  4. package/v3.ts +99 -364
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @pfdev/api-types
2
2
 
3
- TypeScript types for PF API V3, auto-generated from the OpenAPI specification.
3
+ TypeScript types for PF API V3, synced directly from TypeBox schemas in pfapi-fs.
4
4
 
5
5
  ## Quick Start
6
6
 
@@ -13,28 +13,67 @@ npm install @pfdev/api-types
13
13
  ### Use in React/React Native
14
14
 
15
15
  ```typescript
16
- import type { paths } from '@pfdev/api-types';
17
-
18
- type GetSetsResponse = paths['/api/v3/sets/']['get']['responses'][200]['content']['application/json'];
19
- type GetSetParams = paths['/api/v3/sets/{id}']['get']['parameters']['path'];
20
-
21
- // Fully typed API calls
22
- const getSets = async (): Promise<GetSetsResponse> => {
23
- const res = await fetch('/api/v3/sets');
24
- return res.json();
25
- };
26
-
27
- const getSet = async (id: string): Promise<any> => {
28
- const res = await fetch(`/api/v3/sets/${id}`);
29
- return res.json();
30
- };
16
+ import { useState, useEffect } from 'react';
17
+ import type { SetDetail } from '@pfdev/api-types';
18
+
19
+ function SetDisplay({ setId }: { setId: string }) {
20
+ const [set, setSet] = useState<SetDetail | null>(null);
21
+ const [loading, setLoading] = useState(true);
22
+
23
+ useEffect(() => {
24
+ const fetchSet = async () => {
25
+ const response = await fetch(`/api/v3/sets/${setId}`);
26
+ const data: SetDetail = await response.json();
27
+ setSet(data);
28
+ setLoading(false);
29
+ };
30
+
31
+ fetchSet();
32
+ }, [setId]);
33
+
34
+ if (loading) return <div>Loading...</div>;
35
+ if (!set) return <div>Not found</div>;
36
+
37
+ return (
38
+ <div>
39
+ <h1>{set.name}</h1>
40
+ <p>Series: {set.series}</p>
41
+ <p>Released: {set.releaseddate}</p>
42
+ <img src={set.imglogo} alt={set.name} />
43
+
44
+ {set.current_price && (
45
+ <p>Current Price: ${set.current_price}</p>
46
+ )}
47
+
48
+ <div>
49
+ <p>7-day change: {set.change_7d}%</p>
50
+ <p>1-year change: {set.change_1y}%</p>
51
+ </div>
52
+
53
+ <h3>Rarity Distribution</h3>
54
+ <ul>
55
+ {set.rarities.map((rarity) => (
56
+ <li key={rarity.rarity}>
57
+ {rarity.rarity}: {rarity.count} cards
58
+ </li>
59
+ ))}
60
+ </ul>
61
+ </div>
62
+ );
63
+ }
31
64
  ```
32
65
 
66
+ **Benefits:**
67
+ - ✅ Full IDE autocomplete on `set.` properties
68
+ - ✅ Type errors caught at compile time
69
+ - ✅ Optional fields properly typed (e.g., `current_price?: number`)
70
+ - ✅ Nested types fully typed (e.g., `rarities: RarityCount[]`)
71
+
33
72
  ## Features
34
73
 
35
- ✅ **Auto-Generated** - Types sync directly from the API's OpenAPI spec
74
+ ✅ **Single Source of Truth** - Types synced from pfapi-fs schemas
36
75
  ✅ **Fully Typed** - IDE autocompletion on all API responses
37
- ✅ **Zero Dependencies** - Only ~10 KB, no runtime overhead
76
+ ✅ **Zero Dependencies** - Only ~5 KB, no runtime overhead
38
77
  ✅ **Type Safe** - Catch errors at compile time, not runtime
39
78
  ✅ **Free** - No paid npm account needed
40
79
 
@@ -42,19 +81,26 @@ const getSet = async (id: string): Promise<any> => {
42
81
 
43
82
  ### Prerequisites
44
83
 
45
- - pfapi-fs running locally (the backend API)
84
+ - pfapi-fs available locally (no need to run it)
46
85
  - npm installed
47
86
 
48
87
  ### Step-by-Step
49
88
 
50
89
  #### 1. Make changes in pfapi-fs
51
90
 
52
- ```bash
53
- cd ../pfapi-fs
54
- npm run dev # Start the API on https://127.0.0.1:5000
55
- ```
91
+ Add or modify V3 endpoints with TypeBox schemas:
56
92
 
57
- Add or modify V3 endpoints with TypeBox schemas in `src/routes/v3/{resource}/schemas.ts`.
93
+ ```typescript
94
+ // pfapi-fs/src/routes/v3/sets/schemas.ts
95
+ export const SetDetailSchema = Type.Object({
96
+ setid: Type.String(),
97
+ name: Type.String(),
98
+ series: Type.String(),
99
+ // ... more fields
100
+ });
101
+
102
+ export type SetDetail = Static<typeof SetDetailSchema>;
103
+ ```
58
104
 
59
105
  #### 2. Bump version and generate types
60
106
 
@@ -64,11 +110,11 @@ npm version patch # or minor/major
64
110
  ```
65
111
 
66
112
  This automatically:
67
- - ✅ Fetches the OpenAPI spec from https://127.0.0.1:5000/docs/json
68
- - ✅ Runs `openapi-typescript` to generate v3.ts
69
- - ✅ Stages the generated file with git
70
- - ✅ Bumps the version number (3.0.0 → 3.0.1)
71
- - ✅ Creates a git tag
113
+ - ✅ Syncs TypeBox schemas from `../pfapi-fs/src/routes/v3/*/schemas.ts`
114
+ - ✅ Validates with TypeScript
115
+ - ✅ Stages v3.ts with git
116
+ - ✅ Bumps version (3.0.0 → 3.0.1)
117
+ - ✅ Creates git tag
72
118
 
73
119
  #### 3. Publish to npm
74
120
 
@@ -76,7 +122,7 @@ This automatically:
76
122
  npm publish
77
123
  ```
78
124
 
79
- This verifies v3.ts was actually changed, then publishes to npm.
125
+ Verifies v3.ts changed, then publishes to npm.
80
126
 
81
127
  ## Manual Type Generation
82
128
 
@@ -86,11 +132,20 @@ To manually regenerate types without versioning:
86
132
  npm run generate
87
133
  ```
88
134
 
89
- By default, it uses `https://127.0.0.1:5000` (local dev). Specify a different API URL:
135
+ ## How It Works
90
136
 
91
- ```bash
92
- npm run generate https://api.example.com
137
+ ### Single Source of Truth
93
138
  ```
139
+ pfapi-fs/src/routes/v3/sets/schemas.ts ← Edit here
140
+ ↓
141
+ npm run generate
142
+ ↓
143
+ pfapi-types/v3.ts ← Auto-synced
144
+ ↓
145
+ Consumer imports
146
+ ```
147
+
148
+ **No drift possible** — types always match the backend schemas.
94
149
 
95
150
  ## Version Strategy
96
151
 
@@ -98,26 +153,9 @@ npm run generate https://api.example.com
98
153
  - **Minor** (3.0.0 → 3.1.0) - New endpoints/features
99
154
  - **Patch** (3.0.0 → 3.0.1) - Bug fixes in types
100
155
 
101
- ## How It Works
102
-
103
- ### Old Approach (Manual)
104
- 1. Edit schemas in pfapi-fs
105
- 2. Manually copy to pfapi-types/v3.ts
106
- 3. Bump version
107
- 4. Publish
108
-
109
- **Problem**: Easy to forget steps, schema drift between packages.
110
-
111
- ### New Approach (Automated)
112
- 1. Edit schemas in pfapi-fs
113
- 2. Run `npm version patch` (auto-generates and stages)
114
- 3. Publish
115
-
116
- **Benefit**: Types always sync with the API spec, no manual copying.
117
-
118
156
  ## Files
119
157
 
120
- - `generate-types.sh` - Script that fetches the OpenAPI spec and generates types
158
+ - `generate-types.sh` - Script that syncs TypeBox schemas from pfapi-fs
121
159
  - `package.json` - npm metadata with automated scripts
122
160
  - `v3.ts` - Auto-generated types (do not edit manually)
123
161
  - `index.ts` - Main entry point (re-exports v3.ts)
@@ -127,16 +165,14 @@ npm run generate https://api.example.com
127
165
  ### "Error: No changes to v3.ts since last tag"
128
166
 
129
167
  The `preversion` hook prevents versioning if types haven't changed. Make sure:
130
- - pfapi-fs API is running on https://127.0.0.1:5000
131
- - You've made actual changes to V3 endpoints
132
- - Run `npm run generate` to test type generation
168
+ - You've made actual changes to V3 schemas in pfapi-fs
169
+ - Run `npm run generate` to test schema sync
133
170
 
134
- ### "Connection refused" when generating
171
+ ### "TypeScript validation failed"
135
172
 
136
- Ensure pfapi-fs is running:
137
- ```bash
138
- cd ../pfapi-fs && npm run dev
139
- ```
173
+ Check that:
174
+ - All TypeBox schemas in pfapi-fs compile
175
+ - Imports are correct in `src/routes/v3/*/schemas.ts`
140
176
 
141
177
  ### "npm ERR! code E404"
142
178
 
package/index.ts CHANGED
@@ -1,2 +1,2 @@
1
- // Re-export all V3 API types
1
+ // Re-export all V3 API types synced from pfapi-fs
2
2
  export * from './v3';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pfdev/api-types",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "TypeScript types for PF API V3",
5
5
  "main": "index.ts",
6
6
  "types": "index.ts",
package/v3.ts CHANGED
@@ -1,365 +1,100 @@
1
- /**
2
- * This file was auto-generated by openapi-typescript.
3
- * Do not make direct changes to the file.
4
- */
1
+ // Auto-generated from pfapi-fs/src/routes/v3/*/schemas.ts
2
+ // Do not edit manually - run `npm run generate` to update
5
3
 
6
- export interface paths {
7
- "/api/v3/sets/": {
8
- parameters: {
9
- query?: never;
10
- header?: never;
11
- path?: never;
12
- cookie?: never;
13
- };
14
- /** @description List all sets with optional filtering */
15
- get: {
16
- parameters: {
17
- query?: {
18
- /** @description Search by set name */
19
- search?: string;
20
- /** @description Filter by series */
21
- series?: string;
22
- limit?: number;
23
- offset?: number;
24
- };
25
- header?: never;
26
- path?: never;
27
- cookie?: never;
28
- };
29
- requestBody?: never;
30
- responses: {
31
- /** @description Paginated list of Pokemon card sets */
32
- 200: {
33
- headers: {
34
- [name: string]: unknown;
35
- };
36
- content: {
37
- "application/json": {
38
- /** @description Array of sets */
39
- data: {
40
- /** @description Unique set identifier */
41
- setid: string;
42
- /** @description Set name */
43
- name: string;
44
- /** @description Pokemon series */
45
- series: string;
46
- /**
47
- * Format: date
48
- * @description Release date
49
- */
50
- releaseddate: string;
51
- /**
52
- * Format: uri
53
- * @description Symbol image URL
54
- */
55
- imgsymbol: string;
56
- /**
57
- * Format: uri
58
- * @description Logo image URL
59
- */
60
- imglogo: string;
61
- /** @description Total cards in set */
62
- printedtotal: number;
63
- /** @description Current market price */
64
- current_price?: number;
65
- /** @description 7-day price change % */
66
- change_7d?: number;
67
- /** @description URL-friendly slug */
68
- slug: string;
69
- }[];
70
- /** @description Total number of sets available */
71
- total: number;
72
- /** @description Number of sets returned in this response */
73
- count: number;
74
- };
75
- };
76
- };
77
- /** @description Default Response */
78
- 500: {
79
- headers: {
80
- [name: string]: unknown;
81
- };
82
- content: {
83
- "application/json": {
84
- error: string;
85
- message: string;
86
- };
87
- };
88
- };
89
- };
90
- };
91
- put?: never;
92
- post?: never;
93
- delete?: never;
94
- options?: never;
95
- head?: never;
96
- patch?: never;
97
- trace?: never;
98
- };
99
- "/api/v3/sets/{id}": {
100
- parameters: {
101
- query?: never;
102
- header?: never;
103
- path?: never;
104
- cookie?: never;
105
- };
106
- /** @description Get single set details by ID or slug */
107
- get: {
108
- parameters: {
109
- query?: never;
110
- header?: never;
111
- path: {
112
- /** @description Set ID or slug */
113
- setid: string;
114
- };
115
- cookie?: never;
116
- };
117
- requestBody?: never;
118
- responses: {
119
- /** @description Detailed information about a Pokemon card set */
120
- 200: {
121
- headers: {
122
- [name: string]: unknown;
123
- };
124
- content: {
125
- "application/json": {
126
- /** @description Unique set identifier */
127
- setid: string;
128
- /** @description Set name */
129
- name: string;
130
- /** @description Total cards printed (official) */
131
- printedtotal: number;
132
- /** @description Total unique cards in set */
133
- total: number;
134
- /** @description Pokemon series name */
135
- series: string;
136
- /**
137
- * Format: date
138
- * @description Release date
139
- */
140
- releaseddate: string;
141
- /**
142
- * Format: date-time
143
- * @description Last update timestamp
144
- */
145
- updatedat: string;
146
- /**
147
- * Format: uri
148
- * @description Set symbol image URL
149
- */
150
- imgsymbol: string;
151
- /**
152
- * Format: uri
153
- * @description Set logo image URL
154
- */
155
- imglogo: string;
156
- /** @description URL-friendly identifier */
157
- slug: string;
158
- /** @description Pokemon TCG Live code (if available) */
159
- ptcgocode?: string;
160
- /** @description Format legalities */
161
- legalities?: {
162
- [key: string]: string;
163
- };
164
- /** @description Current market price */
165
- current_price?: number;
166
- /** @description 1-day price change % */
167
- change_1d?: number;
168
- /** @description 7-day price change % */
169
- change_7d?: number;
170
- /** @description 30-day price change % */
171
- change_1m?: number;
172
- /** @description 90-day price change % */
173
- change_3m?: number;
174
- /** @description 180-day price change % */
175
- change_6m?: number;
176
- /** @description 1-year price change % */
177
- change_1y?: number;
178
- /** @description Card rarity distribution */
179
- rarities: {
180
- /** @description Rarity level */
181
- rarity: string;
182
- /** @description Number of cards with this rarity */
183
- count: number;
184
- }[];
185
- };
186
- };
187
- };
188
- /** @description Default Response */
189
- 404: {
190
- headers: {
191
- [name: string]: unknown;
192
- };
193
- content: {
194
- "application/json": {
195
- error: string;
196
- message: string;
197
- };
198
- };
199
- };
200
- /** @description Default Response */
201
- 500: {
202
- headers: {
203
- [name: string]: unknown;
204
- };
205
- content: {
206
- "application/json": {
207
- error: string;
208
- message: string;
209
- };
210
- };
211
- };
212
- };
213
- };
214
- put?: never;
215
- post?: never;
216
- delete?: never;
217
- options?: never;
218
- head?: never;
219
- patch?: never;
220
- trace?: never;
221
- };
222
- "/": {
223
- parameters: {
224
- query?: never;
225
- header?: never;
226
- path?: never;
227
- cookie?: never;
228
- };
229
- get: {
230
- parameters: {
231
- query?: never;
232
- header?: never;
233
- path?: never;
234
- cookie?: never;
235
- };
236
- requestBody?: never;
237
- responses: {
238
- /** @description Default Response */
239
- 200: {
240
- headers: {
241
- [name: string]: unknown;
242
- };
243
- content?: never;
244
- };
245
- };
246
- };
247
- put?: never;
248
- post?: never;
249
- delete?: never;
250
- options?: never;
251
- head?: never;
252
- patch?: never;
253
- trace?: never;
254
- };
255
- "/redis": {
256
- parameters: {
257
- query?: never;
258
- header?: never;
259
- path?: never;
260
- cookie?: never;
261
- };
262
- get: {
263
- parameters: {
264
- query?: never;
265
- header?: never;
266
- path?: never;
267
- cookie?: never;
268
- };
269
- requestBody?: never;
270
- responses: {
271
- /** @description Default Response */
272
- 200: {
273
- headers: {
274
- [name: string]: unknown;
275
- };
276
- content?: never;
277
- };
278
- };
279
- };
280
- put?: never;
281
- post?: never;
282
- delete?: never;
283
- options?: never;
284
- head?: never;
285
- patch?: never;
286
- trace?: never;
287
- };
288
- "/debug-sentry": {
289
- parameters: {
290
- query?: never;
291
- header?: never;
292
- path?: never;
293
- cookie?: never;
294
- };
295
- get: {
296
- parameters: {
297
- query?: never;
298
- header?: never;
299
- path?: never;
300
- cookie?: never;
301
- };
302
- requestBody?: never;
303
- responses: {
304
- /** @description Default Response */
305
- 200: {
306
- headers: {
307
- [name: string]: unknown;
308
- };
309
- content?: never;
310
- };
311
- };
312
- };
313
- put?: never;
314
- post?: never;
315
- delete?: never;
316
- options?: never;
317
- head?: never;
318
- patch?: never;
319
- trace?: never;
320
- };
321
- "/pool-status": {
322
- parameters: {
323
- query?: never;
324
- header?: never;
325
- path?: never;
326
- cookie?: never;
327
- };
328
- get: {
329
- parameters: {
330
- query?: never;
331
- header?: never;
332
- path?: never;
333
- cookie?: never;
334
- };
335
- requestBody?: never;
336
- responses: {
337
- /** @description Default Response */
338
- 200: {
339
- headers: {
340
- [name: string]: unknown;
341
- };
342
- content?: never;
343
- };
344
- };
345
- };
346
- put?: never;
347
- post?: never;
348
- delete?: never;
349
- options?: never;
350
- head?: never;
351
- patch?: never;
352
- trace?: never;
353
- };
354
- }
355
- export type webhooks = Record<string, never>;
356
- export interface components {
357
- schemas: never;
358
- responses: never;
359
- parameters: never;
360
- requestBodies: never;
361
- headers: never;
362
- pathItems: never;
363
- }
364
- export type $defs = Record<string, never>;
365
- export type operations = Record<string, never>;
4
+ import { Static, Type } from '@sinclair/typebox';
5
+
6
+
7
+ // ============= SETS =============
8
+
9
+
10
+ // Response for list of sets
11
+ export const SetListItemSchema = Type.Object({
12
+ setid: Type.String({ description: 'Unique set identifier' }),
13
+ name: Type.String({ description: 'Set name' }),
14
+ series: Type.String({ description: 'Pokemon series' }),
15
+ releaseddate: Type.String({ format: 'date', description: 'Release date' }),
16
+ imgsymbol: Type.String({ format: 'uri', description: 'Symbol image URL' }),
17
+ imglogo: Type.String({ format: 'uri', description: 'Logo image URL' }),
18
+ printedtotal: Type.Number({ description: 'Total cards in set' }),
19
+ current_price: Type.Optional(Type.Number({ description: 'Current market price' })),
20
+ change_7d: Type.Optional(Type.Number({ description: '7-day price change %' })),
21
+ slug: Type.String({ description: 'URL-friendly slug' })
22
+ });
23
+
24
+ export type SetListItem = Static<typeof SetListItemSchema>;
25
+
26
+ export const SetListResponseSchema = Type.Object(
27
+ {
28
+ data: Type.Array(SetListItemSchema, { description: 'Array of sets' }),
29
+ total: Type.Number({ description: 'Total number of sets available' }),
30
+ count: Type.Number({ description: 'Number of sets returned in this response' })
31
+ },
32
+ {
33
+ title: 'SetListResponse',
34
+ description: 'Paginated list of Pokemon card sets'
35
+ }
36
+ );
37
+
38
+ export type SetListResponse = Static<typeof SetListResponseSchema>;
39
+
40
+ // Response for set details
41
+ export const RarityCountSchema = Type.Object(
42
+ {
43
+ rarity: Type.String({ description: 'Rarity level' }),
44
+ count: Type.Number({ description: 'Number of cards with this rarity' })
45
+ },
46
+ {
47
+ title: 'RarityCount',
48
+ description: 'Card rarity distribution in a set'
49
+ }
50
+ );
51
+
52
+ export type RarityCount = Static<typeof RarityCountSchema>;
53
+
54
+ export const SetDetailSchema = Type.Object(
55
+ {
56
+ setid: Type.String({ description: 'Unique set identifier' }),
57
+ name: Type.String({ description: 'Set name' }),
58
+ printedtotal: Type.Number({ description: 'Total cards printed (official)' }),
59
+ total: Type.Number({ description: 'Total unique cards in set' }),
60
+ series: Type.String({ description: 'Pokemon series name' }),
61
+ releaseddate: Type.String({ format: 'date', description: 'Release date' }),
62
+ updatedat: Type.String({ format: 'date-time', description: 'Last update timestamp' }),
63
+ imgsymbol: Type.String({ format: 'uri', description: 'Set symbol image URL' }),
64
+ imglogo: Type.String({ format: 'uri', description: 'Set logo image URL' }),
65
+ slug: Type.String({ description: 'URL-friendly identifier' }),
66
+ ptcgocode: Type.Optional(Type.String({ description: 'Pokemon TCG Live code (if available)' })),
67
+ legalities: Type.Optional(Type.Record(Type.String(), Type.String(), { description: 'Format legalities' })),
68
+ current_price: Type.Optional(Type.Number({ description: 'Current market price' })),
69
+ change_1d: Type.Optional(Type.Number({ description: '1-day price change %' })),
70
+ change_7d: Type.Optional(Type.Number({ description: '7-day price change %' })),
71
+ change_1m: Type.Optional(Type.Number({ description: '30-day price change %' })),
72
+ change_3m: Type.Optional(Type.Number({ description: '90-day price change %' })),
73
+ change_6m: Type.Optional(Type.Number({ description: '180-day price change %' })),
74
+ change_1y: Type.Optional(Type.Number({ description: '1-year price change %' })),
75
+ rarities: Type.Array(RarityCountSchema, { description: 'Card rarity distribution' })
76
+ },
77
+ {
78
+ title: 'SetDetail',
79
+ description: 'Detailed information about a Pokemon card set'
80
+ }
81
+ );
82
+
83
+ export type SetDetail = Static<typeof SetDetailSchema>;
84
+
85
+ // Query parameters
86
+ export const SetListQuerySchema = Type.Object({
87
+ search: Type.Optional(Type.String({ description: 'Search by set name' })),
88
+ series: Type.Optional(Type.String({ description: 'Filter by series' })),
89
+ limit: Type.Optional(Type.Number({ minimum: 1, maximum: 100, default: 20 })),
90
+ offset: Type.Optional(Type.Number({ minimum: 0, default: 0 }))
91
+ });
92
+
93
+ export type SetListQuery = Static<typeof SetListQuerySchema>;
94
+
95
+ // Path parameters
96
+ export const SetDetailParamSchema = Type.Object({
97
+ setid: Type.String({ description: 'Set ID or slug' })
98
+ });
99
+
100
+ export type SetDetailParam = Static<typeof SetDetailParamSchema>;