@wishknish/knishio-client-ts 0.9.4 → 0.9.6
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/dist/index.cjs +355 -253
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -28
- package/dist/index.d.ts +32 -28
- package/dist/index.iife.js +21183 -5371
- package/dist/index.iife.js.map +1 -1
- package/dist/index.js +355 -253
- package/dist/index.js.map +1 -1
- package/package.json +20 -23
- package/src/KnishIOClient.ts +37 -8
- package/src/core/AtomMeta.ts +12 -4
- package/src/core/Molecule.ts +61 -0
- package/src/core/Wallet.ts +7 -5
- package/src/index.ts +4 -1
- package/src/instance/rules/Callback.ts +1 -1
- package/src/libraries/GraphQLClient.ts +21 -7
- package/src/libraries/UrqlClientWrapper.ts +8 -3
- package/src/libraries/crypto.ts +4 -6
- package/src/mutation/MutationReplenishToken.ts +28 -0
- package/src/schemas/index.ts +31 -16
- package/src/types/assertions.ts +2 -2
- package/src/validation/ValidationService.ts +1 -3
- package/src/validation/schemas.ts +40 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wishknish/knishio-client-ts",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"productName": "Knish.IO TypeScript SDK Client",
|
|
5
5
|
"description": "TypeScript implementation of the Knish.IO SDK to consume Knish.IO GraphQL APIs with enhanced type safety.",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -58,32 +58,30 @@
|
|
|
58
58
|
"fips-203"
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@noble/post-quantum": "^0.
|
|
62
|
-
"@
|
|
63
|
-
"
|
|
64
|
-
"graphql": "^
|
|
65
|
-
"
|
|
66
|
-
"isomorphic-fetch": "^3.0.0",
|
|
67
|
-
"jssha": "^3.3.1",
|
|
61
|
+
"@noble/post-quantum": "^0.7.0",
|
|
62
|
+
"@urql/core": "^6.0.3",
|
|
63
|
+
"graphql": "^16.14.2",
|
|
64
|
+
"graphql-ws": "^6.2.1",
|
|
65
|
+
"jssha": "^3.3.2",
|
|
68
66
|
"tslib": "^2.8.1",
|
|
69
|
-
"wonka": "^6.3.
|
|
70
|
-
"zod": "^
|
|
67
|
+
"wonka": "^6.3.6",
|
|
68
|
+
"zod": "^4.5.4"
|
|
71
69
|
},
|
|
72
70
|
"devDependencies": {
|
|
73
|
-
"@types/isomorphic-fetch": "^0.0.39",
|
|
74
71
|
"@types/jssha": "^3.0.0",
|
|
75
|
-
"@types/node": "^
|
|
76
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
77
|
-
"@typescript-eslint/parser": "^8.
|
|
78
|
-
"@vitest/coverage-v8": "^4.1.
|
|
79
|
-
"eslint": "^
|
|
80
|
-
"prettier": "^3",
|
|
72
|
+
"@types/node": "^26",
|
|
73
|
+
"@typescript-eslint/eslint-plugin": "^8.67.0",
|
|
74
|
+
"@typescript-eslint/parser": "^8.67.0",
|
|
75
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
76
|
+
"eslint": "^10",
|
|
77
|
+
"prettier": "^3.9.6",
|
|
81
78
|
"tsup": "^8.5.1",
|
|
82
|
-
"tsx": "^4.23.
|
|
79
|
+
"tsx": "^4.23.12",
|
|
83
80
|
"typescript": "^5",
|
|
84
|
-
"
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
"vitest": "^4.1.11"
|
|
82
|
+
},
|
|
83
|
+
"resolutions": {
|
|
84
|
+
"wonka": "6.3.6"
|
|
87
85
|
},
|
|
88
86
|
"browserslist": [
|
|
89
87
|
"> 1%",
|
|
@@ -103,7 +101,6 @@
|
|
|
103
101
|
"test:watch": "vitest",
|
|
104
102
|
"test:coverage": "vitest run --coverage",
|
|
105
103
|
"test:ui": "vitest --ui",
|
|
106
|
-
"dev": "vite",
|
|
107
104
|
"prepublishOnly": "npm run build",
|
|
108
105
|
"selftest": "npm run build && node self-test.cjs"
|
|
109
106
|
},
|
|
@@ -113,7 +110,7 @@
|
|
|
113
110
|
"README.md",
|
|
114
111
|
"LICENSE"
|
|
115
112
|
],
|
|
116
|
-
"packageManager": "yarn@4.
|
|
113
|
+
"packageManager": "yarn@4.15.0",
|
|
117
114
|
"engines": {
|
|
118
115
|
"node": ">=18.0.0"
|
|
119
116
|
}
|
package/src/KnishIOClient.ts
CHANGED
|
@@ -104,6 +104,7 @@ import MutationLinkIdentifier from '@/mutation/MutationLinkIdentifier'
|
|
|
104
104
|
import MutationPeering from '@/mutation/MutationPeering'
|
|
105
105
|
import MutationAppendRequest from '@/mutation/MutationAppendRequest'
|
|
106
106
|
import MutationProposeMolecule from '@/mutation/MutationProposeMolecule'
|
|
107
|
+
import MutationReplenishToken from '@/mutation/MutationReplenishToken'
|
|
107
108
|
|
|
108
109
|
// Response imports
|
|
109
110
|
import ResponseProposeMolecule from '@/response/ResponseProposeMolecule'
|
|
@@ -1836,27 +1837,55 @@ export default class KnishIOClient {
|
|
|
1836
1837
|
}
|
|
1837
1838
|
|
|
1838
1839
|
/**
|
|
1839
|
-
* Replenish
|
|
1840
|
+
* Replenish a non-finite token supply.
|
|
1841
|
+
* Matches JS SDK KnishIOClient.replenishToken (KnishIOClient.js:2195-2231).
|
|
1840
1842
|
*/
|
|
1841
1843
|
async replenishToken({
|
|
1842
1844
|
token,
|
|
1843
1845
|
amount = null,
|
|
1844
1846
|
units = null,
|
|
1845
|
-
sourceWallet
|
|
1847
|
+
sourceWallet = null
|
|
1846
1848
|
}: {
|
|
1847
1849
|
token: TokenSlug | string
|
|
1848
1850
|
amount?: number | string | null
|
|
1849
|
-
units?: string
|
|
1851
|
+
units?: Array<[string, string, Record<string, any>?]> | null
|
|
1850
1852
|
sourceWallet?: Wallet | null
|
|
1851
1853
|
}): Promise<Response> {
|
|
1852
1854
|
this.log('info', `KnishIOClient::replenishToken() - Replenishing ${amount || 'units'} of ${token}...`)
|
|
1853
1855
|
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1856
|
+
if (!sourceWallet) {
|
|
1857
|
+
sourceWallet = (await this.queryBalance({ token }))?.payload() as Wallet | null
|
|
1858
|
+
}
|
|
1859
|
+
if (!sourceWallet) {
|
|
1860
|
+
throw new TransferBalanceException('Source wallet is missing or invalid.')
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// Remainder wallet (same token, so the V-isotope pair conserves)
|
|
1864
|
+
const remainderWallet = sourceWallet.createRemainder(this.getSecret())
|
|
1865
|
+
|
|
1866
|
+
const molecule = await this.createMolecule({
|
|
1867
|
+
sourceWallet,
|
|
1868
|
+
remainderWallet
|
|
1869
|
+
})
|
|
1870
|
+
molecule.replenishToken({
|
|
1871
|
+
amount: Number(amount ?? 0),
|
|
1872
|
+
units: units ?? []
|
|
1859
1873
|
})
|
|
1874
|
+
molecule.sign({ bundle: this.getBundle() })
|
|
1875
|
+
molecule.check()
|
|
1876
|
+
|
|
1877
|
+
const mutation = await this.createMoleculeMutation({
|
|
1878
|
+
mutationClass: MutationReplenishToken,
|
|
1879
|
+
molecule
|
|
1880
|
+
})
|
|
1881
|
+
|
|
1882
|
+
const response = await this.executeQuery(mutation)
|
|
1883
|
+
|
|
1884
|
+
if (!response) {
|
|
1885
|
+
throw new CodeException('Token replenishment failed')
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
return response
|
|
1860
1889
|
}
|
|
1861
1890
|
|
|
1862
1891
|
/**
|
package/src/core/AtomMeta.ts
CHANGED
|
@@ -47,6 +47,7 @@ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
|
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
import Meta, { type NormalizedMeta } from './Meta'
|
|
50
|
+
import PolicyMeta from './PolicyMeta'
|
|
50
51
|
|
|
51
52
|
// Configuration constants matching JS SDK
|
|
52
53
|
const USE_META_CONTEXT = false
|
|
@@ -172,12 +173,19 @@ export default class AtomMeta {
|
|
|
172
173
|
* @return This instance for chaining
|
|
173
174
|
*/
|
|
174
175
|
addPolicy(policy: Record<string, any>): AtomMeta {
|
|
175
|
-
//
|
|
176
|
-
//
|
|
176
|
+
// Normalise through PolicyMeta before serialising. PolicyMeta.fillDefault adds a default
|
|
177
|
+
// entry for every meta key not already covered by the caller's policy, so the stored string
|
|
178
|
+
// carries materially more than the input — and this string is hashed into the atom, so a
|
|
179
|
+
// raw JSON.stringify(policy) diverges from every other SDK.
|
|
180
|
+
// Matches JS SDK AtomMeta.addPolicy (AtomMeta.js:170-175), including passing
|
|
181
|
+
// Object.keys(this.meta): `meta` is a NormalizedMeta[] in both SDKs, so these are array
|
|
182
|
+
// indices rather than meta key names. Faithful to the JS behaviour — do not "correct" it.
|
|
183
|
+
const policyMeta = new PolicyMeta(policy, Object.keys(this.meta))
|
|
184
|
+
|
|
177
185
|
this.merge({
|
|
178
|
-
policy:
|
|
186
|
+
policy: policyMeta.toJson()
|
|
179
187
|
})
|
|
180
|
-
|
|
188
|
+
|
|
181
189
|
return this
|
|
182
190
|
}
|
|
183
191
|
|
package/src/core/Molecule.ts
CHANGED
|
@@ -755,6 +755,67 @@ export default class Molecule {
|
|
|
755
755
|
return this
|
|
756
756
|
}
|
|
757
757
|
|
|
758
|
+
/**
|
|
759
|
+
* Replenishes non-finite token supplies.
|
|
760
|
+
* Matches JS SDK Molecule.replenishToken (Molecule.js:521-566) exactly.
|
|
761
|
+
*
|
|
762
|
+
* Two orderings here are load-bearing for the molecular hash and must not be reordered:
|
|
763
|
+
* the remainder balance is computed BEFORE the source balance is overwritten, and the
|
|
764
|
+
* source V-atom is added BEFORE the remainder V-atom.
|
|
765
|
+
*/
|
|
766
|
+
replenishToken({
|
|
767
|
+
amount,
|
|
768
|
+
units = []
|
|
769
|
+
}: {
|
|
770
|
+
amount: number
|
|
771
|
+
units?: Array<[string, string, Record<string, any>?]>
|
|
772
|
+
}): Molecule {
|
|
773
|
+
if (amount < 0) {
|
|
774
|
+
throw new NegativeAmountException('Molecule::replenishToken() - Amount to replenish must be positive!')
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (!this.sourceWallet || !this.remainderWallet) {
|
|
778
|
+
throw new Error('Source and remainder wallets required for token replenishment')
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
if (units.length) {
|
|
782
|
+
// Prepare token units to formatted style
|
|
783
|
+
const formatted = Wallet.getTokenUnits(units)
|
|
784
|
+
|
|
785
|
+
// Merge token units with source wallet & new items
|
|
786
|
+
this.remainderWallet.tokenUnits = this.sourceWallet.tokenUnits
|
|
787
|
+
for (const unit of formatted) {
|
|
788
|
+
this.remainderWallet.tokenUnits.push(unit)
|
|
789
|
+
}
|
|
790
|
+
this.remainderWallet.balance = String(this.remainderWallet.tokenUnits.length)
|
|
791
|
+
|
|
792
|
+
// Override first atom's token units to replenish values
|
|
793
|
+
this.sourceWallet.tokenUnits = formatted
|
|
794
|
+
this.sourceWallet.balance = String(this.sourceWallet.tokenUnits.length)
|
|
795
|
+
} else {
|
|
796
|
+
// Update wallet's balances
|
|
797
|
+
this.remainderWallet.balance = String(Number(this.sourceWallet.balance) + amount)
|
|
798
|
+
this.sourceWallet.balance = String(amount)
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Initializing a new Atom to remove tokens from source
|
|
802
|
+
this.addAtom(Atom.create({
|
|
803
|
+
isotope: 'V',
|
|
804
|
+
wallet: this.sourceWallet,
|
|
805
|
+
value: Number(this.sourceWallet.balance)
|
|
806
|
+
}))
|
|
807
|
+
|
|
808
|
+
this.addAtom(Atom.create({
|
|
809
|
+
isotope: 'V',
|
|
810
|
+
wallet: this.remainderWallet,
|
|
811
|
+
value: Number(this.remainderWallet.balance),
|
|
812
|
+
metaType: 'walletBundle',
|
|
813
|
+
metaId: this.remainderWallet.bundle!
|
|
814
|
+
}))
|
|
815
|
+
|
|
816
|
+
return this
|
|
817
|
+
}
|
|
818
|
+
|
|
758
819
|
/**
|
|
759
820
|
* Initialize authorization request
|
|
760
821
|
* Creates U-isotope (authorization) atom for requesting auth token
|
package/src/core/Wallet.ts
CHANGED
|
@@ -51,6 +51,7 @@ import { randomString, chunkSubstr, isHex } from '@/libraries/strings'
|
|
|
51
51
|
import { generateBundleHash, generateSecret, shake256, generateBatchId } from '@/libraries/crypto'
|
|
52
52
|
import WalletCredentialException from '@/exception/WalletCredentialException'
|
|
53
53
|
import { isBundleHash } from '@/types'
|
|
54
|
+
import TokenUnit from '@/core/TokenUnit'
|
|
54
55
|
// Post-quantum cryptography for ML-KEM768 key encapsulation
|
|
55
56
|
import { ml_kem768 } from '@noble/post-quantum/ml-kem.js'
|
|
56
57
|
|
|
@@ -271,12 +272,13 @@ export default class Wallet {
|
|
|
271
272
|
}
|
|
272
273
|
|
|
273
274
|
/**
|
|
274
|
-
*
|
|
275
|
-
*
|
|
275
|
+
* Map raw token-unit tuples to TokenUnit instances.
|
|
276
|
+
* Matches JS SDK Wallet.getTokenUnits (Wallet.js:190-196). The serialised shape reaches hashed
|
|
277
|
+
* atom meta via AtomMeta.setAtomWallet -> JSON.stringify(getTokenUnitsData()), so returning raw
|
|
278
|
+
* tuples here would diverge from every other SDK.
|
|
276
279
|
*/
|
|
277
|
-
static getTokenUnits(unitsData: any
|
|
278
|
-
|
|
279
|
-
return unitsData
|
|
280
|
+
static getTokenUnits(unitsData: Array<[string, string, Record<string, any>?]>): TokenUnit[] {
|
|
281
|
+
return unitsData.map(unitData => TokenUnit.createFromDB(unitData))
|
|
280
282
|
}
|
|
281
283
|
|
|
282
284
|
/**
|
package/src/index.ts
CHANGED
|
@@ -359,7 +359,10 @@ export {
|
|
|
359
359
|
// VERSION AND SDK INFORMATION
|
|
360
360
|
// =============================================================================
|
|
361
361
|
|
|
362
|
-
|
|
362
|
+
// MUST equal package.json's "version". The CI `version consistency` job enforces this via
|
|
363
|
+
// .github/scripts/check-version.sh, which parses this exact line — keep the literal form
|
|
364
|
+
// `export const SDK_VERSION = '<semver>'` intact so the gate can read it.
|
|
365
|
+
export const SDK_VERSION = '0.9.6'
|
|
363
366
|
export const SDK_NAME = 'KnishIO-Client-TS'
|
|
364
367
|
export const COMPATIBLE_SERVER_VERSIONS = [4, 5]
|
|
365
368
|
|
|
@@ -69,7 +69,7 @@ const CallbackParamsSchema = z.object({
|
|
|
69
69
|
action: z.string().min(1, 'Action cannot be empty'),
|
|
70
70
|
metaType: z.union([z.string(), z.null()]).optional(),
|
|
71
71
|
metaId: z.union([z.string(), z.null()]).optional(),
|
|
72
|
-
meta: z.union([z.instanceof(Meta), z.record(z.unknown()), z.null()]).optional(),
|
|
72
|
+
meta: z.union([z.instanceof(Meta), z.record(z.string(), z.unknown()), z.null()]).optional(),
|
|
73
73
|
address: z.union([z.string(), z.null()]).optional(),
|
|
74
74
|
token: z.union([z.string(), z.null()]).optional(),
|
|
75
75
|
amount: z.union([z.string(), z.null()]).optional(),
|
|
@@ -141,16 +141,30 @@ export default class GraphQLClient implements IGraphQLClient {
|
|
|
141
141
|
return createClient({
|
|
142
142
|
url: serverUri,
|
|
143
143
|
exchanges,
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
|
|
144
|
+
// urql 5 had no default and always POSTed; urql 6 defaults to 'within-url-limit', which
|
|
145
|
+
// URL-encodes short queries and sends NO body. That would silently disable the CipherHash
|
|
146
|
+
// envelope below — cipherFetch's `typeof init.body === 'string'` guard fails on a GET, so
|
|
147
|
+
// the query would leave as plaintext URL parameters with no error. Pin POST explicitly.
|
|
148
|
+
preferGetMethod: false,
|
|
149
|
+
// Always route through our own fetch. Two reasons: (1) PQ-transport Phase E — when
|
|
150
|
+
// encryption is on, cipherFetch wraps the request body in the CipherHash envelope and
|
|
151
|
+
// decrypts the response; (2) the 60s timeout. urql's makeFetchSource unconditionally
|
|
152
|
+
// overwrites init.signal with its own AbortController, so a signal returned from
|
|
153
|
+
// fetchOptions() is discarded and never fired. Combining it here is the only place it
|
|
154
|
+
// survives.
|
|
155
|
+
fetch: ((input: RequestInfo | URL, init?: RequestInit) => {
|
|
156
|
+
const timeoutSignal = AbortSignal.timeout(60000)
|
|
157
|
+
// Keep urql's own signal so teardown still aborts; AbortSignal.any needs Node >= 18.17.
|
|
158
|
+
const signal = init?.signal && typeof AbortSignal.any === 'function'
|
|
159
|
+
? AbortSignal.any([init.signal, timeoutSignal])
|
|
160
|
+
: (init?.signal ?? timeoutSignal)
|
|
161
|
+
const timedInit: RequestInit = { ...init, signal }
|
|
162
|
+
return this.cipherLink ? this.cipherFetch(input, timedInit) : fetch(input, timedInit)
|
|
163
|
+
}) as typeof fetch,
|
|
148
164
|
fetchOptions: () => ({
|
|
149
165
|
headers: {
|
|
150
166
|
'X-Auth-Token': this.$__authToken
|
|
151
|
-
}
|
|
152
|
-
// Add 60 second timeout
|
|
153
|
-
signal: AbortSignal.timeout(60000)
|
|
167
|
+
}
|
|
154
168
|
})
|
|
155
169
|
})
|
|
156
170
|
}
|
|
@@ -155,12 +155,17 @@ class UrqlClientWrapper {
|
|
|
155
155
|
return createClient({
|
|
156
156
|
url: serverUri,
|
|
157
157
|
exchanges,
|
|
158
|
+
// urql 6 defaults preferGetMethod to 'within-url-limit', sending short queries as GET with
|
|
159
|
+
// no body. Keep the urql-5 behaviour (always POST) so this client matches GraphQLClient and
|
|
160
|
+
// cannot silently change the wire format if it is ever put back into use.
|
|
161
|
+
preferGetMethod: false,
|
|
162
|
+
// No `signal` here: urql's makeFetchSource overwrites init.signal with its own
|
|
163
|
+
// AbortController, so a timeout passed through fetchOptions() is silently discarded and
|
|
164
|
+
// never fires. GraphQLClient applies its 60s timeout inside a wrapping `fetch` instead.
|
|
158
165
|
fetchOptions: () => ({
|
|
159
166
|
headers: {
|
|
160
167
|
'X-Auth-Token': this.$__authToken
|
|
161
|
-
}
|
|
162
|
-
// Add 60 second timeout for debugging
|
|
163
|
-
signal: AbortSignal.timeout(60000)
|
|
168
|
+
}
|
|
164
169
|
})
|
|
165
170
|
})
|
|
166
171
|
}
|
package/src/libraries/crypto.ts
CHANGED
|
@@ -659,13 +659,11 @@ export function verifyOTSSignature(
|
|
|
659
659
|
const enumerated = enumerateMolecularHash(base17Hash)
|
|
660
660
|
const normalized = normalizeMolecularHash(enumerated)
|
|
661
661
|
|
|
662
|
-
// Step 2:
|
|
663
|
-
|
|
662
|
+
// Step 2: Fragments must be the uncompressed 2048-character form. Base64-compressed
|
|
663
|
+
// signatures are not supported and are rejected here rather than expanded.
|
|
664
|
+
const ots = otsFragments
|
|
664
665
|
if (ots.length !== 2048) {
|
|
665
|
-
|
|
666
|
-
if (ots.length !== 2048) {
|
|
667
|
-
return false // Invalid signature length
|
|
668
|
-
}
|
|
666
|
+
return false
|
|
669
667
|
}
|
|
670
668
|
|
|
671
669
|
// Step 3: Subdivide OTS into 16 segments of 128 characters each
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Powered by Knish.IO: Connecting a Decentralized World
|
|
3
|
+
|
|
4
|
+
Please visit https://github.com/WishKnish/KnishIO-Client-TS for information.
|
|
5
|
+
|
|
6
|
+
License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import MutationProposeMolecule from './MutationProposeMolecule'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Mutation for replenishing a non-finite token supply.
|
|
13
|
+
*
|
|
14
|
+
* The JS SDK submits replenishment through `MutationProposeMolecule` directly
|
|
15
|
+
* (KnishIOClient.js:2226-2230), but that class is abstract here because it declares
|
|
16
|
+
* `fillMolecule`. This subclass exists to provide the concrete type; it inherits the identical
|
|
17
|
+
* `ProposeMolecule(molecule: $molecule)` document from the base, so the request on the wire is
|
|
18
|
+
* the same one the JS SDK sends.
|
|
19
|
+
*
|
|
20
|
+
* `fillMolecule` is intentionally a no-op: KnishIOClient.replenishToken builds the V-atom pair
|
|
21
|
+
* via Molecule.replenishToken, then signs and checks the molecule, before this mutation is
|
|
22
|
+
* constructed. There is nothing left to fill.
|
|
23
|
+
*/
|
|
24
|
+
export default class MutationReplenishToken extends MutationProposeMolecule {
|
|
25
|
+
override fillMolecule(): void {
|
|
26
|
+
// Intentionally empty — the molecule arrives fully built, signed and checked.
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/schemas/index.ts
CHANGED
|
@@ -62,6 +62,21 @@ import { z } from 'zod'
|
|
|
62
62
|
const hexStringRegex = /^[0-9a-fA-F]+$/
|
|
63
63
|
const base17HashRegex = /^[0-9a-g]+$/
|
|
64
64
|
|
|
65
|
+
// Zod 3's `.url()` was `try { new URL(v) } catch` and returned the input UNCHANGED.
|
|
66
|
+
// Zod 4's `z.url()` returns the normalized `URL.href` instead — it strips default ports and
|
|
67
|
+
// lowercases the host — and there is no opt-out. The parsed `uri` is the endpoint the client
|
|
68
|
+
// actually talks to (KnishIOClient destructures it out of the validated config), so a
|
|
69
|
+
// refinement is used here: it validates exactly as v3 did without rewriting the value.
|
|
70
|
+
const urlString = (message = 'Invalid URL') =>
|
|
71
|
+
z.string().refine((value) => {
|
|
72
|
+
try {
|
|
73
|
+
new URL(value)
|
|
74
|
+
return true
|
|
75
|
+
} catch {
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
}, message)
|
|
79
|
+
|
|
65
80
|
export const HexStringSchema = z.string()
|
|
66
81
|
.regex(hexStringRegex, 'Must be a valid hexadecimal string')
|
|
67
82
|
.brand<'HexString'>()
|
|
@@ -102,7 +117,7 @@ export const MetaIdSchema = z.string()
|
|
|
102
117
|
.brand<'MetaId'>()
|
|
103
118
|
|
|
104
119
|
export const BatchIdSchema = z.string()
|
|
105
|
-
.
|
|
120
|
+
.regex(/^[0-9a-fA-F]{64}$/, 'Batch ID must be 64 hexadecimal characters')
|
|
106
121
|
.brand<'BatchId'>()
|
|
107
122
|
|
|
108
123
|
export const CellSlugSchema = z.string()
|
|
@@ -115,7 +130,7 @@ export const CellSlugSchema = z.string()
|
|
|
115
130
|
// =============================================================================
|
|
116
131
|
|
|
117
132
|
export const AtomIsotopeSchema = z.enum(['C', 'V', 'U', 'T', 'M', 'I', 'R', 'B', 'F'], {
|
|
118
|
-
|
|
133
|
+
error: 'Invalid atom isotope. Must be one of: C, V, U, T, M, I, R, B, F'
|
|
119
134
|
})
|
|
120
135
|
|
|
121
136
|
// =============================================================================
|
|
@@ -135,7 +150,7 @@ export const AtomMetaDataSchema = z.object({
|
|
|
135
150
|
}).strict()
|
|
136
151
|
|
|
137
152
|
export const MetaDataSchema: z.ZodType<Record<string, any>> = z.lazy(() =>
|
|
138
|
-
z.record(z.union([
|
|
153
|
+
z.record(z.string(), z.union([
|
|
139
154
|
MetaDataValueSchema,
|
|
140
155
|
MetaDataSchema,
|
|
141
156
|
z.array(MetaDataSchema)
|
|
@@ -186,7 +201,7 @@ export const MoleculeParamsSchema = z.object({
|
|
|
186
201
|
// =============================================================================
|
|
187
202
|
|
|
188
203
|
export const KnishIOClientConfigSchema = z.object({
|
|
189
|
-
uri: z.union([
|
|
204
|
+
uri: z.union([urlString(), z.array(urlString())]).optional(),
|
|
190
205
|
cellSlug: z.union([CellSlugSchema, z.string(), z.null()]).optional(),
|
|
191
206
|
client: z.unknown().optional(),
|
|
192
207
|
socket: z.unknown().optional(),
|
|
@@ -209,7 +224,7 @@ export const AuthTokenParamsSchema = z.object({
|
|
|
209
224
|
export const AuthParamsSchema = z.object({
|
|
210
225
|
cellSlug: z.union([CellSlugSchema, z.string(), z.null()]).optional(),
|
|
211
226
|
encrypt: z.boolean().optional(),
|
|
212
|
-
callback: z.
|
|
227
|
+
callback: z.custom<(arg: unknown) => void>((value) => typeof value === 'function').optional()
|
|
213
228
|
}).strict()
|
|
214
229
|
|
|
215
230
|
export const GuestAuthParamsSchema = z.object({
|
|
@@ -224,7 +239,7 @@ export const TransferParamsSchema = z.object({
|
|
|
224
239
|
recipient: z.union([WalletAddressSchema, z.string()]),
|
|
225
240
|
amount: z.union([z.number().positive(), z.string().min(1)]),
|
|
226
241
|
token: z.union([TokenSlugSchema, z.string()]).optional(),
|
|
227
|
-
callbackUrl: z.union([
|
|
242
|
+
callbackUrl: z.union([urlString(), z.null()]).optional(),
|
|
228
243
|
metaType: z.union([MetaTypeSchema, z.string(), z.null()]).optional(),
|
|
229
244
|
metaId: z.union([MetaIdSchema, z.string(), z.null()]).optional(),
|
|
230
245
|
meta: z.union([MetaDataSchema, z.null()]).optional()
|
|
@@ -278,7 +293,7 @@ export const MetaQueryParamsSchema = z.object({
|
|
|
278
293
|
value: z.union([z.string(), z.null()]).optional(),
|
|
279
294
|
latest: z.union([z.boolean(), z.null()]).optional(),
|
|
280
295
|
filter: z.union([z.array(MetaFilterSchema), z.null()]).optional(),
|
|
281
|
-
queryArgs: z.union([z.record(z.unknown()), z.null()]).optional(),
|
|
296
|
+
queryArgs: z.union([z.record(z.string(), z.unknown()), z.null()]).optional(),
|
|
282
297
|
count: z.union([z.number().int().min(1), z.null()]).optional(),
|
|
283
298
|
countBy: z.union([z.string(), z.null()]).optional(),
|
|
284
299
|
cellSlug: z.union([CellSlugSchema, z.string(), z.null()]).optional()
|
|
@@ -316,7 +331,7 @@ export const SignatureResultSchema = z.object({
|
|
|
316
331
|
|
|
317
332
|
export const GraphQLRequestSchema = z.object({
|
|
318
333
|
query: z.string().min(1, 'GraphQL query cannot be empty'),
|
|
319
|
-
variables: z.record(z.unknown()).optional(),
|
|
334
|
+
variables: z.record(z.string(), z.unknown()).optional(),
|
|
320
335
|
operationName: z.union([z.string(), z.null()]).optional()
|
|
321
336
|
}).strict()
|
|
322
337
|
|
|
@@ -327,13 +342,13 @@ export const GraphQLErrorSchema = z.object({
|
|
|
327
342
|
column: z.number().int().min(1)
|
|
328
343
|
})).optional(),
|
|
329
344
|
path: z.array(z.union([z.string(), z.number()])).optional(),
|
|
330
|
-
extensions: z.record(z.unknown()).optional()
|
|
345
|
+
extensions: z.record(z.string(), z.unknown()).optional()
|
|
331
346
|
}).strict()
|
|
332
347
|
|
|
333
348
|
export const GraphQLResponseSchema = z.object({
|
|
334
349
|
data: z.unknown().optional(),
|
|
335
350
|
errors: z.array(GraphQLErrorSchema).nullable().optional(),
|
|
336
|
-
extensions: z.record(z.unknown()).optional()
|
|
351
|
+
extensions: z.record(z.string(), z.unknown()).optional()
|
|
337
352
|
}).strict()
|
|
338
353
|
|
|
339
354
|
// =============================================================================
|
|
@@ -343,8 +358,8 @@ export const GraphQLResponseSchema = z.object({
|
|
|
343
358
|
export const SubscriptionOptionsSchema = z.object({
|
|
344
359
|
operationName: z.string().optional(),
|
|
345
360
|
query: z.string().optional(),
|
|
346
|
-
variables: z.record(z.unknown()).optional(),
|
|
347
|
-
callback: z.
|
|
361
|
+
variables: z.record(z.string(), z.unknown()).optional(),
|
|
362
|
+
callback: z.custom<(arg: unknown) => void>((value) => typeof value === 'function').optional()
|
|
348
363
|
}).strict()
|
|
349
364
|
|
|
350
365
|
// =============================================================================
|
|
@@ -364,7 +379,7 @@ export const ValidationResultSchema = z.object({
|
|
|
364
379
|
|
|
365
380
|
export const ErrorContextSchema = z.object({
|
|
366
381
|
operation: z.string().optional(),
|
|
367
|
-
parameters: z.record(z.unknown()).optional(),
|
|
382
|
+
parameters: z.record(z.string(), z.unknown()).optional(),
|
|
368
383
|
timestamp: z.number().int().min(0).optional(),
|
|
369
384
|
stack: z.string().optional()
|
|
370
385
|
}).strict()
|
|
@@ -407,7 +422,7 @@ export const KnishIOErrorTypeSchema = z.enum([
|
|
|
407
422
|
* Validates and parses data using a Zod schema with enhanced error messages
|
|
408
423
|
*/
|
|
409
424
|
export function parseWithSchema<T>(
|
|
410
|
-
schema: z.
|
|
425
|
+
schema: z.ZodType<T>,
|
|
411
426
|
data: unknown,
|
|
412
427
|
context?: string
|
|
413
428
|
): T {
|
|
@@ -428,7 +443,7 @@ export function parseWithSchema<T>(
|
|
|
428
443
|
* Type-safe validation that returns a result object instead of throwing
|
|
429
444
|
*/
|
|
430
445
|
export function validateWithSchema<T>(
|
|
431
|
-
schema: z.
|
|
446
|
+
schema: z.ZodType<T>,
|
|
432
447
|
data: unknown
|
|
433
448
|
): { success: true; data: T } | { success: false; error: string } {
|
|
434
449
|
const result = schema.safeParse(data)
|
|
@@ -444,7 +459,7 @@ export function validateWithSchema<T>(
|
|
|
444
459
|
* Validates multiple values with their respective schemas
|
|
445
460
|
*/
|
|
446
461
|
export function validateMultiple<T extends Record<string, any>>(
|
|
447
|
-
validations: { [K in keyof T]: { schema: z.
|
|
462
|
+
validations: { [K in keyof T]: { schema: z.ZodType<T[K]>; data: unknown } }
|
|
448
463
|
): T {
|
|
449
464
|
const result = {} as T
|
|
450
465
|
|
package/src/types/assertions.ts
CHANGED
|
@@ -399,12 +399,12 @@ export function assertBatchId(
|
|
|
399
399
|
): asserts value is BatchId {
|
|
400
400
|
if (!isBatchId(value)) {
|
|
401
401
|
throw new AssertionError(
|
|
402
|
-
createAssertionMessage('BatchId (
|
|
402
|
+
createAssertionMessage('BatchId (64-character hex)', value, context),
|
|
403
403
|
'BatchId',
|
|
404
404
|
value,
|
|
405
405
|
{
|
|
406
406
|
operation: 'batch_id_assertion',
|
|
407
|
-
expected: '
|
|
407
|
+
expected: '64-character hexadecimal string',
|
|
408
408
|
actual: value,
|
|
409
409
|
field: context
|
|
410
410
|
}
|
|
@@ -182,9 +182,7 @@ export class ValidationService {
|
|
|
182
182
|
KNISHIO_SERVER_SDK_VERSION: process.env.KNISHIO_SERVER_SDK_VERSION
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
|
|
186
|
-
// doesn't structurally match safeParse's ZodType<T> param; ZodTypeAny sidesteps the mismatch.
|
|
187
|
-
const parseResult = safeParse(Schemas.EnvironmentConfig as z.ZodTypeAny, env)
|
|
185
|
+
const parseResult = safeParse(Schemas.EnvironmentConfig, env)
|
|
188
186
|
|
|
189
187
|
if (!parseResult.success) {
|
|
190
188
|
return {
|