@wishknish/knishio-client-ts 0.8.2 → 0.9.0

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 (38) hide show
  1. package/dist/index.cjs +425 -263
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +44 -17
  4. package/dist/index.d.ts +44 -17
  5. package/dist/index.iife.js +425 -265
  6. package/dist/index.iife.js.map +1 -1
  7. package/dist/index.js +447 -285
  8. package/dist/index.js.map +1 -1
  9. package/package.json +6 -9
  10. package/src/KnishIOClient.ts +180 -57
  11. package/src/constants.ts +1 -2
  12. package/src/core/Molecule.ts +53 -0
  13. package/src/core/Wallet.ts +84 -3
  14. package/src/libraries/Decimal.ts +1 -1
  15. package/src/libraries/GraphQLClient.ts +83 -0
  16. package/src/libraries/Hex.ts +4 -3
  17. package/src/libraries/UrqlClientWrapper.ts +13 -13
  18. package/src/libraries/crypto.ts +4 -4
  19. package/src/mutation/MutationClaimShadowWallet.ts +3 -1
  20. package/src/mutation/MutationTransferTokens.ts +18 -0
  21. package/src/query/QueryActiveSession.ts +0 -1
  22. package/src/query/QueryPolicy.ts +0 -1
  23. package/src/response/ResponseAuthorizationGuest.ts +2 -2
  24. package/src/response/ResponseBalance.ts +9 -6
  25. package/src/response/ResponseLinkIdentifier.ts +1 -1
  26. package/src/response/ResponseRequestAuthorizationGuest.ts +2 -2
  27. package/src/schemas/index.ts +2 -1
  28. package/src/subscribe/Subscribe.ts +3 -2
  29. package/src/types/assertions.ts +1 -6
  30. package/src/types/client.ts +12 -19
  31. package/src/types/guards.ts +2 -3
  32. package/src/types/index.ts +6 -0
  33. package/src/validation/UNIVERSAL_CONFIGURATION_INTERFACES.ts +3 -5
  34. package/src/validation/ValidationService.ts +6 -15
  35. package/src/validation/schemas.ts +5 -1
  36. package/src/versions/HashAtom.ts +11 -9
  37. package/src/query/QueryUserActivity.ts +0 -153
  38. package/src/response/ResponseQueryUserActivity.ts +0 -89
@@ -62,6 +62,22 @@ import type {
62
62
  } from '@/types'
63
63
  import type Wallet from '@/core/Wallet'
64
64
 
