diff --git a/packages/core/src/Util/Reachability.ts b/packages/core/src/Util/Reachability.ts index 94a9ed22d8e..e279ca51997 100644 --- a/packages/core/src/Util/Reachability.ts +++ b/packages/core/src/Util/Reachability.ts @@ -1,3 +1,4 @@ +import { JS } from '@aws-amplify/core'; import Observable, { ZenObservable } from 'zen-observable-ts'; type NetworkStatus = { @@ -10,6 +11,10 @@ export default class ReachabilityNavigator implements Reachability { > = []; networkMonitor(netInfo?: any): Observable { + if (JS.browserOrNode().isNode) { + return Observable.from([{ online: true }]); + } + return new Observable(observer => { observer.next({ online: window.navigator.onLine }); diff --git a/packages/datastore/package.json b/packages/datastore/package.json index b00d522bf0c..0fd40cdfffd 100644 --- a/packages/datastore/package.json +++ b/packages/datastore/package.json @@ -38,17 +38,19 @@ "@react-native-community/netinfo": "4.7.0", "@types/uuid": "3.4.5", "dexie": "3.0.1", - "dexie-export-import": "1.0.0-rc.2", - "fake-indexeddb": "3.0.0" + "dexie-export-import": "1.0.0-rc.2" }, "dependencies": { "@aws-amplify/api": "^3.1.16", "@aws-amplify/core": "^3.3.3", "@aws-amplify/pubsub": "^3.0.17", + "fake-indexeddb": "3.0.0", "idb": "5.0.2", "immer": "6.0.1", + "isomorphic-ws": "^4.0.1", "ulid": "2.3.0", "uuid": "3.3.2", + "ws": "^7.2.3", "zen-observable-ts": "0.8.19", "zen-push": "0.2.1" }, diff --git a/packages/datastore/src/datastore/datastore.ts b/packages/datastore/src/datastore/datastore.ts index 7da82894629..d497200c180 100644 --- a/packages/datastore/src/datastore/datastore.ts +++ b/packages/datastore/src/datastore/datastore.ts @@ -1,4 +1,4 @@ -import { Amplify, ConsoleLogger as Logger, Hub } from '@aws-amplify/core'; +import { Amplify, ConsoleLogger as Logger, Hub, JS } from '@aws-amplify/core'; import { Draft, immerable, produce, setAutoFreeze } from 'immer'; import { v4 as uuid4 } from 'uuid'; import Observable, { ZenObservable } from 'zen-observable-ts'; @@ -52,6 +52,7 @@ setAutoFreeze(true); const logger = new Logger('DataStore'); const ulid = monotonicUlidFactory(Date.now()); +const { isNode } = JS.browserOrNode(); declare class Setting { constructor(init: ModelInit); @@ -100,7 +101,9 @@ let storageClasses: TypeConstructorMap; const initSchema = (userSchema: Schema) => { if (schema !== undefined) { - throw new Error('The schema has already been initialized'); + console.warn('The schema has already been initialized'); + + return userClasses; } logger.log('validating schema', { schema: userSchema }); @@ -325,6 +328,14 @@ const createModelClass = ( draft.id = source.id; }); } + + static fromJSON(json: T | T[]) { + if (Array.isArray(json)) { + return json.map(init => this.fromJSON(init)); + } + + return modelInstanceCreator(clazz, json); + } }); clazz[immerable] = true; @@ -861,7 +872,13 @@ async function start(): Promise { .start({ fullSyncInterval: fullSyncIntervalInMilliseconds }) .subscribe({ next: ({ type, data }) => { - if (type === ControlMessage.SYNC_ENGINE_STORAGE_SUBSCRIBED) { + // In Node, we need to wait for queries to be synced to prevent returning empty arrays. + // In the Browser, we can begin returning data once subscriptions are in place. + const readyType = isNode + ? ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY + : ControlMessage.SYNC_ENGINE_STORAGE_SUBSCRIBED; + + if (type === readyType) { initResolve(); } @@ -940,6 +957,10 @@ function getNamespace(): SchemaNamespace { return namespace; } +const toJSON = (model: T | T[]): JSON => { + return JSON.parse(JSON.stringify(model)); +}; + class DataStore { constructor() { Amplify.register(this); @@ -954,6 +975,7 @@ class DataStore { observe = observe; configure = configure; clear = clear; + toJSON = toJSON; } const instance = new DataStore(); diff --git a/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts b/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts index 8e09cdfafbc..38374254613 100644 --- a/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts +++ b/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts @@ -1,11 +1,16 @@ +import { JS } from '@aws-amplify/core'; import { Adapter } from '..'; const getDefaultAdapter: () => Adapter = () => { - if (window.indexedDB) { + const { isBrowser, isNode } = JS.browserOrNode(); + + if (isNode) { + require('fake-indexeddb/auto'); return require('../indexeddb').default; } - if (process && process.env) { - throw new Error('Node is not supported'); + + if (isBrowser && window.indexedDB) { + return require('../indexeddb').default; } }; diff --git a/packages/predictions/src/Providers/AmazonAIConvertPredictionsProvider.ts b/packages/predictions/src/Providers/AmazonAIConvertPredictionsProvider.ts index 16228431b86..695777d90b2 100644 --- a/packages/predictions/src/Providers/AmazonAIConvertPredictionsProvider.ts +++ b/packages/predictions/src/Providers/AmazonAIConvertPredictionsProvider.ts @@ -24,6 +24,7 @@ import { MessageHeaderValue, } from '@aws-sdk/eventstream-marshaller'; import { fromUtf8, toUtf8 } from '@aws-sdk/util-utf8-node'; + const logger = new Logger('AmazonAIConvertPredictionsProvider'); const eventBuilder = new EventStreamMarshaller(toUtf8, fromUtf8); diff --git a/packages/pubsub/src/Providers/AWSAppSyncRealTimeProvider.ts b/packages/pubsub/src/Providers/AWSAppSyncRealTimeProvider.ts index dbe610f12b2..46182948f74 100644 --- a/packages/pubsub/src/Providers/AWSAppSyncRealTimeProvider.ts +++ b/packages/pubsub/src/Providers/AWSAppSyncRealTimeProvider.ts @@ -28,6 +28,7 @@ import { } from '@aws-amplify/core'; import Cache from '@aws-amplify/cache'; import Auth from '@aws-amplify/auth'; +import WebSocket from 'isomorphic-ws'; import { AbstractPubSubProvider } from './PubSubProvider'; import { CONTROL_MSG } from '../index';