@pfdev/api-types 3.0.0 → 3.0.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.
Files changed (3) hide show
  1. package/README.md +99 -54
  2. package/package.json +5 -2
  3. package/v3.ts +364 -79
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @pfdev/api-types
2
2
 
3
- TypeScript types for PF API V3, directly exported from TypeBox schemas.
3
+ TypeScript types for PF API V3, auto-generated from the OpenAPI specification.
4
4
 
5
5
  ## Quick Start
6
6
 
@@ -13,98 +13,143 @@ npm install @pfdev/api-types
13
13
  ### Use in React/React Native
14
14
 
15
15
  ```typescript
16
- import type { components, paths } from '@pfdev/api-types';
16
+ import type { paths } from '@pfdev/api-types';
17
17
 
18
- // Type your API responses
19
- type SetListResponse = components['schemas']['SetListResponse'];
20
- type SetDetail = components['schemas']['SetDetail'];
18
+ type GetSetsResponse = paths['/api/v3/sets/']['get']['responses'][200]['content']['application/json'];
19
+ type GetSetParams = paths['/api/v3/sets/{id}']['get']['parameters']['path'];
21
20
 
22
- // Type your API calls
23
- const getSets = async (): Promise<SetListResponse> => {
21
+ // Fully typed API calls
22
+ const getSets = async (): Promise<GetSetsResponse> => {
24
23
  const res = await fetch('/api/v3/sets');
25
24
  return res.json();
26
25
  };
27
26
 
28
- const getSet = async (setid: string): Promise<SetDetail> => {
29
- const res = await fetch(`/api/v3/sets/${setid}`);
27
+ const getSet = async (id: string): Promise<any> => {
28
+ const res = await fetch(`/api/v3/sets/${id}`);
30
29
  return res.json();
31
30
  };
32
31
  ```
33
32
 
34
33
  ## Features
35
34
 
36
- ✅ **Fully Typed** - All API endpoints have TypeScript types
37
- ✅ **Auto-Generated** - Types come directly from API schemas
38
- ✅ **Zero Manual Maintenance** - Regenerate when API changes
39
- ✅ **OpenAPI Standard** - Compatible with any OpenAPI tooling
40
- ✅ **Free to Publish** - No paid npm account needed
35
+ ✅ **Auto-Generated** - Types sync directly from the API's OpenAPI spec
36
+ ✅ **Fully Typed** - IDE autocompletion on all API responses
37
+ ✅ **Zero Dependencies** - Only ~10 KB, no runtime overhead
38
+ ✅ **Type Safe** - Catch errors at compile time, not runtime
39
+ ✅ **Free** - No paid npm account needed
41
40
 
42
- ## Development
41
+ ## Publishing Workflow
43
42
 
44
- ### Add/Update Types
43
+ ### Prerequisites
45
44
 
46
- When you add new V3 endpoints:
45
+ - pfapi-fs running locally (the backend API)
46
+ - npm installed
47
47
 
48
- 1. Create schemas in `../pfapi-fs/src/routes/v3/{resource}/schemas.ts`
49
- 2. Export the TypeBox schemas
50
- 3. Copy the types to `v3.ts` in this package
51
- 4. Commit and publish
48
+ ### Step-by-Step
49
+
50
+ #### 1. Make changes in pfapi-fs
52
51
 
53
- Example workflow:
54
52
  ```bash
55
- # Backend: Create new endpoint with TypeBox schemas
56
- # pfapi-fs/src/routes/v3/cards/schemas.ts
57
- export const CardSchema = Type.Object({ ... })
58
- export type Card = Static<typeof CardSchema>
53
+ cd ../pfapi-fs
54
+ npm run dev # Start the API on https://127.0.0.1:5000
55
+ ```
59
56
 
60
- # Types package: Re-export in v3.ts
61
- # pfapi-types/v3.ts
62
- export { CardSchema, type Card } from '...'
57
+ Add or modify V3 endpoints with TypeBox schemas in `src/routes/v3/{resource}/schemas.ts`.
63
58
 
