@partylayer/provider 0.6.0 → 0.6.2
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 +14 -4
- package/dist/index.d.mts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +136 -100
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +136 -100
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -219,6 +219,17 @@ function isRpcShaped(err) {
|
|
|
219
219
|
return typeof e.message === "string" && typeof e.code === "number";
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
// src/request-validation.ts
|
|
223
|
+
function validateRequestPayload(payload) {
|
|
224
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload) || !("method" in payload) || typeof payload.method !== "string") {
|
|
225
|
+
throw new ProviderRpcError("Request must be an object with a string method", JSON_RPC_ERRORS.INVALID_REQUEST);
|
|
226
|
+
}
|
|
227
|
+
const params = "params" in payload ? payload.params : void 0;
|
|
228
|
+
if (params !== void 0 && (params === null || typeof params !== "object")) {
|
|
229
|
+
throw new ProviderRpcError("Request params must be an object or array", JSON_RPC_ERRORS.INVALID_PARAMS);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
222
233
|
// src/method-router.ts
|
|
223
234
|
var MethodRouter = class {
|
|
224
235
|
handlers = /* @__PURE__ */ new Map();
|
|
@@ -240,6 +251,7 @@ var MethodRouter = class {
|
|
|
240
251
|
}
|
|
241
252
|
/** Route a request to the appropriate handler */
|
|
242
253
|
async route(payload) {
|
|
254
|
+
validateRequestPayload(payload);
|
|
243
255
|
const { method, params } = payload;
|
|
244
256
|
const handler = this.handlers.get(method);
|
|
245
257
|
if (!handler) {
|
|
@@ -513,7 +525,7 @@ var PartyLayerProvider = class {
|
|
|
513
525
|
};
|
|
514
526
|
|
|
515
527
|
// src/bridge.ts
|
|
516
|
-
var PROVIDER_VERSION = "0.6.
|
|
528
|
+
var PROVIDER_VERSION = "0.6.2" ;
|
|
517
529
|
function createProviderBridge(client) {
|
|
518
530
|
const eventBus = new CIP0103EventBus();
|
|
519
531
|
const pendingEmitted = /* @__PURE__ */ new Set();
|
|
@@ -538,6 +550,7 @@ function createProviderBridge(client) {
|
|
|
538
550
|
return bridge;
|
|
539
551
|
}
|
|
540
552
|
async function handleRequest(client, args, eventBus, pendingEmitted) {
|
|
553
|
+
validateRequestPayload(args);
|
|
541
554
|
const { method, params } = args;
|
|
542
555
|
try {
|
|
543
556
|
switch (method) {
|
|
@@ -578,9 +591,8 @@ async function handleRequest(client, args, eventBus, pendingEmitted) {
|
|
|
578
591
|
}
|
|
579
592
|
case "getActiveNetwork": {
|
|
580
593
|
const session = await client.getActiveSession();
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
);
|
|
594
|
+
if (!session) throw disconnected("No active session");
|
|
595
|
+
return toCAIP2Network(session.network);
|
|
584
596
|
}
|
|
585
597
|
case "listAccounts": {
|
|
586
598
|
const session = await client.getActiveSession();
|
|
@@ -605,61 +617,42 @@ async function handleRequest(client, args, eventBus, pendingEmitted) {
|
|
|
605
617
|
}
|
|
606
618
|
case "prepareExecute": {
|
|
607
619
|
const p = normalizeParams(params);
|
|
608
|
-
const
|
|
620
|
+
const session = await client.getActiveSession();
|
|
621
|
+
if (!session) throw disconnected("No active session");
|
|
622
|
+
const cmdId = nonempty(p.commandId) ? p.commandId : `cmd_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
623
|
+
if (pendingEmitted.has(cmdId)) throw resourceUnavailable("This commandId is already pending");
|
|
624
|
+
const payload = { ...p, commandId: cmdId };
|
|
609
625
|
pendingEmitted.add(cmdId);
|
|
610
|
-
eventBus.emit(CIP0103_EVENTS.TX_CHANGED, {
|
|
611
|
-
|
|
612
|
-
commandId: cmdId
|
|
613
|
-
});
|
|
626
|
+
eventBus.emit(CIP0103_EVENTS.TX_CHANGED, { status: "pending", commandId: cmdId });
|
|
627
|
+
let submitting = false;
|
|
614
628
|
try {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
party: partyId
|
|
626
|
-
}
|
|
627
|
-
});
|
|
629
|
+
let signedTx = payload;
|
|
630
|
+
if (!session.capabilitiesSnapshot || session.capabilitiesSnapshot.includes("signTransaction")) {
|
|
631
|
+
const signed = await client.signTransaction({ tx: payload });
|
|
632
|
+
if (signed.partyId !== void 0 && signed.partyId !== session.partyId) {
|
|
633
|
+
throw disconnected("Wallet account changed during signing");
|
|
634
|
+
}
|
|
635
|
+
signedTx = signed.signedTx;
|
|
636
|
+
if (signedTx === void 0) throw new Error("Wallet returned no signed transaction");
|
|
637
|
+
const event2 = signedEvent({ signature: signed.signature, signedBy: signed.signedBy, party: signed.partyId }, cmdId);
|
|
638
|
+
if (event2) eventBus.emit(CIP0103_EVENTS.TX_CHANGED, event2);
|
|
628
639
|
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
});
|
|
633
|
-
pendingEmitted.delete(cmdId);
|
|
634
|
-
eventBus.emit(CIP0103_EVENTS.TX_CHANGED, {
|
|
635
|
-
status: "executed",
|
|
636
|
-
commandId: cmdId,
|
|
637
|
-
payload: {
|
|
638
|
-
// All three are real identifiers — the receipt's update id, the
|
|
639
|
-
// wallet's command id, or the bridge's own command id for this
|
|
640
|
-
// operation. `String(receipt.transactionHash)` was the old tail
|
|
641
|
-
// and would now stringify `undefined`.
|
|
642
|
-
updateId: receipt.updateId ?? receipt.commandId ?? cmdId,
|
|
643
|
-
completionOffset: 0
|
|
644
|
-
}
|
|
645
|
-
});
|
|
646
|
-
} catch (submitErr) {
|
|
647
|
-
pendingEmitted.delete(cmdId);
|
|
648
|
-
eventBus.emit(CIP0103_EVENTS.TX_CHANGED, {
|
|
649
|
-
status: "failed",
|
|
650
|
-
commandId: cmdId
|
|
651
|
-
});
|
|
652
|
-
throw submitErr;
|
|
640
|
+
const current = await client.getActiveSession();
|
|
641
|
+
if (!current || current.sessionId !== session.sessionId || current.partyId !== session.partyId || current.network !== session.network) {
|
|
642
|
+
throw disconnected("Wallet session changed before submission");
|
|
653
643
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
644
|
+
submitting = true;
|
|
645
|
+
const receipt = await client.submitTransaction({ signedTx });
|
|
646
|
+
const event = executedEvent(receipt, cmdId);
|
|
647
|
+
if (event) eventBus.emit(CIP0103_EVENTS.TX_CHANGED, event);
|
|
648
|
+
} catch (reason) {
|
|
649
|
+
const code = reason?.code;
|
|
650
|
+
if (!submitting || code === 4001 || code === "USER_REJECTED" || code === -32003) {
|
|
651
|
+
eventBus.emit(CIP0103_EVENTS.TX_CHANGED, { status: "failed", commandId: cmdId });
|
|
661
652
|
}
|
|
662
|
-
throw
|
|
653
|
+
throw reason;
|
|
654
|
+
} finally {
|
|
655
|
+
pendingEmitted.delete(cmdId);
|
|
663
656
|
}
|
|
664
657
|
return void 0;
|
|
665
658
|
}
|
|
@@ -734,59 +727,51 @@ function wireEvents(client, eventBus, pendingEmitted) {
|
|
|
734
727
|
});
|
|
735
728
|
});
|
|
736
729
|
client.on("tx:status", (event) => {
|
|
737
|
-
const e = event;
|
|
738
|
-
if (!e
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
committed: "executed",
|
|
744
|
-
rejected: "failed",
|
|
745
|
-
failed: "failed"
|
|
746
|
-
};
|
|
747
|
-
const mappedStatus = statusMap[e.status] ?? "pending";
|
|
748
|
-
const commandId = String(e.txId);
|
|
749
|
-
if (mappedStatus === "pending" && pendingEmitted.has(commandId)) {
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
if (mappedStatus === "executed" || mappedStatus === "failed") {
|
|
753
|
-
pendingEmitted.delete(commandId);
|
|
754
|
-
}
|
|
730
|
+
const e = record(event);
|
|
731
|
+
if (!e) return;
|
|
732
|
+
const raw = record(e.raw);
|
|
733
|
+
const tx = record(raw?.tx) ?? raw;
|
|
734
|
+
const commandId = nonempty(tx?.commandId) ? tx.commandId : e.txId;
|
|
735
|
+
if (!nonempty(commandId)) return;
|
|
755
736
|
let txEvent;
|
|
756
|
-
switch (
|
|
737
|
+
switch (e.status) {
|
|
738
|
+
case "pending":
|
|
739
|
+
case "submitted":
|
|
740
|
+
if (pendingEmitted.has(commandId)) return;
|
|
741
|
+
txEvent = { status: "pending", commandId };
|
|
742
|
+
break;
|
|
757
743
|
case "signed":
|
|
758
|
-
txEvent =
|
|
759
|
-
status: "signed",
|
|
760
|
-
commandId,
|
|
761
|
-
payload: {
|
|
762
|
-
signature: "",
|
|
763
|
-
signedBy: "",
|
|
764
|
-
party: ""
|
|
765
|
-
}
|
|
766
|
-
};
|
|
744
|
+
txEvent = signedEvent(tx, commandId);
|
|
767
745
|
break;
|
|
768
|
-
case "
|
|
769
|
-
|
|
770
|
-
txEvent = {
|
|
771
|
-
status: "executed",
|
|
772
|
-
commandId,
|
|
773
|
-
payload: {
|
|
774
|
-
updateId: raw?.updateId ?? raw?.commandId ?? commandId,
|
|
775
|
-
completionOffset: 0
|
|
776
|
-
}
|
|
777
|
-
};
|
|
746
|
+
case "committed":
|
|
747
|
+
txEvent = executedEvent(tx, commandId);
|
|
778
748
|
break;
|
|
779
|
-
|
|
749
|
+
case "rejected":
|
|
780
750
|
case "failed":
|
|
781
751
|
txEvent = { status: "failed", commandId };
|
|
782
752
|
break;
|
|
783
753
|
default:
|
|
784
|
-
|
|
785
|
-
break;
|
|
754
|
+
return;
|
|
786
755
|
}
|
|
787
|
-
eventBus.emit(CIP0103_EVENTS.TX_CHANGED, txEvent);
|
|
756
|
+
if (txEvent) eventBus.emit(CIP0103_EVENTS.TX_CHANGED, txEvent);
|
|
788
757
|
});
|
|
789
758
|
}
|
|
759
|
+
function record(value) {
|
|
760
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
761
|
+
}
|
|
762
|
+
function nonempty(value) {
|
|
763
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
764
|
+
}
|
|
765
|
+
function signedEvent(value, commandId) {
|
|
766
|
+
const tx = record(value);
|
|
767
|
+
const p = record(tx?.payload) ?? tx;
|
|
768
|
+
return nonempty(p?.signature) && nonempty(p?.signedBy) && nonempty(p?.party) ? { status: "signed", commandId, payload: { signature: p.signature, signedBy: p.signedBy, party: p.party } } : null;
|
|
769
|
+
}
|
|
770
|
+
function executedEvent(value, commandId) {
|
|
771
|
+
const tx = record(value);
|
|
772
|
+
const p = record(tx?.payload) ?? tx;
|
|
773
|
+
return nonempty(p?.updateId) && typeof p.completionOffset === "number" && Number.isSafeInteger(p.completionOffset) && p.completionOffset >= 0 ? { status: "executed", commandId, payload: { updateId: p.updateId, completionOffset: p.completionOffset } } : null;
|
|
774
|
+
}
|
|
790
775
|
function normalizeParams(params) {
|
|
791
776
|
if (!params) return {};
|
|
792
777
|
if (Array.isArray(params)) return params[0] ?? {};
|
|
@@ -818,11 +803,33 @@ function createExtensionChannelProvider(options = {}) {
|
|
|
818
803
|
const eventBus = new CIP0103EventBus();
|
|
819
804
|
const channelId = `pl-${++channelSeq}-${target ?? "ext"}`;
|
|
820
805
|
let requestSeq = 0;
|
|
806
|
+
let listening = false;
|
|
807
|
+
let destroyed = false;
|
|
808
|
+
const subscribedEvents = /* @__PURE__ */ new Set();
|
|
809
|
+
function listen() {
|
|
810
|
+
if (!listening && typeof window !== "undefined") {
|
|
811
|
+
window.addEventListener("message", onMessage);
|
|
812
|
+
listening = true;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
function detachIfIdle() {
|
|
816
|
+
if (pending.size > 0 || [...subscribedEvents].some((name) => eventBus.listenerCount(name) > 0)) return;
|
|
817
|
+
if (listening && typeof window !== "undefined") window.removeEventListener("message", onMessage);
|
|
818
|
+
listening = false;
|
|
819
|
+
}
|
|
821
820
|
const pending = /* @__PURE__ */ new Map();
|
|
822
821
|
function onMessage(event) {
|
|
823
822
|
if (event.source !== window) return;
|
|
824
823
|
const selfOrigin = typeof window !== "undefined" ? window.location?.origin : void 0;
|
|
825
824
|
if (selfOrigin && selfOrigin !== "null" && event.origin !== selfOrigin) return;
|
|
825
|
+
const notification = event.data;
|
|
826
|
+
if (notification?.type === SPLICE_WALLET_REQUEST && notification.request?.jsonrpc === "2.0" && notification.request.id == null && (!target || notification.target === target)) {
|
|
827
|
+
const method = notification.request.method;
|
|
828
|
+
if (method && ["statusChanged", "accountsChanged", "txChanged", "connected"].includes(method)) {
|
|
829
|
+
eventBus.emit(method, notification.request.params);
|
|
830
|
+
}
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
826
833
|
const data = event.data;
|
|
827
834
|
if (!data || data.type !== SPLICE_WALLET_RESPONSE || !data.response) return;
|
|
828
835
|
const { id, result, error } = data.response;
|
|
@@ -831,6 +838,7 @@ function createExtensionChannelProvider(options = {}) {
|
|
|
831
838
|
if (!entry) return;
|
|
832
839
|
pending.delete(String(id));
|
|
833
840
|
clearTimeout(entry.timer);
|
|
841
|
+
detachIfIdle();
|
|
834
842
|
if (error) {
|
|
835
843
|
entry.reject(
|
|
836
844
|
new ProviderRpcError(
|
|
@@ -843,24 +851,29 @@ function createExtensionChannelProvider(options = {}) {
|
|
|
843
851
|
entry.resolve(result);
|
|
844
852
|
}
|
|
845
853
|
}
|
|
846
|
-
if (typeof window !== "undefined") {
|
|
847
|
-
window.addEventListener("message", onMessage);
|
|
848
|
-
}
|
|
849
854
|
function postTargetOrigin() {
|
|
850
855
|
const origin = typeof window !== "undefined" ? window.location?.origin : void 0;
|
|
851
856
|
return origin && origin !== "null" ? origin : "*";
|
|
852
857
|
}
|
|
853
858
|
const provider = {
|
|
854
859
|
request(args) {
|
|
860
|
+
try {
|
|
861
|
+
validateRequestPayload(args);
|
|
862
|
+
} catch (error) {
|
|
863
|
+
return Promise.reject(error);
|
|
864
|
+
}
|
|
865
|
+
if (destroyed) return Promise.reject(new ProviderRpcError("Extension channel was destroyed", RPC_ERRORS.DISCONNECTED));
|
|
855
866
|
if (typeof window === "undefined") {
|
|
856
867
|
return Promise.reject(
|
|
857
868
|
new ProviderRpcError("No window: extension channel unavailable", JSON_RPC_ERRORS.INTERNAL_ERROR)
|
|
858
869
|
);
|
|
859
870
|
}
|
|
871
|
+
listen();
|
|
860
872
|
const id = `${channelId}-${++requestSeq}`;
|
|
861
873
|
return new Promise((resolve, reject) => {
|
|
862
874
|
const timer = setTimeout(() => {
|
|
863
875
|
pending.delete(id);
|
|
876
|
+
detachIfIdle();
|
|
864
877
|
reject(
|
|
865
878
|
new ProviderRpcError(
|
|
866
879
|
`Wallet request "${args.method}" timed out after ${timeoutMs}ms`,
|
|
@@ -879,11 +892,21 @@ function createExtensionChannelProvider(options = {}) {
|
|
|
879
892
|
},
|
|
880
893
|
...target ? { target } : {}
|
|
881
894
|
};
|
|
882
|
-
|
|
895
|
+
try {
|
|
896
|
+
window.postMessage(message, postTargetOrigin());
|
|
897
|
+
} catch (error) {
|
|
898
|
+
pending.delete(id);
|
|
899
|
+
clearTimeout(timer);
|
|
900
|
+
detachIfIdle();
|
|
901
|
+
reject(error);
|
|
902
|
+
}
|
|
883
903
|
});
|
|
884
904
|
},
|
|
885
905
|
on(event, listener) {
|
|
906
|
+
if (destroyed) return provider;
|
|
886
907
|
eventBus.on(event, listener);
|
|
908
|
+
subscribedEvents.add(event);
|
|
909
|
+
listen();
|
|
887
910
|
return provider;
|
|
888
911
|
},
|
|
889
912
|
emit(event, ...args) {
|
|
@@ -891,7 +914,20 @@ function createExtensionChannelProvider(options = {}) {
|
|
|
891
914
|
},
|
|
892
915
|
removeListener(event, listener) {
|
|
893
916
|
eventBus.removeListener(event, listener);
|
|
917
|
+
detachIfIdle();
|
|
894
918
|
return provider;
|
|
919
|
+
},
|
|
920
|
+
destroy() {
|
|
921
|
+
if (destroyed) return;
|
|
922
|
+
destroyed = true;
|
|
923
|
+
eventBus.removeAllListeners();
|
|
924
|
+
subscribedEvents.clear();
|
|
925
|
+
for (const entry of pending.values()) {
|
|
926
|
+
clearTimeout(entry.timer);
|
|
927
|
+
entry.reject(new ProviderRpcError("Extension channel was destroyed", RPC_ERRORS.DISCONNECTED));
|
|
928
|
+
}
|
|
929
|
+
pending.clear();
|
|
930
|
+
detachIfIdle();
|
|
895
931
|
}
|
|
896
932
|
};
|
|
897
933
|
eventBus.setOwner(provider);
|