@querypanel/node-sdk 1.0.36 → 1.0.37

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/README.md CHANGED
@@ -10,7 +10,7 @@ bun add @querypanel/sdk
10
10
  npm install @querypanel/sdk
11
11
  ```
12
12
 
13
- > **Runtime:** Node.js 18+ (or Bun). The SDK relies on the native `fetch` API.
13
+ > **Runtime:** Node.js 18+, Deno, or Bun. The SDK uses Web Crypto API for JWT signing and the native `fetch` API, making it compatible with modern JavaScript runtimes.
14
14
 
15
15
  ## Quickstart
16
16
 
@@ -133,6 +133,25 @@ dashboard.data.forEach(item => {
133
133
  });
134
134
  ```
135
135
 
136
+ ## Deno Support
137
+
138
+ The SDK is fully compatible with Deno (including Supabase Edge Functions) thanks to its use of Web Crypto API for JWT signing. No additional configuration needed:
139
+
140
+ ```ts
141
+ import { QueryPanelSdkAPI } from "https://esm.sh/@querypanel/sdk";
142
+
143
+ const qp = new QueryPanelSdkAPI(
144
+ Deno.env.get("QUERYPANEL_URL")!,
145
+ Deno.env.get("PRIVATE_KEY")!,
146
+ Deno.env.get("ORGANIZATION_ID")!,
147
+ );
148
+
149
+ // Use the SDK as normal - JWT signing works automatically
150
+ const response = await qp.ask("Show top products", {
151
+ tenantId: "tenant_123",
152
+ });
153
+ ```
154
+
136
155
  ## Building locally
137
156
 