65
+ const CIPHER_HASH_QUERY = 'query ( $Hash: String! ) { CipherHash ( Hash: $Hash ) { hash } }'
66
+
67
+ /**
68
+ * Light parse of a GraphQL request body's operation type + root field name, for the CipherHash
69
+ * bypass decision (no full GraphQL parse needed). Operation type = the first query/mutation/
70
+ * subscription keyword (default 'query' for an anonymous `{ ... }`); root field = the first
71
+ * identifier inside the top-level selection set.
72
+ */
73
+ function parseOperation(query: string): { type: string; name: string } {
74
+ const typeMatch = (query || '').match(/\b(query|mutation|subscription)\b/i)
75
+ const type = typeMatch?.[1]?.toLowerCase() ?? 'query'
76
+ const braceIdx = (query || '').indexOf('{')
77
+ const nameMatch = braceIdx >= 0 ? query.slice(braceIdx + 1).match(/[A-Za-z_][A-Za-z0-9_]*/) : null
78
+ return { type, name: nameMatch?.[0] ?? '' }
79
+ }
80
+
65
81
  /**
66
82
  * GraphQL client wrapper for KnishIO SDK
67
83
  * Provides a unified interface for GraphQL operations
@@ -125,6 +141,10 @@ export default class GraphQLClient implements IGraphQLClient {
125
141
  return createClient({
126
142
  url: serverUri,
127
143
  exchanges,
144
+ // PQ-transport Phase E: when encryption is on, route fetch through the CipherHash wrapper
145
+ // (encrypt the request body to the validator's ML-KEM pubkey, decrypt the response).
146
+ // Omitted → urql uses the global fetch (plaintext).
147
+ ...(this.cipherLink ? { fetch: ((input: any, init: any) => this.cipherFetch(input, init)) as typeof fetch } : {}),
128
148
  fetchOptions: () => ({
129
149
  headers: {
130
150
  'X-Auth-Token': this.$__authToken
@@ -135,6 +155,69 @@ export default class GraphQLClient implements IGraphQLClient {
135
155
  })
136
156
  }
137
157
 
158
+ /**
159
+ * Whether an outgoing GraphQL request body should be wrapped in CipherHash. Bypass (plaintext):
160
+ * introspection `__schema`, `ContinuId`, the `AccessToken` mutation, and the U-isotope
161
+ * `ProposeMolecule` (auth bootstrap — the key exchange itself can't be encrypted). Mirrors the
162
+ * Kotlin/PHP/validator bypass set.
163
+ */
164
+ private shouldEncrypt(body: string): boolean {
165
+ let parsed: any
166
+ try {
167
+ parsed = JSON.parse(body)
168
+ } catch (e) {
169
+ return false
170
+ }
171
+ const { type, name } = parseOperation(parsed.query)
172
+ if (type === 'query' && (name === '__schema' || name === 'ContinuId')) return false
173
+ if (type === 'mutation' && name === 'AccessToken') return false
174
+ if (type === 'mutation' && name === 'ProposeMolecule') {
175
+ const isotope = parsed.variables?.molecule?.atoms?.[0]?.isotope
176
+ if (isotope === 'U') return false
177
+ }
178
+ return true
179
+ }
180
+
181
+ /**
182
+ * Custom `fetch` that wraps a GraphQL request in the ML-KEM CipherHash envelope and decrypts the
183
+ * response (PQ-transport Phase E). Operates on the raw POST body (mirrors the JS/Kotlin transform).
184
+ * Reads the CURRENT client wallet + validator pubkey from auth.
185
+ */
186
+ private async cipherFetch(input: any, init: any): Promise<Response> {
187
+ const wallet = this.$__wallet
188
+ const serverPubkey = this.$__pubkey
189
+ let encryptedRequest = false
190
+ let requestInit = init
191
+
192
+ if (wallet && serverPubkey && init && typeof init.body === 'string' && this.shouldEncrypt(init.body)) {
193
+ const hashVar = await wallet.encryptStringML768(init.body, serverPubkey)
194
+ requestInit = { ...init, body: JSON.stringify({ query: CIPHER_HASH_QUERY, variables: { Hash: hashVar } }) }
195
+ encryptedRequest = true
196
+ }
197
+
198
+ const response = await fetch(input, requestInit)
199
+ if (!encryptedRequest) {
200
+ return response
201
+ }
202
+
203
+ // Decrypt the CipherHash response back to the inner GraphQL response JSON.
204
+ const text = await response.text()
205
+ const init2 = { status: response.status, statusText: response.statusText, headers: response.headers }
206
+ let parsed: any
207
+ try {
208
+ parsed = JSON.parse(text)
209
+ } catch (e) {
210
+ return new Response(text, init2)
211
+ }
212
+ const hash = parsed?.data?.CipherHash?.hash
213
+ if (typeof hash !== 'string') {
214
+ // Plaintext (e.g. a validator-side error response) — pass through unchanged.
215
+ return new Response(text, init2)
216
+ }
217
+ const decrypted = await wallet!.decryptMyMessageML768(JSON.parse(hash))
218
+ return new Response(decrypted != null ? decrypted : text, init2)
219
+ }
220
+
138
221
  setAuthData({
139
222
  token,
140
223
  pubkey,
@@ -72,7 +72,8 @@ export default class Hex {
72
72
  ? ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
73
73
  : ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
74
74
 
75
- return set[Math.floor(val / 16)] + set[val % 16]
75
+ // indices are provably in-range (val is a byte; /16 and %16 are 0..15 for a 16-elem set)
76
+ return set[Math.floor(val / 16)]! + set[val % 16]!
76
77
  }
77
78
 
78
79
  const opts: Required<HexOptions> = Object.assign(
@@ -97,7 +98,7 @@ export default class Hex {
97
98
  let column = 0
98
99
 
99
100
  for (let i = 0; i < arrayLike.length; ++i) {
100
- str += numberToHex(arrayLike[i], opts.uppercase)
101
+ str += numberToHex(arrayLike[i]!, opts.uppercase)
101
102
 
102
103
  if (i === arrayLike.length - 1) {
103
104
  break
@@ -135,7 +136,7 @@ export default class Hex {
135
136
  let curr = -1
136
137
 
137
138
  for (let i = 0; i < target.length; ++i) {
138
- const c = target[i]
139
+ const c = target[i]!
139
140
  const val = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'].indexOf(c)
140
141
 
141
142
  if (val === -1) {
@@ -54,11 +54,10 @@ import {
54
54
  Client,
55
55
  OperationResult
56
56
  } from '@urql/core'
57
- import { createClient as createWSClient, Client as WSClient } from 'graphql-ws'
58
- import { pipe, map, SubscriptionLike } from 'wonka'
57
+ import { createClient as createWSClient, Client as WSClient, type SubscribePayload } from 'graphql-ws'
58
+ import { pipe, map, subscribe, type Subscription } from 'wonka'
59
59
  import type { DocumentNode } from 'graphql'
60
60
  import type Wallet from '@/core/Wallet'
61
- import { GraphQLRequestSchema, GraphQLResponseSchema, parseWithSchema } from '@/schemas'
62
61
 
63
62
  // Enhanced type definitions with proper constraints
64
63
  interface SocketConfig {
@@ -110,7 +109,7 @@ class UrqlClientWrapper {
110
109
  private $__authToken: string = ''
111
110
  private $__pubkey: string | null = null
112
111
  private $__wallet: Wallet | null = null
113
- private readonly serverUri: string
112
+ private serverUri: string
114
113
  private soketi: SocketConfig | null
115
114
  private cipherLink: boolean
116
115
  private readonly $__subscriptionManager: Map<string, SubscriptionManager> = new Map()
@@ -126,8 +125,7 @@ class UrqlClientWrapper {
126
125
 
127
126
  private createUrqlClient({
128
127
  serverUri,
129
- socket,
130
- encrypt
128
+ socket
131
129
  }: {
132
130
  serverUri: string
133
131
  socket?: SocketConfig | null
@@ -147,7 +145,7 @@ class UrqlClientWrapper {
147
145
  exchanges.push(subscriptionExchange({
148
146
  forwardSubscription: operation => ({
149
147
  subscribe: sink => {
150
- const disposable = wsClient.subscribe(operation, sink)
148
+ const disposable = wsClient.subscribe(operation as unknown as SubscribePayload, sink)
151
149
  return { unsubscribe: disposable }
152
150
  }
153
151
  })
@@ -209,12 +207,14 @@ class UrqlClientWrapper {
209
207
  throw new Error('Query is required for subscription')
210
208
  }
211
209
 
212
- const subscription: SubscriptionLike = pipe(
210
+ // wonka v6: subscribe is a pipe operator (no `.subscribe()` method on Source) → pipe(src, ..., subscribe(h))
211
+ const subscription: Subscription = pipe(
213
212
  this.$__client.subscription(query, variables),
214
213
  map(result => {
215
214
  closure(this.formatResponse<TData>(result))
216
- })
217
- ).subscribe(() => {})
215
+ }),
216
+ subscribe(() => {})
217
+ )
218
218
 
219
219
  // Store subscription for later cleanup
220
220
  if (operationName) {
@@ -238,8 +238,8 @@ class UrqlClientWrapper {
238
238
  if (result.error) {
239
239
  response.errors = [{
240
240
  message: result.error.message,
241
- locations: result.error.graphQLErrors?.[0]?.locations,
242
- path: result.error.graphQLErrors?.[0]?.path,
241
+ locations: result.error.graphQLErrors?.[0]?.locations as { line: number; column: number }[] | undefined,
242
+ path: result.error.graphQLErrors?.[0]?.path as (string | number)[] | undefined,
243
243
  extensions: result.error.graphQLErrors?.[0]?.extensions
244
244
  }]
245
245
  }
@@ -263,7 +263,7 @@ class UrqlClientWrapper {
263
263
  }
264
264
 
265
265
  unsubscribeAll(): void {
266
- this.$__subscriptionManager.forEach((subscription, operationName) => {
266
+ this.$__subscriptionManager.forEach((_subscription, operationName) => {
267
267
  this.unsubscribe(operationName)
268
268
  })
269
269
  }
@@ -178,10 +178,10 @@ export function generateSecretEnhanced(options: SecretGenerationOptions): string
178
178
  * Uses SHAKE256 with 256-bit output (64 hex characters)
179
179
  */
180
180
  export function generateBundleHash(secret: string, _source: string | null = null): BundleHash {
181
- if (!secret || secret.length === 0) {
182
- throw new Error('Secret is required for bundle hash generation')
183
- }
184
-
181
+ // Cross-SDK parity (c142): an empty secret hashes to shake256("") = 46b9dd2b… — matching
182
+ // JS/PHP/Kotlin/Rust/Python + the committed `empty_secret` vector. (Previously threw;
183
+ // no real caller passes an empty secret.)
184
+ //
185
185
  // CRITICAL: source parameter must be IGNORED to match JS SDK behavior.
186
186
  // JS SDK's generateBundleHash() accepts source but never uses it.
187
187
  // The bundle hash is an identity anchor and must be deterministic from secret alone.
@@ -71,9 +71,11 @@ export default class MutationClaimShadowWallet extends MutationProposeMolecule {
71
71
  bundle: this.$__molecule.bundle,
72
72
  token,
73
73
  batchId
74
- })
74
+ });
75
75
 
76
76
  // Initialize shadow wallet claim on the molecule
77
+ // (leading-paren statements REQUIRE the preceding semicolon — without it ASI parses
78
+ // `Wallet.create({...})(this.$__molecule)` and throws "Wallet.create(...) is not a function")
77
79
  (this.$__molecule as any).initShadowWalletClaim(wallet)
78
80
  this.$__molecule.sign({})
79
81
  this.$__molecule.check()
@@ -74,6 +74,24 @@ export default class MutationTransferTokens extends MutationProposeMolecule {
74
74
  (this.$__molecule as any).check((this.$__molecule as any).sourceWallet)
75
75
  }
76
76
 
77
+ /**
78
+ * Fills the Molecule for a MULTI-recipient transfer (one source funds N recipients)
79
+ */
80
+ fillMoleculeMulti({
81
+ recipientWallets,
82
+ amounts
83
+ }: {
84
+ recipientWallets: Wallet[]
85
+ amounts: number[]
86
+ }): void {
87
+ (this.$__molecule as any).initValues({
88
+ recipientWallets,
89
+ amounts
90
+ });
91
+ (this.$__molecule as any).sign({});
92
+ (this.$__molecule as any).check((this.$__molecule as any).sourceWallet)
93
+ }
94
+
77
95
  /**
78
96
  * Builds a Response object out of a JSON object
79
97
  * Matches JavaScript SDK createResponse method signature exactly
@@ -51,7 +51,6 @@ import ResponseQueryActiveSession from '@/response/ResponseQueryActiveSession'
51
51
  import { gql } from '@urql/core'
52
52
  import type { GraphQLClient as IGraphQLClient } from '@/types'
53
53
  import type KnishIOClient from '@/KnishIOClient'
54
- import type { BundleHash, MetaType, MetaId } from '@/types'
55
54
 
56
55
  /**
57
56
  * Query for retrieving a list of active User Sessions
@@ -51,7 +51,6 @@ import ResponsePolicy from '@/response/ResponsePolicy'
51
51
  import { gql } from '@urql/core'
52
52
  import type { GraphQLClient as IGraphQLClient } from '@/types'
53
53
  import type KnishIOClient from '@/KnishIOClient'
54
- import type { MetaType, MetaId } from '@/types'
55
54
 
56
55
  /**
57
56
  * Query for retrieving Policy information
@@ -75,14 +75,14 @@ export default class ResponseAuthorizationGuest extends Response {
75
75
  /**
76
76
  * Returns the reason for rejection
77
77
  */
78
- reason(): string {
78
+ override reason(): string {
79
79
  return 'Invalid response from server'
80
80
  }
81
81
 
82
82
  /**
83
83
  * Returns whether molecule was accepted or not
84
84
  */
85
- success(): boolean {
85
+ override success(): boolean {
86
86
  return this.payload() !== null
87
87
  }
88
88
 
@@ -48,6 +48,7 @@ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
48
48
 
49
49
  import Response from './Response'
50
50
  import Wallet from '../core/Wallet'
51
+ import TokenUnit from '../core/TokenUnit'
51
52
  import type Query from '../query/Query'
52
53
 
53
54
  /**
@@ -134,12 +135,14 @@ export default class ResponseBalance extends Response {
134
135
  wallet.tokenFungibility = data.token.fungibility
135
136
  }
136
137
 
137
- // TODO: Implement TokenUnit support when TokenUnit class is available
138
- // if (data.tokenUnits && data.tokenUnits.length) {
139
- // for (const tokenUnitData of data.tokenUnits) {
140
- // wallet.tokenUnits.push(TokenUnit.createFromGraphQL(tokenUnitData))
141
- // }
142
- // }
138
+ // Stackable (NFT) token units — the validator serves Wallet.tokenUnits and QueryBalance
139
+ // selects them; parse them so a queried source wallet carries its units (otherwise every
140
+ // stackable transfer silently degrades to fungible). Mirrors ResponseWalletList.toClientWallet.
141
+ if (data.tokenUnits && data.tokenUnits.length) {
142
+ for (const tokenUnitData of data.tokenUnits) {
143
+ wallet.tokenUnits.push(TokenUnit.createFromGraphQL(tokenUnitData))
144
+ }
145
+ }
143
146
 
144
147
  // Set trade rates if available
145
148
  if (data.tradeRates && data.tradeRates.length) {
@@ -74,7 +74,7 @@ export default class ResponseLinkIdentifier extends Response {
74
74
  /**
75
75
  * Returns success status
76
76
  */
77
- success(): any {
77
+ override success(): any {
78
78
  return Dot.get(this.data(), 'set')
79
79
  }
80
80
 
@@ -75,14 +75,14 @@ export default class ResponseRequestAuthorizationGuest extends Response {
75
75
  /**
76
76
  * Returns the reason for rejection
77
77
  */
78
- reason(): string {
78
+ override reason(): string {
79
79
  return 'Invalid response from server'
80
80
  }
81
81
 
82
82
  /**
83
83
  * Returns whether authorization was successful
84
84
  */
85
- success(): boolean {
85
+ override success(): boolean {
86
86
  return this.payload() !== null
87
87
  }
88
88
 
@@ -191,7 +191,8 @@ export const KnishIOClientConfigSchema = z.object({
191
191
  client: z.unknown().optional(),
192
192
  socket: z.unknown().optional(),
193
193
  serverSdkVersion: z.number().int().min(1).optional(),
194
- logging: z.boolean().optional()
194
+ logging: z.boolean().optional(),
195
+ defaultRequestPolicy: z.enum(['cache-first', 'cache-only', 'network-only', 'cache-and-network']).nullable().optional()
195
196
  }).strict()
196
197
 
197
198
  // =============================================================================
@@ -49,7 +49,6 @@ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
49
49
  import type { GraphQLClient as IGraphQLClient } from '@/types'
50
50
  import { CodeException } from '@/exception'
51
51
  import type { DocumentNode } from 'graphql'
52
- import { GraphQLRequestSchema, parseWithSchema } from '@/schemas'
53
52
 
54
53
  // Define subscription-specific types for better type safety
55
54
  interface SubscriptionRequest {
@@ -72,7 +71,9 @@ interface SubscriptionCreateParams {
72
71
  }
73
72
 
74
73
  // Type for GraphQL client with subscription support
75
- interface GraphQLSubscriptionClient extends IGraphQLClient {
74
+ // Omit the base's required subscribe/getUri, then re-add as OPTIONAL variants (a passed client may
75
+ // not implement them) — avoids TS2430 (an optional member can't override a required base member).
76
+ interface GraphQLSubscriptionClient extends Omit<IGraphQLClient, 'subscribe' | 'getUri'> {
76
77
  subscribe?: (request: SubscriptionRequest, closure: (result: unknown) => void) => Promise<string>
77
78
  getUri?: () => string | null
78
79
  }
@@ -58,19 +58,14 @@ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
58
58
 
59
59
  import type {
60
60
  AtomIsotope,
61
- KnishIOErrorType,
62
- QueryType,
63
- MutationType,
64
- SubscriptionType
61
+ KnishIOErrorType
65
62
  } from '../constants'
66
63
 
67
64
  import type {
68
65
  WalletAddress,
69
66
  BundleHash,
70
67
  TokenSlug,
71
- Position,
72
68
  MolecularHash,
73
- CellSlug,
74
69
  BatchId,
75
70
  AtomParams,
76
71
  WalletParams,
@@ -129,6 +129,18 @@ export interface TransferTokenOptions {
129
129
  callbackUrl?: string | null
130
130
  }
131
131
 
132
+ /**
133
+ * One destination of a multi-recipient transfer (see KnishIOClient.transferTokens).
134
+ * Provide EITHER units (stackable/NFT: amount = units.length) OR amount (fungible), not both.
135
+ * batchId makes the recipient a claimable shadow under that batch.
136
+ */
137
+ export interface TransferRecipientInput {
138
+ bundleHash: BundleHash | string
139
+ units?: string[]
140
+ amount?: number | null
141
+ batchId?: string | null
142
+ }
143
+
132
144
  export interface RequestTokensOptions {
133
145
  to: WalletAddress | string
134
146
  amount: number | string
@@ -269,16 +281,6 @@ export interface ActiveSessionOptions {
269
281
  metaId?: MetaId | string | null
270
282
  }
271
283
 
272
- export interface UserActivityOptions {
273
- bundleHash?: BundleHash | string | null
274
- metaType?: MetaType | string | null
275
- metaId?: MetaId | string | null
276
- dateFrom?: string | null
277
- dateTo?: string | null
278
- limit?: number | null
279
- offset?: number | null
280
- }
281
-
282
284
  export interface PolicyQueryOptions {
283
285
  metaType?: MetaType | string | null
284
286
  metaId?: MetaId | string | null
@@ -478,15 +480,6 @@ export interface ResponseQueryActiveSession extends BaseResponse<{
478
480
  expiresAt: string
479
481
  }> {}
480
482
 
481
- export interface ResponseQueryUserActivity extends BaseResponse<{
482
- activities: Array<{
483
- type: string
484
- description: string
485
- data: Record<string, unknown>
486
- createdAt: string
487
- }>
488
- }> {}
489
-
490
483
  export interface ResponsePolicy extends BaseResponse<{
491
484
  policy: Record<string, unknown>
492
485
  metaType: MetaType | null
@@ -239,7 +239,7 @@ export function isKnishIOErrorType(value: unknown): value is KnishIOErrorType {
239
239
  'WRONG_TOKEN_TYPE_ERROR'
240
240
  ] as const
241
241
 
242
- return typeof value === 'string' && errorTypes.includes(value as KnishIOErrorType)
242
+ return typeof value === 'string' && (errorTypes as readonly string[]).includes(value)
243
243
  }
244
244
 
245
245
  /**
@@ -282,8 +282,7 @@ export function isQueryType(value: unknown): value is QueryType {
282
282
  'Batch',
283
283
  'ActiveSession',
284
284
  'Policy',
285
- 'Token',
286
- 'UserActivity'
285
+ 'Token'
287
286
  ] as const
288
287
 
289
288
  return typeof value === 'string' && queryTypes.includes(value as QueryType)
@@ -371,6 +371,12 @@ export interface SignatureResult {
371
371
  // GRAPHQL OPERATION TYPES
372
372
  // =============================================================================
373
373
 
374
+ /**
375
+ * urql request policy (mirrors @urql/core's RequestPolicy). Controls whether a
376
+ * read hits the client's in-memory cache. 'network-only' bypasses it entirely.
377
+ */
378
+ export type RequestPolicy = 'cache-first' | 'cache-only' | 'network-only' | 'cache-and-network'
379
+
374
380
  export interface GraphQLRequest {
375
381
  query: string
376
382
  variables?: Record<string, unknown>
@@ -61,8 +61,6 @@ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
61
61
  * - Performance optimization hints
62
62
  */
63
63
 
64
- import { z } from 'zod'
65
- import type { ValidationResult } from './ValidationService'
66
64
 
67
65
  // =============================================================================
68
66
  // UNIVERSAL CONFIGURATION TYPES
@@ -227,16 +225,16 @@ export class ConfigValidator {
227
225
 
228
226
  // Enhanced validations
229
227
  if (options.includeWarnings !== false) {
230
- result.warnings.push(...this.generateWarnings(result.data))
228
+ result.warnings.push(...this.generateWarnings(result.data!))
231
229
  }
232
230
 
233
231
  if (options.includeRecommendations !== false) {
234
- result.recommendations.push(...this.generateRecommendations(result.data, options))
232
+ result.recommendations.push(...this.generateRecommendations(result.data!, options))
235
233
  }
236
234
 
237
235
  // Performance validation
238
236
  if (options.includePerformanceHints !== false) {
239
- result.warnings.push(...this.generatePerformanceWarnings(result.data))
237
+ result.warnings.push(...this.generatePerformanceWarnings(result.data!))
240
238
  }
241
239
 
242
240
  } catch (error) {
@@ -57,20 +57,9 @@ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
57
57
  */
58
58
 
59
59
  import { z } from 'zod'
60
- import type {
61
- KnishIOClientConfig,
62
- TransferParams,
63
- CreateTokenParams,
64
- RequestTokensParams,
65
- BalanceQueryParams,
66
- MetaQueryParams,
67
- WalletQueryParams
68
- } from '@/types'
69
60
  import {
70
61
  Schemas,
71
62
  safeParse,
72
- validateStrict,
73
- validateWithDefaults,
74
63
  createTypeGuard,
75
64
  type ValidatedClientConfig,
76
65
  type ValidatedTransferParams,
@@ -164,8 +153,8 @@ export class ValidationService {
164
153
  this.logValidation('ClientConfig', true)
165
154
  return {
166
155
  success: true,
167
- data: parseResult.data,
168
- warnings: this.generateConfigWarnings(parseResult.data)
156
+ data: parseResult.data as ValidatedClientConfig,
157
+ warnings: this.generateConfigWarnings(parseResult.data as ValidatedClientConfig)
169
158
  }
170
159
  } catch (error) {
171
160
  this.logValidation('ClientConfig', false, String(error))
@@ -193,7 +182,9 @@ export class ValidationService {
193
182
  KNISHIO_SERVER_SDK_VERSION: process.env.KNISHIO_SERVER_SDK_VERSION
194
183
  }
195
184
 
196
- const parseResult = safeParse(Schemas.EnvironmentConfig, env)
185
+ // cast: EnvironmentConfig uses .default()/.optional() so its inferred input≠output type and
186
+ // doesn't structurally match safeParse's ZodType<T> param; ZodTypeAny sidesteps the mismatch.
187
+ const parseResult = safeParse(Schemas.EnvironmentConfig as z.ZodTypeAny, env)
197
188
 
198
189
  if (!parseResult.success) {
199
190
  return {
@@ -320,7 +311,7 @@ export class ValidationService {
320
311
  received: 'error',
321
312
  path: [],
322
313
  message: err.message
323
- } as z.ZodIssue)),
314
+ } as unknown as z.ZodIssue)),
324
315
  context: 'GraphQLResponse'
325
316
  }
326
317
  }
@@ -206,7 +206,11 @@ export const KnishIOClientConfigSchema = z.object({
206
206
  client: z.unknown().optional(),
207
207
  socket: z.unknown().optional(),
208
208
  serverSdkVersion: z.number().int().min(1).max(10).default(4),
209
- logging: z.boolean().default(false)
209
+ logging: z.boolean().default(false),
210
+ // Optional default urql request policy for reads (server/sync clients pass
211
+ // 'network-only'). Permitted by the strict schema so the constructor option
212
+ // isn't rejected.
213
+ defaultRequestPolicy: z.enum(['cache-first', 'cache-only', 'network-only', 'cache-and-network']).nullable().optional()
210
214
  }).strict()
211
215
 
212
216
  // Environment configuration with validation
@@ -49,17 +49,19 @@ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
49
49
  import type Atom from '@/core/Atom'
50
50
 
51
51
  // Define structured value types for better type safety
52
- type StructuredValue =
53
- | string
54
- | number
55
- | boolean
56
- | null
52
+ type StructuredValue =
53
+ | string
54
+ | number
55
+ | boolean
56
+ | null
57
57
  | undefined
58
58
  | StructuredObject
59
59
  | StructuredArray
60
60
 
61
- type StructuredObject = Record<string, StructuredValue>
62
- type StructuredArray = StructuredValue[]
61
+ // interfaces (not type aliases) so the recursion goes alias->interface->alias, which TS permits
62
+ // (an all-type-alias cycle triggers TS2456 "circularly references itself").
63
+ interface StructuredObject { [key: string]: StructuredValue }
64
+ interface StructuredArray extends Array<StructuredValue> {}
63
65
 
64
66
  // Define types for hashable values
65
67
  type HashableValue =
@@ -95,7 +97,7 @@ export default class HashAtom {
95
97
  // Extract atom properties with type safety
96
98
  for (const key of Object.keys(atom)) {
97
99
  if (Object.prototype.hasOwnProperty.call(atom, key)) {
98
- parameters[key] = (atom as Record<string, unknown>)[key]
100
+ parameters[key] = (atom as unknown as Record<string, unknown>)[key]
99
101
  }
100
102
  }
101
103
 
@@ -113,7 +115,7 @@ export default class HashAtom {
113
115
 
114
116
  if (isArray(object)) {
115
117
  const result: StructuredArray = []
116
- for (const value of object) {
118
+ for (const value of (object as unknown[])) {
117
119
  result.push(HashAtom.isStructure(value) ? HashAtom.structure(value) : value as StructuredValue)
118
120
  }
119
121
  return result