@aneuhold/core-ts-api-lib 3.0.34 → 3.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 🔖 [3.0.35] (2026-04-21)
9
+
10
+ ### 🏗️ Changed
11
+
12
+ - `GCloudAPIService.call` and `fetchAndDecode` are now typed with both `TInput` and `TOutput` type parameters for stricter input typing.
13
+ - `GCloudAPIService.decodeResponse` now validates the response shape with an `isAPIResponseShape` type guard instead of an unsafe cast. On failures it still returns an `APIResponse<T>` with `data` populated (as an empty object cast to `T`) so the response contract is preserved.
14
+
8
15
  ## 🔖 [3.0.34] (2026-04-17)
9
16
 
10
17
  ### 🏗️ Changed
@@ -397,6 +404,7 @@ No direct code changes; version bump for compatibility.
397
404
  - Updated workflow permissions to allow repository write access
398
405
 
399
406
  <!-- Link References -->
407
+ [3.0.35]: https://github.com/aneuhold/ts-libs/compare/core-ts-api-lib-v3.0.34...core-ts-api-lib-v3.0.35
400
408
  [3.0.34]: https://github.com/aneuhold/ts-libs/compare/core-ts-api-lib-v3.0.33...core-ts-api-lib-v3.0.34
401
409
  [3.0.33]: https://github.com/aneuhold/ts-libs/compare/core-ts-api-lib-v3.0.32...core-ts-api-lib-v3.0.33
402
410
  [3.0.32]: https://github.com/aneuhold/ts-libs/compare/core-ts-api-lib-v3.0.31...core-ts-api-lib-v3.0.32