138
157
  ```bash
package/dist/index.cjs CHANGED
@@ -554,7 +554,6 @@ function sanitize2(value) {
554
554
  }
555
555
 
556
556
  // src/core/client.ts
557
- var import_node_crypto = require("crypto");
558
557
  var ApiClient = class {
559
558
  baseUrl;
560
559
  privateKey;
@@ -562,6 +561,7 @@ var ApiClient = class {
562
561
  defaultTenantId;
563
562
  additionalHeaders;
564
563
  fetchImpl;
564
+ cryptoKey = null;
565
565
  constructor(baseUrl, privateKey, organizationId, options) {
566
566
  if (!baseUrl) {
567
567
  throw new Error("Base URL is required");
@@ -677,6 +677,66 @@ var ApiClient = class {
677
677
  }
678
678
  return headers;
679
679
  }
680
+ /**
681
+ * Base64URL encode a string (works in both Node.js 18+ and Deno)
682
+ */
683
+ base64UrlEncode(str) {
684
+ const bytes = new TextEncoder().encode(str);
685
+ let binary = "";
686
+ const chunkSize = 8192;
687
+ for (let i = 0; i < bytes.length; i += chunkSize) {
688
+ const chunk = bytes.slice(i, i + chunkSize);
689
+ binary += String.fromCharCode(...chunk);
690
+ }
691
+ const base64 = btoa(binary);
692
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
693
+ }
694
+ /**
695
+ * Base64URL encode from Uint8Array (for binary data like signatures)
696
+ */
697
+ base64UrlEncodeBytes(bytes) {
698
+ let binary = "";
699
+ const chunkSize = 8192;
700
+ for (let i = 0; i < bytes.length; i += chunkSize) {
701
+ const chunk = bytes.slice(i, i + chunkSize);
702
+ binary += String.fromCharCode(...chunk);
703
+ }
704
+ const base64 = btoa(binary);
705
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
706
+ }
707
+ /**
708
+ * Import the private key into Web Crypto API format (cached after first import)
709
+ */
710
+ async getCryptoKey() {
711
+ if (this.cryptoKey) {
712
+ return this.cryptoKey;
713
+ }
714
+ this.cryptoKey = await crypto.subtle.importKey(
715
+ "pkcs8",
716
+ this.privateKeyToArrayBuffer(this.privateKey),
717
+ {
718
+ name: "RSASSA-PKCS1-v1_5",
719
+ hash: "SHA-256"
720
+ },
721
+ false,
722
+ ["sign"]
723
+ );
724
+ return this.cryptoKey;
725
+ }
726
+ /**
727
+ * Convert PEM private key to ArrayBuffer for Web Crypto API
728
+ */
729
+ privateKeyToArrayBuffer(pem) {
730
+ const pemHeader = "-----BEGIN PRIVATE KEY-----";
731
+ const pemFooter = "-----END PRIVATE KEY-----";
732
+ const pemContents = pem.replace(pemHeader, "").replace(pemFooter, "").replace(/\s/g, "");
733
+ const binaryString = atob(pemContents);
734
+ const bytes = new Uint8Array(binaryString.length);
735
+ for (let i = 0; i < binaryString.length; i++) {
736
+ bytes[i] = binaryString.charCodeAt(i);
737
+ }
738
+ return bytes.buffer;
739
+ }
680
740
  async generateJWT(tenantId, userId, scopes) {
681
741
  const header = {
682
742
  alg: "RS256",
@@ -688,19 +748,20 @@ var ApiClient = class {
688
748
  };
689
749
  if (userId) payload.userId = userId;
690
750
  if (scopes?.length) payload.scopes = scopes;
691
- const encodeJson = (obj) => {
692
- const json = JSON.stringify(obj);
693
- const base64 = Buffer.from(json).toString("base64");
694
- return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
695
- };
696
- const encodedHeader = encodeJson(header);
697
- const encodedPayload = encodeJson(payload);
751
+ const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
752
+ const encodedPayload = this.base64UrlEncode(JSON.stringify(payload));
698
753
  const data = `${encodedHeader}.${encodedPayload}`;
699
- const signer = (0, import_node_crypto.createSign)("RSA-SHA256");
700
- signer.update(data);
701
- signer.end();
702
- const signature = signer.sign(this.privateKey);
703
- const encodedSignature = signature.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
754
+ const key = await this.getCryptoKey();
755
+ const dataBytes = new TextEncoder().encode(data);
756
+ const signature = await crypto.subtle.sign(
757
+ {
758
+ name: "RSASSA-PKCS1-v1_5"
759
+ },
760
+ key,
761
+ dataBytes
762
+ );
763
+ const signatureBytes = new Uint8Array(signature);
764
+ const encodedSignature = this.base64UrlEncodeBytes(signatureBytes);
704
765
  return `${data}.${encodedSignature}`;
705
766
  }
706
767
  };
@@ -1025,7 +1086,6 @@ function resolveTenantId2(client, tenantId) {
1025
1086
  }
1026
1087
 
1027
1088
  // src/routes/ingest.ts
1028
- var import_node_crypto2 = require("crypto");
1029
1089
  async function syncSchema(client, queryEngine, databaseName, options, signal) {
1030
1090
  const tenantId = resolveTenantId3(client, options.tenantId);
1031
1091
  const adapter = queryEngine.getDatabase(databaseName);
@@ -1037,7 +1097,7 @@ async function syncSchema(client, queryEngine, databaseName, options, signal) {
1037
1097
  if (options.forceReindex) {
1038
1098
  payload.force_reindex = true;
1039
1099
  }
1040
- const sessionId = (0, import_node_crypto2.randomUUID)();
1100
+ const sessionId = crypto.randomUUID();
1041
1101
  const response = await client.post(
1042
1102
  "/ingest",
1043
1103
  payload,
@@ -1086,10 +1146,9 @@ function buildSchemaRequest(databaseName, adapter, introspection, metadata) {
1086
1146
  }
1087
1147
 
1088
1148
  // src/routes/query.ts
1089
- var import_node_crypto3 = require("crypto");
1090
1149
  async function ask(client, queryEngine, question, options, signal) {
1091
1150
  const tenantId = resolveTenantId4(client, options.tenantId);
1092
- const sessionId = (0, import_node_crypto3.randomUUID)();
1151
+ const sessionId = crypto.randomUUID();
1093
1152
  const maxRetry = options.maxRetry ?? 0;
1094
1153
  let attempt = 0;
1095
1154
  let lastError = options.lastError;