@wishknish/knishio-client-ts 0.7.7 → 0.8.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.
@@ -21328,7 +21328,7 @@ ${operationTypes.join("\n")}
21328
21328
  if (seed) {
21329
21329
  const sponge = new ht("SHAKE256", "TEXT");
21330
21330
  sponge.update(seed);
21331
- return sponge.getHash("HEX", { outputLen: length * 2 }).toLowerCase();
21331
+ return sponge.getHash("HEX", { outputLen: length * 4 }).toLowerCase();
21332
21332
  } else {
21333
21333
  return randomString(length);
21334
21334
  }
@@ -25175,13 +25175,8 @@ ${operationTypes.join("\n")}
25175
25175
  }) {
25176
25176
  const atomMeta = new AtomMeta(meta);
25177
25177
  atomMeta.addPolicy(policy);
25178
- const wallet = Wallet.create({
25179
- secret: this.secret,
25180
- bundle: this.sourceWallet.bundle,
25181
- token: "USER"
25182
- });
25183
25178
  this.addAtom(Atom.create({
25184
- wallet,
25179
+ wallet: this.sourceWallet,
25185
25180
  isotope: "R",
25186
25181
  metaType,
25187
25182
  metaId,
@@ -28592,6 +28587,20 @@ ${operationTypes.join("\n")}
28592
28587
  encrypt: this.cipherLink
28593
28588
  });
28594
28589
  }
28590
+ /**
28591
+ * F-8a (cross-SDK parity, 2026-06-03): re-point the GraphQL-subscription WebSocket
28592
+ * without changing the HTTP endpoint. `setUri` only updates `serverUri`, leaving the
28593
+ * subscription socket pinned to whatever was passed at construction; this lets a
28594
+ * caller re-derive the socket when the endpoint changes.
28595
+ */
28596
+ setSocketUri(socketUri) {
28597
+ this.socketUri = socketUri;
28598
+ this.$__client = this.createUrqlClient({
28599
+ serverUri: this.serverUri,
28600
+ socket: { socketUri },
28601
+ encrypt: this.cipherLink
28602
+ });
28603
+ }
28595
28604
  socketDisconnect() {
28596
28605
  if (this.socketUri) {
28597
28606
  this.unsubscribeAll();
@@ -31517,9 +31526,93 @@ ${operationTypes.join("\n")}
31517
31526
  }
31518
31527
  };
31519
31528
 
31529
+ // src/response/ResponseEmbeddingStatus.ts
31530
+ init_Response();
31531
+ var ResponseEmbeddingStatus = class extends exports.Response {
31532
+ /**
31533
+ * Class constructor
31534
+ */
31535
+ constructor({
31536
+ query,
31537
+ json
31538
+ }) {
31539
+ super({
31540
+ query,
31541
+ json,
31542
+ dataKey: "data.embeddingStatus"
31543
+ });
31544
+ }
31545
+ /**
31546
+ * Returns the array of embedding status items, or null if empty.
31547
+ */
31548
+ payload() {
31549
+ const items = this.data();
31550
+ if (!items || !Array.isArray(items) || items.length === 0) {
31551
+ return null;
31552
+ }
31553
+ return items;
31554
+ }
31555
+ };
31556
+
31557
+ // src/query/QueryEmbeddingStatus.ts
31558
+ var QueryEmbeddingStatus = class extends Query {
31559
+ /**
31560
+ * Create new QueryEmbeddingStatus instance
31561
+ */
31562
+ constructor(graphQLClient, knishIOClient) {
31563
+ super(graphQLClient, knishIOClient);
31564
+ this.$__query = gql`query( $metaType: String, $metaId: String, $instances: [EmbeddingStatusInput!] ) {
31565
+ embeddingStatus( metaType: $metaType, metaId: $metaId, instances: $instances ) {
31566
+ metaType,
31567
+ metaId,
31568
+ state,
31569
+ totalMetas,
31570
+ embeddedCount,
31571
+ embeddedAt,
31572
+ model
31573
+ }
31574
+ }`;
31575
+ }
31576
+ /**
31577
+ * Builds a GraphQL-friendly variables object for embedding status queries.
31578
+ *
31579
+ * Single mode: createVariables({ metaType: 'product', metaId: 'SKU-001' })
31580
+ * Bulk mode: createVariables({ instances: [{ metaType: 'product', metaId: 'SKU-001' }, ...] })
31581
+ */
31582
+ static createVariables({
31583
+ metaType = null,
31584
+ metaId = null,
31585
+ instances = null
31586
+ } = {}) {
31587
+ const variables = {};
31588
+ if (instances && instances.length > 0) {
31589
+ variables.instances = instances;
31590
+ }
31591
+ if (metaType) {
31592
+ variables.metaType = metaType;
31593
+ }
31594
+ if (metaId) {
31595
+ variables.metaId = metaId;
31596
+ }
31597
+ return variables;
31598
+ }
31599
+ /**
31600
+ * Returns a Response object
31601
+ */
31602
+ createResponse(json) {
31603
+ return new ResponseEmbeddingStatus({
31604
+ query: this,
31605
+ json
31606
+ });
31607
+ }
31608
+ };
31609
+
31520
31610
  // src/response/ResponseProposeMolecule.ts
31521
31611
  init_Response();
31522
31612
  init_Dot();
31613
+ init_MolecularHashMismatchException();
31614
+ init_SignatureMismatchException();
31615
+ init_AtomIndexException();
31523
31616
  var ResponseProposeMolecule = class extends exports.Response {
31524
31617
  $__clientMolecule;
31525
31618
  /**
@@ -31591,6 +31684,47 @@ ${operationTypes.join("\n")}
31591
31684
  reason() {
31592
31685
  return exports.Dot.get(this.data(), "reason", "Invalid response from server");
31593
31686
  }
31687
+ /**
31688
+ * Map this rejection to a typed SDK exception when the failure mode is one
31689
+ * we know about. Returns null on success or for rejections we don't have a
31690
+ * typed class for.
31691
+ *
31692
+ * This is the single place pattern-matching against validator reason strings
31693
+ * lives — consumers (KnishIOClient cache invalidation, callers needing to
31694
+ * branch on failure mode) can `instanceof`-switch on the result instead.
31695
+ * Future validator versions can rephrase reasons (or populate a structured
31696
+ * field in the response payload) without breaking callers.
31697
+ */
31698
+ toException() {
31699
+ if (this.success()) return null;
31700
+ const reason = this.reason();
31701
+ const lc = reason.toLowerCase();
31702
+ if (/molecularhashmismatch|hash.*mismatch/.test(lc)) {
31703
+ return new exports.MolecularHashMismatchException(reason, {
31704
+ details: { reason },
31705
+ code: "HASH_MISMATCH"
31706
+ });
31707
+ }
31708
+ if (/ots.*position.*reuse|position.*already.*used|ots.*verification/.test(lc)) {
31709
+ return new exports.SignatureMismatchException(reason, {
31710
+ details: { reason },
31711
+ code: "OTS_VERIFICATION_FAILED"
31712
+ });
31713
+ }
31714
+ if (/continuid.*chain|previousposition|chain.*violation/.test(lc)) {
31715
+ return new exports.AtomIndexException(reason, {
31716
+ details: { reason },
31717
+ code: "INDEX_CONFLICT"
31718
+ });
31719
+ }
31720
+ if (/signature.*verification|signature.*invalid/.test(lc)) {
31721
+ return new exports.SignatureMismatchException(reason, {
31722
+ details: { reason },
31723
+ code: "VERIFICATION_FAILED"
31724
+ });
31725
+ }
31726
+ return null;
31727
+ }
31594
31728
  /**
31595
31729
  * Returns payload object
31596
31730
  * Matches JavaScript SDK payload method exactly
@@ -32644,6 +32778,13 @@ ${operationTypes.join("\n")}
32644
32778
  $__remainderWallet = null;
32645
32779
  lastMoleculeQuery = null;
32646
32780
  abortControllers = /* @__PURE__ */ new Map();
32781
+ $__capabilityCache = {};
32782
+ // Promise-chain mutex serializing MutationProposeMolecule submissions on this
32783
+ // client. Without it, concurrent createMolecule() calls both query the same
32784
+ // ContinuID position and both sign with it — the second is rejected with
32785
+ // OTS position reuse. Auth-token flow (MutationRequestAuthorization) inherits
32786
+ // from MutationProposeMolecule so it's covered by the same lock.
32787
+ $__moleculeChain = Promise.resolve();
32647
32788
  /**
32648
32789
  * Enhanced constructor with standardized configuration validation (Phase 2 Enhancement)
32649
32790
  */
@@ -32768,6 +32909,7 @@ ${operationTypes.join("\n")}
32768
32909
  this.$__authToken = null;
32769
32910
  this.$__remainderWallet = null;
32770
32911
  this.lastMoleculeQuery = null;
32912
+ this.$__capabilityCache = {};
32771
32913
  }
32772
32914
  /**
32773
32915
  * Get the GraphQL client
@@ -32788,6 +32930,19 @@ ${operationTypes.join("\n")}
32788
32930
  this.$__client.setUri(this.getRandomUri());
32789
32931
  }
32790
32932
  }
32933
+ /**
32934
+ * Sets the WebSocket (subscription) endpoint for this session.
32935
+ *
32936
+ * F-8a (cross-SDK parity, 2026-06-03): `setUri` only updates the HTTP endpoint; the
32937
+ * subscription socket is built once from the `socket.socketUri` passed at
32938
+ * construction. This lets a caller re-point the socket when the endpoint changes
32939
+ * (mirrors the JS SDK's `setSocketUri`).
32940
+ */
32941
+ setSocketUri(socketUri) {
32942
+ if (this.$__client && "setSocketUri" in this.$__client) {
32943
+ this.$__client.setSocketUri(socketUri);
32944
+ }
32945
+ }
32791
32946
  /**
32792
32947
  * Gets the Knish.IO server URIs
32793
32948
  */
@@ -32911,6 +33066,23 @@ ${operationTypes.join("\n")}
32911
33066
  this.lastMoleculeQuery = mutation;
32912
33067
  return mutation;
32913
33068
  }
