@kumori/aurora-backend-handler 1.1.84 → 1.1.86

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.
@@ -16,6 +16,35 @@ interface DregistryBody {
16
16
  docker_password: string;
17
17
  };
18
18
  }
19
+
20
+ /**
21
+ * Extracts a user-facing code/content pair from a failed request.
22
+ * Errors coming back from the WS layer are usually nested under `.error`
23
+ * (e.g. `{ error: { code, content } }`), but some server-side rejections
24
+ * (e.g. plan/limits validation) return the fields flat on the error object
25
+ * itself (`{ code, content }`). Without this fallback those errors were
26
+ * silently reported as "Unknown error" instead of surfacing the real code.
27
+ */
28
+ const extractWsErrorInfo = (
29
+ err: unknown
30
+ ): { code: string; content: string } => {
31
+ const candidate =
32
+ err && typeof err === "object" && "error" in err
33
+ ? (err as any).error
34
+ : err;
35
+
36
+ const code =
37
+ candidate && typeof candidate === "object" && typeof candidate.code === "string"
38
+ ? candidate.code
39
+ : "Unknown error";
40
+ const content =
41
+ candidate && typeof candidate === "object" && typeof candidate.content === "string"
42
+ ? candidate.content
43
+ : "Unknown error";
44
+
45
+ return { code, content };
46
+ };
47
+
19
48
  /**
20
49
  * Function to create a tenant using the global WebSocket connection
21
50
  * @param tenant Tenant object with the data of the tenant
@@ -79,23 +108,8 @@ export const createTenant = async (tenant: Tenant, security: Security) => {
79
108
  tenant: tenant.name,
80
109
  webSocketStatus: getWebSocketStatus(),
81
110
  });
82
- let contentMessage = "Unknown error";
83
- let errorContent = "Unknown error";
84
-
85
- if (
86
- typeof error === "object" &&
87
- error !== null &&
88
- "error" in error &&
89
- typeof (error as any).error === "object" &&
90
- (error as any).error !== null
91
- ) {
92
- if ("code" in (error as any).error) {
93
- contentMessage = (error as any).error.code;
94
- }
95
- if ("content" in (error as any).error) {
96
- errorContent = (error as any).error.content;
97
- }
98
- }
111
+ const { code: contentMessage, content: errorContent } =
112
+ extractWsErrorInfo(error);
99
113
  const tenantCreationErrorNotification: Notification = {
100
114
  type: "error",
101
115
  subtype: errors.tenant.creationError.subtype,
@@ -146,24 +160,8 @@ export const deleteTenant = async (tenant: Tenant, security: string) => {
146
160
  return response;
147
161
  } catch (err) {
148
162
  console.error("Error in tenant deletion WebSocket request:", err);
149
- eventHelper.tenant.publish.deletionError(tenant);
150
- let contentMessage = "Unknown error";
151
- let errorContent = "Unknown error";
152
-
153
- if (
154
- typeof err === "object" &&
155
- err !== null &&
156
- "error" in err &&
157
- typeof (err as any).error === "object" &&
158
- (err as any).error !== null
159
- ) {
160
- if ("code" in (err as any).error) {
161
- contentMessage = (err as any).error.code;
162
- }
163
- if ("content" in (err as any).error) {
164
- errorContent = (err as any).error.content;
165
- }
166
- }
163
+ const { code: contentMessage, content: errorContent } =
164
+ extractWsErrorInfo(err);
167
165
  const tenantDeletionErrorNotification: Notification = {
168
166
  type: "error",
169
167
  subtype: errors.tenant.deletionError.subtype,
@@ -244,24 +242,8 @@ export const updateTenant = async (tenant: Tenant, security: string) => {
244
242
  return response;
245
243
  } catch (err) {
246
244
  console.error("Error in tenant update WebSocket request:", err);
247
- eventHelper.tenant.publish.updateError(tenant);
248
- let contentMessage = "Unknown error";
249
- let errorContent = "Unknown error";
250
-
251
- if (
252
- typeof err === "object" &&
253
- err !== null &&
254
- "error" in err &&
255
- typeof (err as any).error === "object" &&
256
- (err as any).error !== null
257
- ) {
258
- if ("code" in (err as any).error) {
259
- contentMessage = (err as any).error.code;
260
- }
261
- if ("content" in (err as any).error) {
262
- errorContent = (err as any).error.content;
263
- }
264
- }
245
+ const { code: contentMessage, content: errorContent } =
246
+ extractWsErrorInfo(err);
265
247
  const tenantUpdateErrorNotification: Notification = {
266
248
  type: "error",
267
249
  subtype: errors.tenant.updateError.subtype,
@@ -586,23 +568,8 @@ export const createRegistry = async (
586
568
  webSocketStatus: getWebSocketStatus(),
587
569
  });
588
570
 
589
- let contentMessage = "Unknown error";
590
- let errorContent = "Unknown error";
591
-
592
- if (
593
- typeof error === "object" &&
594
- error !== null &&
595
- "error" in error &&
596
- typeof (error as any).error === "object" &&
597
- (error as any).error !== null
598
- ) {
599
- if ("code" in (error as any).error) {
600
- contentMessage = (error as any).error.code;
601
- }
602
- if ("content" in (error as any).error) {
603
- errorContent = (error as any).error.content;
604
- }
605
- }
571
+ const { code: contentMessage, content: errorContent } =
572
+ extractWsErrorInfo(error);
606
573
 
607
574
  const dregistryCreationErrorNotification: Notification = {
608
575
  type: "error",
@@ -689,23 +656,8 @@ export const updateRegistry = async (
689
656
  webSocketStatus: getWebSocketStatus(),
690
657
  });
691
658
 
692
- let contentMessage = "Unknown error";
693
- let errorContent = "Unknown error";
694
-
695
- if (
696
- typeof error === "object" &&
697
- error !== null &&
698
- "error" in error &&
699
- typeof (error as any).error === "object" &&
700
- (error as any).error !== null
701
- ) {
702
- if ("code" in (error as any).error) {
703
- contentMessage = (error as any).error.code;
704
- }
705
- if ("content" in (error as any).error) {
706
- errorContent = (error as any).error.content;
707
- }
708
- }
659
+ const { code: contentMessage, content: errorContent } =
660
+ extractWsErrorInfo(error);
709
661
 
710
662
  const dregistryUpdateErrorNotification: Notification = {
711
663
  type: "error",
@@ -773,23 +725,8 @@ export const deleteRegistry = async (
773
725
  webSocketStatus: getWebSocketStatus(),
774
726
  });
775
727
 
776
- let contentMessage = "Unknown error";
777
- let errorContent = "Unknown error";
778
-
779
- if (
780
- typeof error === "object" &&
781
- error !== null &&
782
- "error" in error &&
783
- typeof (error as any).error === "object" &&
784
- (error as any).error !== null
785
- ) {
786
- if ("code" in (error as any).error) {
787
- contentMessage = (error as any).error.code;
788
- }
789
- if ("content" in (error as any).error) {
790
- errorContent = (error as any).error.content;
791
- }
792
- }
728
+ const { code: contentMessage, content: errorContent } =
729
+ extractWsErrorInfo(error);
793
730
 
794
731
  const dregistryDeletionErrorNotification: Notification = {
795
732
  type: "error",
@@ -859,23 +796,8 @@ export const inviteUser = async (
859
796
  role: role,
860
797
  webSocketStatus: getWebSocketStatus(),
861
798
  });
862
- let contentMessage = "Unknown error";
863
- let errorContent = "Unknown error";
864
-
865
- if (
866
- typeof error === "object" &&
867
- error !== null &&
868
- "error" in error &&
869
- typeof (error as any).error === "object" &&
870
- (error as any).error !== null
871
- ) {
872
- if ("code" in (error as any).error) {
873
- contentMessage = (error as any).error.code;
874
- }
875
- if ("content" in (error as any).error) {
876
- errorContent = (error as any).error.content;
877
- }
878
- }
799
+ const { code: contentMessage, content: errorContent } =
800
+ extractWsErrorInfo(error);
879
801
  const tenantInviteErrorNotification: Notification = {
880
802
  type: "error",
881
803
  subtype: errors.tenant.userInviteError.subtype,
@@ -938,23 +860,8 @@ export const removeUser = async (
938
860
  userId: userId,
939
861
  webSocketStatus: getWebSocketStatus(),
940
862
  });
941
- let contentMessage = "Unknown error";
942
- let errorContent = "Unknown error";
943
-
944
- if (
945
- typeof error === "object" &&
946
- error !== null &&
947
- "error" in error &&
948
- typeof (error as any).error === "object" &&
949
- (error as any).error !== null
950
- ) {
951
- if ("code" in (error as any).error) {
952
- contentMessage = (error as any).error.code;
953
- }
954
- if ("content" in (error as any).error) {
955
- errorContent = (error as any).error.content;
956
- }
957
- }
863
+ const { code: contentMessage, content: errorContent } =
864
+ extractWsErrorInfo(error);
958
865
  const tenantUserRemovedErrorNotification: Notification = {
959
866
  type: "error",
960
867
  subtype: errors.tenant.userRemovedError.subtype,
@@ -1021,23 +928,8 @@ export const updateUserRole = async (
1021
928
  role: role,
1022
929
  webSocketStatus: getWebSocketStatus(),
1023
930
  });
1024
- let contentMessage = "Unknown error";
1025
- let errorContent = "Unknown error";
1026
-
1027
- if (
1028
- typeof error === "object" &&
1029
- error !== null &&
1030
- "error" in error &&
1031
- typeof (error as any).error === "object" &&
1032
- (error as any).error !== null
1033
- ) {
1034
- if ("code" in (error as any).error) {
1035
- contentMessage = (error as any).error.code;
1036
- }
1037
- if ("content" in (error as any).error) {
1038
- errorContent = (error as any).error.content;
1039
- }
1040
- }
931
+ const { code: contentMessage, content: errorContent } =
932
+ extractWsErrorInfo(error);
1041
933
  const tenantUserUpdatedErrorNotification: Notification = {
1042
934
  type: "error",
1043
935
  subtype: errors.tenant.roleUpdateError.subtype,
@@ -1092,23 +984,8 @@ export const acceptInvite = async (tenant: string, security: string) => {
1092
984
  tenant: tenant,
1093
985
  webSocketStatus: getWebSocketStatus(),
1094
986
  });
1095
- let contentMessage = "Unknown error";
1096
- let errorContent = "Unknown error";
1097
-
1098
- if (
1099
- typeof error === "object" &&
1100
- error !== null &&
1101
- "error" in error &&
1102
- typeof (error as any).error === "object" &&
1103
- (error as any).error !== null
1104
- ) {
1105
- if ("code" in (error as any).error) {
1106
- contentMessage = (error as any).error.code;
1107
- }
1108
- if ("content" in (error as any).error) {
1109
- errorContent = (error as any).error.content;
1110
- }
1111
- }
987
+ const { code: contentMessage, content: errorContent } =
988
+ extractWsErrorInfo(error);
1112
989
  const tenantInviteAcceptedErrorNotification: Notification = {
1113
990
  type: "error",
1114
991
  subtype: errors.tenant.inviteAcceptError.subtype,
@@ -1177,23 +1054,8 @@ export const rejectInvite = async (tenant: string, security: string, leave: bool
1177
1054
  tenant: tenant,
1178
1055
  webSocketStatus: getWebSocketStatus(),
1179
1056
  });
1180
- let contentMessage = "Unknown error";
1181
- let errorContent = "Unknown error";
1182
-
1183
- if (
1184
- typeof error === "object" &&
1185
- error !== null &&
1186
- "error" in error &&
1187
- typeof (error as any).error === "object" &&
1188
- (error as any).error !== null
1189
- ) {
1190
- if ("code" in (error as any).error) {
1191
- contentMessage = (error as any).error.code;
1192
- }
1193
- if ("content" in (error as any).error) {
1194
- errorContent = (error as any).error.content;
1195
- }
1196
- }
1057
+ const { code: contentMessage, content: errorContent } =
1058
+ extractWsErrorInfo(error);
1197
1059
  const tenantInviteRejectedErrorNotification: Notification = {
1198
1060
  type: "error",
1199
1061
  subtype: errors.tenant.inviteRejectError.subtype,
@@ -5,6 +5,7 @@ interface HandleTokenEventParams {
5
5
  eventData: any;
6
6
  tokenMap: Map<string, Token>;
7
7
  userData: UserData;
8
+ deletedTokenIds: Set<string>;
8
9
  }
9
10
 
10
11
  interface HandleTokenEventResult {
@@ -24,10 +25,26 @@ export const handleTokenEvent = ({
24
25
  eventData,
25
26
  tokenMap,
26
27
  userData,
28
+ deletedTokenIds,
27
29
  }: HandleTokenEventParams): HandleTokenEventResult => {
28
30
  const tokenParentKind = eventData.id.parent?.kind;
29
31
  const tokenId = eventData.id.name;
30
32
 
33
+ if (tokenParentKind === "user" && deletedTokenIds.has(tokenId)) {
34
+ // A tombstoned token (already explicitly deleted by the user) must
35
+ // never be resurrected by a stale/out-of-order "token" event -
36
+ // e.g. one reflecting backend state that hasn't caught up with the
37
+ // deletion yet.
38
+ return {
39
+ isUserToken: false,
40
+ isClusterToken: false,
41
+ userToken: null,
42
+ clusterToken: null,
43
+ updatedUserTokens: userData.tokens,
44
+ tokenId,
45
+ };
46
+ }
47
+
31
48
  if (tokenParentKind === "user") {
32
49
  const tokenInMap = tokenMap.get(tokenId);
33
50
 
@@ -112,6 +129,7 @@ interface HandleTokenDeletionParams {
112
129
  tokenId: string;
113
130
  tokenMap: Map<string, Token>;
114
131
  userData: UserData;
132
+ deletedTokenIds: Set<string>;
115
133
  }
116
134
 
117
135
  interface HandleTokenDeletionResult {
@@ -124,7 +142,9 @@ export const handleTokenDeletion = ({
124
142
  tokenId,
125
143
  tokenMap,
126
144
  userData,
145
+ deletedTokenIds,
127
146
  }: HandleTokenDeletionParams): HandleTokenDeletionResult => {
147
+ deletedTokenIds.add(tokenId);
128
148
  const tokenIndex = userData.tokens.findIndex((t) => t.name === tokenId);
129
149
  if (tokenIndex === -1) {
130
150
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kumori/aurora-backend-handler",
3
- "version": "1.1.84",
3
+ "version": "1.1.86",
4
4
  "description": "backend handler",
5
5
  "main": "backend-handler.ts",
6
6
  "scripts": {
@@ -174,6 +174,7 @@ let accountsMap = new Map<string, Account>();
174
174
  let revisionsMap = new Map<string, Revision>();
175
175
  let roleMap = new Map<string, Role[]>();
176
176
  let tokenMap = new Map<string, Token>();
177
+ let deletedTokenIds = new Set<string>();
177
178
  let platformInfo: Platform | null = null;
178
179
  let isPlatformInfoLoading = false;
179
180
  let isPlatformInfoLoaded = false;
@@ -639,7 +640,11 @@ const rebuildHierarchy = () => {
639
640
  notificationsEnabled: preservedUserData.notificationsEnabled,
640
641
  organizations: Array.from(organizationsMap.values()),
641
642
  clusterTokens: Array.from(clusterTokensMap.values()),
642
- tokens: tokenMap.size > 0 ? (preservedUserData.tokens || []).filter(t => tokenMap.has(t.name)) : (preservedUserData.tokens || []),
643
+ tokens: (
644
+ tokenMap.size > 0
645
+ ? (preservedUserData.tokens || []).filter(t => tokenMap.has(t.name))
646
+ : (preservedUserData.tokens || [])
647
+ ).filter(t => !deletedTokenIds.has(t.name)),
643
648
  tenants: Array.from(tenantsMap.values()),
644
649
  axebowPlan: preservedUserData.axebowPlan,
645
650
  companyName: preservedUserData.companyName,
@@ -1129,6 +1134,7 @@ const handleEvent = async (message: WSMessage) => {
1129
1134
  eventData,
1130
1135
  tokenMap,
1131
1136
  userData,
1137
+ deletedTokenIds,
1132
1138
  });
1133
1139
 
1134
1140
  if (tokenResult.isUserToken) {
@@ -1518,6 +1524,7 @@ const handleOperationSuccess = (operation: PendingOperation, response: any) => {
1518
1524
  tokenId: deletedTokenId,
1519
1525
  tokenMap,
1520
1526
  userData,
1527
+ deletedTokenIds,
1521
1528
  });
1522
1529
  if (tokenDeletionResult.removed) {
1523
1530
  userData.tokens = tokenDeletionResult.updatedUserTokens;
@@ -1916,6 +1923,7 @@ const handleDeleteEvent = async (message: WSMessage) => {
1916
1923
  tokenId: keyParts.token,
1917
1924
  tokenMap,
1918
1925
  userData,
1926
+ deletedTokenIds,
1919
1927
  });
1920
1928
  if (tokenDeletionResult.removed) {
1921
1929
  userData.tokens = tokenDeletionResult.updatedUserTokens;
@@ -2056,7 +2064,8 @@ export const updateUserComplete = (updatedUserData: UserData): User => {
2056
2064
  );
2057
2065
  const mergedTokens = [...(updatedUserData.tokens || []), ...localOnlyTokens]
2058
2066
  .filter((t, index, arr) => arr.findIndex(x => x.name === t.name) === index)
2059
- .filter(t => tokenMap.size === 0 || tokenMap.has(t.name));
2067
+ .filter(t => tokenMap.size === 0 || tokenMap.has(t.name))
2068
+ .filter(t => !deletedTokenIds.has(t.name));
2060
2069
 
2061
2070
  userData = {
2062
2071
  id: updatedUserData.id || userData.id,