@wishknish/knishio-client-js 0.7.7 → 0.7.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wishknish/knishio-client-js",
3
- "version": "0.7.7",
3
+ "version": "0.7.8",
4
4
  "type": "module",
5
5
  "productName": "Knish.IO Javascript SDK Client",
6
6
  "description": "JavaScript implementation of the Knish.IO SDK to consume Knish.IO GraphQL APIs.",
@@ -96,6 +96,7 @@ import QueryAtom from './query/QueryAtom.js'
96
96
  import QueryPolicy from './query/QueryPolicy.js'
97
97
  import QueryMetaTypeViaAtom from './query/QueryMetaTypeViaAtom.js'
98
98
  import QueryMetaTypeViaMolecule from './query/QueryMetaTypeViaMolecule.js'
99
+ import QueryEmbeddingStatus from './query/QueryEmbeddingStatus.js'
99
100
  import MutationCreateRule from './mutation/MutationCreateRule.js'
100
101
  import MutationDepositBufferToken from './mutation/MutationDepositBufferToken.js'
101
102
  import MutationWithdrawBufferToken from './mutation/MutationWithdrawBufferToken.js'
@@ -253,6 +254,7 @@ export default class KnishIOClient {
253
254
  this.$__secret = ''
254
255
  this.$__bundle = ''
255
256
  this.remainderWallet = null
257
+ this.$__capabilityCache = {}
256
258
  }
257
259
 