33069
+ /**
33070
+ * Serializes the given async work behind a per-client promise chain. Used to
33071
+ * guarantee that at most one MutationProposeMolecule submission runs at a
33072
+ * time on this client — query position, sign, submit, observe response must
33073
+ * complete before the next one starts, or two callers race for the same OTS
33074
+ * position and the second gets rejected.
33075
+ *
33076
+ * `.then(fn, fn)` runs fn whether the previous holder resolved or rejected;
33077
+ * the queue's `.catch` swallows the rejection so a single failure doesn't
33078
+ * poison every subsequent caller, while the rejection still propagates to
33079
+ * the caller whose fn threw.
33080
+ */
33081
+ withMoleculeLock(fn) {
33082
+ const result = this.$__moleculeChain.then(fn, fn);
33083
+ this.$__moleculeChain = result.catch(() => void 0);
33084
+ return result;
33085
+ }
32914
33086
  /**
32915
33087
  * Executes a query or mutation
32916
33088
  */
@@ -32923,8 +33095,34 @@ ${operationTypes.join("\n")}
32923
33095
  encrypt: this.$__encrypt
32924
33096
  });
32925
33097
  }
33098
+ if (query instanceof MutationProposeMolecule) {
33099
+ return await this.withMoleculeLock(async () => {
33100
+ const response = await query.execute({ variables: variables || {} });
33101
+ this.handlePositionDrift(response);
33102
+ return response;
33103
+ });
33104
+ }
32926
33105
  return await query.execute({ variables: variables || {} });