64
- # Publish
65
- npm version patch
66
- npm publish
59
+ #### 2. Bump version and generate types
60
+
61
+ ```bash
62
+ cd ../pfapi-types
63
+ npm version patch # or minor/major
67
64
  ```
68
65
 
69
- ## Publishing
66
+ 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
70
72
 
71
- ### First Time Only
73
+ #### 3. Publish to npm
72
74
 
73
- 1. Create free npm account: https://www.npmjs.com/signup
74
- 2. Login locally: `npm login`
75
- 3. Publish: `npm publish`
75
+ ```bash
76
+ npm publish
77
+ ```
78
+
79
+ This verifies v3.ts was actually changed, then publishes to npm.
80
+
81
+ ## Manual Type Generation
76
82
 
77
- ### Update Existing Package
83
+ To manually regenerate types without versioning:
78
84
 
79
85
  ```bash
80
- # Regenerate types from updated API
81
86
  npm run generate
87
+ ```
82
88
 
83
- # Bump version
84
- npm version patch # or minor/major
89
+ By default, it uses `https://127.0.0.1:5000` (local dev). Specify a different API URL:
85
90
 
86
- # Publish
87
- npm publish
91
+ ```bash
92
+ npm run generate https://api.example.com
88
93
  ```
89
94
 
90
- ## Version Mapping
95
+ ## Version Strategy
96
+
97
+ - **Major** (3.0.0 → 4.0.0) - Breaking API changes
98
+ - **Minor** (3.0.0 → 3.1.0) - New endpoints/features
99
+ - **Patch** (3.0.0 → 3.0.1) - Bug fixes in types
100
+
101
+ ## How It Works
91
102
 
92
- - **V3.x.x** - API V3 types
93
- - **V2.x.x** - (future) API V2 types
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.
94
117
 
95
118
  ## Files
96
119
 
120
+ - `generate-types.sh` - Script that fetches the OpenAPI spec and generates types
121
+ - `package.json` - npm metadata with automated scripts
122
+ - `v3.ts` - Auto-generated types (do not edit manually)
97
123
  - `index.ts` - Main entry point (re-exports v3.ts)
98
- - `v3.ts` - Auto-generated types (run `npm run generate`)
99
- - `package.json` - npm metadata
100
- - `SETUP.md` - Detailed setup guide
124
+
125
+ ## Troubleshooting
126
+
127
+ ### "Error: No changes to v3.ts since last tag"
128
+
129
+ 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
133
+
134
+ ### "Connection refused" when generating
135
+
136
+ Ensure pfapi-fs is running:
137
+ ```bash
138
+ cd ../pfapi-fs && npm run dev
139
+ ```
140
+
141
+ ### "npm ERR! code E404"
142
+
143
+ Make sure npm is authenticated and you have publish permissions for `@pfdev/api-types`.
144
+
145
+ ## Publishing for the First Time
146
+
147
+ 1. Create free npm account: https://www.npmjs.com/signup
148
+ 2. Login: `npm login`
149
+ 3. Publish: `npm publish`
101
150
 
102
151
  ## No Cost
103
152
 
104
153
  - ✅ npm account: FREE (forever)
105
154
  - ✅ Publish to npm: FREE (unlimited public packages)
106
- - ✅ Type generation: FREE (openapi-typescript is open source)
107
-
108
- ## Support
109
-
110
- See `SETUP.md` for troubleshooting and CI/CD setup.
155
+ - ✅ All tools: FREE (open source)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pfdev/api-types",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "TypeScript types for PF API V3",
5
5
  "main": "index.ts",
6
6
  "types": "index.ts",
@@ -9,7 +9,10 @@
9
9
  "v3.ts"
10
10
  ],
11
11
  "scripts": {
12
- "verify": "tsc --noEmit v3.ts"
12
+ "verify": "tsc --noEmit v3.ts",
13
+ "generate": "bash generate-types.sh",
14
+ "preversion": "npm run generate && git add v3.ts",
15
+ "prepublishonly": "git diff HEAD v3.ts || (echo 'Error: No changes to v3.ts since last publish' && exit 1)"
13
16
  },
