@valon-technologies/gestalt 0.0.1-alpha.13 → 0.0.1-alpha.16
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/gen/google/rpc/status_pb.ts +76 -0
- package/gen/v1/agent_pb.ts +561 -65
- package/gen/v1/datastore_pb.ts +457 -2
- package/gen/v1/plugin_pb.ts +31 -14
- package/gen/v1/pluginruntime_pb.ts +120 -81
- package/gen/v1/runtime_pb.ts +29 -1
- package/gen/v1/s3_pb.ts +101 -1
- package/gen/v1/workflow_pb.ts +644 -58
- package/package.json +5 -3
- package/src/agent.ts +168 -15
- package/src/api.ts +4 -1
- package/src/build.ts +2 -0
- package/src/index.ts +69 -18
- package/src/indexeddb.ts +481 -1
- package/src/invoker.ts +3 -0
- package/src/plugin.ts +3 -184
- package/src/pluginruntime.ts +220 -0
- package/src/provider-kind.ts +6 -0
- package/src/provider.ts +16 -0
- package/src/runtime-log-host.ts +244 -0
- package/src/runtime.ts +46 -26
- package/src/s3.ts +81 -0
- package/src/telemetry.ts +429 -0
- package/src/workflow-manager.ts +11 -0
- package/src/manifest-metadata.ts +0 -107
package/src/indexeddb.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { connect } from "node:net";
|
|
2
|
+
|
|
1
3
|
import { createClient, type Client, type Interceptor } from "@connectrpc/connect";
|
|
2
4
|
import { createGrpcTransport } from "@connectrpc/connect-node";
|
|
3
5
|
import {
|
|
4
6
|
IndexedDB as IndexedDBService,
|
|
5
7
|
CursorDirection as ProtoCursorDirection,
|
|
8
|
+
TransactionMode as ProtoTransactionMode,
|
|
9
|
+
TransactionDurabilityHint as ProtoTransactionDurabilityHint,
|
|
6
10
|
} from "../gen/v1/datastore_pb";
|
|
7
11
|
|
|
8
12
|
const ENV_INDEXEDDB_SOCKET = "GESTALT_INDEXEDDB_SOCKET";
|
|
@@ -422,11 +426,38 @@ export class AlreadyExistsError extends Error {
|
|
|
422
426
|
}
|
|
423
427
|
}
|
|
424
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Error returned when an explicit transaction fails or is already closed.
|
|
431
|
+
*/
|
|
432
|
+
export class TransactionError extends Error {
|
|
433
|
+
constructor(message?: string) {
|
|
434
|
+
super(message ?? "transaction failed");
|
|
435
|
+
this.name = "TransactionError";
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
425
439
|
/**
|
|
426
440
|
* Plain object record stored in IndexedDB.
|
|
427
441
|
*/
|
|
428
442
|
export type Record = { [key: string]: unknown };
|
|
429
443
|
|
|
444
|
+
/**
|
|
445
|
+
* IndexedDB transaction mode.
|
|
446
|
+
*/
|
|
447
|
+
export type TransactionMode = "readonly" | "readwrite";
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* IndexedDB transaction durability hint.
|
|
451
|
+
*/
|
|
452
|
+
export type TransactionDurabilityHint = "default" | "strict" | "relaxed";
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Options for explicit IndexedDB transactions.
|
|
456
|
+
*/
|
|
457
|
+
export interface TransactionOptions {
|
|
458
|
+
durabilityHint?: TransactionDurabilityHint;
|
|
459
|
+
}
|
|
460
|
+
|
|
430
461
|
/**
|
|
431
462
|
* Key range used to filter object store and index operations.
|
|
432
463
|
*/
|
|
@@ -499,8 +530,16 @@ export class IndexedDB {
|
|
|
499
530
|
throw new Error(`${envName} is not set`);
|
|
500
531
|
}
|
|
501
532
|
const token = process.env[indexedDBSocketTokenEnv(name)]?.trim() ?? "";
|
|
533
|
+
const transportOptions = indexedDBTransportOptions(target);
|
|
502
534
|
const transport = createGrpcTransport({
|
|
503
|
-
...
|
|
535
|
+
...transportOptions,
|
|
536
|
+
...(transportOptions.nodeOptions
|
|
537
|
+
? {
|
|
538
|
+
nodeOptions: {
|
|
539
|
+
createConnection: () => connect(transportOptions.nodeOptions!.path),
|
|
540
|
+
},
|
|
541
|
+
}
|
|
542
|
+
: {}),
|
|
504
543
|
interceptors: token ? [indexedDBRelayTokenInterceptor(token)] : [],
|
|
505
544
|
});
|
|
506
545
|
this.client = createClient(IndexedDBService, transport);
|
|
@@ -542,6 +581,17 @@ export class IndexedDB {
|
|
|
542
581
|
objectStore(name: string): ObjectStore {
|
|
543
582
|
return new ObjectStore(this.client, name);
|
|
544
583
|
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Opens an explicit transaction over a fixed object-store scope.
|
|
587
|
+
*/
|
|
588
|
+
async transaction(
|
|
589
|
+
stores: string[],
|
|
590
|
+
mode: TransactionMode = "readonly",
|
|
591
|
+
options?: TransactionOptions,
|
|
592
|
+
): Promise<Transaction> {
|
|
593
|
+
return Transaction.open(this.client, stores, mode, options);
|
|
594
|
+
}
|
|
545
595
|
}
|
|
546
596
|
|
|
547
597
|
function indexedDBRelayTokenInterceptor(token: string): Interceptor {
|
|
@@ -551,6 +601,395 @@ function indexedDBRelayTokenInterceptor(token: string): Interceptor {
|
|
|
551
601
|
};
|
|
552
602
|
}
|
|
553
603
|
|
|
604
|
+
/**
|
|
605
|
+
* Explicit transaction over one or more object stores.
|
|
606
|
+
*/
|
|
607
|
+
export class Transaction {
|
|
608
|
+
private sendQueue: AsyncQueue<any>;
|
|
609
|
+
private responseIterator: AsyncIterator<any>;
|
|
610
|
+
private closed = false;
|
|
611
|
+
private requestId = 0n;
|
|
612
|
+
private streamChain: Promise<void> = Promise.resolve();
|
|
613
|
+
|
|
614
|
+
private constructor(sendQueue: AsyncQueue<any>, responseIterator: AsyncIterator<any>) {
|
|
615
|
+
this.sendQueue = sendQueue;
|
|
616
|
+
this.responseIterator = responseIterator;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* @internal
|
|
621
|
+
*/
|
|
622
|
+
static async open(
|
|
623
|
+
client: Client<typeof IndexedDBService>,
|
|
624
|
+
stores: string[],
|
|
625
|
+
mode: TransactionMode,
|
|
626
|
+
options?: TransactionOptions,
|
|
627
|
+
): Promise<Transaction> {
|
|
628
|
+
const sendQueue = new AsyncQueue<any>();
|
|
629
|
+
sendQueue.push({
|
|
630
|
+
msg: {
|
|
631
|
+
case: "begin" as const,
|
|
632
|
+
value: {
|
|
633
|
+
stores,
|
|
634
|
+
mode: toProtoTransactionMode(mode),
|
|
635
|
+
durabilityHint: toProtoTransactionDurabilityHint(options?.durabilityHint ?? "default"),
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
const responses = client.transaction(sendQueue);
|
|
640
|
+
const responseIterator = responses[Symbol.asyncIterator]();
|
|
641
|
+
const tx = new Transaction(sendQueue, responseIterator);
|
|
642
|
+
try {
|
|
643
|
+
const { value: resp, done } = await responseIterator.next();
|
|
644
|
+
if (done || !resp) {
|
|
645
|
+
tx.closeLocally();
|
|
646
|
+
throw new TransactionError("transaction stream ended during begin");
|
|
647
|
+
}
|
|
648
|
+
if (resp.msg?.case !== "begin") {
|
|
649
|
+
tx.closeLocally();
|
|
650
|
+
throw new TransactionError("expected transaction begin response");
|
|
651
|
+
}
|
|
652
|
+
} catch (err: any) {
|
|
653
|
+
tx.closeLocally();
|
|
654
|
+
mapTransactionTransportError(err);
|
|
655
|
+
}
|
|
656
|
+
return tx;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Returns a transaction-scoped object store.
|
|
661
|
+
*/
|
|
662
|
+
objectStore(name: string): TransactionObjectStore {
|
|
663
|
+
return new TransactionObjectStore(this, name);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Commits the transaction.
|
|
668
|
+
*/
|
|
669
|
+
async commit(): Promise<void> {
|
|
670
|
+
return this.withStreamLock(async () => {
|
|
671
|
+
this.ensureOpen();
|
|
672
|
+
this.closed = true;
|
|
673
|
+
this.sendQueue.push({ msg: { case: "commit" as const, value: {} } });
|
|
674
|
+
try {
|
|
675
|
+
const { value: resp, done } = await this.responseIterator.next();
|
|
676
|
+
this.sendQueue.end();
|
|
677
|
+
if (done || !resp) {
|
|
678
|
+
throw new TransactionError("transaction stream ended during commit");
|
|
679
|
+
}
|
|
680
|
+
if (resp.msg?.case !== "commit") {
|
|
681
|
+
throw new TransactionError("expected transaction commit response");
|
|
682
|
+
}
|
|
683
|
+
raiseStatus(resp.msg.value.error);
|
|
684
|
+
} catch (err: any) {
|
|
685
|
+
this.closeLocally();
|
|
686
|
+
mapTransactionTransportError(err);
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Aborts the transaction. Calling abort after the transaction is already done is a no-op.
|
|
693
|
+
*/
|
|
694
|
+
async abort(reason = ""): Promise<void> {
|
|
695
|
+
return this.withStreamLock(async () => {
|
|
696
|
+
if (this.closed) return;
|
|
697
|
+
this.closed = true;
|
|
698
|
+
this.sendQueue.push({ msg: { case: "abort" as const, value: { reason } } });
|
|
699
|
+
try {
|
|
700
|
+
const { value: resp, done } = await this.responseIterator.next();
|
|
701
|
+
this.sendQueue.end();
|
|
702
|
+
if (done || !resp) {
|
|
703
|
+
throw new TransactionError("transaction stream ended during abort");
|
|
704
|
+
}
|
|
705
|
+
if (resp.msg?.case !== "abort") {
|
|
706
|
+
throw new TransactionError("expected transaction abort response");
|
|
707
|
+
}
|
|
708
|
+
raiseStatus(resp.msg.value.error);
|
|
709
|
+
} catch (err: any) {
|
|
710
|
+
this.closeLocally();
|
|
711
|
+
mapTransactionTransportError(err);
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* @internal
|
|
718
|
+
*/
|
|
719
|
+
async sendOperation(operation: any): Promise<any> {
|
|
720
|
+
return this.withStreamLock(async () => {
|
|
721
|
+
this.ensureOpen();
|
|
722
|
+
this.requestId += 1n;
|
|
723
|
+
const requestId = this.requestId;
|
|
724
|
+
this.sendQueue.push({
|
|
725
|
+
msg: {
|
|
726
|
+
case: "operation" as const,
|
|
727
|
+
value: { requestId, operation },
|
|
728
|
+
},
|
|
729
|
+
});
|
|
730
|
+
try {
|
|
731
|
+
const { value: resp, done } = await this.responseIterator.next();
|
|
732
|
+
if (done || !resp) {
|
|
733
|
+
this.closeLocally();
|
|
734
|
+
throw new TransactionError("transaction stream ended during operation");
|
|
735
|
+
}
|
|
736
|
+
if (resp.msg?.case !== "operation") {
|
|
737
|
+
this.closeLocally();
|
|
738
|
+
throw new TransactionError("expected transaction operation response");
|
|
739
|
+
}
|
|
740
|
+
const opResp = resp.msg.value;
|
|
741
|
+
if (opResp.requestId !== requestId) {
|
|
742
|
+
this.closeLocally();
|
|
743
|
+
throw new TransactionError("transaction response request id mismatch");
|
|
744
|
+
}
|
|
745
|
+
raiseStatus(opResp.error);
|
|
746
|
+
return opResp;
|
|
747
|
+
} catch (err: any) {
|
|
748
|
+
this.closeLocally();
|
|
749
|
+
mapTransactionTransportError(err);
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
private async withStreamLock<T>(fn: () => Promise<T>): Promise<T> {
|
|
755
|
+
const previous = this.streamChain;
|
|
756
|
+
let release!: () => void;
|
|
757
|
+
this.streamChain = new Promise<void>((resolve) => {
|
|
758
|
+
release = resolve;
|
|
759
|
+
});
|
|
760
|
+
await previous;
|
|
761
|
+
try {
|
|
762
|
+
return await fn();
|
|
763
|
+
} finally {
|
|
764
|
+
release();
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
private ensureOpen(): void {
|
|
769
|
+
if (this.closed) throw new TransactionError("transaction is already finished");
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
private closeLocally(): void {
|
|
773
|
+
this.closed = true;
|
|
774
|
+
this.sendQueue.end();
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Transaction-scoped object-store client.
|
|
780
|
+
*/
|
|
781
|
+
export class TransactionObjectStore {
|
|
782
|
+
/**
|
|
783
|
+
* @internal
|
|
784
|
+
*/
|
|
785
|
+
constructor(
|
|
786
|
+
private tx: Transaction,
|
|
787
|
+
private store: string,
|
|
788
|
+
) {}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* Reads a record by primary key inside the transaction.
|
|
792
|
+
*/
|
|
793
|
+
async get(id: string): Promise<Record> {
|
|
794
|
+
const resp = await this.tx.sendOperation({
|
|
795
|
+
case: "get" as const,
|
|
796
|
+
value: { store: this.store, id },
|
|
797
|
+
});
|
|
798
|
+
return fromProtoRecord(resp.result.value.record);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Reads the generated primary key for a record inside the transaction.
|
|
803
|
+
*/
|
|
804
|
+
async getKey(id: string): Promise<string> {
|
|
805
|
+
const resp = await this.tx.sendOperation({
|
|
806
|
+
case: "getKey" as const,
|
|
807
|
+
value: { store: this.store, id },
|
|
808
|
+
});
|
|
809
|
+
return resp.result.value.key;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Inserts a new record inside the transaction.
|
|
814
|
+
*/
|
|
815
|
+
async add(record: Record): Promise<void> {
|
|
816
|
+
await this.tx.sendOperation({
|
|
817
|
+
case: "add" as const,
|
|
818
|
+
value: { store: this.store, record: toProtoRecord(record) },
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Inserts or replaces a record inside the transaction.
|
|
824
|
+
*/
|
|
825
|
+
async put(record: Record): Promise<void> {
|
|
826
|
+
await this.tx.sendOperation({
|
|
827
|
+
case: "put" as const,
|
|
828
|
+
value: { store: this.store, record: toProtoRecord(record) },
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Deletes a record by primary key inside the transaction.
|
|
834
|
+
*/
|
|
835
|
+
async delete(id: string): Promise<void> {
|
|
836
|
+
await this.tx.sendOperation({
|
|
837
|
+
case: "delete" as const,
|
|
838
|
+
value: { store: this.store, id },
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Clears every record in the object store inside the transaction.
|
|
844
|
+
*/
|
|
845
|
+
async clear(): Promise<void> {
|
|
846
|
+
await this.tx.sendOperation({
|
|
847
|
+
case: "clear" as const,
|
|
848
|
+
value: { store: this.store },
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Reads all records inside the transaction.
|
|
854
|
+
*/
|
|
855
|
+
async getAll(keyRange?: KeyRange): Promise<Record[]> {
|
|
856
|
+
const resp = await this.tx.sendOperation({
|
|
857
|
+
case: "getAll" as const,
|
|
858
|
+
value: { store: this.store, range: keyRange ? toProtoKeyRange(keyRange) : undefined },
|
|
859
|
+
});
|
|
860
|
+
return resp.result.value.records.map((r: any) => fromProtoRecord(r));
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Reads all primary keys inside the transaction.
|
|
865
|
+
*/
|
|
866
|
+
async getAllKeys(keyRange?: KeyRange): Promise<string[]> {
|
|
867
|
+
const resp = await this.tx.sendOperation({
|
|
868
|
+
case: "getAllKeys" as const,
|
|
869
|
+
value: { store: this.store, range: keyRange ? toProtoKeyRange(keyRange) : undefined },
|
|
870
|
+
});
|
|
871
|
+
return resp.result.value.keys;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Counts records inside the transaction.
|
|
876
|
+
*/
|
|
877
|
+
async count(keyRange?: KeyRange): Promise<number> {
|
|
878
|
+
const resp = await this.tx.sendOperation({
|
|
879
|
+
case: "count" as const,
|
|
880
|
+
value: { store: this.store, range: keyRange ? toProtoKeyRange(keyRange) : undefined },
|
|
881
|
+
});
|
|
882
|
+
return Number(resp.result.value.count);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Deletes records in a key range inside the transaction.
|
|
887
|
+
*/
|
|
888
|
+
async deleteRange(keyRange: KeyRange): Promise<number> {
|
|
889
|
+
const resp = await this.tx.sendOperation({
|
|
890
|
+
case: "deleteRange" as const,
|
|
891
|
+
value: { store: this.store, range: toProtoKeyRange(keyRange) },
|
|
892
|
+
});
|
|
893
|
+
return Number(resp.result.value.deleted);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Returns a transaction-scoped secondary index.
|
|
898
|
+
*/
|
|
899
|
+
index(name: string): TransactionIndex {
|
|
900
|
+
return new TransactionIndex(this.tx, this.store, name);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* Transaction-scoped secondary-index client.
|
|
906
|
+
*/
|
|
907
|
+
export class TransactionIndex {
|
|
908
|
+
/**
|
|
909
|
+
* @internal
|
|
910
|
+
*/
|
|
911
|
+
constructor(
|
|
912
|
+
private tx: Transaction,
|
|
913
|
+
private store: string,
|
|
914
|
+
private indexName: string,
|
|
915
|
+
) {}
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Reads the first matching indexed record inside the transaction.
|
|
919
|
+
*/
|
|
920
|
+
async get(...values: unknown[]): Promise<Record> {
|
|
921
|
+
const resp = await this.tx.sendOperation({
|
|
922
|
+
case: "indexGet" as const,
|
|
923
|
+
value: this.indexRequest(values),
|
|
924
|
+
});
|
|
925
|
+
return fromProtoRecord(resp.result.value.record);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Reads the primary key for the first matching index entry inside the transaction.
|
|
930
|
+
*/
|
|
931
|
+
async getKey(...values: unknown[]): Promise<string> {
|
|
932
|
+
const resp = await this.tx.sendOperation({
|
|
933
|
+
case: "indexGetKey" as const,
|
|
934
|
+
value: this.indexRequest(values),
|
|
935
|
+
});
|
|
936
|
+
return resp.result.value.key;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Reads all indexed records inside the transaction.
|
|
941
|
+
*/
|
|
942
|
+
async getAll(keyRange?: KeyRange, ...values: unknown[]): Promise<Record[]> {
|
|
943
|
+
const resp = await this.tx.sendOperation({
|
|
944
|
+
case: "indexGetAll" as const,
|
|
945
|
+
value: this.indexRequest(values, keyRange),
|
|
946
|
+
});
|
|
947
|
+
return resp.result.value.records.map((r: any) => fromProtoRecord(r));
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* Reads all indexed primary keys inside the transaction.
|
|
952
|
+
*/
|
|
953
|
+
async getAllKeys(keyRange?: KeyRange, ...values: unknown[]): Promise<string[]> {
|
|
954
|
+
const resp = await this.tx.sendOperation({
|
|
955
|
+
case: "indexGetAllKeys" as const,
|
|
956
|
+
value: this.indexRequest(values, keyRange),
|
|
957
|
+
});
|
|
958
|
+
return resp.result.value.keys;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Counts indexed records inside the transaction.
|
|
963
|
+
*/
|
|
964
|
+
async count(keyRange?: KeyRange, ...values: unknown[]): Promise<number> {
|
|
965
|
+
const resp = await this.tx.sendOperation({
|
|
966
|
+
case: "indexCount" as const,
|
|
967
|
+
value: this.indexRequest(values, keyRange),
|
|
968
|
+
});
|
|
969
|
+
return Number(resp.result.value.count);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* Deletes indexed records inside the transaction.
|
|
974
|
+
*/
|
|
975
|
+
async delete(...values: unknown[]): Promise<number> {
|
|
976
|
+
const resp = await this.tx.sendOperation({
|
|
977
|
+
case: "indexDelete" as const,
|
|
978
|
+
value: this.indexRequest(values),
|
|
979
|
+
});
|
|
980
|
+
return Number(resp.result.value.deleted);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
private indexRequest(values: unknown[], keyRange?: KeyRange): any {
|
|
984
|
+
return {
|
|
985
|
+
store: this.store,
|
|
986
|
+
index: this.indexName,
|
|
987
|
+
values: values.map(toProtoTypedValue),
|
|
988
|
+
range: keyRange ? toProtoKeyRange(keyRange) : undefined,
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
554
993
|
/**
|
|
555
994
|
* Object store client used for primary-key operations.
|
|
556
995
|
*/
|
|
@@ -957,3 +1396,44 @@ async function rpc<T>(fn: () => Promise<T>): Promise<T> {
|
|
|
957
1396
|
throw err;
|
|
958
1397
|
}
|
|
959
1398
|
}
|
|
1399
|
+
|
|
1400
|
+
function toProtoTransactionMode(mode: TransactionMode): ProtoTransactionMode {
|
|
1401
|
+
switch (mode) {
|
|
1402
|
+
case "readonly":
|
|
1403
|
+
return ProtoTransactionMode.TRANSACTION_READONLY;
|
|
1404
|
+
case "readwrite":
|
|
1405
|
+
return ProtoTransactionMode.TRANSACTION_READWRITE;
|
|
1406
|
+
default:
|
|
1407
|
+
throw new TransactionError(`unsupported transaction mode: ${String(mode)}`);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
function toProtoTransactionDurabilityHint(hint: TransactionDurabilityHint): ProtoTransactionDurabilityHint {
|
|
1412
|
+
switch (hint) {
|
|
1413
|
+
case "default":
|
|
1414
|
+
return ProtoTransactionDurabilityHint.TRANSACTION_DURABILITY_DEFAULT;
|
|
1415
|
+
case "strict":
|
|
1416
|
+
return ProtoTransactionDurabilityHint.TRANSACTION_DURABILITY_STRICT;
|
|
1417
|
+
case "relaxed":
|
|
1418
|
+
return ProtoTransactionDurabilityHint.TRANSACTION_DURABILITY_RELAXED;
|
|
1419
|
+
default:
|
|
1420
|
+
throw new TransactionError(`unsupported transaction durability hint: ${String(hint)}`);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
function raiseStatus(status: any): void {
|
|
1425
|
+
if (!status || status.code === 0) return;
|
|
1426
|
+
if (status.code === 5) throw new NotFoundError(status.message);
|
|
1427
|
+
if (status.code === 6) throw new AlreadyExistsError(status.message);
|
|
1428
|
+
throw new TransactionError(status.message);
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
function mapTransactionTransportError(err: any): never {
|
|
1432
|
+
if (err instanceof NotFoundError || err instanceof AlreadyExistsError || err instanceof TransactionError) {
|
|
1433
|
+
throw err;
|
|
1434
|
+
}
|
|
1435
|
+
if (err?.code === 5) throw new NotFoundError(err.message);
|
|
1436
|
+
if (err?.code === 6) throw new AlreadyExistsError(err.message);
|
|
1437
|
+
if (err?.code === 3 || err?.code === 9) throw new TransactionError(err.message);
|
|
1438
|
+
throw err;
|
|
1439
|
+
}
|
package/src/invoker.ts
CHANGED
|
@@ -12,6 +12,7 @@ const PLUGIN_INVOKER_RELAY_TOKEN_HEADER = "x-gestalt-host-service-relay-token";
|
|
|
12
12
|
export interface PluginInvokeOptions {
|
|
13
13
|
connection?: string;
|
|
14
14
|
instance?: string;
|
|
15
|
+
idempotencyKey?: string;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export interface PluginInvocationGrant {
|
|
@@ -60,6 +61,7 @@ export class PluginInvoker {
|
|
|
60
61
|
params: toJsonObject(params),
|
|
61
62
|
connection: options?.connection ?? "",
|
|
62
63
|
instance: options?.instance ?? "",
|
|
64
|
+
idempotencyKey: options?.idempotencyKey?.trim() ?? "",
|
|
63
65
|
});
|
|
64
66
|
return {
|
|
65
67
|
status: response.status,
|
|
@@ -86,6 +88,7 @@ export class PluginInvoker {
|
|
|
86
88
|
: {}),
|
|
87
89
|
connection: options?.connection ?? "",
|
|
88
90
|
instance: options?.instance ?? "",
|
|
91
|
+
idempotencyKey: options?.idempotencyKey?.trim() ?? "",
|
|
89
92
|
});
|
|
90
93
|
return {
|
|
91
94
|
status: response.status,
|