@@ -1 +1 @@
1
- {"version":3,"file":"GCloudAPIService.d.ts","sourceRoot":"","sources":["../../../src/services/GCloudAPIService/GCloudAPIService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,KAAK,EACV,0BAA0B,EAC1B,2BAA2B,EAC5B,MAAM,+CAA+C,CAAC;AAEvD;;;GAGG;AACH,KAAK,yBAAyB,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,KAAK,IAAI,CAAC;AAE3F;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;;IACnC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAmC;IAcrE;;;;OAIG;IACH,MAAM,CAAC,MAAM,IAAI,MAAM;IAIvB;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIhC;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI1C;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIjD;;;;;;OAMG;IACH,MAAM,CAAC,oBAAoB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI,GAAG,IAAI;IAI7E;;;;OAIG;WACU,gBAAgB,CAC3B,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAI/C;;;OAGG;WACU,UAAU,IAAI,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAU1D;;;;OAIG;WACU,gBAAgB,CAC3B,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAI/C;;;;OAIG;WACU,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAIxE;;;;OAIG;WACU,cAAc,CACzB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC;IAIpD;;;;;;;OAOG;mBACkB,IAAI;IAiBzB;;;;OAIG;mBACkB,gBAAgB;IAuBrC;;;;;OAKG;mBACkB,cAAc;IAuBnC;;;;OAIG;mBACkB,cAAc;CAYpC"}
1
+ {"version":3,"file":"GCloudAPIService.d.ts","sourceRoot":"","sources":["../../../src/services/GCloudAPIService/GCloudAPIService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAK9D,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,KAAK,EACV,0BAA0B,EAC1B,2BAA2B,EAC5B,MAAM,+CAA+C,CAAC;AAEvD;;;GAGG;AACH,KAAK,yBAAyB,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,KAAK,IAAI,CAAC;AAE3F;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAgB;;IACnC,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAmC;IAcrE;;;;OAIG;IACH,MAAM,CAAC,MAAM,IAAI,MAAM;IAIvB;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIhC;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI1C;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIjD;;;;;;OAMG;IACH,MAAM,CAAC,oBAAoB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI,GAAG,IAAI;IAI7E;;;;OAIG;WACU,gBAAgB,CAC3B,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAI/C;;;OAGG;WACU,UAAU,IAAI,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAU1D;;;;OAIG;WACU,gBAAgB,CAC3B,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAI/C;;;;OAIG;WACU,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IAIxE;;;;OAIG;WACU,cAAc,CACzB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC;IAOpD;;;;;;;OAOG;mBACkB,IAAI;IAiBzB;;;;OAIG;mBACkB,gBAAgB;IAyBrC;;;;;OAKG;mBACkB,cAAc;IAuBnC;;;;OAIG;mBACkB,cAAc;CAsBpC"}
@@ -128,16 +128,15 @@ export default class GCloudAPIService {
128
128
  if (!this.#refreshTokenString) {
129
129
  return false;
130
130
  }
131
- const { decoded } = await this.fetchAndDecode('auth/refresh', {
132
- refreshTokenString: this.#refreshTokenString
133
- });
131
+ const { decoded } = await this.fetchAndDecode('auth/refresh', { refreshTokenString: this.#refreshTokenString });
134
132
  if (!decoded.success) {
135
133
  return false;
136
134
  }
137
- this.#accessToken = decoded.data.accessToken;
138
- this.#refreshTokenString = decoded.data.refreshTokenString;
135
+ const { accessToken, refreshTokenString } = decoded.data;
136
+ this.#accessToken = accessToken;
137
+ this.#refreshTokenString = refreshTokenString;
139
138
  if (this.#onTokensRefreshed) {
140
- this.#onTokensRefreshed(decoded.data.accessToken, decoded.data.refreshTokenString);
139
+ this.#onTokensRefreshed(accessToken, refreshTokenString);
141
140
  }
142
141
  return true;
143
142
  }
@@ -172,15 +171,47 @@ export default class GCloudAPIService {
172
171
  static async decodeResponse(response) {
173
172
  try {
174
173
  const text = await response.text();
175
- return JSON.parse(text, DateService.dateReviver);
174
+ const parsed = JSON.parse(text, DateService.dateReviver);
175
+ if (!isAPIResponseShape(parsed)) {
176
+ return {
177
+ success: false,
178
+ errors: ['Response did not match the expected APIResponse shape'],
179
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
180
+ data: {}
181
+ };
182
+ }
183
+ return parsed;
176
184
  }
177
185
  catch (error) {
178
186
  return {
179
187
  success: false,
180
188
  errors: ['Failed to parse response', ErrorUtils.getErrorString(error)],
189
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
181
190
  data: {}
182
191
  };
183
192
  }
184
193
  }
185
194
  }
195
+ /**
196
+ * Type guard that validates the structural shape of an {@link APIResponse}.
197
+ * The generic `data` payload is trusted (not validated) since its shape is
198
+ * only known at the call site.
199
+ *
200
+ * @param value - The parsed JSON value to inspect.
201
+ */
202
+ function isAPIResponseShape(value) {
203
+ if (typeof value !== 'object' || value === null) {
204
+ return false;
205
+ }
206
+ if (!('success' in value) || typeof value.success !== 'boolean') {
207
+ return false;
208
+ }
209
+ if (!('errors' in value) || !Array.isArray(value.errors)) {
210
+ return false;
211
+ }
212
+ if (!value.errors.every((err) => typeof err === 'string')) {
213
+ return false;
214
+ }
215
+ return 'data' in value;
216
+ }
186
217
  //# sourceMappingURL=GCloudAPIService.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GCloudAPIService.js","sourceRoot":"","sources":["../../../src/services/GCloudAPIService/GCloudAPIService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GCloudAPIService.js","sourceRoot":"","sources":["../../../src/services/GCloudAPIService/GCloudAPIService.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,10 @@
1
1
  import { DateService, ErrorUtils } from '@aneuhold/core-ts-lib';
2
2
  import type { AdminInput, AdminOutput } from '../../types/Admin.js';
3
3
  import type { APIResponse } from '../../types/APIResponse.js';
4
- import type { AuthRefreshTokenOutput } from '../../types/AuthRefreshToken.js';
4
+ import type {
5
+ AuthRefreshTokenInput,
6
+ AuthRefreshTokenOutput
7
+ } from '../../types/AuthRefreshToken.js';
5
8
  import type {
6
9
  AuthValidateUserInput,
7
10
  AuthValidateUserOutput
@@ -95,7 +98,7 @@ export default class GCloudAPIService {
95
98
  static async authValidateUser(
96
99
  input: AuthValidateUserInput
97
100
  ): Promise<APIResponse<AuthValidateUserOutput>> {
98
- return this.call<AuthValidateUserOutput>('auth/validateUser', input);
101
+ return this.call<AuthValidateUserInput, AuthValidateUserOutput>('auth/validateUser', input);
99
102
  }
100
103
 
101
104
  /**
@@ -106,7 +109,7 @@ export default class GCloudAPIService {
106
109
  if (!this.#refreshTokenString) {
107
110
  return { success: true, errors: [], data: undefined };
108
111
  }
109
- const { decoded } = await this.fetchAndDecode<undefined>('auth/logout', {
112
+ const { decoded } = await this.fetchAndDecode<AuthRefreshTokenInput, undefined>('auth/logout', {
110
113
  refreshTokenString: this.#refreshTokenString
111
114
  });
112
115
  return decoded;
@@ -120,7 +123,7 @@ export default class GCloudAPIService {
120
123
  static async projectDashboard(
121
124
  input: ProjectDashboardInput
122
125
  ): Promise<APIResponse<ProjectDashboardOutput>> {
123
- return this.call<ProjectDashboardOutput>('project/dashboard', input);
126
+ return this.call<ProjectDashboardInput, ProjectDashboardOutput>('project/dashboard', input);
124
127
  }
125
128
 
126
129
  /**
@@ -129,7 +132,7 @@ export default class GCloudAPIService {
129
132
  * @param input - The input for the admin endpoint.
130
133
  */
131
134
  static async admin(input: AdminInput): Promise<APIResponse<AdminOutput>> {
132
- return this.call<AdminOutput>('admin', input);
135
+ return this.call<AdminInput, AdminOutput>('admin', input);
133
136
  }
134
137
 
135
138
  /**
@@ -140,7 +143,10 @@ export default class GCloudAPIService {
140
143
  static async projectWorkout(
141
144
  input: ProjectWorkoutPrimaryInput
142
145
  ): Promise<APIResponse<ProjectWorkoutPrimaryOutput>> {
143
- return this.call<ProjectWorkoutPrimaryOutput>('project/workout', input);
146
+ return this.call<ProjectWorkoutPrimaryInput, ProjectWorkoutPrimaryOutput>(
147
+ 'project/workout',
148
+ input
149
+ );
144
150
  }
145
151
 
146
152
  /**
@@ -151,16 +157,16 @@ export default class GCloudAPIService {
151
157
  * @param urlPath - The path to the endpoint.
152
158
  * @param input - The input to the endpoint.
153
159
  */
154
- private static async call<TOutput>(
160
+ private static async call<TInput, TOutput>(
155
161
  urlPath: string,
156
- input: object
162
+ input: TInput
157
163
  ): Promise<APIResponse<TOutput>> {
158
- const { response, decoded } = await this.fetchAndDecode<TOutput>(urlPath, input);
164
+ const { response, decoded } = await this.fetchAndDecode<TInput, TOutput>(urlPath, input);
159
165
 
160
166
  if (response.status === 401 && this.#refreshTokenString) {
161
167
  const refreshed = await this.tryRefreshTokens();
162
168
  if (refreshed) {
163
- const retry = await this.fetchAndDecode<TOutput>(urlPath, input);
169
+ const retry = await this.fetchAndDecode<TInput, TOutput>(urlPath, input);
164
170
  return retry.decoded;
165
171
  }
166
172
  }
@@ -178,19 +184,21 @@ export default class GCloudAPIService {
178
184
  return false;
179
185
  }
180
186
 
181
- const { decoded } = await this.fetchAndDecode<AuthRefreshTokenOutput>('auth/refresh', {
182
- refreshTokenString: this.#refreshTokenString
183
- });
187
+ const { decoded } = await this.fetchAndDecode<AuthRefreshTokenInput, AuthRefreshTokenOutput>(
188
+ 'auth/refresh',
189
+ { refreshTokenString: this.#refreshTokenString }
190
+ );
184
191
 
185
192
  if (!decoded.success) {
186
193
  return false;
187
194
  }
188
195
 
189
- this.#accessToken = decoded.data.accessToken;
190
- this.#refreshTokenString = decoded.data.refreshTokenString;
196
+ const { accessToken, refreshTokenString } = decoded.data;
197
+ this.#accessToken = accessToken;
198
+ this.#refreshTokenString = refreshTokenString;
191
199
 
192
200
  if (this.#onTokensRefreshed) {
193
- this.#onTokensRefreshed(decoded.data.accessToken, decoded.data.refreshTokenString);
201
+ this.#onTokensRefreshed(accessToken, refreshTokenString);
194
202
  }
195
203
 
196
204
  return true;
@@ -202,9 +210,9 @@ export default class GCloudAPIService {
202
210
  * @param urlPath - The path to the endpoint.
203
211
  * @param input - The input to the endpoint.
204
212
  */
205
- private static async fetchAndDecode<TOutput>(
213
+ private static async fetchAndDecode<TInput, TOutput>(
206
214
  urlPath: string,
207
- input: object
215
+ input: TInput
208
216
  ): Promise<{ response: Response; decoded: APIResponse<TOutput> }> {
209
217
  const headers = new Headers({
210
218
  Connection: 'keep-alive',
@@ -233,13 +241,46 @@ export default class GCloudAPIService {
233
241
  private static async decodeResponse<TOutput>(response: Response): Promise<APIResponse<TOutput>> {
234
242
  try {
235
243
  const text = await response.text();
236
- return JSON.parse(text, DateService.dateReviver) as APIResponse<TOutput>;
244
+ const parsed: unknown = JSON.parse(text, DateService.dateReviver);
245
+ if (!isAPIResponseShape<TOutput>(parsed)) {
246
+ return {
247
+ success: false,
248
+ errors: ['Response did not match the expected APIResponse shape'],
249
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
250
+ data: {} as TOutput
251
+ };
252
+ }
253
+ return parsed;
237
254
  } catch (error) {
238
255
  return {
239
256
  success: false,
240
257
  errors: ['Failed to parse response', ErrorUtils.getErrorString(error)],
258
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
241
259
  data: {} as TOutput
242
260
  };
243
261
  }
244
262
  }
245
263
  }
264
+
265
+ /**
266
+ * Type guard that validates the structural shape of an {@link APIResponse}.
267
+ * The generic `data` payload is trusted (not validated) since its shape is
268
+ * only known at the call site.
269
+ *
270
+ * @param value - The parsed JSON value to inspect.
271
+ */
272
+ function isAPIResponseShape<TOutput>(value: unknown): value is APIResponse<TOutput> {
273
+ if (typeof value !== 'object' || value === null) {
274
+ return false;
275
+ }
276
+ if (!('success' in value) || typeof value.success !== 'boolean') {
277
+ return false;
278
+ }
279
+ if (!('errors' in value) || !Array.isArray(value.errors)) {
280
+ return false;
281
+ }
282
+ if (!value.errors.every((err) => typeof err === 'string')) {
283
+ return false;
284
+ }
285
+ return 'data' in value;
286
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@aneuhold/core-ts-api-lib",
3
3
  "author": "Anton G. Neuhold Jr.",
4
4
  "license": "MIT",
5
- "version": "3.0.34",
5
+ "version": "3.0.35",
6
6
  "description": "A library for interacting with the backend and defining the backend API for personal projects.",
7
7
  "packageManager": "pnpm@10.25.0",
8
8
  "type": "module",
@@ -72,20 +72,20 @@
72
72
  "TypeScript"
73
73
  ],
74
74
  "dependencies": {
75
- "@aneuhold/core-ts-db-lib": "^5.0.6",
76
- "@aneuhold/core-ts-lib": "^2.4.3",
75
+ "@aneuhold/core-ts-db-lib": "^5.0.7",
76
+ "@aneuhold/core-ts-lib": "^2.4.4",
77
77
  "bson": "^7.0.0"
78
78
  },
79
79
  "devDependencies": {
80
- "@aneuhold/local-npm-registry": "^0.2.30",
81
- "@aneuhold/main-scripts": "^2.8.3",
80
+ "@aneuhold/local-npm-registry": "^0.2.31",
81
+ "@aneuhold/main-scripts": "^2.9.3",
82
82
  "@types/node": "^25.0.2",
83
83
  "eslint": "^9.39.2",
84
84
  "jsr": "^0.13.5",
85
85
  "prettier": "^3.7.4",
86
86
  "rimraf": "^6.1.2",
87
87
  "tsx": "^4.21.0",
88
- "typescript": "^5.9.3",
88
+ "typescript": "^6.0.3",
89
89
  "vitest": "^4.0.15"
90
90
  }
91
91
  }