14
17
  "keywords": [
15
18
  "api",
package/v3.ts CHANGED
@@ -1,80 +1,365 @@
1
- // V3 API Types
2
- // Manually exported from backend TypeBox schemas
3
- // Update this as new V3 endpoints are added
4
-
5
- import { Static, Type } from '@sinclair/typebox';
6
-
7
- // ============= SETS =============
8
-
9
- export const SetListItemSchema = Type.Object({
10
- setid: Type.String({ description: 'Unique set identifier' }),
11
- name: Type.String({ description: 'Set name' }),
12
- series: Type.String({ description: 'Pokemon series' }),
13
- releaseddate: Type.String({ format: 'date', description: 'Release date' }),
14
- imgsymbol: Type.String({ format: 'uri', description: 'Symbol image URL' }),
15
- imglogo: Type.String({ format: 'uri', description: 'Logo image URL' }),
16
- printedtotal: Type.Number({ description: 'Total cards in set' }),
17
- current_price: Type.Optional(Type.Number({ description: 'Current market price' })),
18
- change_7d: Type.Optional(Type.Number({ description: '7-day price change %' })),
19
- slug: Type.String({ description: 'URL-friendly slug' })
20
- });
21
-
22
- export type SetListItem = Static<typeof SetListItemSchema>;
23
-
24
- export const SetListResponseSchema = Type.Object({
25
- data: Type.Array(SetListItemSchema),
26
- total: Type.Number({ description: 'Total number of sets' }),
27
- count: Type.Number({ description: 'Number of sets returned' })
28
- });
29
-
30
- export type SetListResponse = Static<typeof SetListResponseSchema>;
31
-
32
- export const RarityCountSchema = Type.Object({
33
- rarity: Type.String(),
34
- count: Type.Number()
35
- });
36
-
37
- export type RarityCount = Static<typeof RarityCountSchema>;
38
-
39
- export const SetDetailSchema = Type.Object({
40
- setid: Type.String(),
41
- name: Type.String(),
42
- printedtotal: Type.Number(),
43
- total: Type.Number(),
44
- series: Type.String(),
45
- releaseddate: Type.String({ format: 'date' }),
46
- updatedat: Type.String({ format: 'date-time' }),
47
- imgsymbol: Type.String({ format: 'uri' }),
48
- imglogo: Type.String({ format: 'uri' }),
49
- slug: Type.String(),
50
- ptcgocode: Type.Optional(Type.String()),
51
- legalities: Type.Optional(Type.Record(Type.String(), Type.String())),
52
- current_price: Type.Optional(Type.Number()),
53
- change_1d: Type.Optional(Type.Number()),
54
- change_7d: Type.Optional(Type.Number()),
55
- change_1m: Type.Optional(Type.Number()),
56
- change_3m: Type.Optional(Type.Number()),
57
- change_6m: Type.Optional(Type.Number()),
58
- change_1y: Type.Optional(Type.Number()),
59
- rarities: Type.Array(RarityCountSchema)
60
- });
61
-
62
- export type SetDetail = Static<typeof SetDetailSchema>;
63
-
64
- // ============= QUERY/PARAMS =============
65
-
66
- export const SetListQuerySchema = Type.Object({
67
- search: Type.Optional(Type.String({ description: 'Search by set name' })),
68
- series: Type.Optional(Type.String({ description: 'Filter by series' })),
69
- limit: Type.Optional(Type.Number({ minimum: 1, maximum: 100, default: 20 })),
70
- offset: Type.Optional(Type.Number({ minimum: 0, default: 0 }))
71
- });
72
-
73
- export type SetListQuery = Static<typeof SetListQuerySchema>;
74
-
75
- export const SetDetailParamSchema = Type.Object({
76
- setid: Type.String({ description: 'Set ID or slug' })
77
- });
78
-
79
- export type SetDetailParam = Static<typeof SetDetailParamSchema>;
1
+ /**
2
+ * This file was auto-generated by openapi-typescript.
3
+ * Do not make direct changes to the file.
4
+ */
80
5
 
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>;