@wishknish/knishio-client-ts 0.7.6 → 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.
@@ -25043,6 +25043,7 @@ ${operationTypes.join("\n")}
25043
25043
  remainderWallet;
25044
25044
  atoms;
25045
25045
  version;
25046
+ continuIdPosition;
25046
25047
  parentHashes;
25047
25048
  local;
25048
25049
  /**
@@ -25055,7 +25056,8 @@ ${operationTypes.join("\n")}
25055
25056
  sourceWallet = null,
25056
25057
  remainderWallet = null,
25057
25058
  cellSlug = null,
25058
- version: version2 = null
25059
+ version: version2 = null,
25060
+ continuIdPosition = null
25059
25061
  } = {}) {
25060
25062
  this.status = null;
25061
25063
  this.molecularHash = null;
@@ -25064,6 +25066,7 @@ ${operationTypes.join("\n")}
25064
25066
  this.secret = secret;
25065
25067
  this.bundle = bundle;
25066
25068
  this.sourceWallet = sourceWallet;
25069
+ this.continuIdPosition = continuIdPosition;
25067
25070
  this.atoms = [];
25068
25071
  this.parentHashes = [];
25069
25072
  const versionRegistry = versions_default;
@@ -25141,7 +25144,9 @@ ${operationTypes.join("\n")}
25141
25144
  });
25142
25145
  }
25143
25146
  const continuIdMeta = {};
25144
- if (this.sourceWallet && this.sourceWallet.position) {
25147
+ if (this.continuIdPosition) {
25148
+ continuIdMeta.previousPosition = this.continuIdPosition;
25149
+ } else if (this.sourceWallet && this.sourceWallet.position) {
25145
25150
  continuIdMeta.previousPosition = this.sourceWallet.position;
25146
25151
  }
25147
25152
  if (this.remainderWallet.pubkey) {
@@ -25170,13 +25175,8 @@ ${operationTypes.join("\n")}
25170
25175
  }) {
25171
25176
  const atomMeta = new AtomMeta(meta);
25172
25177
  atomMeta.addPolicy(policy);
25173
- const wallet = Wallet.create({
25174
- secret: this.secret,
25175
- bundle: this.sourceWallet.bundle,
25176
- token: "USER"
25177
- });
25178
25178
  this.addAtom(Atom.create({
25179
- wallet,
25179
+ wallet: this.sourceWallet,
25180
25180
  isotope: "R",
25181
25181
  metaType,
25182
25182
  metaId,
@@ -25485,10 +25485,21 @@ ${operationTypes.join("\n")}
25485
25485
  if (Number(this.sourceWallet.balance) - amount < 0) {
25486
25486
  throw new BalanceInsufficientException();
25487
25487
  }
25488
+ const burnWallet = new Wallet({
25489
+ bundle: "0000000000000000000000000000000000000000000000000000000000000000",
25490
+ token: this.sourceWallet.token
25491
+ });
25488
25492
  this.addAtom(Atom.create({
25489
25493
  isotope: "V",
25490
25494
  wallet: this.sourceWallet,
25491
- value: -amount
25495
+ value: -Number(this.sourceWallet.balance)
25496
+ }));
25497
+ this.addAtom(Atom.create({
25498
+ isotope: "V",
25499
+ wallet: burnWallet,
25500
+ value: amount,
25501
+ metaType: "walletBundle",
25502
+ metaId: burnWallet.bundle
25492
25503
  }));
25493
25504
  this.addAtom(Atom.create({
25494
25505
  isotope: "V",
@@ -28576,6 +28587,20 @@ ${operationTypes.join("\n")}
28576
28587
  encrypt: this.cipherLink
28577
28588
  });
28578
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
+ }
28579
28604
  socketDisconnect() {
28580
28605
  if (this.socketUri) {
28581
28606
  this.unsubscribeAll();
@@ -31501,9 +31526,93 @@ ${operationTypes.join("\n")}
31501
31526
  }
31502
31527
  };
31503
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
+
31504
31610
  // src/response/ResponseProposeMolecule.ts
31505
31611
  init_Response();
31506
31612
  init_Dot();
31613
+ init_MolecularHashMismatchException();
31614
+ init_SignatureMismatchException();
31615
+ init_AtomIndexException();
31507
31616
  var ResponseProposeMolecule = class extends exports.Response {
31508
31617
  $__clientMolecule;
31509
31618
  /**
@@ -31575,6 +31684,47 @@ ${operationTypes.join("\n")}
31575
31684
  reason() {
31576
31685
  return exports.Dot.get(this.data(), "reason", "Invalid response from server");
31577
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
+ }
31578
31728
  /**
31579
31729
  * Returns payload object
31580
31730
  * Matches JavaScript SDK payload method exactly
@@ -32628,6 +32778,13 @@ ${operationTypes.join("\n")}
32628
32778
  $__remainderWallet = null;
32629
32779
  lastMoleculeQuery = null;
32630
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();
32631
32788
  /**
32632
32789
  * Enhanced constructor with standardized configuration validation (Phase 2 Enhancement)
32633
32790
  */
@@ -32752,6 +32909,7 @@ ${operationTypes.join("\n")}
32752
32909
  this.$__authToken = null;
32753
32910
  this.$__remainderWallet = null;
32754
32911
  this.lastMoleculeQuery = null;
32912
+ this.$__capabilityCache = {};
32755
32913
  }
32756
32914
  /**
32757
32915
  * Get the GraphQL client
@@ -32772,6 +32930,19 @@ ${operationTypes.join("\n")}
32772
32930
  this.$__client.setUri(this.getRandomUri());
32773
32931
  }
32774
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
+ }
32775
32946
  /**
32776
32947
  * Gets the Knish.IO server URIs
32777
32948
  */
