@kehto/runtime 0.11.0 → 0.13.0
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 +5 -5
- package/dist/index.d.ts +48 -48
- package/dist/index.js +128 -139
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
// src/enforce.ts
|
|
2
|
-
import {
|
|
3
|
-
import { resolveCapabilitiesNub } from "@kehto/acl";
|
|
4
|
-
var CLASS_CAPABILITY_ALLOWLIST = Object.freeze({
|
|
5
|
-
"class-1": new Set(ALL_CAPABILITIES),
|
|
6
|
-
"class-2": new Set(ALL_CAPABILITIES.filter(
|
|
7
|
-
(c) => c !== "relay:write" && c !== "outbox:write" && c !== "intent:write"
|
|
8
|
-
))
|
|
9
|
-
});
|
|
2
|
+
import { resolveCapabilitiesNap } from "@kehto/acl";
|
|
10
3
|
function createEnforceGate(config) {
|
|
11
4
|
const { checkAcl, resolveIdentity, onAclCheck } = config;
|
|
12
5
|
return function enforce(pubkey, capability, message) {
|
|
@@ -23,29 +16,12 @@ function createEnforceGate(config) {
|
|
|
23
16
|
return { allowed, capability, reason };
|
|
24
17
|
};
|
|
25
18
|
}
|
|
26
|
-
function
|
|
19
|
+
function createNapEnforceGate(config) {
|
|
27
20
|
const { checkAcl, resolveIdentityByWindowId, onAclCheck } = config;
|
|
28
|
-
return function
|
|
21
|
+
return function enforceNap(windowId, capability, message) {
|
|
29
22
|
const entry = resolveIdentityByWindowId(windowId);
|
|
30
23
|
const dTag = entry?.dTag ?? "";
|
|
31
24
|
const aggregateHash = entry?.aggregateHash ?? "";
|
|
32
|
-
const nappletClass = entry?.class ?? null;
|
|
33
|
-
if (nappletClass !== null) {
|
|
34
|
-
const allowlist = CLASS_CAPABILITY_ALLOWLIST[nappletClass];
|
|
35
|
-
if (!allowlist || !allowlist.has(capability)) {
|
|
36
|
-
const identity2 = { pubkey: "", dTag, hash: aggregateHash };
|
|
37
|
-
if (onAclCheck) {
|
|
38
|
-
onAclCheck({
|
|
39
|
-
identity: identity2,
|
|
40
|
-
capability,
|
|
41
|
-
decision: "deny",
|
|
42
|
-
message,
|
|
43
|
-
reason: "class-forbidden"
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
return { allowed: false, capability, reason: "class-forbidden" };
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
25
|
const allowed = checkAcl("", dTag, aggregateHash, capability);
|
|
50
26
|
const identity = { pubkey: "", dTag, hash: aggregateHash };
|
|
51
27
|
const decision = allowed ? "allow" : "deny";
|
|
@@ -513,12 +489,12 @@ function routeServiceMessage(windowId, message, services, sendToNapplet) {
|
|
|
513
489
|
handler.handleMessage(windowId, message, (msg) => sendToNapplet(windowId, msg));
|
|
514
490
|
return true;
|
|
515
491
|
}
|
|
516
|
-
const
|
|
517
|
-
if (message.type === "
|
|
518
|
-
const prefix =
|
|
519
|
-
const
|
|
520
|
-
if (
|
|
521
|
-
|
|
492
|
+
const incMessage = message;
|
|
493
|
+
if (message.type === "inc.emit" && typeof incMessage.topic === "string") {
|
|
494
|
+
const prefix = incMessage.topic.split(":")[0];
|
|
495
|
+
const incHandler = services[prefix];
|
|
496
|
+
if (incHandler) {
|
|
497
|
+
incHandler.handleMessage(windowId, message, (msg) => sendToNapplet(windowId, msg));
|
|
522
498
|
return true;
|
|
523
499
|
}
|
|
524
500
|
}
|
|
@@ -600,9 +576,10 @@ function handleRelaySubscribe(context, windowId, msg, m) {
|
|
|
600
576
|
}
|
|
601
577
|
return;
|
|
602
578
|
}
|
|
603
|
-
|
|
579
|
+
const relayHint = typeof m.relay === "string" && m.relay.length > 0 ? m.relay : void 0;
|
|
580
|
+
deliverFromRuntimeBackends(context, windowId, subId, subKey, filters, isShellKind, deliver, relayHint);
|
|
604
581
|
}
|
|
605
|
-
function deliverFromRuntimeBackends(context, windowId, subId, subKey, filters, isShellKind, deliver) {
|
|
582
|
+
function deliverFromRuntimeBackends(context, windowId, subId, subKey, filters, isShellKind, deliver, relayHint) {
|
|
606
583
|
const { hooks } = context;
|
|
607
584
|
const cache = hooks.cache;
|
|
608
585
|
if (cache?.isAvailable() && !isShellKind) {
|
|
@@ -617,7 +594,7 @@ function deliverFromRuntimeBackends(context, windowId, subId, subKey, filters, i
|
|
|
617
594
|
return;
|
|
618
595
|
}
|
|
619
596
|
if (!pool?.isAvailable() || isShellKind) return;
|
|
620
|
-
const relayUrls = pool.selectRelayTier(filters);
|
|
597
|
+
const relayUrls = relayHint ? [relayHint] : pool.selectRelayTier(filters);
|
|
621
598
|
let eoseSent = false;
|
|
622
599
|
const eoseFallbackTimer = setTimeout(() => {
|
|
623
600
|
if (!eoseSent) {
|
|
@@ -837,39 +814,28 @@ function createIdentityHandler(context) {
|
|
|
837
814
|
};
|
|
838
815
|
}
|
|
839
816
|
|
|
840
|
-
// src/
|
|
841
|
-
function
|
|
817
|
+
// src/inc-handler.ts
|
|
818
|
+
function createIncRuntime(hooks, sessionRegistry) {
|
|
842
819
|
const state = {
|
|
843
820
|
subscriptions: /* @__PURE__ */ new Map(),
|
|
844
821
|
channels: /* @__PURE__ */ new Map(),
|
|
845
|
-
channelsByWindow: /* @__PURE__ */ new Map()
|
|
846
|
-
domainByWindow: /* @__PURE__ */ new Map()
|
|
822
|
+
channelsByWindow: /* @__PURE__ */ new Map()
|
|
847
823
|
};
|
|
848
824
|
return {
|
|
849
825
|
handleMessage(windowId, msg) {
|
|
850
|
-
|
|
826
|
+
handleIncMessage(state, hooks, sessionRegistry, windowId, msg);
|
|
851
827
|
},
|
|
852
828
|
destroyWindow(windowId) {
|
|
853
829
|
removeWindowChannels(state, hooks, windowId);
|
|
854
830
|
removeWindowSubscriptions(state, windowId);
|
|
855
|
-
state.domainByWindow.delete(windowId);
|
|
856
831
|
},
|
|
857
832
|
clear() {
|
|
858
833
|
state.subscriptions.clear();
|
|
859
834
|
state.channels.clear();
|
|
860
835
|
state.channelsByWindow.clear();
|
|
861
|
-
state.domainByWindow.clear();
|
|
862
836
|
}
|
|
863
837
|
};
|
|
864
838
|
}
|
|
865
|
-
function domainOf(type) {
|
|
866
|
-
const dot = type.indexOf(".");
|
|
867
|
-
const d = dot === -1 ? type : type.slice(0, dot);
|
|
868
|
-
return d === "inc" ? "inc" : "ifc";
|
|
869
|
-
}
|
|
870
|
-
function prefixFor(state, windowId, fallback) {
|
|
871
|
-
return state.domainByWindow.get(windowId) ?? fallback;
|
|
872
|
-
}
|
|
873
839
|
function addChannel(state, channelId, peerA, peerB) {
|
|
874
840
|
state.channels.set(channelId, { channelId, peerA, peerB });
|
|
875
841
|
for (const windowId of [peerA, peerB]) {
|
|
@@ -905,60 +871,55 @@ function resolveTarget(sessionRegistry, target) {
|
|
|
905
871
|
const byPubkey = entries.find((entry) => entry.pubkey === target);
|
|
906
872
|
return byPubkey?.windowId ?? null;
|
|
907
873
|
}
|
|
908
|
-
function
|
|
874
|
+
function handleIncMessage(state, hooks, sessionRegistry, windowId, msg) {
|
|
909
875
|
const m = msg;
|
|
910
876
|
const dotIdx = msg.type.indexOf(".");
|
|
911
877
|
const action = msg.type.slice(dotIdx + 1);
|
|
912
|
-
const incomingDomain = domainOf(msg.type);
|
|
913
|
-
if (!state.domainByWindow.has(windowId)) {
|
|
914
|
-
state.domainByWindow.set(windowId, incomingDomain);
|
|
915
|
-
}
|
|
916
878
|
switch (action) {
|
|
917
879
|
case "emit":
|
|
918
|
-
handleEmit(state, hooks, windowId, m
|
|
880
|
+
handleEmit(state, hooks, windowId, m);
|
|
919
881
|
return;
|
|
920
882
|
case "subscribe":
|
|
921
|
-
handleSubscribe(state, hooks, windowId, m
|
|
883
|
+
handleSubscribe(state, hooks, windowId, m);
|
|
922
884
|
return;
|
|
923
885
|
case "unsubscribe":
|
|
924
886
|
handleUnsubscribe(state, windowId, m);
|
|
925
887
|
return;
|
|
926
888
|
case "channel.open":
|
|
927
|
-
handleChannelOpen(state, hooks, sessionRegistry, windowId, m
|
|
889
|
+
handleChannelOpen(state, hooks, sessionRegistry, windowId, m);
|
|
928
890
|
return;
|
|
929
891
|
case "channel.emit":
|
|
930
|
-
handleChannelEmit(state, hooks, windowId, m
|
|
892
|
+
handleChannelEmit(state, hooks, windowId, m);
|
|
931
893
|
return;
|
|
932
894
|
case "channel.broadcast":
|
|
933
|
-
handleChannelBroadcast(state, hooks, windowId, m
|
|
895
|
+
handleChannelBroadcast(state, hooks, windowId, m);
|
|
934
896
|
return;
|
|
935
897
|
case "channel.list":
|
|
936
|
-
handleChannelList(state, hooks, windowId, m
|
|
898
|
+
handleChannelList(state, hooks, windowId, m);
|
|
937
899
|
return;
|
|
938
900
|
case "channel.close":
|
|
939
|
-
handleChannelClose(state, hooks, windowId, m
|
|
901
|
+
handleChannelClose(state, hooks, windowId, m);
|
|
940
902
|
return;
|
|
941
903
|
default:
|
|
942
904
|
return;
|
|
943
905
|
}
|
|
944
906
|
}
|
|
945
|
-
function handleEmit(state, hooks, windowId, m
|
|
907
|
+
function handleEmit(state, hooks, windowId, m) {
|
|
946
908
|
const topic = m.topic ?? "";
|
|
947
909
|
if (!topic) return;
|
|
948
910
|
const subscribers = state.subscriptions.get(topic);
|
|
949
911
|
if (!subscribers) return;
|
|
950
912
|
for (const subscriberWindowId of subscribers) {
|
|
951
913
|
if (subscriberWindowId !== windowId) {
|
|
952
|
-
|
|
953
|
-
hooks.sendToNapplet(subscriberWindowId, { type: `${prefix}.event`, topic, payload: m.payload, sender: windowId });
|
|
914
|
+
hooks.sendToNapplet(subscriberWindowId, { type: "inc.event", topic, payload: m.payload, sender: windowId });
|
|
954
915
|
}
|
|
955
916
|
}
|
|
956
917
|
}
|
|
957
|
-
function handleSubscribe(state, hooks, windowId, m
|
|
918
|
+
function handleSubscribe(state, hooks, windowId, m) {
|
|
958
919
|
const id = m.id ?? "";
|
|
959
920
|
const topic = m.topic ?? "";
|
|
960
921
|
if (!topic) {
|
|
961
|
-
hooks.sendToNapplet(windowId, { type:
|
|
922
|
+
hooks.sendToNapplet(windowId, { type: "inc.subscribe.result", id, error: "missing topic" });
|
|
962
923
|
return;
|
|
963
924
|
}
|
|
964
925
|
let subscriptions = state.subscriptions.get(topic);
|
|
@@ -967,7 +928,7 @@ function handleSubscribe(state, hooks, windowId, m, incomingDomain) {
|
|
|
967
928
|
state.subscriptions.set(topic, subscriptions);
|
|
968
929
|
}
|
|
969
930
|
subscriptions.add(windowId);
|
|
970
|
-
hooks.sendToNapplet(windowId, { type:
|
|
931
|
+
hooks.sendToNapplet(windowId, { type: "inc.subscribe.result", id });
|
|
971
932
|
}
|
|
972
933
|
function handleUnsubscribe(state, windowId, m) {
|
|
973
934
|
const topic = m.topic ?? "";
|
|
@@ -977,36 +938,34 @@ function handleUnsubscribe(state, windowId, m) {
|
|
|
977
938
|
subscriptions.delete(windowId);
|
|
978
939
|
if (subscriptions.size === 0) state.subscriptions.delete(topic);
|
|
979
940
|
}
|
|
980
|
-
function handleChannelOpen(state, hooks, sessionRegistry, windowId, m
|
|
941
|
+
function handleChannelOpen(state, hooks, sessionRegistry, windowId, m) {
|
|
981
942
|
const id = m.id ?? "";
|
|
982
943
|
const peerWindow = resolveTarget(sessionRegistry, m.target ?? "");
|
|
983
944
|
if (!peerWindow) {
|
|
984
|
-
hooks.sendToNapplet(windowId, { type:
|
|
945
|
+
hooks.sendToNapplet(windowId, { type: "inc.channel.open.result", id, error: "target not found" });
|
|
985
946
|
return;
|
|
986
947
|
}
|
|
987
948
|
const channelId = hooks.crypto.randomUUID().replace(/-/g, "").slice(0, 32);
|
|
988
949
|
addChannel(state, channelId, windowId, peerWindow);
|
|
989
|
-
hooks.sendToNapplet(windowId, { type:
|
|
950
|
+
hooks.sendToNapplet(windowId, { type: "inc.channel.open.result", id, channelId, peer: peerWindow });
|
|
990
951
|
}
|
|
991
|
-
function handleChannelEmit(state, hooks, windowId, m
|
|
952
|
+
function handleChannelEmit(state, hooks, windowId, m) {
|
|
992
953
|
const peer = peerOf(state, m.channelId ?? "", windowId);
|
|
993
954
|
if (peer) {
|
|
994
|
-
|
|
995
|
-
hooks.sendToNapplet(peer, { type: `${prefix}.channel.event`, channelId: m.channelId ?? "", sender: windowId, payload: m.payload });
|
|
955
|
+
hooks.sendToNapplet(peer, { type: "inc.channel.event", channelId: m.channelId ?? "", sender: windowId, payload: m.payload });
|
|
996
956
|
}
|
|
997
957
|
}
|
|
998
|
-
function handleChannelBroadcast(state, hooks, windowId, m
|
|
958
|
+
function handleChannelBroadcast(state, hooks, windowId, m) {
|
|
999
959
|
const channels = state.channelsByWindow.get(windowId);
|
|
1000
960
|
if (!channels) return;
|
|
1001
961
|
for (const channelId of channels) {
|
|
1002
962
|
const peer = peerOf(state, channelId, windowId);
|
|
1003
963
|
if (peer) {
|
|
1004
|
-
|
|
1005
|
-
hooks.sendToNapplet(peer, { type: `${prefix}.channel.event`, channelId, sender: windowId, payload: m.payload });
|
|
964
|
+
hooks.sendToNapplet(peer, { type: "inc.channel.event", channelId, sender: windowId, payload: m.payload });
|
|
1006
965
|
}
|
|
1007
966
|
}
|
|
1008
967
|
}
|
|
1009
|
-
function handleChannelList(state, hooks, windowId, m
|
|
968
|
+
function handleChannelList(state, hooks, windowId, m) {
|
|
1010
969
|
const channels = [];
|
|
1011
970
|
const set = state.channelsByWindow.get(windowId);
|
|
1012
971
|
if (set) {
|
|
@@ -1015,15 +974,14 @@ function handleChannelList(state, hooks, windowId, m, incomingDomain) {
|
|
|
1015
974
|
if (peer) channels.push({ id: channelId, peer });
|
|
1016
975
|
}
|
|
1017
976
|
}
|
|
1018
|
-
hooks.sendToNapplet(windowId, { type:
|
|
977
|
+
hooks.sendToNapplet(windowId, { type: "inc.channel.list.result", id: m.id ?? "", channels });
|
|
1019
978
|
}
|
|
1020
|
-
function handleChannelClose(state, hooks, windowId, m
|
|
979
|
+
function handleChannelClose(state, hooks, windowId, m) {
|
|
1021
980
|
const channelId = m.channelId ?? "";
|
|
1022
981
|
const peer = peerOf(state, channelId, windowId);
|
|
1023
982
|
if (!peer) return;
|
|
1024
|
-
hooks.sendToNapplet(windowId, { type:
|
|
1025
|
-
|
|
1026
|
-
hooks.sendToNapplet(peer, { type: `${peerPrefix}.channel.closed`, channelId });
|
|
983
|
+
hooks.sendToNapplet(windowId, { type: "inc.channel.closed", channelId });
|
|
984
|
+
hooks.sendToNapplet(peer, { type: "inc.channel.closed", channelId });
|
|
1027
985
|
removeChannel(state, channelId);
|
|
1028
986
|
}
|
|
1029
987
|
function removeWindowSubscriptions(state, windowId) {
|
|
@@ -1038,17 +996,20 @@ function removeWindowChannels(state, hooks, windowId) {
|
|
|
1038
996
|
for (const channelId of Array.from(channelIds)) {
|
|
1039
997
|
const peer = peerOf(state, channelId, windowId);
|
|
1040
998
|
if (peer) {
|
|
1041
|
-
|
|
1042
|
-
const peerPrefix = prefixFor(state, peer, destroyeeDomain);
|
|
1043
|
-
hooks.sendToNapplet(peer, { type: `${peerPrefix}.channel.closed`, channelId });
|
|
999
|
+
hooks.sendToNapplet(peer, { type: "inc.channel.closed", channelId });
|
|
1044
1000
|
}
|
|
1045
1001
|
removeChannel(state, channelId);
|
|
1046
1002
|
}
|
|
1047
1003
|
}
|
|
1048
1004
|
|
|
1049
1005
|
// src/state-handler.ts
|
|
1050
|
-
|
|
1051
|
-
|
|
1006
|
+
var INSTANCE_MARKER = "@i/";
|
|
1007
|
+
function instanceSegment(windowId) {
|
|
1008
|
+
return `${INSTANCE_MARKER}${windowId}:`;
|
|
1009
|
+
}
|
|
1010
|
+
function scopedKey(dTag, aggregateHash, userKey, instance = false, windowId = "") {
|
|
1011
|
+
const segment = instance ? instanceSegment(windowId) : "";
|
|
1012
|
+
return `napplet-state:${dTag}:${aggregateHash}:${segment}${userKey}`;
|
|
1052
1013
|
}
|
|
1053
1014
|
function legacyScopedKey(pubkey, dTag, aggregateHash, userKey) {
|
|
1054
1015
|
return `napplet-state:${pubkey}:${dTag}:${aggregateHash}:${userKey}`;
|
|
@@ -1067,31 +1028,43 @@ function byteLength(str) {
|
|
|
1067
1028
|
}
|
|
1068
1029
|
return bytes;
|
|
1069
1030
|
}
|
|
1070
|
-
function
|
|
1031
|
+
function handleStorageNap(windowId, msg, sendToNapplet, sessionRegistry, aclState, statePersistence) {
|
|
1071
1032
|
const m = msg;
|
|
1072
1033
|
const id = m.id ?? "";
|
|
1073
1034
|
const action = msg.type.split(".")[1];
|
|
1074
1035
|
function sendResult(payload) {
|
|
1075
1036
|
sendToNapplet(windowId, { type: `${msg.type}.result`, id, ...payload });
|
|
1076
1037
|
}
|
|
1077
|
-
function
|
|
1038
|
+
function sendErrorNap(error) {
|
|
1078
1039
|
sendToNapplet(windowId, { type: `${msg.type}.result`, id, error });
|
|
1079
1040
|
}
|
|
1080
1041
|
const entry = sessionRegistry.getEntryByWindowId(windowId);
|
|
1081
1042
|
if (!entry) {
|
|
1082
|
-
|
|
1043
|
+
sendErrorNap("not registered");
|
|
1083
1044
|
return;
|
|
1084
1045
|
}
|
|
1085
1046
|
const { dTag, aggregateHash, pubkey } = entry;
|
|
1086
1047
|
const prefix = `napplet-state:${dTag}:${aggregateHash}:`;
|
|
1087
1048
|
const legacyPrefix = pubkey ? `napplet-state:${pubkey}:${dTag}:${aggregateHash}:` : "";
|
|
1049
|
+
if (m.scope !== void 0 && m.scope !== "shared" && m.scope !== "instance") {
|
|
1050
|
+
sendErrorNap(`invalid scope: ${String(m.scope)} (expected "shared" or "instance")`);
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
const scope = m.scope === "instance" ? "instance" : "shared";
|
|
1054
|
+
const isInstance = scope === "instance";
|
|
1055
|
+
const instancePrefix = `${prefix}${instanceSegment(windowId)}`;
|
|
1056
|
+
const keyFor = (userKey) => scopedKey(dTag, aggregateHash, userKey, isInstance, windowId);
|
|
1088
1057
|
switch (action) {
|
|
1089
1058
|
case "get": {
|
|
1090
1059
|
const key = m.key;
|
|
1091
1060
|
if (!key) {
|
|
1092
|
-
|
|
1061
|
+
sendErrorNap("missing key");
|
|
1093
1062
|
return;
|
|
1094
1063
|
}
|
|
1064
|
+
if (isInstance) {
|
|
1065
|
+
sendResult({ value: statePersistence.get(keyFor(key)) });
|
|
1066
|
+
break;
|
|
1067
|
+
}
|
|
1095
1068
|
const newKey = scopedKey(dTag, aggregateHash, key);
|
|
1096
1069
|
let result = statePersistence.get(newKey);
|
|
1097
1070
|
if (result === null && pubkey) {
|
|
@@ -1107,15 +1080,15 @@ function handleStorageNub(windowId, msg, sendToNapplet, sessionRegistry, aclStat
|
|
|
1107
1080
|
const key = m.key;
|
|
1108
1081
|
const value = m.value ?? "";
|
|
1109
1082
|
if (!key) {
|
|
1110
|
-
|
|
1083
|
+
sendErrorNap("missing key");
|
|
1111
1084
|
return;
|
|
1112
1085
|
}
|
|
1113
1086
|
const quota = aclState.getStateQuota(pubkey ?? "", dTag, aggregateHash);
|
|
1114
|
-
const sk =
|
|
1087
|
+
const sk = keyFor(key);
|
|
1115
1088
|
const newWriteBytes = byteLength(sk + value);
|
|
1116
1089
|
const existingBytes = statePersistence.calculateBytes(prefix, key);
|
|
1117
1090
|
if (existingBytes + newWriteBytes > quota) {
|
|
1118
|
-
|
|
1091
|
+
sendErrorNap(`quota exceeded: napplet state limit is ${quota} bytes`);
|
|
1119
1092
|
return;
|
|
1120
1093
|
}
|
|
1121
1094
|
const success = statePersistence.set(sk, value);
|
|
@@ -1125,29 +1098,46 @@ function handleStorageNub(windowId, msg, sendToNapplet, sessionRegistry, aclStat
|
|
|
1125
1098
|
case "remove": {
|
|
1126
1099
|
const key = m.key;
|
|
1127
1100
|
if (!key) {
|
|
1128
|
-
|
|
1101
|
+
sendErrorNap("missing key");
|
|
1129
1102
|
return;
|
|
1130
1103
|
}
|
|
1131
|
-
statePersistence.remove(
|
|
1104
|
+
statePersistence.remove(keyFor(key));
|
|
1132
1105
|
void legacyPrefix;
|
|
1133
1106
|
sendResult({ ok: true });
|
|
1134
1107
|
break;
|
|
1135
1108
|
}
|
|
1136
1109
|
case "clear": {
|
|
1137
|
-
|
|
1110
|
+
sendErrorNap("storage.clear is not in @napplet/nap/storage; action not supported");
|
|
1138
1111
|
break;
|
|
1139
1112
|
}
|
|
1140
1113
|
case "keys": {
|
|
1114
|
+
if (isInstance) {
|
|
1115
|
+
const instKeys = statePersistence.keys(instancePrefix);
|
|
1116
|
+
const instanceKeySet = /* @__PURE__ */ new Set();
|
|
1117
|
+
for (const k of instKeys) {
|
|
1118
|
+
instanceKeySet.add(k.startsWith(instancePrefix) ? k.slice(instancePrefix.length) : k);
|
|
1119
|
+
}
|
|
1120
|
+
sendResult({ keys: Array.from(instanceKeySet) });
|
|
1121
|
+
break;
|
|
1122
|
+
}
|
|
1141
1123
|
const newKeys = statePersistence.keys(prefix);
|
|
1142
1124
|
const legacyKeys = legacyPrefix ? statePersistence.keys(legacyPrefix) : [];
|
|
1143
1125
|
const userKeySet = /* @__PURE__ */ new Set();
|
|
1144
|
-
for (const k of newKeys)
|
|
1126
|
+
for (const k of newKeys) {
|
|
1127
|
+
if (!k.startsWith(prefix)) {
|
|
1128
|
+
userKeySet.add(k);
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
const userKey = k.slice(prefix.length);
|
|
1132
|
+
if (userKey.startsWith(INSTANCE_MARKER)) continue;
|
|
1133
|
+
userKeySet.add(userKey);
|
|
1134
|
+
}
|
|
1145
1135
|
for (const k of legacyKeys) userKeySet.add(k.startsWith(legacyPrefix) ? k.slice(legacyPrefix.length) : k);
|
|
1146
1136
|
sendResult({ keys: Array.from(userKeySet) });
|
|
1147
1137
|
break;
|
|
1148
1138
|
}
|
|
1149
1139
|
default:
|
|
1150
|
-
|
|
1140
|
+
sendErrorNap(`unknown storage action: ${action}`);
|
|
1151
1141
|
break;
|
|
1152
1142
|
}
|
|
1153
1143
|
}
|
|
@@ -1179,7 +1169,7 @@ function createRuntimeDomainHandlers(context) {
|
|
|
1179
1169
|
}
|
|
1180
1170
|
function handleStorageMessage(context, windowId, msg) {
|
|
1181
1171
|
const { aclState, hooks, sessionRegistry } = context;
|
|
1182
|
-
|
|
1172
|
+
handleStorageNap(windowId, msg, hooks.sendToNapplet, sessionRegistry, aclState, hooks.statePersistence);
|
|
1183
1173
|
}
|
|
1184
1174
|
function handleMediaMessage(context, windowId, msg) {
|
|
1185
1175
|
const { hooks, serviceRegistry } = context;
|
|
@@ -1297,31 +1287,30 @@ function createRegisteredServices(serviceRegistry) {
|
|
|
1297
1287
|
}
|
|
1298
1288
|
return registeredServices;
|
|
1299
1289
|
}
|
|
1300
|
-
function
|
|
1290
|
+
function createNapEnvelopeDispatcher(handlers) {
|
|
1301
1291
|
let currentWindowId = null;
|
|
1302
|
-
const
|
|
1292
|
+
const napDispatch = createDispatch();
|
|
1303
1293
|
const adapt = (handler) => (msg) => {
|
|
1304
1294
|
if (currentWindowId !== null) handler(currentWindowId, msg);
|
|
1305
1295
|
};
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
nubDispatch.registerNap("intent", adapt(handlers.intent));
|
|
1296
|
+
napDispatch.registerNap("relay", adapt(handlers.relay));
|
|
1297
|
+
napDispatch.registerNap("identity", adapt(handlers.identity));
|
|
1298
|
+
napDispatch.registerNap("keys", adapt(handlers.keys));
|
|
1299
|
+
napDispatch.registerNap("media", adapt(handlers.media));
|
|
1300
|
+
napDispatch.registerNap("notify", adapt(handlers.notify));
|
|
1301
|
+
napDispatch.registerNap("storage", adapt(handlers.storage));
|
|
1302
|
+
napDispatch.registerNap("inc", adapt(handlers.inc));
|
|
1303
|
+
napDispatch.registerNap("theme", adapt(handlers.theme));
|
|
1304
|
+
napDispatch.registerNap("config", adapt(handlers.config));
|
|
1305
|
+
napDispatch.registerNap("resource", adapt(handlers.resource));
|
|
1306
|
+
napDispatch.registerNap("cvm", adapt(handlers.cvm));
|
|
1307
|
+
napDispatch.registerNap("outbox", adapt(handlers.outbox));
|
|
1308
|
+
napDispatch.registerNap("upload", adapt(handlers.upload));
|
|
1309
|
+
napDispatch.registerNap("intent", adapt(handlers.intent));
|
|
1321
1310
|
return (windowId, envelope) => {
|
|
1322
1311
|
currentWindowId = windowId;
|
|
1323
1312
|
try {
|
|
1324
|
-
|
|
1313
|
+
napDispatch.dispatch(envelope);
|
|
1325
1314
|
} finally {
|
|
1326
1315
|
currentWindowId = null;
|
|
1327
1316
|
}
|
|
@@ -1387,15 +1376,15 @@ function createFirewallGate(config) {
|
|
|
1387
1376
|
return "dispatch";
|
|
1388
1377
|
};
|
|
1389
1378
|
}
|
|
1390
|
-
function createMessageHandler(hooks,
|
|
1379
|
+
function createMessageHandler(hooks, enforceNap, dispatchNapEnvelope, firewallGate) {
|
|
1391
1380
|
return (windowId, msg) => {
|
|
1392
1381
|
if (typeof msg !== "object" || msg === null || !("type" in msg)) return;
|
|
1393
1382
|
const envelope = msg;
|
|
1394
1383
|
const dotIdx = envelope.type.indexOf(".");
|
|
1395
1384
|
if (dotIdx === -1) return;
|
|
1396
|
-
const caps =
|
|
1385
|
+
const caps = resolveCapabilitiesNap(envelope);
|
|
1397
1386
|
if (caps.senderCap) {
|
|
1398
|
-
const result =
|
|
1387
|
+
const result = enforceNap(windowId, caps.senderCap, envelope);
|
|
1399
1388
|
if (!result.allowed) {
|
|
1400
1389
|
const id = envelope.id ?? "";
|
|
1401
1390
|
const isStorageEnvelope = envelope.type.startsWith("storage.");
|
|
@@ -1407,7 +1396,7 @@ function createMessageHandler(hooks, enforceNub, dispatchNubEnvelope, firewallGa
|
|
|
1407
1396
|
}
|
|
1408
1397
|
const verdict = firewallGate(windowId, envelope, caps.senderCap);
|
|
1409
1398
|
if (verdict === "drop") return;
|
|
1410
|
-
|
|
1399
|
+
dispatchNapEnvelope(windowId, envelope);
|
|
1411
1400
|
};
|
|
1412
1401
|
}
|
|
1413
1402
|
function createInjectedEvent(hooks, topic, payload) {
|
|
@@ -1428,7 +1417,7 @@ function createRuntimeInstance(context) {
|
|
|
1428
1417
|
firewallState,
|
|
1429
1418
|
eventBuffer,
|
|
1430
1419
|
hooks,
|
|
1431
|
-
|
|
1420
|
+
incRuntime,
|
|
1432
1421
|
manifestCache,
|
|
1433
1422
|
registeredServices,
|
|
1434
1423
|
replayDetector,
|
|
@@ -1449,7 +1438,7 @@ function createRuntimeInstance(context) {
|
|
|
1449
1438
|
firewallState.persist();
|
|
1450
1439
|
replayDetector.clear();
|
|
1451
1440
|
subscriptions.clear();
|
|
1452
|
-
|
|
1441
|
+
incRuntime.clear();
|
|
1453
1442
|
eventBuffer.clear();
|
|
1454
1443
|
registeredServices.clear();
|
|
1455
1444
|
undeclaredServiceConsents.clear();
|
|
@@ -1476,7 +1465,7 @@ function createRuntimeInstance(context) {
|
|
|
1476
1465
|
hooks.relayPool?.untrackSubscription(key);
|
|
1477
1466
|
}
|
|
1478
1467
|
}
|
|
1479
|
-
|
|
1468
|
+
incRuntime.destroyWindow(windowId);
|
|
1480
1469
|
notifyServiceWindowDestroyed(windowId, serviceRegistry);
|
|
1481
1470
|
},
|
|
1482
1471
|
get sessionRegistry() {
|
|
@@ -1527,11 +1516,11 @@ function createRuntime(hooks) {
|
|
|
1527
1516
|
},
|
|
1528
1517
|
onAclCheck: hooks.onAclCheck
|
|
1529
1518
|
});
|
|
1530
|
-
const
|
|
1519
|
+
const enforceNap = createNapEnforceGate({
|
|
1531
1520
|
checkAcl: (pubkey, dTag, aggregateHash, capability) => aclState.check(pubkey, dTag, aggregateHash, capability),
|
|
1532
1521
|
resolveIdentityByWindowId: (windowId) => {
|
|
1533
1522
|
const entry = sessionRegistry.getEntryByWindowId(windowId);
|
|
1534
|
-
return entry ? { dTag: entry.dTag, aggregateHash: entry.aggregateHash
|
|
1523
|
+
return entry ? { dTag: entry.dTag, aggregateHash: entry.aggregateHash } : void 0;
|
|
1535
1524
|
},
|
|
1536
1525
|
onAclCheck: hooks.onAclCheck
|
|
1537
1526
|
});
|
|
@@ -1546,15 +1535,15 @@ function createRuntime(hooks) {
|
|
|
1546
1535
|
aclState.load();
|
|
1547
1536
|
firewallState.load();
|
|
1548
1537
|
manifestCache.load();
|
|
1549
|
-
const
|
|
1538
|
+
const incRuntime = createIncRuntime(hooks, sessionRegistry);
|
|
1550
1539
|
const domainHandlers = createRuntimeDomainHandlers({ hooks, serviceRegistry, sessionRegistry, aclState });
|
|
1551
|
-
const
|
|
1540
|
+
const dispatchNapEnvelope = createNapEnvelopeDispatcher({
|
|
1552
1541
|
relay: createRelayHandler({ hooks, serviceRegistry, subscriptions, eventBuffer, replayDetector }),
|
|
1553
1542
|
identity: createIdentityHandler({ hooks, serviceRegistry }),
|
|
1554
|
-
|
|
1543
|
+
inc: incRuntime.handleMessage,
|
|
1555
1544
|
...domainHandlers
|
|
1556
1545
|
});
|
|
1557
|
-
const handleMessage = createMessageHandler(hooks,
|
|
1546
|
+
const handleMessage = createMessageHandler(hooks, enforceNap, dispatchNapEnvelope, firewallGate);
|
|
1558
1547
|
return createRuntimeInstance({
|
|
1559
1548
|
hooks,
|
|
1560
1549
|
serviceRegistry,
|
|
@@ -1562,7 +1551,7 @@ function createRuntime(hooks) {
|
|
|
1562
1551
|
replayDetector,
|
|
1563
1552
|
subscriptions,
|
|
1564
1553
|
eventBuffer,
|
|
1565
|
-
|
|
1554
|
+
incRuntime,
|
|
1566
1555
|
sessionRegistry,
|
|
1567
1556
|
aclState,
|
|
1568
1557
|
firewallState,
|
|
@@ -1573,9 +1562,9 @@ function createRuntime(hooks) {
|
|
|
1573
1562
|
}
|
|
1574
1563
|
|
|
1575
1564
|
// src/index.ts
|
|
1576
|
-
import { ALL_CAPABILITIES
|
|
1565
|
+
import { ALL_CAPABILITIES } from "@kehto/acl/capabilities";
|
|
1577
1566
|
export {
|
|
1578
|
-
|
|
1567
|
+
ALL_CAPABILITIES,
|
|
1579
1568
|
RING_BUFFER_SIZE,
|
|
1580
1569
|
cleanupNappState,
|
|
1581
1570
|
createAclState,
|
|
@@ -1583,17 +1572,17 @@ export {
|
|
|
1583
1572
|
createEventBuffer,
|
|
1584
1573
|
createFirewallState,
|
|
1585
1574
|
createManifestCache,
|
|
1575
|
+
createNapEnforceGate,
|
|
1586
1576
|
createNappKeyRegistry,
|
|
1587
|
-
createNubEnforceGate,
|
|
1588
1577
|
createReplayDetector,
|
|
1589
1578
|
createRuntime,
|
|
1590
1579
|
createSessionRegistry,
|
|
1591
1580
|
formatDenialReason,
|
|
1592
|
-
|
|
1581
|
+
handleStorageNap,
|
|
1593
1582
|
matchesAnyFilter,
|
|
1594
1583
|
matchesFilter,
|
|
1595
1584
|
notifyServiceWindowDestroyed,
|
|
1596
|
-
|
|
1585
|
+
resolveCapabilitiesNap,
|
|
1597
1586
|
routeServiceMessage
|
|
1598
1587
|
};
|
|
1599
1588
|
//# sourceMappingURL=index.js.map
|