258
260
  /**
@@ -288,6 +290,21 @@ export default class KnishIOClient {
288
290
  }
289
291
  }
290
292
 
293
+ /**
294
+ * Sets the WebSocket (subscription) endpoint for this session.
295
+ *
296
+ * `setUri()` only updates the HTTP endpoint; the subscription socket is built once
297
+ * at construction from the `socket.socketUri` passed in. This lets a caller re-point
298
+ * the socket when the endpoint changes (F-8a, E2E gauntlet 2026-06-02).
299
+ *
300
+ * @param {string} socketUri
301
+ */
302
+ setSocketUri (socketUri) {
303
+ if (this.$__client && typeof this.$__client.setSocketUri === 'function') {
304
+ this.$__client.setSocketUri({ socketUri, appKey: 'knishio' })
305
+ }
306
+ }
307
+
291
308
  /**
292
309
  * Retrieves the endpoint URI for this session
293
310
  *
@@ -906,6 +923,74 @@ export default class KnishIOClient {
906
923
  return response
907
924
  }
908
925
 
926
+ /**
927
+ * Probes the connected server to check whether it supports a named root query field.
928
+ * Result is cached per URI so the network round-trip happens at most once per URI.
929
+ *
930
+ * Uses GraphQL introspection which is universally supported by spec-compliant servers.
931
+ *
932
+ * @param {string} fieldName - The root Query field name to check (e.g. 'embeddingStatus')
933
+ * @return {Promise<boolean>}
934
+ */
935
+ async hasQueryField (fieldName) {
936
+ const uri = this.getUri()
937
+ const cacheKey = `${ uri }::${ fieldName }`
938
+
939
+ if (typeof this.$__capabilityCache[cacheKey] === 'boolean') {
940
+ return this.$__capabilityCache[cacheKey]
941
+ }
942
+
943
+ try {
944
+ const result = await this.$__client.query({
945
+ query: '{ __schema { queryType { fields { name } } } }',
946
+ variables: {}
947
+ })
948
+
949
+ const fields = result?.data?.__schema?.queryType?.fields || []
950
+ const supported = fields.some(f => f.name === fieldName)
951
+ this.$__capabilityCache[cacheKey] = supported
952
+ return supported
953
+ } catch (err) {
954
+ this.log('warn', `KnishIOClient::hasQueryField() - Capability probe for '${ fieldName }' failed: ${ err.message }`)
955
+ this.$__capabilityCache[cacheKey] = false
956
+ return false
957
+ }
958
+ }
959
+
960
+ /**
961
+ * Queries embedding status for one or more meta instances (DataBraid observability).
962
+ *
963
+ * If the connected server does not support the embeddingStatus query,
964
+ * returns null without throwing an error (graceful degradation).
965
+ *
966
+ * Single mode: queryEmbeddingStatus({ metaType: 'product', metaId: 'SKU-001' })
967
+ * Bulk mode: queryEmbeddingStatus({ instances: [{ metaType: 'product', metaId: 'SKU-001' }, ...] })
968
+ *
969
+ * @param {string|null} metaType - Metadata type (single-instance mode)
970
+ * @param {string|null} metaId - Instance identifier (single-instance mode)
971
+ * @param {Array<{metaType: string, metaId: string}>|null} instances - Bulk mode input
972
+ * @return {Promise<ResponseEmbeddingStatus|null>} Response with payload(), or null if unsupported
973
+ */
974
+ async queryEmbeddingStatus ({
975
+ metaType = null,
976
+ metaId = null,
977
+ instances = null
978
+ }) {
979
+ this.log('info', `KnishIOClient::queryEmbeddingStatus() - Checking embedding status for metaType: ${ metaType || '(bulk)' }...`)
980
+
981
+ const supported = await this.hasQueryField('embeddingStatus')
982
+
983
+ if (!supported) {
984
+ this.log('warn', 'KnishIOClient::queryEmbeddingStatus() - Server does not support embeddingStatus query. Returning null.')
985
+ return null
986
+ }
987
+
988
+ const query = this.createQuery(QueryEmbeddingStatus)
989
+ const variables = QueryEmbeddingStatus.createVariables({ metaType, metaId, instances })
990
+
991
+ return this.executeQuery(query, variables)
992
+ }
993
+
909
994
  /**
910
995
  * Query batch to get cascading meta instances by batchID
911
996
  *
@@ -1446,7 +1531,12 @@ export default class KnishIOClient {
1446
1531
  metaId,
1447
1532
  policy = {}
1448
1533
  }) {
1449
- // Create a molecule
1534
+ // Create a molecule. F-3 (E2E gauntlet 2026-06-02): the ContinuID-rejection bug
1535
+ // here was NOT this construction — it was molecule.addPolicyAtom() signing the
1536
+ // R-isotope atom from a fresh random wallet (now fixed in Molecule.addPolicyAtom
1537
+ // to use this.sourceWallet). A policy is a policy-only R-atom (no rules), so this
1538
+ // path is kept as-is: routing through createRule with an empty rule list would
1539
+ // fail the SDK's own `Check::isotopeR() - No rules!` validation.
1450
1540
  const molecule = await this.createMolecule({})
1451
1541
  molecule.addPolicyAtom({
1452
1542
  metaType,
package/src/Molecule.js CHANGED
@@ -393,14 +393,14 @@ export default class Molecule {
393
393
  const atomMeta = new AtomMeta(meta)
394
394
  atomMeta.addPolicy(policy)
395
395
 
396
- const wallet = Wallet.create({
397
- secret: this.secret,
398
- bundle: this.sourceWallet.bundle,
399
- token: 'USER'
400
- })
401
-
396
+ // F-3 (E2E gauntlet 2026-06-02): sign the policy (R-isotope) atom from the
397
+ // established ContinuID source wallet, NOT a freshly-derived Wallet.create() at a
398
+ // random position. As the signing (first) atom in createPolicy(), a fresh-wallet
399
+ // position is unknown to the validator → "ContinuID chain validation failed:
400
+ // signing position … not found as wallet" (a different position each retry). This
401
+ // mirrors createRule(), which adds its R-atom at this.sourceWallet.
402
402
  this.addAtom(Atom.create({
403
- wallet,
403
+ wallet: this.sourceWallet,
404
404
  isotope: 'R',
405
405
  metaType,
406
406
  metaId,
package/src/index.js CHANGED
@@ -85,6 +85,7 @@ import QueryBalance from './query/QueryBalance.js'
85
85
  import QueryBatch from './query/QueryBatch.js'
86
86
  import QueryBatchHistory from './query/QueryBatchHistory.js'
87
87
  import QueryContinuId from './query/QueryContinuId.js'
88
+ import QueryEmbeddingStatus from './query/QueryEmbeddingStatus.js'
88
89
  import QueryMetaType from './query/QueryMetaType.js'
89
90
  import QueryMetaTypeViaAtom from './query/QueryMetaTypeViaAtom.js'
90
91
  import QueryMetaTypeViaMolecule from './query/QueryMetaTypeViaMolecule.js'
@@ -132,6 +133,7 @@ import ResponseCreateMeta from './response/ResponseCreateMeta.js'
132
133
  import ResponseCreateRule from './response/ResponseCreateRule.js'
133
134
  import ResponseCreateToken from './response/ResponseCreateToken.js'
134
135
  import ResponseCreateWallet from './response/ResponseCreateWallet.js'
136
+ import ResponseEmbeddingStatus from './response/ResponseEmbeddingStatus.js'
135
137
  import ResponseLinkIdentifier from './response/ResponseLinkIdentifier.js'
136
138
  import ResponseMetaType from './response/ResponseMetaType.js'
137
139
  import ResponseMetaTypeViaAtom from './response/ResponseMetaTypeViaAtom.js'
@@ -245,6 +247,7 @@ export {
245
247
  QueryBatch,
246
248
  QueryBatchHistory,
247
249
  QueryContinuId,
250
+ QueryEmbeddingStatus,
248
251
  QueryMetaType,
249
252
  QueryMetaTypeViaAtom,
250
253
  QueryMetaTypeViaMolecule,
@@ -286,6 +289,7 @@ export {
286
289
  ResponseCreateRule,
287
290
  ResponseCreateToken,
288
291
  ResponseCreateWallet,
292
+ ResponseEmbeddingStatus,
289
293
  ResponseLinkIdentifier,
290
294
  ResponseMetaType,
291
295
  ResponseMetaTypeViaAtom,
@@ -5,7 +5,7 @@ import {
5
5
  fetchExchange
6
6
  } from '@urql/core'
7
7
  import { createClient as createWSClient } from 'graphql-ws'
8
- import { pipe, map } from 'wonka'
8
+ import { pipe, map, subscribe } from 'wonka'
9
9
 
10
10
  class UrqlClientWrapper {
11
11
  constructor ({ serverUri, socket = null, encrypt = false }) {
@@ -82,15 +82,19 @@ class UrqlClientWrapper {
82
82
  subscribe (request, closure) {
83
83
  const { query, variables, operationName } = request
84
84
 
85
- const { unsubscribe } = pipe(
85
+ // F-22 fix: wonka v3 — `subscribe` is a function applied via pipe, not a
86
+ // method on the result. The pipe-with-subscribe expression returns the
87
+ // subscription object directly (already shaped as `{ unsubscribe }`).
88
+ const subscription = pipe(
86
89
  this.$__client.subscription(query, variables),
87
90
  map(result => {
88
91
  closure(this.formatResponse(result))
89
- })
90
- ).subscribe(() => {})
92
+ }),
93
+ subscribe(() => {})
94
+ )
91
95
 
92
96
  // Store subscription for later cleanup
93
- this.$__subscriptionManager.set(operationName, { unsubscribe })
97
+ this.$__subscriptionManager.set(operationName, subscription)
94
98
 
95
99
  return {
96
100
  unsubscribe: () => this.unsubscribe(operationName)
@@ -0,0 +1,132 @@
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-JS for information.
45
+
46
+ License: https://github.com/WishKnish/KnishIO-Client-JS/blob/master/LICENSE
47
+ */
48
+ import Query from './Query.js'
49
+ import ResponseEmbeddingStatus from '../response/ResponseEmbeddingStatus.js'
50
+ import { gql } from '@urql/core'
51
+
52
+ /**
53
+ * Query for retrieving DataBraid embedding status for meta instances.
54
+ *
55
+ * Supports both single-instance and bulk modes:
56
+ * - Single: { metaType: 'product', metaId: 'SKU-001' }
57
+ * - Bulk: { instances: [{ metaType: 'product', metaId: 'SKU-001' }, ...] }
58
+ *
59
+ * Returns embedding state (PENDING, STALE, COMPLETE) for each instance,
60
+ * allowing apps to render spinner badges and completion indicators.
61
+ *
62
+ * NOTE: Only available on servers with EMBEDDING_ENABLED=true.
63
+ * Use KnishIOClient.queryEmbeddingStatus() which handles capability
64
+ * detection and returns null for unsupported servers.
65
+ */
66
+ export default class QueryEmbeddingStatus extends Query {
67
+ /**
68
+ * @param {UrqlClientWrapper} graphQLClient
69
+ * @param {KnishIOClient} knishIOClient
70
+ */
71
+ constructor (graphQLClient, knishIOClient) {
72
+ super(graphQLClient, knishIOClient)
73
+
74
+ this.$__query = gql`query( $metaType: String, $metaId: String, $instances: [EmbeddingStatusInput!] ) {
75
+ embeddingStatus( metaType: $metaType, metaId: $metaId, instances: $instances ) {
76
+ metaType,
77
+ metaId,
78
+ state,
79
+ totalMetas,
80
+ embeddedCount,
81
+ embeddedAt,
82
+ model
83
+ }
84
+ }`
85
+ }
86
+
87
+ /**
88
+ * Builds a GraphQL-friendly variables object for embedding status queries.
89
+ *
90
+ * Single mode: createVariables({ metaType: 'product', metaId: 'SKU-001' })
91
+ * Bulk mode: createVariables({ instances: [{ metaType: 'product', metaId: 'SKU-001' }, ...] })
92
+ *
93
+ * @param {string|null} metaType
94
+ * @param {string|null} metaId
95
+ * @param {Array<{metaType: string, metaId: string}>|null} instances
96
+ * @return {{}}
97
+ */
98
+ static createVariables ({
99
+ metaType = null,
100
+ metaId = null,
101
+ instances = null
102
+ }) {
103
+ const variables = {}
104
+
105
+ if (instances && instances.length > 0) {
106
+ variables.instances = instances
107
+ }
108
+
109
+ if (metaType) {
110
+ variables.metaType = metaType
111
+ }
112
+
113
+ if (metaId) {
114
+ variables.metaId = metaId
115
+ }
116
+
117
+ return variables
118
+ }
119
+
120
+ /**
121
+ * Returns a Response object
122
+ *
123
+ * @param {object} json
124
+ * @return {ResponseEmbeddingStatus}
125
+ */
126
+ createResponse (json) {
127
+ return new ResponseEmbeddingStatus({
128
+ query: this,
129
+ json
130
+ })
131
+ }
132
+ }
@@ -0,0 +1,100 @@
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-JS for information.
45
+
46
+ License: https://github.com/WishKnish/KnishIO-Client-JS/blob/master/LICENSE
47
+ */
48
+
49
+ import Response from './Response.js'
50
+
51
+ /**
52
+ * Response for EmbeddingStatus Query
53
+ *
54
+ * Payload is an array of EmbeddingStatusItem objects:
55
+ * { metaType, metaId, state, totalMetas, embeddedCount, embeddedAt, model }
56
+ *
57
+ * state is one of: 'PENDING' | 'STALE' | 'COMPLETE'
58
+ */
59
+ export default class ResponseEmbeddingStatus extends Response {
60
+ /**
61
+ * Class constructor
62
+ *
63
+ * @param {Query} query
64
+ * @param {object} json
65
+ */
66
+ constructor ({
67
+ query,
68
+ json
69
+ }) {
70
+ super({
71
+ query,
72
+ json,
73
+ dataKey: 'data.embeddingStatus'
74
+ })
75
+ }
76
+
77
+ /**
78
+ * Returns the array of embedding status items, or null if empty.
79
+ *
80
+ * Each item contains:
81
+ * - metaType {string} - Metadata type (echoed from input)
82
+ * - metaId {string} - Instance identifier (echoed from input)
83
+ * - state {string} - PENDING | STALE | COMPLETE
84
+ * - totalMetas {number} - Total meta rows for this instance
85
+ * - embeddedCount {number} - Rows with current-model embeddings
86
+ * - embeddedAt {number|null} - Unix epoch of most recent embedding
87
+ * - model {string|null} - Model name used for embeddings
88
+ *
89
+ * @return {Array<object>|null}
90
+ */
91
+ payload () {
92
+ const items = this.data()
93
+
94
+ if (!items || !Array.isArray(items) || items.length === 0) {
95
+ return null
96
+ }
97
+
98
+ return items
99
+ }
100
+ }