@@ -32845,6 +33016,17 @@ ${operationTypes.join("\n")}
32845
33016
  this.log("info", "KnishIOClient::createMolecule() - Creating a new molecule...");
32846
33017
  secret = secret || this.getSecret();
32847
33018
  bundle = bundle || this.getBundle();
33019
+ let continuIdPosition = null;
33020
+ if (sourceWallet && sourceWallet.token !== "USER") {
33021
+ if (this.lastMoleculeQuery && this.getRemainderWallet() && this.getRemainderWallet()?.token === "USER" && this.lastMoleculeQuery.response() && this.lastMoleculeQuery.response()?.success()) {
33022
+ continuIdPosition = this.getRemainderWallet()?.position || null;
33023
+ this.log("info", `KnishIOClient::createMolecule() - Captured USER ContinuID position ${continuIdPosition?.substring(0, 16)}... for non-USER source wallet`);
33024
+ } else {
33025
+ const userWallet = await this.getSourceWallet();
33026
+ continuIdPosition = userWallet?.position || null;
33027
+ this.log("info", `KnishIOClient::createMolecule() - Queried USER ContinuID position ${continuIdPosition?.substring(0, 16)}... for non-USER source wallet`);
33028
+ }
33029
+ }
32848
33030
  if (!sourceWallet && this.lastMoleculeQuery && this.getRemainderWallet()?.token === "USER" && this.lastMoleculeQuery.response() && this.lastMoleculeQuery.response()?.success()) {
32849
33031
  sourceWallet = this.getRemainderWallet();
32850
33032
  this.log("info", `KnishIOClient::createMolecule() - Using carry-forward remainder wallet at position ${sourceWallet?.position?.substring(0, 16)}...`);
@@ -32864,7 +33046,8 @@ ${operationTypes.join("\n")}
32864
33046
  sourceWallet,
32865
33047
  remainderWallet: this.getRemainderWallet(),
32866
33048
  cellSlug: this.getCellSlug(),
32867
- version: this.getServerSdkVersion()
33049
+ version: this.getServerSdkVersion(),
33050
+ continuIdPosition
32868
33051
  });
32869
33052
  }
32870
33053
  /**
@@ -32883,6 +33066,23 @@ ${operationTypes.join("\n")}
32883
33066
  this.lastMoleculeQuery = mutation;
32884
33067
  return mutation;
32885
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
+ }
32886
33086
  /**
32887
33087
  * Executes a query or mutation
32888
33088
  */
@@ -32895,8 +33095,34 @@ ${operationTypes.join("\n")}
32895
33095
  encrypt: this.$__encrypt
32896
33096
  });
32897
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
+ }
32898
33105
  return await query.execute({ variables: variables || {} });
32899
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
+ }
32900
33126
  /**
32901
33127
  * Sets the secret for this session
32902
33128
  */
@@ -33276,6 +33502,63 @@ ${operationTypes.join("\n")}
33276
33502
  }
33277
33503
  return response;
33278
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
+ }
33279
33562
  /**
33280
33563
  * Query cascading meta instances for batchId
33281
33564
  */
@@ -33595,8 +33878,10 @@ ${operationTypes.join("\n")}
33595
33878
  amount: amount || 0
33596
33879
  });
33597
33880
  }
33881
+ const remainderWallet = sourceWallet.createRemainder(this.getSecret());
33598
33882
  const molecule = await this.createMolecule({
33599
- sourceWallet
33883
+ sourceWallet,
33884
+ remainderWallet
33600
33885
  });
33601
33886
  const mutation = await this.createMoleculeMutation({
33602
33887
  mutationClass: MutationTransferTokens,
@@ -33892,7 +34177,15 @@ ${operationTypes.join("\n")}
33892
34177
  amount
33893
34178
  });
33894
34179
  }
33895
- const mutation = await this.createMoleculeMutation({ mutationClass: MutationDepositBufferToken });
34180
+ const remainderWallet = sourceWallet.createRemainder(this.getSecret());
34181
+ const molecule = await this.createMolecule({
34182
+ sourceWallet,
34183
+ remainderWallet
34184
+ });
34185
+ const mutation = await this.createMoleculeMutation({
34186
+ mutationClass: MutationDepositBufferToken,
34187
+ molecule
34188
+ });
33896
34189
  await mutation.fillMolecule({
33897
34190
  amount: typeof amount === "string" ? Number(amount) : amount,
33898
34191
  tradeRates
@@ -34267,6 +34560,7 @@ ${operationTypes.join("\n")}
34267
34560
  exports.QueryBalance = QueryBalance;
34268
34561
  exports.QueryBatch = QueryBatch;
34269
34562
  exports.QueryContinuId = QueryContinuId;
34563
+ exports.QueryEmbeddingStatus = QueryEmbeddingStatus;
34270
34564
  exports.QueryMetaType = QueryMetaType;
34271
34565
  exports.QueryMetaTypeViaAtom = QueryMetaTypeViaAtom;
34272
34566
  exports.QueryWalletBundle = QueryWalletBundle;
@@ -34278,6 +34572,7 @@ ${operationTypes.join("\n")}
34278
34572
  exports.ResponseCreateMeta = ResponseCreateMeta;
34279
34573
  exports.ResponseCreateToken = ResponseCreateToken;
34280
34574
  exports.ResponseCreateWallet = ResponseCreateWallet;
34575
+ exports.ResponseEmbeddingStatus = ResponseEmbeddingStatus;
34281
34576
  exports.ResponseMetaType = ResponseMetaType;
34282
34577
  exports.ResponseMetaTypeViaAtom = ResponseMetaTypeViaAtom;
34283
34578
  exports.ResponsePeering = ResponsePeering;