@dynamic-labs-sdk/client 0.17.1 → 0.17.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/dist/{InvalidParamError-3-1rSNtf.esm.js → InvalidParamError-Dc2HgrDm.esm.js} +12 -132
- package/dist/InvalidParamError-Dc2HgrDm.esm.js.map +1 -0
- package/dist/{InvalidParamError-zIxsXkiH.cjs.js → InvalidParamError-z_h8HyKL.cjs.js} +12 -189
- package/dist/InvalidParamError-z_h8HyKL.cjs.js.map +1 -0
- package/dist/{NotWaasWalletAccountError-DVIcEgHJ.esm.js → NotWaasWalletAccountError-B4xS_9HL.esm.js} +3 -3
- package/dist/{NotWaasWalletAccountError-DVIcEgHJ.esm.js.map → NotWaasWalletAccountError-B4xS_9HL.esm.js.map} +1 -1
- package/dist/{NotWaasWalletAccountError-B_Wl6sTh.cjs.js → NotWaasWalletAccountError-DsYkBFyx.cjs.js} +3 -3
- package/dist/{NotWaasWalletAccountError-B_Wl6sTh.cjs.js.map → NotWaasWalletAccountError-DsYkBFyx.cjs.js.map} +1 -1
- package/dist/client/core/createCore/createCore.d.ts.map +1 -1
- package/dist/client/core/types/DynamicCore.d.ts +10 -0
- package/dist/client/core/types/DynamicCore.d.ts.map +1 -1
- package/dist/client/core/types/DynamicCoreConfig.d.ts +1 -1
- package/dist/client/core/types/DynamicCoreConfig.d.ts.map +1 -1
- package/dist/core.cjs.js +24 -11
- package/dist/core.cjs.js.map +1 -1
- package/dist/core.esm.js +16 -6
- package/dist/core.esm.js.map +1 -1
- package/dist/exports/core.d.ts +10 -0
- package/dist/exports/core.d.ts.map +1 -1
- package/dist/{getNetworkProviderFromNetworkId-BJR1GciB.cjs.js → getNetworkProviderFromNetworkId-CllkC31J.cjs.js} +432 -69
- package/dist/getNetworkProviderFromNetworkId-CllkC31J.cjs.js.map +1 -0
- package/dist/{getNetworkProviderFromNetworkId-C7jp--76.esm.js → getNetworkProviderFromNetworkId-DO13PEvc.esm.js} +374 -68
- package/dist/getNetworkProviderFromNetworkId-DO13PEvc.esm.js.map +1 -0
- package/dist/{getSignedSessionId-Alfz9eul.esm.js → getSignedSessionId-BM05CMyp.esm.js} +7 -17
- package/dist/getSignedSessionId-BM05CMyp.esm.js.map +1 -0
- package/dist/{getSignedSessionId-DVpOgsL9.cjs.js → getSignedSessionId-DqmEBqI9.cjs.js} +7 -17
- package/dist/getSignedSessionId-DqmEBqI9.cjs.js.map +1 -0
- package/dist/{getVerifiedCredentialForWalletAccount-DLtDL1Gl.esm.js → getVerifiedCredentialForWalletAccount-57Omjjyi.esm.js} +2 -2
- package/dist/{getVerifiedCredentialForWalletAccount-DLtDL1Gl.esm.js.map → getVerifiedCredentialForWalletAccount-57Omjjyi.esm.js.map} +1 -1
- package/dist/{getVerifiedCredentialForWalletAccount-D25Vyxub.cjs.js → getVerifiedCredentialForWalletAccount-BK4IBZlH.cjs.js} +3 -2
- package/dist/{getVerifiedCredentialForWalletAccount-D25Vyxub.cjs.js.map → getVerifiedCredentialForWalletAccount-BK4IBZlH.cjs.js.map} +1 -1
- package/dist/index.cjs.js +107 -27
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +99 -19
- package/dist/index.esm.js.map +1 -1
- package/dist/{isMfaRequiredForAction-CYYU8V1B.cjs.js → isMfaRequiredForAction-BS239c-s.cjs.js} +2 -2
- package/dist/{isMfaRequiredForAction-CYYU8V1B.cjs.js.map → isMfaRequiredForAction-BS239c-s.cjs.js.map} +1 -1
- package/dist/{isMfaRequiredForAction-CPTFDCJp.esm.js → isMfaRequiredForAction-CXRaUbFE.esm.js} +2 -2
- package/dist/{isMfaRequiredForAction-CPTFDCJp.esm.js.map → isMfaRequiredForAction-CXRaUbFE.esm.js.map} +1 -1
- package/dist/modules/auth/logout/logout.d.ts.map +1 -1
- package/dist/modules/deviceRegistration/getDeviceSigner/getDeviceSigner.d.ts.map +1 -1
- package/dist/modules/initializeClient/initializeClient.d.ts.map +1 -1
- package/dist/modules/keychainMigration/migrateSessionKeyToKeychain/KeyMigrationError.d.ts +5 -0
- package/dist/modules/keychainMigration/migrateSessionKeyToKeychain/KeyMigrationError.d.ts.map +1 -0
- package/dist/modules/keychainMigration/migrateSessionKeyToKeychain/migrateSessionKeyToKeychain.d.ts +24 -0
- package/dist/modules/keychainMigration/migrateSessionKeyToKeychain/migrateSessionKeyToKeychain.d.ts.map +1 -0
- package/dist/modules/sessionKeys/generateNonceSignature/generateNonceSignature.d.ts.map +1 -1
- package/dist/modules/sessionKeys/generateSessionKeys/generateSessionKeys.d.ts.map +1 -1
- package/dist/modules/sessionKeys/generateSessionSignature/generateSessionSignature.d.ts.map +1 -1
- package/dist/modules/sessionKeys/getSessionKeys/getSessionKeys.d.ts.map +1 -1
- package/dist/modules/sessionKeys/sessionKeys.types.d.ts +0 -2
- package/dist/modules/sessionKeys/sessionKeys.types.d.ts.map +1 -1
- package/dist/services/keychain/createIndexedDBKeychainService/KeyNotFoundError.d.ts +5 -0
- package/dist/services/keychain/createIndexedDBKeychainService/KeyNotFoundError.d.ts.map +1 -0
- package/dist/services/keychain/createIndexedDBKeychainService/createIndexedDBKeychainService.d.ts +7 -0
- package/dist/services/keychain/createIndexedDBKeychainService/createIndexedDBKeychainService.d.ts.map +1 -0
- package/dist/services/keychain/createIndexedDBKeychainService/createIndexedDBKeychainService.types.d.ts +6 -0
- package/dist/services/keychain/createIndexedDBKeychainService/createIndexedDBKeychainService.types.d.ts.map +1 -0
- package/dist/services/keychain/createIndexedDBKeychainService/index.d.ts +2 -0
- package/dist/services/keychain/createIndexedDBKeychainService/index.d.ts.map +1 -0
- package/dist/services/keychain/createIndexedDBKeychainService/utils/constants.d.ts +2 -0
- package/dist/services/keychain/createIndexedDBKeychainService/utils/constants.d.ts.map +1 -0
- package/dist/services/keychain/createIndexedDBKeychainService/utils/deleteIndexedDBItem.d.ts +2 -0
- package/dist/services/keychain/createIndexedDBKeychainService/utils/deleteIndexedDBItem.d.ts.map +1 -0
- package/dist/services/keychain/createIndexedDBKeychainService/utils/getIndexedDBItem.d.ts +3 -0
- package/dist/services/keychain/createIndexedDBKeychainService/utils/getIndexedDBItem.d.ts.map +1 -0
- package/dist/services/keychain/createIndexedDBKeychainService/utils/openDatabase.d.ts +2 -0
- package/dist/services/keychain/createIndexedDBKeychainService/utils/openDatabase.d.ts.map +1 -0
- package/dist/services/keychain/createIndexedDBKeychainService/utils/setIndexedDBItem.d.ts +3 -0
- package/dist/services/keychain/createIndexedDBKeychainService/utils/setIndexedDBItem.d.ts.map +1 -0
- package/dist/services/keychain/errors/KeychainNotConfiguredError.d.ts +5 -0
- package/dist/services/keychain/errors/KeychainNotConfiguredError.d.ts.map +1 -0
- package/dist/services/keychain/index.d.ts +3 -0
- package/dist/services/keychain/index.d.ts.map +1 -0
- package/dist/services/keychain/keychain.types.d.ts +9 -0
- package/dist/services/keychain/keychain.types.d.ts.map +1 -0
- package/dist/services/realtime/createRealtimeService/createRealtimeService.d.ts +11 -0
- package/dist/services/realtime/createRealtimeService/createRealtimeService.d.ts.map +1 -0
- package/dist/services/realtime/createRealtimeService/index.d.ts +2 -0
- package/dist/services/realtime/createRealtimeService/index.d.ts.map +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/waas.cjs.js +4 -3
- package/dist/waas.cjs.js.map +1 -1
- package/dist/waas.esm.js +3 -3
- package/dist/waasCore.cjs.js +4 -3
- package/dist/waasCore.cjs.js.map +1 -1
- package/dist/waasCore.esm.js +3 -3
- package/package.json +2 -2
- package/dist/InvalidParamError-3-1rSNtf.esm.js.map +0 -1
- package/dist/InvalidParamError-zIxsXkiH.cjs.js.map +0 -1
- package/dist/getNetworkProviderFromNetworkId-BJR1GciB.cjs.js.map +0 -1
- package/dist/getNetworkProviderFromNetworkId-C7jp--76.esm.js.map +0 -1
- package/dist/getSignedSessionId-Alfz9eul.esm.js.map +0 -1
- package/dist/getSignedSessionId-DVpOgsL9.cjs.js.map +0 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { A as
|
|
3
|
-
import { D as onceEvent, E as onEvent, O as setCookie, S as splitWalletProviderKey, T as offEvent, _ as getWalletAccounts, b as formatWalletAccountId, c as getWalletProviders, d as DYNAMIC_AUTH_COOKIE_NAME, f as getWalletProviderFromWalletAccount, g as NoWalletProviderFoundError, i as restoreUserSharesForAllWalletAccounts, l as checkAndRaiseWalletAccountsChangedEvent, n as getWalletProviderByKey, r as updateAuthFromVerifyResponse, t as getVerifiedCredentialForWalletAccount, u as emitWalletAccountsChangedEvent, w as emitEvent, x as normalizeAddress } from "./getVerifiedCredentialForWalletAccount-
|
|
4
|
-
import { n as refreshAuth, t as NotWaasWalletAccountError } from "./NotWaasWalletAccountError-
|
|
5
|
-
import { n as getMfaMethods, r as consumeMfaToken, t as isMfaRequiredForAction } from "./isMfaRequiredForAction-
|
|
1
|
+
import { A as getDefaultClient, C as isCookieEnabled, D as CLIENT_SDK_NAME, E as randomString, F as name, I as version, N as BaseError, P as getCore, T as ValueMustBeDefinedError, _ as MfaRateLimitedError, a as DYNAMIC_ICONIC_SPRITE_URL, b as InvalidExternalAuthError, c as CHAINS_INFO_MAP, d as UnauthorizedError, g as SandboxMaximumThresholdReachedError, h as getNonce, j as setDefaultClient, k as NONCE_POOL_SIZE, l as fetchAndStoreNonces, o as SDK_API_CORE_VERSION, s as getChainFromVerifiedCredentialChain, t as InvalidParamError, u as createApiClient, v as MfaInvalidOtpError, w as assertDefined, x as APIError, y as LinkCredentialError } from "./InvalidParamError-Dc2HgrDm.esm.js";
|
|
2
|
+
import { A as getBuffer, C as CannotTrackError, D as INITIALIZE_STORAGE_SYNC_TRACKER_KEY, E as GENERATE_SESSION_KEYS_TRACKER_KEY, F as createLocalStorageAdapter, I as subscribeWithSelector, L as isEqualShallow, M as createStorageKeySchema, N as createStorage, O as REFRESH_USER_STATE_FROM_COOKIE_TRACKER_KEY, T as FETCH_PROJECT_SETTINGS_TRACKER_KEY, _ as NoNetworkProvidersError, a as updateWalletProviderKeysForVerifiedCredentials, b as createLogger, c as createSignInMessageStatement, d as createVisit, f as hasExtension, g as WalletAlreadyLinkedToAnotherUserError, h as isCaptchaRequired, i as getNetworksData, j as generateSessionKeys, k as isServerSideRendering, l as formatSignInMessage, m as consumeCaptchaToken, o as verifyMessageSignatureOwnership, p as setCaptchaToken, s as removeUnverifiedWalletAccount, t as getNetworkProviderFromNetworkId, u as setUnverifiedWalletAccounts, v as createRealtimeService, w as createDeferredPromise, x as createCrossTabBroadcast, y as createIndexedDBKeychainService } from "./getNetworkProviderFromNetworkId-DO13PEvc.esm.js";
|
|
3
|
+
import { D as onceEvent, E as onEvent, O as setCookie, S as splitWalletProviderKey, T as offEvent, _ as getWalletAccounts, b as formatWalletAccountId, c as getWalletProviders, d as DYNAMIC_AUTH_COOKIE_NAME, f as getWalletProviderFromWalletAccount, g as NoWalletProviderFoundError, i as restoreUserSharesForAllWalletAccounts, l as checkAndRaiseWalletAccountsChangedEvent, n as getWalletProviderByKey, r as updateAuthFromVerifyResponse, t as getVerifiedCredentialForWalletAccount, u as emitWalletAccountsChangedEvent, w as emitEvent, x as normalizeAddress } from "./getVerifiedCredentialForWalletAccount-57Omjjyi.esm.js";
|
|
4
|
+
import { n as refreshAuth, t as NotWaasWalletAccountError } from "./NotWaasWalletAccountError-B4xS_9HL.esm.js";
|
|
5
|
+
import { n as getMfaMethods, r as consumeMfaToken, t as isMfaRequiredForAction } from "./isMfaRequiredForAction-CXRaUbFE.esm.js";
|
|
6
6
|
import { assertPackageVersion } from "@dynamic-labs-sdk/assert-package-version";
|
|
7
7
|
import { AuthModeEnum, ExchangeKeyEnum, JwtVerifiedCredentialFormatEnum, MFAAction, MfaBackupCodeAcknowledgement, ProviderEnum, WaasBackupOptionsEnum, WalletProviderEnum } from "@dynamic-labs/sdk-api-core";
|
|
8
8
|
import * as z from "zod/mini";
|
|
@@ -203,6 +203,7 @@ const logout = async (client = getDefaultClient()) => {
|
|
|
203
203
|
*/
|
|
204
204
|
if (isCookieEnabled(client)) setCookie(`${DYNAMIC_AUTH_COOKIE_NAME}=; Max-Age=-99999999; path=/; SameSite=Lax`);
|
|
205
205
|
}
|
|
206
|
+
await core.keychain.removeKey("session");
|
|
206
207
|
core.state.set({
|
|
207
208
|
captchaToken: null,
|
|
208
209
|
elevatedAccessTokens: [],
|
|
@@ -296,20 +297,6 @@ const setupCrossTabEventSync = (client) => {
|
|
|
296
297
|
core.crossTabBroadcast.on("deviceRegistrationCompleted", handleCrossTabDeviceRegistrationCompleted);
|
|
297
298
|
};
|
|
298
299
|
|
|
299
|
-
//#endregion
|
|
300
|
-
//#region src/modules/state/raiseStateEvents/raiseStateEvents.ts
|
|
301
|
-
const raiseStateEvents = (client) => {
|
|
302
|
-
getCore(client).state.subscribe((value, previous) => {
|
|
303
|
-
Object.entries(stateChangeEvents).forEach(([key, event]) => {
|
|
304
|
-
if (isEqualShallow(value[key], previous[key])) return;
|
|
305
|
-
emitEvent({
|
|
306
|
-
args: { [key]: value[key] },
|
|
307
|
-
event
|
|
308
|
-
}, client);
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
};
|
|
312
|
-
|
|
313
300
|
//#endregion
|
|
314
301
|
//#region src/modules/wallets/unverifiedWalletAccounts/schema.ts
|
|
315
302
|
const unverifiedWalletAccountSchema = z.object({
|
|
@@ -362,6 +349,87 @@ const sessionStorageKeySchema = createStorageKeySchema({
|
|
|
362
349
|
})
|
|
363
350
|
});
|
|
364
351
|
|
|
352
|
+
//#endregion
|
|
353
|
+
//#region src/modules/keychainMigration/migrateSessionKeyToKeychain/KeyMigrationError.ts
|
|
354
|
+
var KeyMigrationError = class extends BaseError {
|
|
355
|
+
constructor(expectedPublicKey, importedPublicKey) {
|
|
356
|
+
super({
|
|
357
|
+
cause: null,
|
|
358
|
+
code: "key_migration_public_key_mismatch",
|
|
359
|
+
docsUrl: null,
|
|
360
|
+
name: "KeyMigrationError",
|
|
361
|
+
shortMessage: `Public key mismatch after import: expected "${expectedPublicKey}", got "${importedPublicKey}"`
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
//#endregion
|
|
367
|
+
//#region src/modules/keychainMigration/migrateSessionKeyToKeychain/migrateSessionKeyToKeychain.ts
|
|
368
|
+
/**
|
|
369
|
+
* Migrates legacy session keys from the hydrated state into the IndexedDB keychain
|
|
370
|
+
* as non-extractable CryptoKey objects.
|
|
371
|
+
*
|
|
372
|
+
* Must run after hydrateStateWithStorage so that state.sessionKeys already holds
|
|
373
|
+
* the legacy base64 blob (or the new public key hex if already migrated).
|
|
374
|
+
*
|
|
375
|
+
* Idempotent: skips if the keychain already has a `session` key.
|
|
376
|
+
* Throws on failure — callers should handle errors (e.g. .catch(() => logout(client))).
|
|
377
|
+
*/
|
|
378
|
+
const migrateSessionKeyToKeychain = async ({ keychain, logger, state, storage }) => {
|
|
379
|
+
if (isServerSideRendering()) return;
|
|
380
|
+
logger.debug("[migrateSessionKeyToKeychain] Checking for existing session key in keychain");
|
|
381
|
+
if (await keychain.hasKey("session")) {
|
|
382
|
+
logger.debug("[migrateSessionKeyToKeychain] Session key already exists in keychain, skipping migration");
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
const encodedSessionKeys = state.get().sessionKeys;
|
|
386
|
+
if (!encodedSessionKeys) {
|
|
387
|
+
logger.debug("[migrateSessionKeyToKeychain] No session keys in state, nothing to migrate");
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
let blob;
|
|
391
|
+
try {
|
|
392
|
+
blob = JSON.parse(getBuffer().from(encodedSessionKeys, "base64").toString());
|
|
393
|
+
} catch {
|
|
394
|
+
logger.debug("[migrateSessionKeyToKeychain] Session keys are not a legacy blob, skipping migration");
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const { privateKeyJwk, publicKey } = blob;
|
|
398
|
+
if (!privateKeyJwk || !publicKey) {
|
|
399
|
+
logger.debug("[migrateSessionKeyToKeychain] Legacy blob missing privateKeyJwk or publicKey, skipping migration");
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
logger.debug("[migrateSessionKeyToKeychain] Found legacy session key, importing into keychain", { expectedPublicKey: publicKey });
|
|
403
|
+
const importedPublicKey = await keychain.importKey("session", privateKeyJwk);
|
|
404
|
+
logger.debug("[migrateSessionKeyToKeychain] Key imported", {
|
|
405
|
+
expectedPublicKey: publicKey,
|
|
406
|
+
importedPublicKey
|
|
407
|
+
});
|
|
408
|
+
if (importedPublicKey !== publicKey) throw new KeyMigrationError(publicKey, importedPublicKey);
|
|
409
|
+
logger.debug("[migrateSessionKeyToKeychain] Public key validated, updating state and storage");
|
|
410
|
+
state.set({ sessionKeys: importedPublicKey });
|
|
411
|
+
const currentSession = await storage.getItem(sessionStorageKeySchema);
|
|
412
|
+
if (currentSession) await storage.setItem(sessionStorageKeySchema, {
|
|
413
|
+
...currentSession,
|
|
414
|
+
sessionKeys: importedPublicKey
|
|
415
|
+
});
|
|
416
|
+
logger.debug("[migrateSessionKeyToKeychain] Migration complete");
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
//#endregion
|
|
420
|
+
//#region src/modules/state/raiseStateEvents/raiseStateEvents.ts
|
|
421
|
+
const raiseStateEvents = (client) => {
|
|
422
|
+
getCore(client).state.subscribe((value, previous) => {
|
|
423
|
+
Object.entries(stateChangeEvents).forEach(([key, event]) => {
|
|
424
|
+
if (isEqualShallow(value[key], previous[key])) return;
|
|
425
|
+
emitEvent({
|
|
426
|
+
args: { [key]: value[key] },
|
|
427
|
+
event
|
|
428
|
+
}, client);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
};
|
|
432
|
+
|
|
365
433
|
//#endregion
|
|
366
434
|
//#region src/modules/storageSync/hydrateStateWithStorage/hydrateStateWithStorage.ts
|
|
367
435
|
const hydrateStateWithStorage = async (client) => {
|
|
@@ -477,6 +545,14 @@ const initializeClient = async (client = getDefaultClient()) => {
|
|
|
477
545
|
setupCrossTabEventSync(client);
|
|
478
546
|
const initializeStorageSyncPromise = initializeStorageSync(client);
|
|
479
547
|
const fetchProjectSettingsPromise = initializeStorageSyncPromise.then(async () => {
|
|
548
|
+
await migrateSessionKeyToKeychain({
|
|
549
|
+
keychain: core.keychain,
|
|
550
|
+
logger: core.logger,
|
|
551
|
+
state: core.state,
|
|
552
|
+
storage: core.storage
|
|
553
|
+
}).catch(() => logout(client));
|
|
554
|
+
if (core.state.get().sessionKeys && !await core.keychain.hasKey("session")) await logout(client);
|
|
555
|
+
}).then(async () => {
|
|
480
556
|
if (!core.state.get().projectSettings) await fetchProjectSettings(client);
|
|
481
557
|
});
|
|
482
558
|
fetchProjectSettingsPromise.then(() => prefetchNoncesIfNeeded(client)).catch((error) => {
|
|
@@ -842,7 +918,9 @@ const createCore = (config) => {
|
|
|
842
918
|
const initTrack = createAsyncTrack();
|
|
843
919
|
const runtimeServices = createRuntimeServices();
|
|
844
920
|
const passkey = config.coreConfig?.passkey ?? createWebPasskeyService();
|
|
921
|
+
const realtime = config.coreConfig?.realtime ?? createRealtimeService();
|
|
845
922
|
const deviceSigner = config.coreConfig?.deviceSigner;
|
|
923
|
+
const keychain = config.coreConfig?.keychain ?? createIndexedDBKeychainService({ dbName: `dynamic_${config.environmentId}_keychain` });
|
|
846
924
|
return {
|
|
847
925
|
apiBaseUrl,
|
|
848
926
|
crossTabBroadcast: config.coreConfig?.crossTabBroadcast ?? createCrossTabBroadcast({ channelName: `dynamic_${config.environmentId}_broadcast` }),
|
|
@@ -854,6 +932,7 @@ const createCore = (config) => {
|
|
|
854
932
|
fetch,
|
|
855
933
|
getApiHeaders: config.coreConfig?.getApiHeaders ?? (() => ({})),
|
|
856
934
|
initTrack,
|
|
935
|
+
keychain,
|
|
857
936
|
logger,
|
|
858
937
|
metadata: {
|
|
859
938
|
...config.metadata,
|
|
@@ -862,6 +941,7 @@ const createCore = (config) => {
|
|
|
862
941
|
navigate,
|
|
863
942
|
openDeeplink,
|
|
864
943
|
passkey,
|
|
944
|
+
realtime,
|
|
865
945
|
runtimeServices,
|
|
866
946
|
state,
|
|
867
947
|
storage,
|