@wishknish/knishio-client-ts 0.9.5 → 0.9.7

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.
@@ -0,0 +1,121 @@
1
+ /*
2
+ (
3
+ (/(
4
+ (//(
5
+ (///(
6
+ (/////(
7
+ (//////( )
8
+ (////////( (/)
9
+ (////////( (///)
10
+ (//////////( (////)
11
+ (//////////( (//////)
12
+ (////////////( (///////)
13
+ (/////////////( (/////////)
14
+ (//////////////( (///////////)
15
+ (///////////////( (/////////////)
16
+ (////////////////( (//////////////)
17
+ ((((((((((((((((((( (((((((((((((((
18
+ ((((((((((((((((((( ((((((((((((((
19
+ ((((((((((((((((((( ((((((((((((((
20
+ (((((((((((((((((((( (((((((((((((
21
+ (((((((((((((((((((( ((((((((((((
22
+ ((((((((((((((((((( ((((((((((((
23
+ ((((((((((((((((((( ((((((((((
24
+ ((((((((((((((((((/ (((((((((
25
+ (((((((((((((((((( ((((((((
26
+ ((((((((((((((((( (((((((
27
+ (((((((((((((((((( (((((
28
+ ################# ##
29
+ ################ #
30
+ ################# ##
31
+ %################ ###
32
+ ###############( ####
33
+ ############### ####
34
+ ############### ######
35
+ %#############( (#######
36
+ %############# #########
37
+ ############( ##########
38
+ ########### #############
39
+ ######### ##############
40
+ %######
41
+
42
+ Powered by Knish.IO: Connecting a Decentralized World
43
+
44
+ Please visit https://github.com/WishKnish/KnishIO-Client-TS for information.
45
+
46
+ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
47
+ */
48
+
49
+ /**
50
+ * Memory hygiene and zeroization utilities for sensitive cryptographic material
51
+ */
52
+
53
+ const textEncoder = new TextEncoder()
54
+
55
+ /**
56
+ * Overwrite byte array contents with zeros
57
+ */
58
+ export function zeroizeBytes(buffer: Uint8Array | number[]): void {
59
+ if (buffer instanceof Uint8Array) {
60
+ buffer.fill(0)
61
+ } else if (Array.isArray(buffer)) {
62
+ for (let i = 0; i < buffer.length; i++) {
63
+ buffer[i] = 0
64
+ }
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Execute a callback with a byte buffer and guarantee zeroization upon completion
70
+ */
71
+ export async function withSecureBytes<T>(
72
+ bytes: Uint8Array,
73
+ fn: (bytes: Uint8Array) => Promise<T> | T
74
+ ): Promise<T> {
75
+ try {
76
+ return await fn(bytes)
77
+ } finally {
78
+ zeroizeBytes(bytes)
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Execute a callback with a secret string, ensuring temporary byte buffers are cleared
84
+ */
85
+ export async function withSecureString<T>(
86
+ secret: string,
87
+ fn: (cleanSecret: string) => Promise<T> | T
88
+ ): Promise<T> {
89
+ const bytes = textEncoder.encode(secret)
90
+ try {
91
+ return await fn(secret)
92
+ } finally {
93
+ zeroizeBytes(bytes)
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Constant-time comparison of two byte arrays or strings to prevent timing attacks
99
+ */
100
+ export function constantTimeCompare(
101
+ a: Uint8Array | string,
102
+ b: Uint8Array | string
103
+ ): boolean {
104
+ const bytesA = typeof a === 'string' ? textEncoder.encode(a) : a
105
+ const bytesB = typeof b === 'string' ? textEncoder.encode(b) : b
106
+
107
+ let result = bytesA.length === bytesB.length ? 0 : 1
108
+ const len = Math.min(bytesA.length, bytesB.length)
109
+
110
+ for (let i = 0; i < len; i++) {
111
+ const byteA = bytesA[i] ?? 0
112
+ const byteB = bytesB[i] ?? 0
113
+ result |= byteA ^ byteB
114
+ }
115
+
116
+ // Zeroize temporary buffers if they were converted from strings
117
+ if (typeof a === 'string') zeroizeBytes(bytesA)
118
+ if (typeof b === 'string') zeroizeBytes(bytesB)
119
+
120
+ return result === 0
121
+ }
@@ -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
+ }
@@ -117,7 +117,7 @@ export const MetaIdSchema = z.string()
117
117
  .brand<'MetaId'>()
118
118
 
119
119
  export const BatchIdSchema = z.string()
120
- .min(1, 'Batch ID cannot be empty')
120
+ .regex(/^[0-9a-fA-F]{64}$/, 'Batch ID must be 64 hexadecimal characters')
121
121
  .brand<'BatchId'>()
122
122
 
123
123
  export const CellSlugSchema = z.string()
@@ -207,7 +207,8 @@ export const KnishIOClientConfigSchema = z.object({
207
207
  socket: z.unknown().optional(),
208
208
  serverSdkVersion: z.number().int().min(1).optional(),
209
209
  logging: z.boolean().optional(),
210
- defaultRequestPolicy: z.enum(['cache-first', 'cache-only', 'network-only', 'cache-and-network']).nullable().optional()
210
+ defaultRequestPolicy: z.enum(['cache-first', 'cache-only', 'network-only', 'cache-and-network']).nullable().optional(),
211
+ secretStorage: z.unknown().optional()
211
212
  }).strict()
212
213
 
213
214
  // =============================================================================
@@ -0,0 +1,156 @@
1
+ /*
2
+ (
3
+ (/(
4
+ (//(
5
+ (///(
6
+ (/////(
7
+ (//////( )
8
+ (////////( (/)
9
+ (////////( (///)
10
+ (//////////( (////)
11
+ (//////////( (//////)
12
+ (////////////( (///////)
13
+ (/////////////( (/////////)
14
+ (//////////////( (///////////)
15
+ (///////////////( (/////////////)
16
+ (////////////////( (//////////////)
17
+ ((((((((((((((((((( (((((((((((((((
18
+ ((((((((((((((((((( ((((((((((((((
19
+ ((((((((((((((((((( ((((((((((((((
20
+ (((((((((((((((((((( (((((((((((((
21
+ (((((((((((((((((((( ((((((((((((
22
+ ((((((((((((((((((( ((((((((((((
23
+ ((((((((((((((((((( ((((((((((
24
+ ((((((((((((((((((/ (((((((((
25
+ (((((((((((((((((( ((((((((
26
+ ((((((((((((((((( (((((((
27
+ (((((((((((((((((( (((((
28
+ ################# ##
29
+ ################ #
30
+ ################# ##
31
+ %################ ###
32
+ ###############( ####
33
+ ############### ####
34
+ ############### ######
35
+ %#############( (#######
36
+ %############# #########
37
+ ############( ##########
38
+ ########### #############
39
+ ######### ##############
40
+ %######
41
+
42
+ Powered by Knish.IO: Connecting a Decentralized World
43
+
44
+ Please visit https://github.com/WishKnish/KnishIO-Client-TS for information.
45
+
46
+ License: https://github.com/WishKnish/KnishIO-Client-TS/blob/master/LICENSE
47
+ */
48
+
49
+ import type { ISecretStorageProvider, SecretStorageMetadata } from '@/types/storage'
50
+ import SecretStorageException from '@/exception/SecretStorageException'
51
+ import { withSecureString } from '@/libraries/secureMemory'
52
+
53
+ interface MemorySecretEntry {
54
+ secret: string
55
+ metadata: SecretStorageMetadata
56
+ }
57
+
58
+ /**
59
+ * In-memory secret storage provider
60
+ * Used for testing, headless runners, and backward-compatible fallback
61
+ */
62
+ export default class MemorySecretStorageProvider implements ISecretStorageProvider {
63
+ public readonly providerType = 'memory'
64
+ private secrets: Map<string, MemorySecretEntry> = new Map()
65
+
66
+ /**
67
+ * Memory storage is not hardware backed
68
+ */
69
+ isHardwareBacked(): boolean {
70
+ return false
71
+ }
72
+
73
+ /**
74
+ * Memory storage is always available
75
+ */
76
+ async isAvailable(): Promise<boolean> {
77
+ return true
78
+ }
79
+
80
+ /**
81
+ * Store a secret in memory
82
+ */
83
+ async storeSecret(
84
+ bundleHash: string,
85
+ secret: string,
86
+ options?: { label?: string; passphrase?: string }
87
+ ): Promise<void> {
88
+ if (!bundleHash) {
89
+ throw new SecretStorageException('Bundle hash cannot be empty')
90
+ }
91
+ if (!secret) {
92
+ throw new SecretStorageException('Secret cannot be empty')
93
+ }
94
+
95
+ const metadata: SecretStorageMetadata = {
96
+ bundleHash,
97
+ label: options?.label,
98
+ createdAt: Date.now(),
99
+ hardwareBacked: false,
100
+ providerType: this.providerType
101
+ }
102
+
103
+ this.secrets.set(bundleHash, { secret, metadata })
104
+ }
105
+
106
+ /**
107
+ * Retrieve a secret from memory
108
+ */
109
+ async retrieveSecret(bundleHash: string): Promise<string | null> {
110
+ const entry = this.secrets.get(bundleHash)
111
+ return entry ? entry.secret : null
112
+ }
113
+
114
+ /**
115
+ * Delete a stored secret
116
+ */
117
+ async deleteSecret(bundleHash: string): Promise<boolean> {
118
+ return this.secrets.delete(bundleHash)
119
+ }
120
+
121
+ /**
122
+ * Check if a secret exists
123
+ */
124
+ async hasSecret(bundleHash: string): Promise<boolean> {
125
+ return this.secrets.has(bundleHash)
126
+ }
127
+
128
+ /**
129
+ * List all stored secret metadata
130
+ */
131
+ async listSecrets(): Promise<SecretStorageMetadata[]> {
132
+ return Array.from(this.secrets.values()).map(entry => ({ ...entry.metadata }))
133
+ }
134
+
135
+ /**
136
+ * Execute callback with unwrapped secret and ensure cleanup
137
+ */
138
+ async withSecret<T>(
139
+ bundleHash: string,
140
+ fn: (secret: string) => Promise<T> | T
141
+ ): Promise<T> {
142
+ const entry = this.secrets.get(bundleHash)
143
+ if (!entry) {
144
+ throw SecretStorageException.notFound(bundleHash)
145
+ }
146
+
147
+ return withSecureString(entry.secret, fn)
148
+ }
149
+
150
+ /**
151
+ * Clear all secrets from memory
152
+ */
153
+ clear(): void {
154
+ this.secrets.clear()
155
+ }
156
+ }