@reifydb/client 0.4.2 → 0.4.6
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/dist/index.d.ts +87 -30
- package/dist/index.js +595 -145
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -143,7 +143,9 @@ var WsClient = class _WsClient {
|
|
|
143
143
|
socket.addEventListener("error", onError);
|
|
144
144
|
});
|
|
145
145
|
}
|
|
146
|
-
|
|
146
|
+
if (options.token) {
|
|
147
|
+
socket.send(JSON.stringify({ id: "auth-1", type: "Auth", payload: { token: options.token } }));
|
|
148
|
+
}
|
|
147
149
|
return new _WsClient(socket, options);
|
|
148
150
|
}
|
|
149
151
|
/**
|
|
@@ -384,6 +386,71 @@ var WsClient = class _WsClient {
|
|
|
384
386
|
}
|
|
385
387
|
return value;
|
|
386
388
|
}
|
|
389
|
+
async loginWithPassword(principal, password) {
|
|
390
|
+
return this.login("password", principal, { password });
|
|
391
|
+
}
|
|
392
|
+
async loginWithToken(principal, token) {
|
|
393
|
+
return this.login("token", principal, { token });
|
|
394
|
+
}
|
|
395
|
+
async login(method, principal, credentials) {
|
|
396
|
+
const id = `auth-${this.nextId++}`;
|
|
397
|
+
const request = {
|
|
398
|
+
id,
|
|
399
|
+
type: "Auth",
|
|
400
|
+
payload: { method, principal, credentials }
|
|
401
|
+
};
|
|
402
|
+
const response = await new Promise((resolve, reject) => {
|
|
403
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
404
|
+
const timeout = setTimeout(() => {
|
|
405
|
+
this.pending.delete(id);
|
|
406
|
+
reject(new Error("Login timeout"));
|
|
407
|
+
}, timeoutMs);
|
|
408
|
+
this.pending.set(id, (res) => {
|
|
409
|
+
clearTimeout(timeout);
|
|
410
|
+
resolve(res);
|
|
411
|
+
});
|
|
412
|
+
this.socket.send(JSON.stringify(request));
|
|
413
|
+
});
|
|
414
|
+
if (response.type === "Err") {
|
|
415
|
+
throw new ReifyError(response);
|
|
416
|
+
}
|
|
417
|
+
if (response.type !== "Auth") {
|
|
418
|
+
throw new Error(`Unexpected response type: ${response.type}`);
|
|
419
|
+
}
|
|
420
|
+
const payload = response.payload;
|
|
421
|
+
if (payload.status !== "authenticated" || !payload.token || !payload.identity) {
|
|
422
|
+
throw new Error("Authentication failed");
|
|
423
|
+
}
|
|
424
|
+
this.options.token = payload.token;
|
|
425
|
+
return { token: payload.token, identity: payload.identity };
|
|
426
|
+
}
|
|
427
|
+
async logout() {
|
|
428
|
+
if (!this.options.token) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const id = `logout-${this.nextId++}`;
|
|
432
|
+
const request = {
|
|
433
|
+
id,
|
|
434
|
+
type: "Logout",
|
|
435
|
+
payload: {}
|
|
436
|
+
};
|
|
437
|
+
const response = await new Promise((resolve, reject) => {
|
|
438
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
439
|
+
const timeout = setTimeout(() => {
|
|
440
|
+
this.pending.delete(id);
|
|
441
|
+
reject(new Error("Logout timeout"));
|
|
442
|
+
}, timeoutMs);
|
|
443
|
+
this.pending.set(id, (res) => {
|
|
444
|
+
clearTimeout(timeout);
|
|
445
|
+
resolve(res);
|
|
446
|
+
});
|
|
447
|
+
this.socket.send(JSON.stringify(request));
|
|
448
|
+
});
|
|
449
|
+
if (response.type === "Err") {
|
|
450
|
+
throw new ReifyError(response);
|
|
451
|
+
}
|
|
452
|
+
this.options = { ...this.options, token: void 0 };
|
|
453
|
+
}
|
|
387
454
|
disconnect() {
|
|
388
455
|
this.shouldReconnect = false;
|
|
389
456
|
this.subscriptions.clear();
|
|
@@ -435,7 +502,9 @@ var WsClient = class _WsClient {
|
|
|
435
502
|
socket.addEventListener("error", onError);
|
|
436
503
|
});
|
|
437
504
|
}
|
|
438
|
-
|
|
505
|
+
if (this.options.token) {
|
|
506
|
+
socket.send(JSON.stringify({ id: "auth-1", type: "Auth", payload: { token: this.options.token } }));
|
|
507
|
+
}
|
|
439
508
|
this.socket = socket;
|
|
440
509
|
this.setupSocketHandlers();
|
|
441
510
|
this.reconnectAttempts = 0;
|
|
@@ -468,14 +537,14 @@ var WsClient = class _WsClient {
|
|
|
468
537
|
if (frames.length === 0) return;
|
|
469
538
|
const frame = frames[0];
|
|
470
539
|
const opColumn = frame.columns.find((c) => c.name === "_op");
|
|
471
|
-
if (!opColumn || opColumn.
|
|
540
|
+
if (!opColumn || opColumn.payload.length === 0) {
|
|
472
541
|
console.error("Missing or empty _op column:", { opColumn, frame });
|
|
473
542
|
return;
|
|
474
543
|
}
|
|
475
544
|
const rows = this.frameToRows(frame, state.schema);
|
|
476
545
|
const batches = [];
|
|
477
546
|
for (let i = 0; i < rows.length; i++) {
|
|
478
|
-
const opValue = parseInt(opColumn.
|
|
547
|
+
const opValue = parseInt(opColumn.payload[i]);
|
|
479
548
|
const operation = opValue === 1 ? "INSERT" : opValue === 2 ? "UPDATE" : opValue === 3 ? "REMOVE" : "INSERT";
|
|
480
549
|
const { _op, ...cleanRow } = rows[i];
|
|
481
550
|
if (batches.length > 0 && batches[batches.length - 1].op === operation) {
|
|
@@ -500,12 +569,12 @@ var WsClient = class _WsClient {
|
|
|
500
569
|
}
|
|
501
570
|
frameToRows(frame, schema) {
|
|
502
571
|
if (!frame.columns || frame.columns.length === 0) return [];
|
|
503
|
-
const rowCount = frame.columns[0].
|
|
572
|
+
const rowCount = frame.columns[0].payload.length;
|
|
504
573
|
const rows = [];
|
|
505
574
|
for (let i = 0; i < rowCount; i++) {
|
|
506
575
|
const row = {};
|
|
507
576
|
for (const col of frame.columns) {
|
|
508
|
-
row[col.name] = decode({ type: col.type, value: col.
|
|
577
|
+
row[col.name] = decode({ type: col.type, value: col.payload[i] });
|
|
509
578
|
}
|
|
510
579
|
rows.push(row);
|
|
511
580
|
}
|
|
@@ -557,17 +626,390 @@ var WsClient = class _WsClient {
|
|
|
557
626
|
}
|
|
558
627
|
};
|
|
559
628
|
function columnsToRows(columns) {
|
|
560
|
-
const rowCount = columns[0]?.
|
|
629
|
+
const rowCount = columns[0]?.payload.length ?? 0;
|
|
561
630
|
return Array.from({ length: rowCount }, (_, i) => {
|
|
562
631
|
const row = {};
|
|
563
632
|
for (const col of columns) {
|
|
564
|
-
row[col.name] = decode({ type: col.type, value: col.
|
|
633
|
+
row[col.name] = decode({ type: col.type, value: col.payload[i] });
|
|
565
634
|
}
|
|
566
635
|
return row;
|
|
567
636
|
});
|
|
568
637
|
}
|
|
569
638
|
|
|
570
|
-
// src/
|
|
639
|
+
// src/http.ts
|
|
640
|
+
import {
|
|
641
|
+
decode as decode2
|
|
642
|
+
} from "@reifydb/core";
|
|
643
|
+
var HttpClient = class _HttpClient {
|
|
644
|
+
constructor(options) {
|
|
645
|
+
this.options = options;
|
|
646
|
+
}
|
|
647
|
+
static connect(options) {
|
|
648
|
+
return new _HttpClient(options);
|
|
649
|
+
}
|
|
650
|
+
async loginWithPassword(principal, password) {
|
|
651
|
+
return this.login("password", principal, { password });
|
|
652
|
+
}
|
|
653
|
+
async loginWithToken(principal, token) {
|
|
654
|
+
return this.login("token", principal, { token });
|
|
655
|
+
}
|
|
656
|
+
async login(method, principal, credentials) {
|
|
657
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
658
|
+
const controller = new AbortController();
|
|
659
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
660
|
+
try {
|
|
661
|
+
const response = await fetch(`${this.options.url}/v1/authenticate`, {
|
|
662
|
+
method: "POST",
|
|
663
|
+
headers: { "Content-Type": "application/json" },
|
|
664
|
+
body: JSON.stringify({ method, principal, credentials }),
|
|
665
|
+
signal: controller.signal
|
|
666
|
+
});
|
|
667
|
+
clearTimeout(timeout);
|
|
668
|
+
const body = await response.json();
|
|
669
|
+
if (body.status !== "authenticated" || !body.token || !body.identity) {
|
|
670
|
+
throw new Error(body.reason || "Authentication failed");
|
|
671
|
+
}
|
|
672
|
+
this.options = { ...this.options, token: body.token };
|
|
673
|
+
return { token: body.token, identity: body.identity };
|
|
674
|
+
} catch (err) {
|
|
675
|
+
clearTimeout(timeout);
|
|
676
|
+
if (err.name === "AbortError") throw new Error("Login timeout");
|
|
677
|
+
throw err;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
async logout() {
|
|
681
|
+
if (!this.options.token) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
685
|
+
const controller = new AbortController();
|
|
686
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
687
|
+
try {
|
|
688
|
+
const response = await fetch(`${this.options.url}/v1/logout`, {
|
|
689
|
+
method: "POST",
|
|
690
|
+
headers: {
|
|
691
|
+
"Authorization": `Bearer ${this.options.token}`
|
|
692
|
+
},
|
|
693
|
+
signal: controller.signal
|
|
694
|
+
});
|
|
695
|
+
clearTimeout(timeout);
|
|
696
|
+
if (!response.ok) {
|
|
697
|
+
const body = await response.text();
|
|
698
|
+
throw new Error(`Logout failed: HTTP ${response.status}: ${body}`);
|
|
699
|
+
}
|
|
700
|
+
this.options = { ...this.options, token: void 0 };
|
|
701
|
+
} catch (err) {
|
|
702
|
+
clearTimeout(timeout);
|
|
703
|
+
if (err.name === "AbortError") throw new Error("Logout timeout");
|
|
704
|
+
throw err;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
async admin(statements, params, schemas) {
|
|
708
|
+
const statementArray = Array.isArray(statements) ? statements : [statements];
|
|
709
|
+
const outputStatements = statementArray.length > 1 ? statementArray.map((s) => s.trim() ? `OUTPUT ${s}` : s) : statementArray;
|
|
710
|
+
const encodedParams = params !== void 0 && params !== null ? encodeParams(params) : void 0;
|
|
711
|
+
const result = await this.send("admin", outputStatements, encodedParams);
|
|
712
|
+
const transformedFrames = result.map((frame, frameIndex) => {
|
|
713
|
+
const frameSchema = schemas[frameIndex];
|
|
714
|
+
if (!frameSchema) {
|
|
715
|
+
return frame;
|
|
716
|
+
}
|
|
717
|
+
return frame.map((row) => this.transformResult(row, frameSchema));
|
|
718
|
+
});
|
|
719
|
+
return transformedFrames;
|
|
720
|
+
}
|
|
721
|
+
async command(statements, params, schemas) {
|
|
722
|
+
const statementArray = Array.isArray(statements) ? statements : [statements];
|
|
723
|
+
const outputStatements = statementArray.length > 1 ? statementArray.map((s) => s.trim() ? `OUTPUT ${s}` : s) : statementArray;
|
|
724
|
+
const encodedParams = params !== void 0 && params !== null ? encodeParams(params) : void 0;
|
|
725
|
+
const result = await this.send("command", outputStatements, encodedParams);
|
|
726
|
+
const transformedFrames = result.map((frame, frameIndex) => {
|
|
727
|
+
const frameSchema = schemas[frameIndex];
|
|
728
|
+
if (!frameSchema) {
|
|
729
|
+
return frame;
|
|
730
|
+
}
|
|
731
|
+
return frame.map((row) => this.transformResult(row, frameSchema));
|
|
732
|
+
});
|
|
733
|
+
return transformedFrames;
|
|
734
|
+
}
|
|
735
|
+
async query(statements, params, schemas) {
|
|
736
|
+
const statementArray = Array.isArray(statements) ? statements : [statements];
|
|
737
|
+
const outputStatements = statementArray.length > 1 ? statementArray.map((s) => s.trim() ? `OUTPUT ${s}` : s) : statementArray;
|
|
738
|
+
const encodedParams = params !== void 0 && params !== null ? encodeParams(params) : void 0;
|
|
739
|
+
const result = await this.send("query", outputStatements, encodedParams);
|
|
740
|
+
const transformedFrames = result.map((frame, frameIndex) => {
|
|
741
|
+
const frameSchema = schemas[frameIndex];
|
|
742
|
+
if (!frameSchema) {
|
|
743
|
+
return frame;
|
|
744
|
+
}
|
|
745
|
+
return frame.map((row) => this.transformResult(row, frameSchema));
|
|
746
|
+
});
|
|
747
|
+
return transformedFrames;
|
|
748
|
+
}
|
|
749
|
+
async send(endpoint, statements, params) {
|
|
750
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
751
|
+
const controller = new AbortController();
|
|
752
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
753
|
+
const headers = {
|
|
754
|
+
"Content-Type": "application/json"
|
|
755
|
+
};
|
|
756
|
+
if (this.options.token) {
|
|
757
|
+
headers["Authorization"] = `Bearer ${this.options.token}`;
|
|
758
|
+
}
|
|
759
|
+
const body = { statements };
|
|
760
|
+
if (params !== void 0) {
|
|
761
|
+
body.params = params;
|
|
762
|
+
}
|
|
763
|
+
try {
|
|
764
|
+
const response = await fetch(`${this.options.url}/v1/${endpoint}`, {
|
|
765
|
+
method: "POST",
|
|
766
|
+
headers,
|
|
767
|
+
body: JSON.stringify(body),
|
|
768
|
+
signal: controller.signal,
|
|
769
|
+
credentials: "include"
|
|
770
|
+
});
|
|
771
|
+
clearTimeout(timeout);
|
|
772
|
+
const responseBody = await response.text();
|
|
773
|
+
let parsed;
|
|
774
|
+
try {
|
|
775
|
+
parsed = JSON.parse(responseBody);
|
|
776
|
+
} catch {
|
|
777
|
+
throw new Error(`Invalid JSON response: ${responseBody}`);
|
|
778
|
+
}
|
|
779
|
+
if (!response.ok) {
|
|
780
|
+
if (parsed.diagnostic) {
|
|
781
|
+
throw new ReifyError({
|
|
782
|
+
id: "",
|
|
783
|
+
type: "Err",
|
|
784
|
+
payload: { diagnostic: parsed.diagnostic }
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
throw new Error(parsed.error || `HTTP ${response.status}: ${responseBody}`);
|
|
788
|
+
}
|
|
789
|
+
const frames = parsed.frames || [];
|
|
790
|
+
return frames.map(
|
|
791
|
+
(frame) => columnsToRows2(frame.columns)
|
|
792
|
+
);
|
|
793
|
+
} catch (err) {
|
|
794
|
+
clearTimeout(timeout);
|
|
795
|
+
if (err.name === "AbortError") {
|
|
796
|
+
throw new Error("ReifyDB query timeout");
|
|
797
|
+
}
|
|
798
|
+
throw err;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
transformResult(row, resultSchema) {
|
|
802
|
+
if (resultSchema && resultSchema.kind === "object" && resultSchema.properties) {
|
|
803
|
+
const transformedRow = {};
|
|
804
|
+
for (const [key, value] of Object.entries(row)) {
|
|
805
|
+
const propertySchema = resultSchema.properties[key];
|
|
806
|
+
if (propertySchema && propertySchema.kind === "primitive") {
|
|
807
|
+
if (value && typeof value === "object" && typeof value.valueOf === "function") {
|
|
808
|
+
const rawValue = value.valueOf();
|
|
809
|
+
transformedRow[key] = this.coerceToPrimitiveType(rawValue, propertySchema.type);
|
|
810
|
+
} else {
|
|
811
|
+
transformedRow[key] = this.coerceToPrimitiveType(value, propertySchema.type);
|
|
812
|
+
}
|
|
813
|
+
} else if (propertySchema && propertySchema.kind === "value") {
|
|
814
|
+
transformedRow[key] = value;
|
|
815
|
+
} else {
|
|
816
|
+
transformedRow[key] = propertySchema ? this.transformResult(value, propertySchema) : value;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return transformedRow;
|
|
820
|
+
}
|
|
821
|
+
if (resultSchema && resultSchema.kind === "primitive") {
|
|
822
|
+
if (row && typeof row === "object" && typeof row.valueOf === "function") {
|
|
823
|
+
return this.coerceToPrimitiveType(row.valueOf(), resultSchema.type);
|
|
824
|
+
}
|
|
825
|
+
return this.coerceToPrimitiveType(row, resultSchema.type);
|
|
826
|
+
}
|
|
827
|
+
if (resultSchema && resultSchema.kind === "value") {
|
|
828
|
+
return row;
|
|
829
|
+
}
|
|
830
|
+
if (resultSchema && resultSchema.kind === "array") {
|
|
831
|
+
if (Array.isArray(row)) {
|
|
832
|
+
return row.map((item) => this.transformResult(item, resultSchema.items));
|
|
833
|
+
}
|
|
834
|
+
return row;
|
|
835
|
+
}
|
|
836
|
+
if (resultSchema && resultSchema.kind === "optional") {
|
|
837
|
+
if (row === void 0 || row === null) {
|
|
838
|
+
return void 0;
|
|
839
|
+
}
|
|
840
|
+
return this.transformResult(row, resultSchema.schema);
|
|
841
|
+
}
|
|
842
|
+
return row;
|
|
843
|
+
}
|
|
844
|
+
coerceToPrimitiveType(value, schemaType) {
|
|
845
|
+
if (value === void 0 || value === null) {
|
|
846
|
+
return value;
|
|
847
|
+
}
|
|
848
|
+
const bigintTypes = ["Int8", "Int16", "Uint8", "Uint16"];
|
|
849
|
+
if (bigintTypes.includes(schemaType)) {
|
|
850
|
+
if (typeof value === "bigint") {
|
|
851
|
+
return value;
|
|
852
|
+
}
|
|
853
|
+
if (typeof value === "number") {
|
|
854
|
+
return BigInt(Math.trunc(value));
|
|
855
|
+
}
|
|
856
|
+
if (typeof value === "string") {
|
|
857
|
+
return BigInt(value);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return value;
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
function columnsToRows2(columns) {
|
|
864
|
+
const rowCount = columns[0]?.payload.length ?? 0;
|
|
865
|
+
return Array.from({ length: rowCount }, (_, i) => {
|
|
866
|
+
const row = {};
|
|
867
|
+
for (const col of columns) {
|
|
868
|
+
row[col.name] = decode2({ type: col.type, value: col.payload[i] });
|
|
869
|
+
}
|
|
870
|
+
return row;
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// src/json-http.ts
|
|
875
|
+
var JsonHttpClient = class _JsonHttpClient {
|
|
876
|
+
constructor(options) {
|
|
877
|
+
this.options = options;
|
|
878
|
+
}
|
|
879
|
+
static connect(options) {
|
|
880
|
+
return new _JsonHttpClient(options);
|
|
881
|
+
}
|
|
882
|
+
async loginWithPassword(principal, password) {
|
|
883
|
+
return this.login("password", principal, { password });
|
|
884
|
+
}
|
|
885
|
+
async loginWithToken(principal, token) {
|
|
886
|
+
return this.login("token", principal, { token });
|
|
887
|
+
}
|
|
888
|
+
async login(method, principal, credentials) {
|
|
889
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
890
|
+
const controller = new AbortController();
|
|
891
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
892
|
+
try {
|
|
893
|
+
const response = await fetch(`${this.options.url}/v1/authenticate`, {
|
|
894
|
+
method: "POST",
|
|
895
|
+
headers: { "Content-Type": "application/json" },
|
|
896
|
+
body: JSON.stringify({ method, principal, credentials }),
|
|
897
|
+
signal: controller.signal
|
|
898
|
+
});
|
|
899
|
+
clearTimeout(timeout);
|
|
900
|
+
const body = await response.json();
|
|
901
|
+
if (body.status !== "authenticated" || !body.token || !body.identity) {
|
|
902
|
+
throw new Error(body.reason || "Authentication failed");
|
|
903
|
+
}
|
|
904
|
+
this.options = { ...this.options, token: body.token };
|
|
905
|
+
return { token: body.token, identity: body.identity };
|
|
906
|
+
} catch (err) {
|
|
907
|
+
clearTimeout(timeout);
|
|
908
|
+
if (err.name === "AbortError") throw new Error("Login timeout");
|
|
909
|
+
throw err;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
async logout() {
|
|
913
|
+
if (!this.options.token) {
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
917
|
+
const controller = new AbortController();
|
|
918
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
919
|
+
try {
|
|
920
|
+
const response = await fetch(`${this.options.url}/v1/logout`, {
|
|
921
|
+
method: "POST",
|
|
922
|
+
headers: {
|
|
923
|
+
"Authorization": `Bearer ${this.options.token}`
|
|
924
|
+
},
|
|
925
|
+
signal: controller.signal
|
|
926
|
+
});
|
|
927
|
+
clearTimeout(timeout);
|
|
928
|
+
if (!response.ok) {
|
|
929
|
+
const body = await response.text();
|
|
930
|
+
throw new Error(`Logout failed: HTTP ${response.status}: ${body}`);
|
|
931
|
+
}
|
|
932
|
+
this.options = { ...this.options, token: void 0 };
|
|
933
|
+
} catch (err) {
|
|
934
|
+
clearTimeout(timeout);
|
|
935
|
+
if (err.name === "AbortError") throw new Error("Logout timeout");
|
|
936
|
+
throw err;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
async admin(statements, params) {
|
|
940
|
+
const statementArray = Array.isArray(statements) ? statements : [statements];
|
|
941
|
+
const outputStatements = statementArray.length > 1 ? statementArray.map((s) => s.trim() ? `OUTPUT ${s}` : s) : statementArray;
|
|
942
|
+
const encodedParams = params !== void 0 && params !== null ? encodeParams(params) : void 0;
|
|
943
|
+
return this.send("admin", outputStatements, encodedParams);
|
|
944
|
+
}
|
|
945
|
+
async command(statements, params) {
|
|
946
|
+
const statementArray = Array.isArray(statements) ? statements : [statements];
|
|
947
|
+
const outputStatements = statementArray.length > 1 ? statementArray.map((s) => s.trim() ? `OUTPUT ${s}` : s) : statementArray;
|
|
948
|
+
const encodedParams = params !== void 0 && params !== null ? encodeParams(params) : void 0;
|
|
949
|
+
return this.send("command", outputStatements, encodedParams);
|
|
950
|
+
}
|
|
951
|
+
async query(statements, params) {
|
|
952
|
+
const statementArray = Array.isArray(statements) ? statements : [statements];
|
|
953
|
+
const outputStatements = statementArray.length > 1 ? statementArray.map((s) => s.trim() ? `OUTPUT ${s}` : s) : statementArray;
|
|
954
|
+
const encodedParams = params !== void 0 && params !== null ? encodeParams(params) : void 0;
|
|
955
|
+
return this.send("query", outputStatements, encodedParams);
|
|
956
|
+
}
|
|
957
|
+
async send(endpoint, statements, params) {
|
|
958
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
959
|
+
const controller = new AbortController();
|
|
960
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
961
|
+
const headers = {
|
|
962
|
+
"Content-Type": "application/json"
|
|
963
|
+
};
|
|
964
|
+
if (this.options.token) {
|
|
965
|
+
headers["Authorization"] = `Bearer ${this.options.token}`;
|
|
966
|
+
}
|
|
967
|
+
const body = { statements };
|
|
968
|
+
if (params !== void 0) {
|
|
969
|
+
body.params = params;
|
|
970
|
+
}
|
|
971
|
+
const queryParams = new URLSearchParams({ format: "json" });
|
|
972
|
+
if (this.options.unwrap) {
|
|
973
|
+
queryParams.set("unwrap", "true");
|
|
974
|
+
}
|
|
975
|
+
try {
|
|
976
|
+
const response = await fetch(`${this.options.url}/v1/${endpoint}?${queryParams}`, {
|
|
977
|
+
method: "POST",
|
|
978
|
+
headers,
|
|
979
|
+
body: JSON.stringify(body),
|
|
980
|
+
signal: controller.signal,
|
|
981
|
+
credentials: "include"
|
|
982
|
+
});
|
|
983
|
+
clearTimeout(timeout);
|
|
984
|
+
const responseBody = await response.text();
|
|
985
|
+
let parsed;
|
|
986
|
+
try {
|
|
987
|
+
parsed = JSON.parse(responseBody);
|
|
988
|
+
} catch {
|
|
989
|
+
throw new Error(`Invalid JSON response: ${responseBody}`);
|
|
990
|
+
}
|
|
991
|
+
if (!response.ok) {
|
|
992
|
+
if (parsed.diagnostic) {
|
|
993
|
+
throw new ReifyError({
|
|
994
|
+
id: "",
|
|
995
|
+
type: "Err",
|
|
996
|
+
payload: { diagnostic: parsed.diagnostic }
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
throw new Error(parsed.error || `HTTP ${response.status}: ${responseBody}`);
|
|
1000
|
+
}
|
|
1001
|
+
return parsed;
|
|
1002
|
+
} catch (err) {
|
|
1003
|
+
clearTimeout(timeout);
|
|
1004
|
+
if (err.name === "AbortError") {
|
|
1005
|
+
throw new Error("ReifyDB query timeout");
|
|
1006
|
+
}
|
|
1007
|
+
throw err;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
// src/json-ws.ts
|
|
571
1013
|
async function createWebSocket2(url) {
|
|
572
1014
|
if (typeof window !== "undefined" && typeof window.WebSocket !== "undefined") {
|
|
573
1015
|
return new WebSocket(url);
|
|
@@ -576,7 +1018,7 @@ async function createWebSocket2(url) {
|
|
|
576
1018
|
return new wsModule.WebSocket(url);
|
|
577
1019
|
}
|
|
578
1020
|
}
|
|
579
|
-
var
|
|
1021
|
+
var JsonWebsocketClient = class _JsonWebsocketClient {
|
|
580
1022
|
constructor(socket, options) {
|
|
581
1023
|
this.pending = /* @__PURE__ */ new Map();
|
|
582
1024
|
this.reconnectAttempts = 0;
|
|
@@ -614,27 +1056,61 @@ var JsonWsClient = class _JsonWsClient {
|
|
|
614
1056
|
socket.addEventListener("error", onError);
|
|
615
1057
|
});
|
|
616
1058
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
return this.send("Query", statements, params);
|
|
622
|
-
}
|
|
623
|
-
async command(statements, params) {
|
|
624
|
-
return this.send("Command", statements, params);
|
|
1059
|
+
if (options.token) {
|
|
1060
|
+
socket.send(JSON.stringify({ id: "auth-1", type: "Auth", payload: { token: options.token } }));
|
|
1061
|
+
}
|
|
1062
|
+
return new _JsonWebsocketClient(socket, options);
|
|
625
1063
|
}
|
|
626
1064
|
async admin(statements, params) {
|
|
627
|
-
|
|
1065
|
+
const id = `req-${this.nextId++}`;
|
|
1066
|
+
const statementArray = Array.isArray(statements) ? statements : [statements];
|
|
1067
|
+
const outputStatements = statementArray.length > 1 ? statementArray.map((s) => s.trim() ? `OUTPUT ${s}` : s) : statementArray;
|
|
1068
|
+
const encodedParams = params !== void 0 && params !== null ? encodeParams(params) : void 0;
|
|
1069
|
+
return this.send({
|
|
1070
|
+
id,
|
|
1071
|
+
type: "Admin",
|
|
1072
|
+
payload: {
|
|
1073
|
+
statements: outputStatements,
|
|
1074
|
+
params: encodedParams,
|
|
1075
|
+
format: "json",
|
|
1076
|
+
...this.options.unwrap ? { unwrap: true } : {}
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
628
1079
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
1080
|
+
async command(statements, params) {
|
|
1081
|
+
const id = `req-${this.nextId++}`;
|
|
1082
|
+
const statementArray = Array.isArray(statements) ? statements : [statements];
|
|
1083
|
+
const outputStatements = statementArray.length > 1 ? statementArray.map((s) => s.trim() ? `OUTPUT ${s}` : s) : statementArray;
|
|
1084
|
+
const encodedParams = params !== void 0 && params !== null ? encodeParams(params) : void 0;
|
|
1085
|
+
return this.send({
|
|
1086
|
+
id,
|
|
1087
|
+
type: "Command",
|
|
1088
|
+
payload: {
|
|
1089
|
+
statements: outputStatements,
|
|
1090
|
+
params: encodedParams,
|
|
1091
|
+
format: "json",
|
|
1092
|
+
...this.options.unwrap ? { unwrap: true } : {}
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
632
1095
|
}
|
|
633
|
-
async
|
|
1096
|
+
async query(statements, params) {
|
|
634
1097
|
const id = `req-${this.nextId++}`;
|
|
635
1098
|
const statementArray = Array.isArray(statements) ? statements : [statements];
|
|
636
1099
|
const outputStatements = statementArray.length > 1 ? statementArray.map((s) => s.trim() ? `OUTPUT ${s}` : s) : statementArray;
|
|
637
1100
|
const encodedParams = params !== void 0 && params !== null ? encodeParams(params) : void 0;
|
|
1101
|
+
return this.send({
|
|
1102
|
+
id,
|
|
1103
|
+
type: "Query",
|
|
1104
|
+
payload: {
|
|
1105
|
+
statements: outputStatements,
|
|
1106
|
+
params: encodedParams,
|
|
1107
|
+
format: "json",
|
|
1108
|
+
...this.options.unwrap ? { unwrap: true } : {}
|
|
1109
|
+
}
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
async send(req) {
|
|
1113
|
+
const id = req.id;
|
|
638
1114
|
if (this.socket.readyState !== 1) {
|
|
639
1115
|
throw new ReifyError({
|
|
640
1116
|
id: "connection-error",
|
|
@@ -658,36 +1134,84 @@ var JsonWsClient = class _JsonWsClient {
|
|
|
658
1134
|
clearTimeout(timeout);
|
|
659
1135
|
resolve(res);
|
|
660
1136
|
});
|
|
661
|
-
this.socket.send(JSON.stringify(
|
|
662
|
-
id,
|
|
663
|
-
type,
|
|
664
|
-
payload: {
|
|
665
|
-
statements: outputStatements,
|
|
666
|
-
params: encodedParams,
|
|
667
|
-
format: "json"
|
|
668
|
-
}
|
|
669
|
-
}));
|
|
1137
|
+
this.socket.send(JSON.stringify(req));
|
|
670
1138
|
});
|
|
671
1139
|
if (response.type === "Err") {
|
|
672
1140
|
throw new ReifyError(response);
|
|
673
1141
|
}
|
|
674
|
-
if (response.type !== type) {
|
|
1142
|
+
if (response.type !== req.type) {
|
|
675
1143
|
throw new Error(`Unexpected response type: ${response.type}`);
|
|
676
1144
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
1145
|
+
return response.payload.body;
|
|
1146
|
+
}
|
|
1147
|
+
async loginWithPassword(principal, password) {
|
|
1148
|
+
return this.login("password", principal, { password });
|
|
1149
|
+
}
|
|
1150
|
+
async loginWithToken(principal, token) {
|
|
1151
|
+
return this.login("token", principal, { token });
|
|
1152
|
+
}
|
|
1153
|
+
async login(method, principal, credentials) {
|
|
1154
|
+
const id = `auth-${this.nextId++}`;
|
|
1155
|
+
const request = {
|
|
1156
|
+
id,
|
|
1157
|
+
type: "Auth",
|
|
1158
|
+
payload: { method, principal, credentials }
|
|
1159
|
+
};
|
|
1160
|
+
const response = await new Promise((resolve, reject) => {
|
|
1161
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
1162
|
+
const timeout = setTimeout(() => {
|
|
1163
|
+
this.pending.delete(id);
|
|
1164
|
+
reject(new Error("Login timeout"));
|
|
1165
|
+
}, timeoutMs);
|
|
1166
|
+
this.pending.set(id, (res) => {
|
|
1167
|
+
clearTimeout(timeout);
|
|
1168
|
+
resolve(res);
|
|
1169
|
+
});
|
|
1170
|
+
this.socket.send(JSON.stringify(request));
|
|
1171
|
+
});
|
|
1172
|
+
if (response.type === "Err") {
|
|
1173
|
+
throw new ReifyError(response);
|
|
680
1174
|
}
|
|
681
|
-
if (
|
|
682
|
-
|
|
1175
|
+
if (response.type !== "Auth") {
|
|
1176
|
+
throw new Error(`Unexpected response type: ${response.type}`);
|
|
683
1177
|
}
|
|
684
|
-
|
|
685
|
-
|
|
1178
|
+
const payload = response.payload;
|
|
1179
|
+
if (payload.status !== "authenticated" || !payload.token || !payload.identity) {
|
|
1180
|
+
throw new Error("Authentication failed");
|
|
686
1181
|
}
|
|
687
|
-
|
|
688
|
-
|
|
1182
|
+
this.options.token = payload.token;
|
|
1183
|
+
return { token: payload.token, identity: payload.identity };
|
|
1184
|
+
}
|
|
1185
|
+
async logout() {
|
|
1186
|
+
if (!this.options.token) {
|
|
1187
|
+
return;
|
|
689
1188
|
}
|
|
690
|
-
|
|
1189
|
+
const id = `logout-${this.nextId++}`;
|
|
1190
|
+
const request = {
|
|
1191
|
+
id,
|
|
1192
|
+
type: "Logout",
|
|
1193
|
+
payload: {}
|
|
1194
|
+
};
|
|
1195
|
+
const response = await new Promise((resolve, reject) => {
|
|
1196
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
1197
|
+
const timeout = setTimeout(() => {
|
|
1198
|
+
this.pending.delete(id);
|
|
1199
|
+
reject(new Error("Logout timeout"));
|
|
1200
|
+
}, timeoutMs);
|
|
1201
|
+
this.pending.set(id, (res) => {
|
|
1202
|
+
clearTimeout(timeout);
|
|
1203
|
+
resolve(res);
|
|
1204
|
+
});
|
|
1205
|
+
this.socket.send(JSON.stringify(request));
|
|
1206
|
+
});
|
|
1207
|
+
if (response.type === "Err") {
|
|
1208
|
+
throw new ReifyError(response);
|
|
1209
|
+
}
|
|
1210
|
+
this.options = { ...this.options, token: void 0 };
|
|
1211
|
+
}
|
|
1212
|
+
disconnect() {
|
|
1213
|
+
this.shouldReconnect = false;
|
|
1214
|
+
this.socket.close();
|
|
691
1215
|
}
|
|
692
1216
|
handleDisconnect() {
|
|
693
1217
|
this.rejectAllPendingRequests();
|
|
@@ -696,6 +1220,7 @@ var JsonWsClient = class _JsonWsClient {
|
|
|
696
1220
|
}
|
|
697
1221
|
const maxAttempts = this.options.maxReconnectAttempts ?? 5;
|
|
698
1222
|
if (this.reconnectAttempts >= maxAttempts) {
|
|
1223
|
+
console.error(`Max reconnection attempts (${maxAttempts}) reached`);
|
|
699
1224
|
return;
|
|
700
1225
|
}
|
|
701
1226
|
this.attemptReconnect();
|
|
@@ -705,6 +1230,7 @@ var JsonWsClient = class _JsonWsClient {
|
|
|
705
1230
|
this.reconnectAttempts++;
|
|
706
1231
|
const baseDelay = this.options.reconnectDelayMs ?? 1e3;
|
|
707
1232
|
const delay = baseDelay * Math.pow(2, this.reconnectAttempts - 1);
|
|
1233
|
+
console.log(`Attempting reconnection in ${delay}ms`);
|
|
708
1234
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
709
1235
|
try {
|
|
710
1236
|
const socket = await createWebSocket2(this.options.url);
|
|
@@ -733,7 +1259,9 @@ var JsonWsClient = class _JsonWsClient {
|
|
|
733
1259
|
socket.addEventListener("error", onError);
|
|
734
1260
|
});
|
|
735
1261
|
}
|
|
736
|
-
|
|
1262
|
+
if (this.options.token) {
|
|
1263
|
+
socket.send(JSON.stringify({ id: "auth-1", type: "Auth", payload: { token: this.options.token } }));
|
|
1264
|
+
}
|
|
737
1265
|
this.socket = socket;
|
|
738
1266
|
this.setupSocketHandlers();
|
|
739
1267
|
this.reconnectAttempts = 0;
|
|
@@ -749,15 +1277,16 @@ var JsonWsClient = class _JsonWsClient {
|
|
|
749
1277
|
if (!msg.id) {
|
|
750
1278
|
return;
|
|
751
1279
|
}
|
|
752
|
-
const { id } = msg;
|
|
1280
|
+
const { id, type, payload } = msg;
|
|
753
1281
|
const handler = this.pending.get(id);
|
|
754
1282
|
if (!handler) {
|
|
755
1283
|
return;
|
|
756
1284
|
}
|
|
757
1285
|
this.pending.delete(id);
|
|
758
|
-
handler(
|
|
1286
|
+
handler({ id, type, payload });
|
|
759
1287
|
};
|
|
760
|
-
this.socket.onerror = () => {
|
|
1288
|
+
this.socket.onerror = (err) => {
|
|
1289
|
+
console.error("WebSocket error", err);
|
|
761
1290
|
};
|
|
762
1291
|
this.socket.onclose = () => {
|
|
763
1292
|
this.handleDisconnect();
|
|
@@ -782,95 +1311,6 @@ var JsonWsClient = class _JsonWsClient {
|
|
|
782
1311
|
}
|
|
783
1312
|
};
|
|
784
1313
|
|
|
785
|
-
// src/http.ts
|
|
786
|
-
var JsonHttpClient = class _JsonHttpClient {
|
|
787
|
-
constructor(options) {
|
|
788
|
-
this.options = options;
|
|
789
|
-
}
|
|
790
|
-
static connect(options) {
|
|
791
|
-
return new _JsonHttpClient(options);
|
|
792
|
-
}
|
|
793
|
-
async query(statements, params) {
|
|
794
|
-
return this.send("query", statements, params);
|
|
795
|
-
}
|
|
796
|
-
async command(statements, params) {
|
|
797
|
-
return this.send("command", statements, params);
|
|
798
|
-
}
|
|
799
|
-
async admin(statements, params) {
|
|
800
|
-
return this.send("admin", statements, params);
|
|
801
|
-
}
|
|
802
|
-
async send(endpoint, statements, params) {
|
|
803
|
-
const statementArray = Array.isArray(statements) ? statements : [statements];
|
|
804
|
-
const outputStatements = statementArray.length > 1 ? statementArray.map((s) => s.trim() ? `OUTPUT ${s}` : s) : statementArray;
|
|
805
|
-
const encodedParams = params !== void 0 && params !== null ? encodeParams(params) : void 0;
|
|
806
|
-
const headers = {
|
|
807
|
-
"Content-Type": "application/json"
|
|
808
|
-
};
|
|
809
|
-
if (this.options.token) {
|
|
810
|
-
headers["Authorization"] = `Bearer ${this.options.token}`;
|
|
811
|
-
} else if (this.options.apiKey) {
|
|
812
|
-
headers["X-Api-Key"] = this.options.apiKey;
|
|
813
|
-
}
|
|
814
|
-
const baseUrl = this.options.url.replace(/\/+$/, "");
|
|
815
|
-
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
816
|
-
const controller = new AbortController();
|
|
817
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
818
|
-
let response;
|
|
819
|
-
try {
|
|
820
|
-
response = await fetch(`${baseUrl}/v1/${endpoint}?format=json`, {
|
|
821
|
-
method: "POST",
|
|
822
|
-
headers,
|
|
823
|
-
body: JSON.stringify({
|
|
824
|
-
statements: outputStatements,
|
|
825
|
-
params: encodedParams
|
|
826
|
-
}),
|
|
827
|
-
signal: controller.signal
|
|
828
|
-
});
|
|
829
|
-
} catch (err) {
|
|
830
|
-
if (err.name === "AbortError") {
|
|
831
|
-
throw new Error("ReifyDB query timeout");
|
|
832
|
-
}
|
|
833
|
-
throw err;
|
|
834
|
-
} finally {
|
|
835
|
-
clearTimeout(timeout);
|
|
836
|
-
}
|
|
837
|
-
const body = await response.json();
|
|
838
|
-
if (!response.ok) {
|
|
839
|
-
if (body.diagnostic) {
|
|
840
|
-
throw new ReifyError({
|
|
841
|
-
id: "",
|
|
842
|
-
type: "Err",
|
|
843
|
-
payload: { diagnostic: body.diagnostic }
|
|
844
|
-
});
|
|
845
|
-
}
|
|
846
|
-
if (body.error) {
|
|
847
|
-
throw new ReifyError({
|
|
848
|
-
id: "",
|
|
849
|
-
type: "Err",
|
|
850
|
-
payload: {
|
|
851
|
-
diagnostic: {
|
|
852
|
-
code: body.code ?? "HTTP_ERROR",
|
|
853
|
-
message: body.error,
|
|
854
|
-
notes: []
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
860
|
-
}
|
|
861
|
-
if (Array.isArray(body) && body.length > 0 && Array.isArray(body[0])) {
|
|
862
|
-
return body;
|
|
863
|
-
}
|
|
864
|
-
if (Array.isArray(body)) {
|
|
865
|
-
return [body];
|
|
866
|
-
}
|
|
867
|
-
if (body && typeof body === "object") {
|
|
868
|
-
return [[body]];
|
|
869
|
-
}
|
|
870
|
-
return [];
|
|
871
|
-
}
|
|
872
|
-
};
|
|
873
|
-
|
|
874
1314
|
// src/index.ts
|
|
875
1315
|
import { ReifyError as ReifyError2, asFrameResults } from "@reifydb/core";
|
|
876
1316
|
var Client = class {
|
|
@@ -884,28 +1324,38 @@ var Client = class {
|
|
|
884
1324
|
return WsClient.connect({ url, ...options });
|
|
885
1325
|
}
|
|
886
1326
|
/**
|
|
887
|
-
* Connect to ReifyDB via
|
|
888
|
-
* @param url
|
|
1327
|
+
* Connect to ReifyDB via HTTP
|
|
1328
|
+
* @param url HTTP URL
|
|
889
1329
|
* @param options Optional configuration
|
|
890
|
-
* @returns
|
|
1330
|
+
* @returns HTTP client (sync, no connection to await)
|
|
891
1331
|
*/
|
|
892
|
-
static
|
|
893
|
-
return
|
|
1332
|
+
static connect_http(url, options = {}) {
|
|
1333
|
+
return HttpClient.connect({ url, ...options });
|
|
894
1334
|
}
|
|
895
1335
|
/**
|
|
896
|
-
* Connect to ReifyDB via HTTP with JSON format
|
|
897
|
-
* @param url
|
|
1336
|
+
* Connect to ReifyDB via HTTP with JSON response format
|
|
1337
|
+
* @param url HTTP URL
|
|
898
1338
|
* @param options Optional configuration
|
|
899
|
-
* @returns JSON HTTP client
|
|
1339
|
+
* @returns JSON HTTP client (sync, no connection to await)
|
|
900
1340
|
*/
|
|
901
1341
|
static connect_json_http(url, options = {}) {
|
|
902
1342
|
return JsonHttpClient.connect({ url, ...options });
|
|
903
1343
|
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Connect to ReifyDB via WebSocket with JSON response format
|
|
1346
|
+
* @param url WebSocket URL
|
|
1347
|
+
* @param options Optional configuration
|
|
1348
|
+
* @returns Connected JSON WebSocket client
|
|
1349
|
+
*/
|
|
1350
|
+
static async connect_json_ws(url, options = {}) {
|
|
1351
|
+
return JsonWebsocketClient.connect({ url, ...options });
|
|
1352
|
+
}
|
|
904
1353
|
};
|
|
905
1354
|
export {
|
|
906
1355
|
Client,
|
|
1356
|
+
HttpClient,
|
|
907
1357
|
JsonHttpClient,
|
|
908
|
-
|
|
1358
|
+
JsonWebsocketClient,
|
|
909
1359
|
ReifyError2 as ReifyError,
|
|
910
1360
|
WsClient,
|
|
911
1361
|
asFrameResults
|