@oxy.so/core 4.5.0-oxy-one.0 → 4.5.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 +1 -1
- package/dist/cjs/HttpService.js +63 -19
- package/dist/cjs/api/accounts.js +10 -3
- package/dist/cjs/api/agency.js +3 -1
- package/dist/cjs/api/apps.js +15 -4
- package/dist/cjs/api/assets.js +49 -12
- package/dist/cjs/api/auth.js +37 -11
- package/dist/cjs/api/billing.js +24 -7
- package/dist/cjs/api/civic.js +14 -4
- package/dist/cjs/api/follows.js +9 -2
- package/dist/cjs/api/identity.js +31 -9
- package/dist/cjs/api/nodes.js +3 -1
- package/dist/cjs/api/privacy.js +8 -2
- package/dist/cjs/api/session.js +15 -4
- package/dist/cjs/api/store.js +15 -5
- package/dist/cjs/api/topics.js +10 -3
- package/dist/cjs/api/users.js +16 -4
- package/dist/cjs/boot/sessionColdBoot.js +40 -13
- package/dist/cjs/constants/version.js +6 -6
- package/dist/cjs/crypto/identityMarker.js +3 -1
- package/dist/cjs/crypto/identityProof.js +6 -1
- package/dist/cjs/crypto/keyManager.js +87 -32
- package/dist/cjs/crypto/recoveryPhrase.js +2 -2
- package/dist/cjs/crypto/signatureService.js +3 -4
- package/dist/cjs/i18n/index.js +16 -3
- package/dist/cjs/i18n/locales/ar-SA.js +366 -366
- package/dist/cjs/i18n/locales/ca-ES.js +366 -366
- package/dist/cjs/i18n/locales/de-DE.js +366 -366
- package/dist/cjs/i18n/locales/en-US.js +1994 -1994
- package/dist/cjs/i18n/locales/es-ES.js +1994 -1994
- package/dist/cjs/i18n/locales/fr-FR.js +366 -366
- package/dist/cjs/i18n/locales/it-IT.js +366 -366
- package/dist/cjs/i18n/locales/ja-JP.js +366 -366
- package/dist/cjs/i18n/locales/ko-KR.js +366 -366
- package/dist/cjs/i18n/locales/pt-PT.js +366 -366
- package/dist/cjs/i18n/locales/zh-CN.js +366 -366
- package/dist/cjs/inference/OxyInferenceClient.js +12 -18
- package/dist/cjs/models/interfaces.js +11 -11
- package/dist/cjs/server/OxyServer.js +86 -28
- package/dist/cjs/server/agentAccount.js +6 -2
- package/dist/cjs/server/aliaMachineCredential.js +39 -14
- package/dist/cjs/server/auth.js +5 -4
- package/dist/cjs/server/capabilityTicket.js +5 -5
- package/dist/cjs/server/index.js +2 -1
- package/dist/cjs/server/liveCapabilityTicket.js +4 -1
- package/dist/cjs/server/middleware.js +61 -42
- package/dist/cjs/server/namespaces.js +28 -7
- package/dist/cjs/server/requesterAssertion.js +36 -30
- package/dist/cjs/server/serviceTokenLifetime.js +7 -4
- package/dist/cjs/server/traffic.js +53 -14
- package/dist/cjs/server/trafficNodeHttp.js +33 -8
- package/dist/cjs/server/workloadIdentity.client.js +4 -0
- package/dist/cjs/server/workloadIdentity.js +28 -1
- package/dist/cjs/session/SessionClient.js +54 -17
- package/dist/cjs/session/accountDelegationAccess.js +4 -6
- package/dist/cjs/session/accountDialogController.js +1 -1
- package/dist/cjs/session/authStateStore.js +18 -9
- package/dist/cjs/session/identityPin.js +6 -2
- package/dist/cjs/session/refresh.js +81 -23
- package/dist/cjs/session/sharedDeviceCredential.js +28 -15
- package/dist/cjs/shared/utils/errorUtils.js +1 -1
- package/dist/cjs/shared/utils/networkUtils.js +1 -1
- package/dist/cjs/shared/utils/themeUtils.js +1 -1
- package/dist/cjs/utils/accountCacheSweep.js +3 -1
- package/dist/cjs/utils/accountUtils.js +1 -3
- package/dist/cjs/utils/asyncUtils.js +1 -1
- package/dist/cjs/utils/authHelpers.js +2 -1
- package/dist/cjs/utils/avatarUtils.js +4 -2
- package/dist/cjs/utils/cache.js +1 -1
- package/dist/cjs/utils/cacheKey.js +3 -1
- package/dist/cjs/utils/deviceManager.js +4 -4
- package/dist/cjs/utils/errorUtils.js +6 -4
- package/dist/cjs/utils/languageUtils.js +404 -57
- package/dist/cjs/utils/profileLinks.js +1 -3
- package/dist/cjs/utils/requestUtils.js +7 -1
- package/dist/cjs/utils/userIdentity.js +1 -1
- package/dist/cjs/utils/validationUtils.js +5 -6
- package/dist/esm/HttpService.js +64 -20
- package/dist/esm/api/accounts.js +11 -4
- package/dist/esm/api/agency.js +3 -1
- package/dist/esm/api/apps.js +15 -4
- package/dist/esm/api/assets.js +49 -12
- package/dist/esm/api/auth.js +37 -11
- package/dist/esm/api/billing.js +25 -8
- package/dist/esm/api/civic.js +14 -4
- package/dist/esm/api/follows.js +9 -2
- package/dist/esm/api/identity.js +31 -9
- package/dist/esm/api/nodes.js +3 -1
- package/dist/esm/api/privacy.js +8 -2
- package/dist/esm/api/session.js +15 -4
- package/dist/esm/api/store.js +15 -5
- package/dist/esm/api/topics.js +10 -3
- package/dist/esm/api/users.js +16 -4
- package/dist/esm/boot/sessionColdBoot.js +41 -14
- package/dist/esm/constants/version.js +6 -6
- package/dist/esm/crypto/identityMarker.js +3 -1
- package/dist/esm/crypto/identityProof.js +6 -1
- package/dist/esm/crypto/internal.js +1 -1
- package/dist/esm/crypto/keyManager.js +87 -32
- package/dist/esm/crypto/recoveryPhrase.js +2 -2
- package/dist/esm/crypto/signatureService.js +3 -4
- package/dist/esm/i18n/index.js +16 -3
- package/dist/esm/i18n/locales/ar-SA.js +366 -366
- package/dist/esm/i18n/locales/ca-ES.js +366 -366
- package/dist/esm/i18n/locales/de-DE.js +366 -366
- package/dist/esm/i18n/locales/en-US.js +1994 -1994
- package/dist/esm/i18n/locales/es-ES.js +1994 -1994
- package/dist/esm/i18n/locales/fr-FR.js +366 -366
- package/dist/esm/i18n/locales/it-IT.js +366 -366
- package/dist/esm/i18n/locales/ja-JP.js +366 -366
- package/dist/esm/i18n/locales/ko-KR.js +366 -366
- package/dist/esm/i18n/locales/pt-PT.js +366 -366
- package/dist/esm/i18n/locales/zh-CN.js +366 -366
- package/dist/esm/index.js +1 -1
- package/dist/esm/inference/OxyInferenceClient.js +13 -19
- package/dist/esm/models/interfaces.js +11 -11
- package/dist/esm/server/OxyServer.js +87 -29
- package/dist/esm/server/agentAccount.js +6 -2
- package/dist/esm/server/aliaMachineCredential.js +39 -14
- package/dist/esm/server/auth.js +6 -5
- package/dist/esm/server/capabilityTicket.js +6 -6
- package/dist/esm/server/index.js +5 -5
- package/dist/esm/server/liveCapabilityTicket.js +6 -3
- package/dist/esm/server/middleware.js +62 -43
- package/dist/esm/server/namespaces.js +31 -10
- package/dist/esm/server/requesterAssertion.js +36 -30
- package/dist/esm/server/serviceTokenLifetime.js +7 -4
- package/dist/esm/server/traffic.js +55 -16
- package/dist/esm/server/trafficNodeHttp.js +34 -9
- package/dist/esm/server/userInvalidation.js +1 -1
- package/dist/esm/server/workloadIdentity.client.js +3 -0
- package/dist/esm/server/workloadIdentity.js +27 -1
- package/dist/esm/session/SessionClient.js +54 -17
- package/dist/esm/session/accountDelegationAccess.js +5 -7
- package/dist/esm/session/accountDialogController.js +1 -1
- package/dist/esm/session/authStateStore.js +18 -9
- package/dist/esm/session/identityPin.js +6 -2
- package/dist/esm/session/refresh.js +82 -24
- package/dist/esm/session/sharedDeviceCredential.js +28 -15
- package/dist/esm/shared/utils/errorUtils.js +1 -1
- package/dist/esm/shared/utils/networkUtils.js +1 -1
- package/dist/esm/shared/utils/themeUtils.js +1 -1
- package/dist/esm/utils/accountCacheSweep.js +3 -1
- package/dist/esm/utils/accountUtils.js +1 -3
- package/dist/esm/utils/asyncUtils.js +1 -1
- package/dist/esm/utils/authHelpers.js +2 -1
- package/dist/esm/utils/avatarUtils.js +4 -2
- package/dist/esm/utils/cache.js +1 -1
- package/dist/esm/utils/cacheKey.js +3 -1
- package/dist/esm/utils/deviceManager.js +4 -4
- package/dist/esm/utils/errorUtils.js +6 -4
- package/dist/esm/utils/languageUtils.js +404 -57
- package/dist/esm/utils/profileLinks.js +1 -3
- package/dist/esm/utils/requestUtils.js +7 -1
- package/dist/esm/utils/userIdentity.js +1 -1
- package/dist/esm/utils/validationUtils.js +5 -6
- package/dist/types/HttpService.d.ts +1 -1
- package/dist/types/api/accounts.d.ts +1 -1
- package/dist/types/api/apps.d.ts +4 -4
- package/dist/types/api/assets.d.ts +2 -0
- package/dist/types/api/auth.d.ts +8 -3
- package/dist/types/crypto/internal.d.ts +1 -1
- package/dist/types/index.d.ts +4 -4
- package/dist/types/server/index.d.ts +6 -6
- package/dist/types/server/requesterAssertion.d.ts +2 -2
- package/dist/types/server/workloadIdentity.client.d.ts +5 -1
- package/dist/types/server/workloadIdentity.d.ts +26 -0
- package/dist/types/session/accountDelegationAccess.d.ts +1 -1
- package/dist/types/session/index.d.ts +1 -1
- package/dist/types/utils/userIdentity.d.ts +1 -1
- package/dist/types/utils/validationUtils.d.ts +4 -5
- package/package.json +2 -3
- package/src/HttpService.ts +183 -53
- package/src/OxyServices.errors.ts +7 -3
- package/src/OxyServices.ts +6 -2
- package/src/api/accounts.ts +76 -26
- package/src/api/agency.ts +24 -9
- package/src/api/appData.ts +12 -2
- package/src/api/apps.ts +81 -31
- package/src/api/assets.ts +122 -40
- package/src/api/auth.ts +154 -79
- package/src/api/billing.ts +112 -65
- package/src/api/civic.ts +61 -19
- package/src/api/contacts.ts +4 -1
- package/src/api/devices.ts +54 -21
- package/src/api/follows.ts +88 -31
- package/src/api/identity.ts +101 -33
- package/src/api/linkedAccounts.ts +26 -8
- package/src/api/nodes.ts +18 -6
- package/src/api/notifications.ts +26 -6
- package/src/api/privacy.ts +42 -8
- package/src/api/reputation.ts +18 -7
- package/src/api/session.ts +23 -5
- package/src/api/store.ts +81 -31
- package/src/api/topics.ts +18 -6
- package/src/api/users.ts +98 -35
- package/src/boot/sessionColdBoot.ts +60 -19
- package/src/civic/payloads.ts +1 -4
- package/src/client/context.ts +6 -1
- package/src/constants/version.ts +7 -6
- package/src/crypto/aead.ts +1 -5
- package/src/crypto/ecdh.ts +1 -4
- package/src/crypto/identityDerivations.ts +5 -1
- package/src/crypto/identityMarker.ts +3 -1
- package/src/crypto/identityProof.ts +6 -1
- package/src/crypto/index.ts +11 -11
- package/src/crypto/internal.ts +6 -1
- package/src/crypto/keyManager.ts +348 -98
- package/src/crypto/recoveryPhrase.ts +14 -9
- package/src/crypto/registrationPow.ts +1 -1
- package/src/crypto/signatureService.ts +14 -6
- package/src/i18n/accountCategoryLabels.ts +1 -4
- package/src/i18n/accountRoleLabels.ts +1 -4
- package/src/i18n/index.ts +25 -7
- package/src/i18n/locales/ar-SA.ts +375 -366
- package/src/i18n/locales/ca-ES.ts +382 -366
- package/src/i18n/locales/de-DE.ts +387 -366
- package/src/i18n/locales/en-US.ts +2034 -1994
- package/src/i18n/locales/es-ES.ts +2043 -1994
- package/src/i18n/locales/fr-FR.ts +387 -366
- package/src/i18n/locales/it-IT.ts +380 -366
- package/src/i18n/locales/ja-JP.ts +380 -366
- package/src/i18n/locales/ko-KR.ts +377 -366
- package/src/i18n/locales/pt-PT.ts +383 -366
- package/src/i18n/locales/zh-CN.ts +373 -366
- package/src/i18n/trustTierLabels.ts +1 -4
- package/src/index.ts +371 -349
- package/src/inference/OxyInferenceClient.ts +784 -780
- package/src/models/interfaces.ts +30 -27
- package/src/server/OxyServer.ts +227 -68
- package/src/server/agentAccount.ts +8 -2
- package/src/server/aliaMachineCredential.ts +57 -16
- package/src/server/auth.ts +9 -5
- package/src/server/capabilityTicket.ts +30 -21
- package/src/server/index.ts +29 -6
- package/src/server/liveCapabilityTicket.ts +31 -9
- package/src/server/middleware.ts +212 -121
- package/src/server/namespaces.ts +247 -94
- package/src/server/rateLimit.ts +2 -4
- package/src/server/requesterAssertion.ts +93 -54
- package/src/server/serviceTokenLifetime.ts +9 -4
- package/src/server/traffic.ts +117 -23
- package/src/server/trafficNodeHttp.ts +61 -14
- package/src/server/userInvalidation.ts +1 -4
- package/src/server/workloadIdentity.client.ts +12 -1
- package/src/server/workloadIdentity.ts +52 -5
- package/src/session/SessionClient.ts +170 -49
- package/src/session/accountDelegationAccess.ts +24 -31
- package/src/session/accountDialogController.ts +59 -14
- package/src/session/accountSwitchTargets.ts +10 -15
- package/src/session/authStateStore.ts +121 -81
- package/src/session/deviceDirectory.ts +1 -3
- package/src/session/identityPin.ts +10 -11
- package/src/session/identitySession.ts +3 -1
- package/src/session/index.ts +87 -82
- package/src/session/refresh.ts +138 -44
- package/src/session/sessionClientHost.ts +1 -3
- package/src/session/sharedDeviceCredential.ts +35 -14
- package/src/session/socketLoader.ts +9 -2
- package/src/shared/utils/errorUtils.ts +5 -2
- package/src/shared/utils/networkUtils.ts +5 -8
- package/src/shared/utils/themeUtils.ts +2 -6
- package/src/types/buffer.d.ts +66 -21
- package/src/types/expo-secure-store.d.ts +5 -1
- package/src/utils/accountCacheSweep.ts +4 -5
- package/src/utils/accountUtils.ts +107 -108
- package/src/utils/apiUtils.ts +3 -3
- package/src/utils/asyncUtils.ts +2 -2
- package/src/utils/authHelpers.ts +10 -5
- package/src/utils/avatarUtils.ts +11 -6
- package/src/utils/cache.ts +9 -10
- package/src/utils/cacheKey.ts +5 -1
- package/src/utils/coldBoot.ts +1 -3
- package/src/utils/commonsDelivery.ts +1 -4
- package/src/utils/deviceManager.ts +15 -14
- package/src/utils/errorUtils.ts +68 -86
- package/src/utils/languageUtils.ts +407 -58
- package/src/utils/oauthPkce.ts +1 -4
- package/src/utils/platform.ts +0 -1
- package/src/utils/profileLinks.ts +2 -5
- package/src/utils/requestUtils.ts +18 -14
- package/src/utils/sessionUtils.ts +34 -33
- package/src/utils/userHandle.ts +3 -1
- package/src/utils/userIdentity.ts +1 -1
- package/src/utils/validationUtils.ts +11 -9
|
@@ -138,7 +138,10 @@ export function readLocalDeviceCredential(
|
|
|
138
138
|
*/
|
|
139
139
|
export function normalizeSharedDeviceSessionRead(raw: unknown): SharedDeviceCredentialRead {
|
|
140
140
|
if (!raw || typeof raw !== 'object') {
|
|
141
|
-
return {
|
|
141
|
+
return {
|
|
142
|
+
state: 'unavailable',
|
|
143
|
+
cause: new Error('shared device session bridge returned a non-object'),
|
|
144
|
+
};
|
|
142
145
|
}
|
|
143
146
|
const payload = raw as Record<string, unknown>;
|
|
144
147
|
if (payload.status === 'absent') {
|
|
@@ -160,16 +163,23 @@ export function normalizeSharedDeviceSessionRead(raw: unknown): SharedDeviceCred
|
|
|
160
163
|
// overwrite whatever is really in there.
|
|
161
164
|
return {
|
|
162
165
|
state: 'unavailable',
|
|
163
|
-
cause: new Error(
|
|
166
|
+
cause: new Error(
|
|
167
|
+
'shared device session bridge reported `present` with an incomplete credential',
|
|
168
|
+
),
|
|
164
169
|
};
|
|
165
170
|
}
|
|
166
171
|
if (payload.status === 'unavailable') {
|
|
167
172
|
const reason = typeof payload.reason === 'string' ? payload.reason : 'unknown';
|
|
168
|
-
return {
|
|
173
|
+
return {
|
|
174
|
+
state: 'unavailable',
|
|
175
|
+
cause: new Error(`shared device session slot unavailable: ${reason}`),
|
|
176
|
+
};
|
|
169
177
|
}
|
|
170
178
|
return {
|
|
171
179
|
state: 'unavailable',
|
|
172
|
-
cause: new Error(
|
|
180
|
+
cause: new Error(
|
|
181
|
+
`shared device session bridge returned an unrecognised status: ${String(payload.status)}`,
|
|
182
|
+
),
|
|
173
183
|
};
|
|
174
184
|
}
|
|
175
185
|
|
|
@@ -318,15 +328,23 @@ export function createSharedMirroringAuthStateStore(deps: {
|
|
|
318
328
|
...local,
|
|
319
329
|
load: () => local.load(),
|
|
320
330
|
clear: () => local.clear(),
|
|
321
|
-
...(local.saveIfCurrent
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
331
|
+
...(local.saveIfCurrent
|
|
332
|
+
? {
|
|
333
|
+
saveIfCurrent: async (state: PersistedAuthState, guard: AuthStateWriteGuard) => {
|
|
334
|
+
const committed = await local.saveIfCurrent?.(state, guard);
|
|
335
|
+
if (!committed) return false;
|
|
336
|
+
const credential = readLocalDeviceCredential(state);
|
|
337
|
+
if (credential && guard.isCurrent()) {
|
|
338
|
+
try {
|
|
339
|
+
await publishProvenDeviceCredential({ shared, credential });
|
|
340
|
+
} catch {
|
|
341
|
+
/* Local durability already verified. */
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return true;
|
|
345
|
+
},
|
|
346
|
+
}
|
|
347
|
+
: {}),
|
|
330
348
|
save: async (state) => {
|
|
331
349
|
// The durable local write is the contract this store owes its caller —
|
|
332
350
|
// run it first and report ITS result, unchanged. The mirror is additive.
|
|
@@ -344,7 +362,10 @@ export function createSharedMirroringAuthStateStore(deps: {
|
|
|
344
362
|
}
|
|
345
363
|
try {
|
|
346
364
|
const outcome = await publishProvenDeviceCredential({ shared, credential });
|
|
347
|
-
if (
|
|
365
|
+
if (
|
|
366
|
+
outcome.status === 'published' ||
|
|
367
|
+
(outcome.status === 'skipped' && outcome.reason === 'already-current')
|
|
368
|
+
) {
|
|
348
369
|
mirrored = credential;
|
|
349
370
|
}
|
|
350
371
|
} catch (error) {
|
|
@@ -20,11 +20,18 @@ export async function getSocketIO(): Promise<SocketIOFactory | null> {
|
|
|
20
20
|
if (loadAttempted) return null;
|
|
21
21
|
loadAttempted = true;
|
|
22
22
|
try {
|
|
23
|
-
const mod = (await import('socket.io-client')) as {
|
|
23
|
+
const mod = (await import('socket.io-client')) as {
|
|
24
|
+
io?: SocketIOFactory;
|
|
25
|
+
default?: SocketIOFactory;
|
|
26
|
+
};
|
|
24
27
|
cachedFactory = mod.io ?? mod.default ?? null;
|
|
25
28
|
return cachedFactory;
|
|
26
29
|
} catch (error) {
|
|
27
|
-
logger.warn(
|
|
30
|
+
logger.warn(
|
|
31
|
+
'[SessionClient] socket.io-client import failed; realtime session sync disabled',
|
|
32
|
+
{ component: 'SessionClient' },
|
|
33
|
+
error,
|
|
34
|
+
);
|
|
28
35
|
return null;
|
|
29
36
|
}
|
|
30
37
|
}
|
|
@@ -160,14 +160,17 @@ export const isNetworkError = (error: unknown): boolean => {
|
|
|
160
160
|
|
|
161
161
|
// Check for common network error indicators
|
|
162
162
|
if (err.name === 'NetworkError') return true;
|
|
163
|
-
if (err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED' || err.code === 'ETIMEDOUT')
|
|
163
|
+
if (err.code === 'ENOTFOUND' || err.code === 'ECONNREFUSED' || err.code === 'ETIMEDOUT')
|
|
164
|
+
return true;
|
|
164
165
|
|
|
165
166
|
// Axios-style: has request but no response
|
|
166
167
|
if (err.request && !err.response) return true;
|
|
167
168
|
|
|
168
169
|
// Message-based detection
|
|
169
170
|
const message = getErrorMessage(error, '').toLowerCase();
|
|
170
|
-
return
|
|
171
|
+
return (
|
|
172
|
+
message.includes('network') || message.includes('connection') || message.includes('timeout')
|
|
173
|
+
);
|
|
171
174
|
};
|
|
172
175
|
|
|
173
176
|
/**
|
|
@@ -63,7 +63,7 @@ export const DEFAULT_CIRCUIT_BREAKER_CONFIG: Required<CircuitBreakerConfig> = {
|
|
|
63
63
|
* ```
|
|
64
64
|
*/
|
|
65
65
|
export const createCircuitBreakerState = (
|
|
66
|
-
config: CircuitBreakerConfig = {}
|
|
66
|
+
config: CircuitBreakerConfig = {},
|
|
67
67
|
): CircuitBreakerState => {
|
|
68
68
|
const { baseInterval, maxInterval, maxFailures } = {
|
|
69
69
|
...DEFAULT_CIRCUIT_BREAKER_CONFIG,
|
|
@@ -97,10 +97,7 @@ export const calculateBackoffInterval = (state: CircuitBreakerState): number =>
|
|
|
97
97
|
|
|
98
98
|
if (consecutiveFailures === 0) return baseInterval;
|
|
99
99
|
|
|
100
|
-
const backoffMultiplier = Math.min(
|
|
101
|
-
2 ** (consecutiveFailures - 1),
|
|
102
|
-
maxInterval / baseInterval
|
|
103
|
-
);
|
|
100
|
+
const backoffMultiplier = Math.min(2 ** (consecutiveFailures - 1), maxInterval / baseInterval);
|
|
104
101
|
|
|
105
102
|
return Math.min(baseInterval * backoffMultiplier, maxInterval);
|
|
106
103
|
};
|
|
@@ -168,7 +165,7 @@ export const recordSuccess = (state: CircuitBreakerState): CircuitBreakerState =
|
|
|
168
165
|
*/
|
|
169
166
|
export const shouldAllowRequest = (
|
|
170
167
|
state: CircuitBreakerState,
|
|
171
|
-
recoveryTimeout: number = DEFAULT_CIRCUIT_BREAKER_CONFIG.recoveryTimeout
|
|
168
|
+
recoveryTimeout: number = DEFAULT_CIRCUIT_BREAKER_CONFIG.recoveryTimeout,
|
|
172
169
|
): boolean => {
|
|
173
170
|
if (!state.isOpen) return true;
|
|
174
171
|
|
|
@@ -190,7 +187,7 @@ export const shouldAllowRequest = (
|
|
|
190
187
|
* ```
|
|
191
188
|
*/
|
|
192
189
|
export const delay = (ms: number): Promise<void> =>
|
|
193
|
-
new Promise(resolve => setTimeout(resolve, ms));
|
|
190
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
194
191
|
|
|
195
192
|
/**
|
|
196
193
|
* Executes a function with exponential backoff retry.
|
|
@@ -216,7 +213,7 @@ export const withRetry = async <T>(
|
|
|
216
213
|
maxDelay?: number;
|
|
217
214
|
shouldRetry?: (error: unknown) => boolean;
|
|
218
215
|
onRetry?: (error: unknown, attempt: number) => void;
|
|
219
|
-
} = {}
|
|
216
|
+
} = {},
|
|
220
217
|
): Promise<T> => {
|
|
221
218
|
const {
|
|
222
219
|
maxRetries = 3,
|
|
@@ -51,10 +51,7 @@ export const normalizeTheme = (theme?: string | null): ThemeValue =>
|
|
|
51
51
|
* normalizeColorScheme(undefined, undefined); // 'light'
|
|
52
52
|
* ```
|
|
53
53
|
*/
|
|
54
|
-
export const normalizeColorScheme = (
|
|
55
|
-
colorScheme?: string | null,
|
|
56
|
-
theme?: string
|
|
57
|
-
): ThemeValue => {
|
|
54
|
+
export const normalizeColorScheme = (colorScheme?: string | null, theme?: string): ThemeValue => {
|
|
58
55
|
if (colorScheme === 'light' || colorScheme === 'dark') {
|
|
59
56
|
return colorScheme;
|
|
60
57
|
}
|
|
@@ -111,5 +108,4 @@ export const systemPrefersDarkMode = (): boolean => {
|
|
|
111
108
|
* const theme = getSystemColorScheme();
|
|
112
109
|
* ```
|
|
113
110
|
*/
|
|
114
|
-
export const getSystemColorScheme = (): ThemeValue =>
|
|
115
|
-
systemPrefersDarkMode() ? 'dark' : 'light';
|
|
111
|
+
export const getSystemColorScheme = (): ThemeValue => (systemPrefersDarkMode() ? 'dark' : 'light');
|
package/src/types/buffer.d.ts
CHANGED
|
@@ -6,36 +6,54 @@ declare module 'buffer' {
|
|
|
6
6
|
constructor(arrayBuffer: ArrayBuffer);
|
|
7
7
|
constructor(array: ReadonlyArray<number>);
|
|
8
8
|
constructor(buffer: Buffer);
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
static from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer;
|
|
11
11
|
static from(data: Uint8Array | ReadonlyArray<number>): Buffer;
|
|
12
12
|
static from(str: string, encoding?: BufferEncoding): Buffer;
|
|
13
|
-
static from(
|
|
14
|
-
|
|
13
|
+
static from(
|
|
14
|
+
object: { valueOf(): string | object } | { [Symbol.toPrimitive](hint: 'string'): string },
|
|
15
|
+
offsetOrEncoding?: number | string,
|
|
16
|
+
length?: number,
|
|
17
|
+
): Buffer;
|
|
18
|
+
|
|
15
19
|
static alloc(size: number, fill?: string | Buffer | number, encoding?: BufferEncoding): Buffer;
|
|
16
20
|
static allocUnsafe(size: number): Buffer;
|
|
17
21
|
static allocUnsafeSlow(size: number): Buffer;
|
|
18
|
-
|
|
22
|
+
|
|
19
23
|
static isBuffer(obj: unknown): obj is Buffer;
|
|
20
24
|
static isEncoding(encoding: string): encoding is BufferEncoding;
|
|
21
|
-
static byteLength(
|
|
25
|
+
static byteLength(
|
|
26
|
+
string: string | Buffer | ArrayBuffer | SharedArrayBuffer,
|
|
27
|
+
encoding?: BufferEncoding,
|
|
28
|
+
): number;
|
|
22
29
|
static concat(list: ReadonlyArray<Uint8Array>, totalLength?: number): Buffer;
|
|
23
30
|
static compare(buf1: Uint8Array, buf2: Uint8Array): number;
|
|
24
|
-
|
|
31
|
+
|
|
25
32
|
write(string: string, encoding?: BufferEncoding): number;
|
|
26
33
|
write(string: string, offset: number, encoding?: BufferEncoding): number;
|
|
27
34
|
write(string: string, offset: number, length: number, encoding?: BufferEncoding): number;
|
|
28
|
-
|
|
35
|
+
|
|
29
36
|
toString(encoding?: BufferEncoding, start?: number, end?: number): string;
|
|
30
37
|
toJSON(): { type: 'Buffer'; data: number[] };
|
|
31
|
-
|
|
38
|
+
|
|
32
39
|
equals(otherBuffer: Uint8Array): boolean;
|
|
33
|
-
compare(
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
compare(
|
|
41
|
+
target: Uint8Array,
|
|
42
|
+
targetStart?: number,
|
|
43
|
+
targetEnd?: number,
|
|
44
|
+
sourceStart?: number,
|
|
45
|
+
sourceEnd?: number,
|
|
46
|
+
): number;
|
|
47
|
+
copy(
|
|
48
|
+
target: Uint8Array,
|
|
49
|
+
targetStart?: number,
|
|
50
|
+
sourceStart?: number,
|
|
51
|
+
sourceEnd?: number,
|
|
52
|
+
): number;
|
|
53
|
+
|
|
36
54
|
slice(start?: number, end?: number): Buffer;
|
|
37
55
|
subarray(start?: number, end?: number): Buffer;
|
|
38
|
-
|
|
56
|
+
|
|
39
57
|
readBigInt64BE(offset?: number): bigint;
|
|
40
58
|
readBigInt64LE(offset?: number): bigint;
|
|
41
59
|
readBigUInt64BE(offset?: number): bigint;
|
|
@@ -58,11 +76,11 @@ declare module 'buffer' {
|
|
|
58
76
|
readUInt32LE(offset?: number): number;
|
|
59
77
|
readUIntBE(offset: number, byteLength: number): number;
|
|
60
78
|
readUIntLE(offset: number, byteLength: number): number;
|
|
61
|
-
|
|
79
|
+
|
|
62
80
|
swap16(): Buffer;
|
|
63
81
|
swap32(): Buffer;
|
|
64
82
|
swap64(): Buffer;
|
|
65
|
-
|
|
83
|
+
|
|
66
84
|
writeBigInt64BE(value: bigint, offset?: number): number;
|
|
67
85
|
writeBigInt64LE(value: bigint, offset?: number): number;
|
|
68
86
|
writeBigUInt64BE(value: bigint, offset?: number): number;
|
|
@@ -85,13 +103,40 @@ declare module 'buffer' {
|
|
|
85
103
|
writeUInt32LE(value: number, offset?: number): number;
|
|
86
104
|
writeUIntBE(value: number, offset: number, byteLength: number): number;
|
|
87
105
|
writeUIntLE(value: number, offset: number, byteLength: number): number;
|
|
88
|
-
|
|
89
|
-
fill(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
|
|
107
|
+
fill(
|
|
108
|
+
value: string | Uint8Array | number,
|
|
109
|
+
offset?: number,
|
|
110
|
+
end?: number,
|
|
111
|
+
encoding?: BufferEncoding,
|
|
112
|
+
): this;
|
|
113
|
+
indexOf(
|
|
114
|
+
value: string | number | Uint8Array,
|
|
115
|
+
byteOffset?: number,
|
|
116
|
+
encoding?: BufferEncoding,
|
|
117
|
+
): number;
|
|
118
|
+
lastIndexOf(
|
|
119
|
+
value: string | number | Uint8Array,
|
|
120
|
+
byteOffset?: number,
|
|
121
|
+
encoding?: BufferEncoding,
|
|
122
|
+
): number;
|
|
123
|
+
includes(
|
|
124
|
+
value: string | number | Buffer,
|
|
125
|
+
byteOffset?: number,
|
|
126
|
+
encoding?: BufferEncoding,
|
|
127
|
+
): boolean;
|
|
93
128
|
}
|
|
94
129
|
|
|
95
|
-
type BufferEncoding =
|
|
130
|
+
type BufferEncoding =
|
|
131
|
+
| 'ascii'
|
|
132
|
+
| 'utf8'
|
|
133
|
+
| 'utf-8'
|
|
134
|
+
| 'utf16le'
|
|
135
|
+
| 'ucs2'
|
|
136
|
+
| 'ucs-2'
|
|
137
|
+
| 'base64'
|
|
138
|
+
| 'base64url'
|
|
139
|
+
| 'latin1'
|
|
140
|
+
| 'binary'
|
|
141
|
+
| 'hex';
|
|
96
142
|
}
|
|
97
|
-
|
|
@@ -12,6 +12,10 @@ declare module 'expo-secure-store' {
|
|
|
12
12
|
export const AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: number;
|
|
13
13
|
|
|
14
14
|
export function getItemAsync(key: string, options?: SecureStoreOptions): Promise<string | null>;
|
|
15
|
-
export function setItemAsync(
|
|
15
|
+
export function setItemAsync(
|
|
16
|
+
key: string,
|
|
17
|
+
value: string,
|
|
18
|
+
options?: SecureStoreOptions,
|
|
19
|
+
): Promise<void>;
|
|
16
20
|
export function deleteItemAsync(key: string, options?: SecureStoreOptions): Promise<void>;
|
|
17
21
|
}
|
|
@@ -78,12 +78,11 @@ export function oxyAccountDetailCacheKey(accountId: string): string {
|
|
|
78
78
|
* account in it (create, archive, ownership transfer) has no
|
|
79
79
|
* detail row to name, and clears only the lists.
|
|
80
80
|
*/
|
|
81
|
-
export function evictOxyAccountForestCache(
|
|
82
|
-
http: OxyAccountCacheEvictor,
|
|
83
|
-
accountId?: string,
|
|
84
|
-
): void {
|
|
81
|
+
export function evictOxyAccountForestCache(http: OxyAccountCacheEvictor, accountId?: string): void {
|
|
85
82
|
http.invalidateCache({
|
|
86
|
-
keys: accountId
|
|
83
|
+
keys: accountId
|
|
84
|
+
? [OXY_ACCOUNT_LIST_CACHE_KEY, oxyAccountDetailCacheKey(accountId)]
|
|
85
|
+
: [OXY_ACCOUNT_LIST_CACHE_KEY],
|
|
87
86
|
prefixes: [OXY_ACCOUNT_LIST_CACHE_QUERY_PREFIX],
|
|
88
87
|
});
|
|
89
88
|
}
|
|
@@ -7,39 +7,41 @@ import { translate } from '../i18n';
|
|
|
7
7
|
import { getNormalizedUserHandle } from './userHandle';
|
|
8
8
|
|
|
9
9
|
export interface QuickAccount {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
10
|
+
sessionId: string;
|
|
11
|
+
userId?: string;
|
|
12
|
+
username: string;
|
|
13
|
+
displayName: string;
|
|
14
|
+
avatar?: string;
|
|
15
|
+
avatarUrl?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Device-local account slot index, 0..N-1 (Google-style multi-account).
|
|
18
|
+
* Optional so that pre-multi-account QuickAccounts (sessionId-only) remain
|
|
19
|
+
* valid; the device session set populates it where available.
|
|
20
|
+
*/
|
|
21
|
+
authuser?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Account's preferred Bloom color preset (e.g. `"blue"`, `"oxy"`). Drives
|
|
24
|
+
* per-account theming in the account chooser. `null` / `undefined` means
|
|
25
|
+
* the account has no preference and the base theme should be used.
|
|
26
|
+
*/
|
|
27
|
+
color?: string | null;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/** Minimal user shape accepted by display-name helpers. Avoids importing the full User type. */
|
|
31
31
|
export interface DisplayNameUserShape {
|
|
32
|
-
|
|
32
|
+
name?:
|
|
33
|
+
| string
|
|
34
|
+
| {
|
|
33
35
|
displayName?: string;
|
|
34
36
|
first?: string;
|
|
35
37
|
last?: string;
|
|
36
38
|
full?: string;
|
|
37
39
|
[key: string]: unknown;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
};
|
|
41
|
+
/** Pre-normalized account-row display name, not the API User DTO field. */
|
|
42
|
+
displayName?: string;
|
|
43
|
+
username?: string;
|
|
44
|
+
publicKey?: string;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
/**
|
|
@@ -47,9 +49,9 @@ export interface DisplayNameUserShape {
|
|
|
47
49
|
* Falls back to the raw key if it's too short to truncate.
|
|
48
50
|
*/
|
|
49
51
|
export const formatPublicKeyHandle = (publicKey: string): string => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
const cleaned = publicKey.startsWith('0x') ? publicKey.slice(2) : publicKey;
|
|
53
|
+
if (cleaned.length <= 8) return `0x${cleaned}`;
|
|
54
|
+
return `0x${cleaned.slice(0, 8)}…`;
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
/**
|
|
@@ -68,37 +70,37 @@ export const formatPublicKeyHandle = (publicKey: string): string => {
|
|
|
68
70
|
* caller does not pass a locale, the default English translation is used.
|
|
69
71
|
*/
|
|
70
72
|
export const getAccountDisplayName = (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
user: DisplayNameUserShape | null | undefined,
|
|
74
|
+
locale?: string,
|
|
73
75
|
): string => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const { name, displayName, username, publicKey } = user;
|
|
77
|
-
|
|
78
|
-
if (name && typeof name === 'object') {
|
|
79
|
-
if (typeof name.displayName === 'string' && name.displayName.trim()) {
|
|
80
|
-
return name.displayName.trim();
|
|
81
|
-
}
|
|
82
|
-
if (typeof name.full === 'string' && name.full.trim()) return name.full.trim();
|
|
83
|
-
const first = typeof name.first === 'string' ? name.first.trim() : '';
|
|
84
|
-
const last = typeof name.last === 'string' ? name.last.trim() : '';
|
|
85
|
-
const composed = [first, last].filter(Boolean).join(' ').trim();
|
|
86
|
-
if (composed) return composed;
|
|
87
|
-
} else if (typeof name === 'string' && name.trim()) {
|
|
88
|
-
return name.trim();
|
|
89
|
-
}
|
|
76
|
+
if (!user) return translate(locale, 'common.unnamed');
|
|
90
77
|
|
|
91
|
-
|
|
78
|
+
const { name, displayName, username, publicKey } = user;
|
|
92
79
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return translate(locale, 'common.accountFallback', {
|
|
97
|
-
handle: formatPublicKeyHandle(publicKey),
|
|
98
|
-
});
|
|
80
|
+
if (name && typeof name === 'object') {
|
|
81
|
+
if (typeof name.displayName === 'string' && name.displayName.trim()) {
|
|
82
|
+
return name.displayName.trim();
|
|
99
83
|
}
|
|
100
|
-
|
|
101
|
-
|
|
84
|
+
if (typeof name.full === 'string' && name.full.trim()) return name.full.trim();
|
|
85
|
+
const first = typeof name.first === 'string' ? name.first.trim() : '';
|
|
86
|
+
const last = typeof name.last === 'string' ? name.last.trim() : '';
|
|
87
|
+
const composed = [first, last].filter(Boolean).join(' ').trim();
|
|
88
|
+
if (composed) return composed;
|
|
89
|
+
} else if (typeof name === 'string' && name.trim()) {
|
|
90
|
+
return name.trim();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (typeof displayName === 'string' && displayName.trim()) return displayName.trim();
|
|
94
|
+
|
|
95
|
+
if (typeof username === 'string' && username.trim()) return username.trim();
|
|
96
|
+
|
|
97
|
+
if (typeof publicKey === 'string' && publicKey.length > 0) {
|
|
98
|
+
return translate(locale, 'common.accountFallback', {
|
|
99
|
+
handle: formatPublicKeyHandle(publicKey),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return translate(locale, 'common.unnamed');
|
|
102
104
|
};
|
|
103
105
|
|
|
104
106
|
/**
|
|
@@ -109,29 +111,29 @@ export const getAccountDisplayName = (
|
|
|
109
111
|
* available — callers can decide whether to hide the line entirely.
|
|
110
112
|
*/
|
|
111
113
|
export const getAccountFallbackHandle = (
|
|
112
|
-
|
|
114
|
+
user: DisplayNameUserShape | null | undefined,
|
|
113
115
|
): string | undefined => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
if (!user) return undefined;
|
|
117
|
+
if (typeof user.username === 'string' && user.username.trim()) return user.username.trim();
|
|
118
|
+
if (typeof user.publicKey === 'string' && user.publicKey.length > 0) {
|
|
119
|
+
return formatPublicKeyHandle(user.publicKey);
|
|
120
|
+
}
|
|
121
|
+
return undefined;
|
|
120
122
|
};
|
|
121
123
|
|
|
122
124
|
/**
|
|
123
125
|
* Build an ordered array of QuickAccounts from a map and order list.
|
|
124
126
|
*/
|
|
125
127
|
export const buildAccountsArray = (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
+
accounts: Record<string, QuickAccount>,
|
|
129
|
+
order: string[],
|
|
128
130
|
): QuickAccount[] => {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
const result: QuickAccount[] = [];
|
|
132
|
+
for (const id of order) {
|
|
133
|
+
const account = accounts[id];
|
|
134
|
+
if (account) result.push(account);
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
135
137
|
};
|
|
136
138
|
|
|
137
139
|
/**
|
|
@@ -143,45 +145,42 @@ export const buildAccountsArray = (
|
|
|
143
145
|
* @param getFileDownloadUrl - Function to generate avatar download URL from file ID
|
|
144
146
|
*/
|
|
145
147
|
export const createQuickAccount = (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
sessionId: string,
|
|
149
|
+
userData: {
|
|
150
|
+
name?: string | { displayName?: string; full?: string; first?: string; last?: string };
|
|
151
|
+
username?: string;
|
|
152
|
+
publicKey?: string;
|
|
153
|
+
id?: string;
|
|
154
|
+
_id?: { toString(): string } | string;
|
|
155
|
+
avatar?: string | null;
|
|
156
|
+
},
|
|
157
|
+
existingAccount?: QuickAccount,
|
|
158
|
+
getFileDownloadUrl?: (fileId: string, variant: string) => string,
|
|
157
159
|
): QuickAccount => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
avatar,
|
|
183
|
-
avatarUrl,
|
|
184
|
-
};
|
|
160
|
+
const nameObj = userData.name && typeof userData.name === 'object' ? userData.name : undefined;
|
|
161
|
+
const apiDisplayName = typeof nameObj?.displayName === 'string' ? nameObj.displayName.trim() : '';
|
|
162
|
+
const displayName =
|
|
163
|
+
apiDisplayName || getNormalizedUserHandle(userData) || getAccountDisplayName(null);
|
|
164
|
+
const userId =
|
|
165
|
+
userData.id || (typeof userData._id === 'string' ? userData._id : userData._id?.toString());
|
|
166
|
+
|
|
167
|
+
// Preserve existing avatarUrl if avatar hasn't changed (prevents image reload)
|
|
168
|
+
let avatarUrl: string | undefined;
|
|
169
|
+
const avatar = userData.avatar ?? undefined;
|
|
170
|
+
if (existingAccount && existingAccount.avatar === avatar && existingAccount.avatarUrl) {
|
|
171
|
+
avatarUrl = existingAccount.avatarUrl;
|
|
172
|
+
} else if (avatar && getFileDownloadUrl) {
|
|
173
|
+
avatarUrl = getFileDownloadUrl(avatar, 'thumb');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
sessionId,
|
|
178
|
+
userId,
|
|
179
|
+
username: userData.username || '',
|
|
180
|
+
displayName,
|
|
181
|
+
avatar,
|
|
182
|
+
avatarUrl,
|
|
183
|
+
};
|
|
185
184
|
};
|
|
186
185
|
|
|
187
186
|
/**
|
|
@@ -190,5 +189,5 @@ export const createQuickAccount = (
|
|
|
190
189
|
* drive per-account theming without duplicating the nullish-handling.
|
|
191
190
|
*/
|
|
192
191
|
export const getAccountColor = (account: QuickAccount): string | null => {
|
|
193
|
-
|
|
192
|
+
return account.color ?? null;
|
|
194
193
|
};
|
package/src/utils/apiUtils.ts
CHANGED
|
@@ -53,10 +53,10 @@ export function buildSearchParams<T extends object>(params: T): URLSearchParams
|
|
|
53
53
|
*/
|
|
54
54
|
export function buildUrl<T extends object>(baseUrl: string, params?: T): string {
|
|
55
55
|
if (!params) return baseUrl;
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
const searchParams = buildSearchParams(params);
|
|
58
58
|
const queryString = searchParams.toString();
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
return queryString ? `${baseUrl}?${queryString}` : baseUrl;
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -134,4 +134,4 @@ export function safeJsonParse<T>(data: unknown, fallback: T): T {
|
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
return data as T;
|
|
137
|
-
}
|
|
137
|
+
}
|