32927
33106
  }
33107
+ /**
33108
+ * When a ProposeMolecule submission is rejected for a position-related
33109
+ * reason (OTS reuse, ContinuID chain violation, molecular hash mismatch),
33110
+ * the cached remainder wallet and lastMoleculeQuery are stale — the
33111
+ * validator's chain has advanced past what we know. Clear them so the next
33112
+ * createMolecule call re-queries queryContinuId for the authoritative
33113
+ * position. Other failure modes (network, malformed meta, bad signature
33114
+ * bytes) don't imply drift; leave cached state alone.
33115
+ */
33116
+ handlePositionDrift(response) {
33117
+ if (!(response instanceof ResponseProposeMolecule)) return;
33118
+ const exc = response.toException();
33119
+ if (!exc) return;
33120
+ if (exc instanceof exports.MolecularHashMismatchException || exc instanceof exports.AtomIndexException || exc instanceof exports.SignatureMismatchException && exc.code === "OTS_VERIFICATION_FAILED") {
33121
+ this.$__remainderWallet = null;
33122
+ this.lastMoleculeQuery = null;
33123
+ this.log("warn", `KnishIOClient::executeQuery() - position drift detected (${exc.name}/${exc.code}); cleared cached remainder wallet`);
33124
+ }
33125
+ }
32928
33126
  /**
32929
33127
  * Sets the secret for this session
32930
33128
  */
@@ -33304,6 +33502,63 @@ ${operationTypes.join("\n")}
33304
33502
  }
33305
33503
  return response;
33306
33504
  }
33505
+ /**
33506
+ * Probes the connected server to check whether it supports a named root query field.
33507
+ * Result is cached per URI so the network round-trip happens at most once per URI.
33508
+ *
33509
+ * Uses GraphQL introspection which is universally supported by spec-compliant servers.
33510
+ *
33511
+ * @param fieldName - The root Query field name to check (e.g. 'embeddingStatus')
33512
+ * @returns true if the server schema includes the field, false otherwise
33513
+ */
33514
+ async hasQueryField(fieldName) {
33515
+ const uri = this.$__client.getUri();
33516
+ const cacheKey = `${uri}::${fieldName}`;
33517
+ if (typeof this.$__capabilityCache[cacheKey] === "boolean") {
33518
+ return this.$__capabilityCache[cacheKey];
33519
+ }
33520
+ try {
33521
+ const result = await this.$__client.query({
33522
+ query: "{ __schema { queryType { fields { name } } } }",
33523
+ variables: {}
33524
+ });
33525
+ const data = result?.data;
33526
+ const fields = data?.__schema?.queryType?.fields || [];
33527
+ const supported = fields.some((f3) => f3.name === fieldName);
33528
+ this.$__capabilityCache[cacheKey] = supported;
33529
+ return supported;
33530
+ } catch (err) {
33531
+ this.log("warn", `KnishIOClient::hasQueryField() - Capability probe for '${fieldName}' failed: ${err.message}`);
33532
+ this.$__capabilityCache[cacheKey] = false;
33533
+ return false;
33534
+ }
33535
+ }
33536
+ /**
33537
+ * Queries embedding status for one or more meta instances (DataBraid observability).
33538
+ *
33539
+ * If the connected server does not support the embeddingStatus query,
33540
+ * returns null without throwing an error (graceful degradation).
33541
+ *
33542
+ * Single mode: queryEmbeddingStatus({ metaType: 'product', metaId: 'SKU-001' })
33543
+ * Bulk mode: queryEmbeddingStatus({ instances: [{ metaType: 'product', metaId: 'SKU-001' }, ...] })
33544
+ *
33545
+ * @returns Response with payload(), or null if the server does not support this query
33546
+ */
33547
+ async queryEmbeddingStatus({
33548
+ metaType = null,
33549
+ metaId = null,
33550
+ instances = null
33551
+ }) {
33552
+ this.log("info", `KnishIOClient::queryEmbeddingStatus() - Checking embedding status for metaType: ${metaType || "(bulk)"}...`);
33553
+ const supported = await this.hasQueryField("embeddingStatus");
33554
+ if (!supported) {
33555
+ this.log("warn", "KnishIOClient::queryEmbeddingStatus() - Server does not support embeddingStatus query. Returning null.");
33556
+ return null;
33557
+ }
33558
+ const query = this.createQuery(QueryEmbeddingStatus);
33559
+ const variables = QueryEmbeddingStatus.createVariables({ metaType, metaId, instances });
33560
+ return this.executeQuery(query, variables);
33561
+ }
33307
33562
  /**
33308
33563
  * Query cascading meta instances for batchId
33309
33564
  */
@@ -34305,6 +34560,7 @@ ${operationTypes.join("\n")}
34305
34560
  exports.QueryBalance = QueryBalance;
34306
34561
  exports.QueryBatch = QueryBatch;
34307
34562
  exports.QueryContinuId = QueryContinuId;
34563
+ exports.QueryEmbeddingStatus = QueryEmbeddingStatus;
34308
34564
  exports.QueryMetaType = QueryMetaType;
34309
34565
  exports.QueryMetaTypeViaAtom = QueryMetaTypeViaAtom;
34310
34566
  exports.QueryWalletBundle = QueryWalletBundle;
@@ -34316,6 +34572,7 @@ ${operationTypes.join("\n")}
34316
34572
  exports.ResponseCreateMeta = ResponseCreateMeta;
34317
34573
  exports.ResponseCreateToken = ResponseCreateToken;
34318
34574
  exports.ResponseCreateWallet = ResponseCreateWallet;
34575
+ exports.ResponseEmbeddingStatus = ResponseEmbeddingStatus;
34319
34576
  exports.ResponseMetaType = ResponseMetaType;
34320
34577
  exports.ResponseMetaTypeViaAtom = ResponseMetaTypeViaAtom;
34321
34578
  exports.ResponsePeering = ResponsePeering;