Browse Source

feat(grpc): add syncing protocol buffers (#930)

* feat(grpc): add syncing protocol buffers

* wip

* feat: syncing implementation

* fix: sendign metadata

* fix: grpc sync request mapping

* fix grpc response mapper
Karol Sójko 1 năm trước cách đây
mục cha
commit
5b84f078c6
46 tập tin đã thay đổi với 7610 bổ sung183 xóa
  1. 8 6
      .pnp.cjs
  2. BIN
      .yarn/cache/@grpc-grpc-js-npm-1.9.11-5bb7febd65-71b8517b4f.zip
  3. 1 1
      docker-compose.ci.yml
  4. 5 0
      docker/docker-entrypoint.sh
  5. 1 0
      packages/api-gateway/.env.sample
  6. 1 1
      packages/api-gateway/package.json
  7. 44 2
      packages/api-gateway/src/Bootstrap/Container.ts
  8. 6 0
      packages/api-gateway/src/Bootstrap/Types.ts
  9. 94 0
      packages/api-gateway/src/Mapping/Sync/GRPC/SyncRequestGRPCMapper.ts
  10. 164 0
      packages/api-gateway/src/Mapping/Sync/GRPC/SyncResponseGRPCMapper.ts
  11. 8 0
      packages/api-gateway/src/Mapping/Sync/Http/ItemConflictHttpRepresentation.ts
  12. 17 0
      packages/api-gateway/src/Mapping/Sync/Http/ItemHashHttpRepresentation.ts
  13. 19 0
      packages/api-gateway/src/Mapping/Sync/Http/ItemHttpRepresentation.ts
  14. 9 0
      packages/api-gateway/src/Mapping/Sync/Http/MessageHttpRepresentation.ts
  15. 8 0
      packages/api-gateway/src/Mapping/Sync/Http/NotificationHttpRepresentation.ts
  16. 15 0
      packages/api-gateway/src/Mapping/Sync/Http/SavedItemHttpRepresentation.ts
  17. 7 0
      packages/api-gateway/src/Mapping/Sync/Http/SharedVaultHttpRepresentation.ts
  18. 10 0
      packages/api-gateway/src/Mapping/Sync/Http/SharedVaultInviteHttpRepresentation.ts
  19. 19 0
      packages/api-gateway/src/Mapping/Sync/Http/SyncResponseHttpRepresentation.ts
  20. 11 15
      packages/api-gateway/src/Service/DirectCall/DirectCallServiceProxy.ts
  21. 28 69
      packages/api-gateway/src/Service/Http/HttpServiceProxy.ts
  22. 38 69
      packages/api-gateway/src/Service/gRPC/GRPCServiceProxy.ts
  23. 52 0
      packages/api-gateway/src/Service/gRPC/GRPCSyncingServerServiceProxy.ts
  24. 9 0
      packages/auth/bin/server.ts
  25. 1 2
      packages/auth/package.json
  26. 2 0
      packages/grpc/lib/index.d.ts
  27. 2 0
      packages/grpc/lib/index.js
  28. 41 0
      packages/grpc/lib/sync_grpc_pb.d.ts
  29. 44 0
      packages/grpc/lib/sync_grpc_pb.js
  30. 683 0
      packages/grpc/lib/sync_pb.d.ts
  31. 5675 0
      packages/grpc/lib/sync_pb.js
  32. 1 1
      packages/grpc/package.json
  33. 146 0
      packages/grpc/proto/sync.proto
  34. 1 0
      packages/syncing-server/.env.sample
  35. 49 0
      packages/syncing-server/bin/server.ts
  36. 2 0
      packages/syncing-server/package.json
  37. 6 0
      packages/syncing-server/src/Bootstrap/Container.ts
  38. 1 0
      packages/syncing-server/src/Bootstrap/Types.ts
  39. 1 1
      packages/syncing-server/src/Domain/UseCase/Syncing/GetItems/GetItems.spec.ts
  40. 1 1
      packages/syncing-server/src/Domain/UseCase/Syncing/GetItems/GetItems.ts
  41. 4 4
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts
  42. 1 1
      packages/syncing-server/src/Domain/UseCase/Syncing/SyncItems/SyncItemsDTO.ts
  43. 3 3
      packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts
  44. 113 0
      packages/syncing-server/src/Infra/gRPC/SyncingServer.ts
  45. 250 0
      packages/syncing-server/src/Mapping/gRPC/SyncResponseGRPCMapper.ts
  46. 9 7
      yarn.lock

+ 8 - 6
.pnp.cjs

@@ -2674,10 +2674,10 @@ const RAW_RUNTIME_STATE =
       }]\
     ]],\
     ["@grpc/grpc-js", [\
-      ["npm:1.9.10", {\
-        "packageLocation": "./.yarn/cache/@grpc-grpc-js-npm-1.9.10-28317a9d2d-243cf994e6.zip/node_modules/@grpc/grpc-js/",\
+      ["npm:1.9.11", {\
+        "packageLocation": "./.yarn/cache/@grpc-grpc-js-npm-1.9.11-5bb7febd65-71b8517b4f.zip/node_modules/@grpc/grpc-js/",\
         "packageDependencies": [\
-          ["@grpc/grpc-js", "npm:1.9.10"],\
+          ["@grpc/grpc-js", "npm:1.9.11"],\
           ["@grpc/proto-loader", "npm:0.7.10"],\
           ["@types/node", "npm:20.2.5"]\
         ],\
@@ -6401,7 +6401,7 @@ const RAW_RUNTIME_STATE =
         "packageLocation": "./packages/api-gateway/",\
         "packageDependencies": [\
           ["@standardnotes/api-gateway", "workspace:packages/api-gateway"],\
-          ["@grpc/grpc-js", "npm:1.9.10"],\
+          ["@grpc/grpc-js", "npm:1.9.11"],\
           ["@standardnotes/domain-core", "workspace:packages/domain-core"],\
           ["@standardnotes/domain-events", "workspace:packages/domain-events"],\
           ["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
@@ -6451,7 +6451,7 @@ const RAW_RUNTIME_STATE =
           ["@aws-sdk/client-sqs", "npm:3.427.0"],\
           ["@cbor-extract/cbor-extract-linux-arm64", "npm:2.1.1"],\
           ["@cbor-extract/cbor-extract-linux-x64", "npm:2.1.1"],\
-          ["@grpc/grpc-js", "npm:1.9.10"],\
+          ["@grpc/grpc-js", "npm:1.9.11"],\
           ["@simplewebauthn/server", "npm:8.1.1"],\
           ["@simplewebauthn/typescript-types", "npm:8.0.0"],\
           ["@standardnotes/api", "npm:1.26.26"],\
@@ -6678,7 +6678,7 @@ const RAW_RUNTIME_STATE =
         "packageLocation": "./packages/grpc/",\
         "packageDependencies": [\
           ["@standardnotes/grpc", "workspace:packages/grpc"],\
-          ["@grpc/grpc-js", "npm:1.9.10"],\
+          ["@grpc/grpc-js", "npm:1.9.11"],\
           ["@types/google-protobuf", "npm:3.15.10"],\
           ["google-protobuf", "npm:3.21.2"],\
           ["grpc-tools", "npm:1.12.4"],\
@@ -6951,11 +6951,13 @@ const RAW_RUNTIME_STATE =
           ["@aws-sdk/client-s3", "npm:3.427.0"],\
           ["@aws-sdk/client-sns", "npm:3.427.0"],\
           ["@aws-sdk/client-sqs", "npm:3.427.0"],\
+          ["@grpc/grpc-js", "npm:1.9.11"],\
           ["@standardnotes/api", "npm:1.26.26"],\
           ["@standardnotes/common", "workspace:packages/common"],\
           ["@standardnotes/domain-core", "workspace:packages/domain-core"],\
           ["@standardnotes/domain-events", "workspace:packages/domain-events"],\
           ["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
+          ["@standardnotes/grpc", "workspace:packages/grpc"],\
           ["@standardnotes/responses", "npm:1.13.27"],\
           ["@standardnotes/security", "workspace:packages/security"],\
           ["@standardnotes/settings", "workspace:packages/settings"],\

BIN
.yarn/cache/@grpc-grpc-js-npm-1.9.10-28317a9d2d-243cf994e6.zip → .yarn/cache/@grpc-grpc-js-npm-1.9.11-5bb7febd65-71b8517b4f.zip


+ 1 - 1
docker-compose.ci.yml

@@ -22,11 +22,11 @@ services:
     environment:
       DB_TYPE: "${DB_TYPE}"
       CACHE_TYPE: "${CACHE_TYPE}"
+      SERVICE_PROXY_TYPE: "${SERVICE_PROXY_TYPE}"
     container_name: server-ci
     ports:
       - 3123:3000
       - 3125:3104
-      - 50051:50051
     volumes:
       - ./logs:/var/lib/server/logs
     networks:

+ 5 - 0
docker/docker-entrypoint.sh

@@ -14,6 +14,10 @@ if [ -z "$SYNCING_SERVER_PORT" ]; then
   export SYNCING_SERVER_PORT=3101
 fi
 
+if [ -z "$SYNCING_SERVER_GRPC_PORT" ]; then
+  export SYNCING_SERVER_GRPC_PORT=50052
+fi
+
 if [ -z "$AUTH_SERVER_PORT" ]; then
   export AUTH_SERVER_PORT=3103
 fi
@@ -356,6 +360,7 @@ export API_GATEWAY_NODE_ENV=production
 export API_GATEWAY_VERSION=local
 
 export API_GATEWAY_SYNCING_SERVER_JS_URL=http://localhost:$SYNCING_SERVER_PORT
+export API_GATEWAY_SYNCING_SERVER_GRPC_URL=0.0.0.0:$SYNCING_SERVER_GRPC_PORT
 export API_GATEWAY_AUTH_SERVER_URL=http://localhost:$AUTH_SERVER_PORT
 export API_GATEWAY_AUTH_SERVER_GRPC_URL=0.0.0.0:$AUTH_SERVER_GRPC_PORT
 export API_GATEWAY_REVISIONS_SERVER_URL=http://localhost:$REVISIONS_SERVER_PORT

+ 1 - 0
packages/api-gateway/.env.sample

@@ -6,6 +6,7 @@ VERSION=development
 PORT=3000
 
 SYNCING_SERVER_JS_URL=http://syncing_server_js:3000
+SYNCING_SERVER_GRPC_URL=http://syncing_server_js:50052
 AUTH_SERVER_URL=http://auth:3000
 AUTH_SERVER_GRPC_URL=http://auth:50051
 WEB_SOCKET_SERVER_URL=http://websockets:3000

+ 1 - 1
packages/api-gateway/package.json

@@ -26,7 +26,7 @@
     "start": "yarn node dist/bin/server.js"
   },
   "dependencies": {
-    "@grpc/grpc-js": "^1.9.10",
+    "@grpc/grpc-js": "^1.9.11",
     "@standardnotes/domain-core": "workspace:^",
     "@standardnotes/domain-events": "workspace:*",
     "@standardnotes/domain-events-infra": "workspace:*",

+ 44 - 2
packages/api-gateway/src/Bootstrap/Container.ts

@@ -16,14 +16,25 @@ import { RedisCrossServiceTokenCache } from '../Infra/Redis/RedisCrossServiceTok
 import { WebSocketAuthMiddleware } from '../Controller/WebSocketAuthMiddleware'
 import { InMemoryCrossServiceTokenCache } from '../Infra/InMemory/InMemoryCrossServiceTokenCache'
 import { DirectCallServiceProxy } from '../Service/DirectCall/DirectCallServiceProxy'
-import { ServiceContainerInterface } from '@standardnotes/domain-core'
+import { MapperInterface, ServiceContainerInterface } from '@standardnotes/domain-core'
 import { EndpointResolverInterface } from '../Service/Resolver/EndpointResolverInterface'
 import { EndpointResolver } from '../Service/Resolver/EndpointResolver'
 import { RequiredCrossServiceTokenMiddleware } from '../Controller/RequiredCrossServiceTokenMiddleware'
 import { OptionalCrossServiceTokenMiddleware } from '../Controller/OptionalCrossServiceTokenMiddleware'
 import { Transform } from 'stream'
-import { ISessionsClient, SessionsClient } from '@standardnotes/grpc'
+import {
+  ISessionsClient,
+  ISyncingClient,
+  SessionsClient,
+  SyncRequest,
+  SyncResponse,
+  SyncingClient,
+} from '@standardnotes/grpc'
 import { GRPCServiceProxy } from '../Service/gRPC/GRPCServiceProxy'
+import { GRPCSyncingServerServiceProxy } from '../Service/gRPC/GRPCSyncingServerServiceProxy'
+import { SyncResponseHttpRepresentation } from '../Mapping/Sync/Http/SyncResponseHttpRepresentation'
+import { SyncRequestGRPCMapper } from '../Mapping/Sync/GRPC/SyncRequestGRPCMapper'
+import { SyncResponseGRPCMapper } from '../Mapping/Sync/GRPC/SyncResponseGRPCMapper'
 
 export class ContainerConfigLoader {
   async load(configuration?: {
@@ -145,6 +156,7 @@ export class ContainerConfigLoader {
       const isConfiguredForGRPCProxy = env.get('SERVICE_PROXY_TYPE', true) === 'grpc'
       if (isConfiguredForGRPCProxy) {
         container.bind(TYPES.ApiGateway_AUTH_SERVER_GRPC_URL).toConstantValue(env.get('AUTH_SERVER_GRPC_URL'))
+        container.bind(TYPES.ApiGateway_SYNCING_SERVER_GRPC_URL).toConstantValue(env.get('SYNCING_SERVER_GRPC_URL'))
         const grpcAgentKeepAliveTimeout = env.get('GRPC_AGENT_KEEP_ALIVE_TIMEOUT', true)
           ? +env.get('GRPC_AGENT_KEEP_ALIVE_TIMEOUT', true)
           : 8_000
@@ -158,6 +170,35 @@ export class ContainerConfigLoader {
             },
           ),
         )
+        container.bind<ISyncingClient>(TYPES.ApiGateway_GRPCSyncingClient).toConstantValue(
+          new SyncingClient(
+            container.get<string>(TYPES.ApiGateway_SYNCING_SERVER_GRPC_URL),
+            grpc.credentials.createInsecure(),
+            {
+              'grpc.keepalive_time_ms': grpcAgentKeepAliveTimeout * 2,
+              'grpc.keepalive_timeout_ms': grpcAgentKeepAliveTimeout,
+            },
+          ),
+        )
+
+        container
+          .bind<MapperInterface<Record<string, unknown>, SyncRequest>>(TYPES.Mapper_SyncRequestGRPCMapper)
+          .toConstantValue(new SyncRequestGRPCMapper())
+        container
+          .bind<MapperInterface<SyncResponse, SyncResponseHttpRepresentation>>(TYPES.Mapper_SyncResponseGRPCMapper)
+          .toConstantValue(new SyncResponseGRPCMapper())
+
+        container
+          .bind<GRPCSyncingServerServiceProxy>(TYPES.ApiGateway_GRPCSyncingServerServiceProxy)
+          .toConstantValue(
+            new GRPCSyncingServerServiceProxy(
+              container.get<ISyncingClient>(TYPES.ApiGateway_GRPCSyncingClient),
+              container.get<MapperInterface<Record<string, unknown>, SyncRequest>>(TYPES.Mapper_SyncRequestGRPCMapper),
+              container.get<MapperInterface<SyncResponse, SyncResponseHttpRepresentation>>(
+                TYPES.Mapper_SyncResponseGRPCMapper,
+              ),
+            ),
+          )
         container
           .bind<ServiceProxyInterface>(TYPES.ApiGateway_ServiceProxy)
           .toConstantValue(
@@ -175,6 +216,7 @@ export class ContainerConfigLoader {
               container.get<winston.Logger>(TYPES.ApiGateway_Logger),
               container.get<TimerInterface>(TYPES.ApiGateway_Timer),
               container.get<ISessionsClient>(TYPES.ApiGateway_GRPCSessionsClient),
+              container.get<GRPCSyncingServerServiceProxy>(TYPES.ApiGateway_GRPCSyncingServerServiceProxy),
             ),
           )
       } else {

+ 6 - 0
packages/api-gateway/src/Bootstrap/Types.ts

@@ -6,6 +6,7 @@ export const TYPES = {
   ApiGateway_SYNCING_SERVER_JS_URL: Symbol.for('ApiGateway_SYNCING_SERVER_JS_URL'),
   ApiGateway_AUTH_SERVER_URL: Symbol.for('ApiGateway_AUTH_SERVER_URL'),
   ApiGateway_AUTH_SERVER_GRPC_URL: Symbol.for('ApiGateway_AUTH_SERVER_GRPC_URL'),
+  ApiGateway_SYNCING_SERVER_GRPC_URL: Symbol.for('ApiGateway_SYNCING_SERVER_GRPC_URL'),
   ApiGateway_PAYMENTS_SERVER_URL: Symbol.for('ApiGateway_PAYMENTS_SERVER_URL'),
   ApiGateway_FILES_SERVER_URL: Symbol.for('ApiGateway_FILES_SERVER_URL'),
   ApiGateway_REVISIONS_SERVER_URL: Symbol.for('ApiGateway_REVISIONS_SERVER_URL'),
@@ -24,10 +25,15 @@ export const TYPES = {
   ApiGateway_OptionalCrossServiceTokenMiddleware: Symbol.for('ApiGateway_OptionalCrossServiceTokenMiddleware'),
   ApiGateway_WebSocketAuthMiddleware: Symbol.for('ApiGateway_WebSocketAuthMiddleware'),
   ApiGateway_SubscriptionTokenAuthMiddleware: Symbol.for('ApiGateway_SubscriptionTokenAuthMiddleware'),
+  // Mapping
+  Mapper_SyncRequestGRPCMapper: Symbol.for('Mapper_SyncRequestGRPCMapper'),
+  Mapper_SyncResponseGRPCMapper: Symbol.for('Mapper_SyncResponseGRPCMapper'),
   // Services
+  ApiGateway_GRPCSyncingServerServiceProxy: Symbol.for('ApiGateway_GRPCSyncingServerServiceProxy'),
   ApiGateway_ServiceProxy: Symbol.for('ApiGateway_ServiceProxy'),
   ApiGateway_CrossServiceTokenCache: Symbol.for('ApiGateway_CrossServiceTokenCache'),
   ApiGateway_Timer: Symbol.for('ApiGateway_Timer'),
   ApiGateway_EndpointResolver: Symbol.for('ApiGateway_EndpointResolver'),
   ApiGateway_GRPCSessionsClient: Symbol.for('ApiGateway_GRPCSessionsClient'),
+  ApiGateway_GRPCSyncingClient: Symbol.for('ApiGateway_GRPCSyncingClient'),
 }

+ 94 - 0
packages/api-gateway/src/Mapping/Sync/GRPC/SyncRequestGRPCMapper.ts

@@ -0,0 +1,94 @@
+import { MapperInterface, Validator } from '@standardnotes/domain-core'
+import { ItemHash, SyncRequest } from '@standardnotes/grpc'
+
+export class SyncRequestGRPCMapper implements MapperInterface<Record<string, unknown>, SyncRequest> {
+  toDomain(_projection: SyncRequest): Record<string, unknown> {
+    throw new Error('Method not implemented.')
+  }
+
+  toProjection(domain: Record<string, unknown>): SyncRequest {
+    const syncRequest = new SyncRequest()
+    if ('items' in domain) {
+      syncRequest.setItemsList((domain.items as Record<string, unknown>[]).map((item) => this.createItemHash(item)))
+    }
+
+    if ('shared_vault_uuids' in domain) {
+      const sharedVaultUuidsValidation = Validator.isNotEmpty(domain.shared_vault_uuids)
+      if (!sharedVaultUuidsValidation.isFailed()) {
+        syncRequest.setSharedVaultUuidsList(domain.shared_vault_uuids as string[])
+      }
+    }
+
+    if ('compute_integrity' in domain) {
+      syncRequest.setComputeIntegrity(!!domain.compute_integrity)
+    }
+
+    if ('sync_token' in domain) {
+      syncRequest.setSyncToken(domain.sync_token as string)
+    }
+
+    if ('cursor_token' in domain) {
+      syncRequest.setCursorToken(domain.cursor_token as string)
+    }
+
+    if ('limit' in domain) {
+      syncRequest.setLimit(domain.limit as number)
+    }
+
+    if ('content_type' in domain) {
+      syncRequest.setContentType(domain.content_type as string)
+    }
+
+    if ('api' in domain) {
+      syncRequest.setApiVersion(domain.api as string)
+    }
+
+    return syncRequest
+  }
+
+  private createItemHash(record: Record<string, unknown>): ItemHash {
+    const itemHash = new ItemHash()
+    itemHash.setUuid(record.uuid as string)
+    if (record.content) {
+      itemHash.setContent(record.content as string)
+    }
+    if (record.content_type) {
+      itemHash.setContentType(record.content_type as string)
+    }
+    if (record.deleted !== undefined) {
+      itemHash.setDeleted(!!record.deleted)
+    }
+    if (record.duplicate_of) {
+      itemHash.setDuplicateOf(record.duplicate_of as string)
+    }
+    if (record.auth_hash) {
+      itemHash.setAuthHash(record.auth_hash as string)
+    }
+    if (record.enc_item_key) {
+      itemHash.setEncItemKey(record.enc_item_key as string)
+    }
+    if (record.items_key_id) {
+      itemHash.setItemsKeyId(record.items_key_id as string)
+    }
+    if (record.key_system_identifier) {
+      itemHash.setKeySystemIdentifier(record.key_system_identifier as string)
+    }
+    if (record.shared_vault_uuid) {
+      itemHash.setSharedVaultUuid(record.shared_vault_uuid as string)
+    }
+    if (record.created_at) {
+      itemHash.setCreatedAt(record.created_at as string)
+    }
+    if (record.created_at_timestamp) {
+      itemHash.setCreatedAtTimestamp(record.created_at_timestamp as number)
+    }
+    if (record.updated_at) {
+      itemHash.setUpdatedAt(record.updated_at as string)
+    }
+    if (record.updated_at_timestamp) {
+      itemHash.setUpdatedAtTimestamp(record.updated_at_timestamp as number)
+    }
+
+    return itemHash
+  }
+}

+ 164 - 0
packages/api-gateway/src/Mapping/Sync/GRPC/SyncResponseGRPCMapper.ts

@@ -0,0 +1,164 @@
+import { MapperInterface } from '@standardnotes/domain-core'
+import {
+  ItemConflictRepresentation,
+  ItemHashRepresentation,
+  ItemRepresentation,
+  MessageRepresentation,
+  NotificationRepresentation,
+  SavedItemRepresentation,
+  SharedVaultInviteRepresentation,
+  SharedVaultRepresentation,
+  SyncResponse,
+} from '@standardnotes/grpc'
+
+import { ItemHttpRepresentation } from '../Http/ItemHttpRepresentation'
+import { SavedItemHttpRepresentation } from '../Http/SavedItemHttpRepresentation'
+import { ItemConflictHttpRepresentation } from '../Http/ItemConflictHttpRepresentation'
+import { ItemHashHttpRepresentation } from '../Http/ItemHashHttpRepresentation'
+import { MessageHttpRepresentation } from '../Http/MessageHttpRepresentation'
+import { SharedVaultHttpRepresentation } from '../Http/SharedVaultHttpRepresentation'
+import { SharedVaultInviteHttpRepresentation } from '../Http/SharedVaultInviteHttpRepresentation'
+import { NotificationHttpRepresentation } from '../Http/NotificationHttpRepresentation'
+import { SyncResponseHttpRepresentation } from '../Http/SyncResponseHttpRepresentation'
+
+export class SyncResponseGRPCMapper implements MapperInterface<SyncResponse, SyncResponseHttpRepresentation> {
+  toDomain(_projection: SyncResponseHttpRepresentation): SyncResponse {
+    throw new Error('Method not implemented.')
+  }
+
+  toProjection(domain: SyncResponse): SyncResponseHttpRepresentation {
+    return {
+      retrieved_items: domain.getRetrievedItemsList().map((item) => this.createItem(item)),
+      saved_items: domain.getSavedItemsList().map((item) => this.createSavedItem(item)),
+      conflicts: domain.getConflictsList().map((conflict) => this.createConflict(conflict)),
+      sync_token: domain.getSyncToken(),
+      cursor_token: domain.getCursorToken(),
+      messages: domain.getMessagesList().map((message) => this.createMessage(message)),
+      shared_vaults: domain.getSharedVaultsList().map((sharedVault) => this.createSharedVault(sharedVault)),
+      shared_vault_invites: domain
+        .getSharedVaultInvitesList()
+        .map((sharedVaultInvite) => this.createSharedVaultInvite(sharedVaultInvite)),
+      notifications: domain.getNotificationsList().map((notification) => this.createNotification(notification)),
+    }
+  }
+
+  private createNotification(notification: NotificationRepresentation): NotificationHttpRepresentation {
+    return {
+      uuid: notification.getUuid(),
+      user_uuid: notification.getUserUuid(),
+      type: notification.getType(),
+      payload: notification.getPayload(),
+      created_at_timestamp: notification.getCreatedAtTimestamp(),
+      updated_at_timestamp: notification.getUpdatedAtTimestamp(),
+    }
+  }
+
+  private createSharedVaultInvite(
+    sharedVaultInvite: SharedVaultInviteRepresentation,
+  ): SharedVaultInviteHttpRepresentation {
+    return {
+      uuid: sharedVaultInvite.getUuid(),
+      shared_vault_uuid: sharedVaultInvite.getSharedVaultUuid(),
+      user_uuid: sharedVaultInvite.getUserUuid(),
+      sender_uuid: sharedVaultInvite.getSenderUuid(),
+      encrypted_message: sharedVaultInvite.getEncryptedMessage(),
+      permission: sharedVaultInvite.getPermission(),
+      created_at_timestamp: sharedVaultInvite.getCreatedAtTimestamp(),
+      updated_at_timestamp: sharedVaultInvite.getUpdatedAtTimestamp(),
+    }
+  }
+
+  private createSharedVault(sharedVault: SharedVaultRepresentation): SharedVaultHttpRepresentation {
+    return {
+      uuid: sharedVault.getUuid(),
+      user_uuid: sharedVault.getUserUuid(),
+      file_upload_bytes_used: sharedVault.getFileUploadBytesUsed(),
+      created_at_timestamp: sharedVault.getCreatedAtTimestamp(),
+      updated_at_timestamp: sharedVault.getUpdatedAtTimestamp(),
+    }
+  }
+
+  private createMessage(message: MessageRepresentation): MessageHttpRepresentation {
+    return {
+      uuid: message.getUuid(),
+      recipient_uuid: message.getRecipientUuid(),
+      sender_uuid: message.getSenderUuid(),
+      encrypted_message: message.getEncryptedMessage(),
+      replaceability_identifier: message.getReplaceabilityIdentifier() ?? null,
+      created_at_timestamp: message.getCreatedAtTimestamp(),
+      updated_at_timestamp: message.getUpdatedAtTimestamp(),
+    }
+  }
+
+  private createConflict(conflict: ItemConflictRepresentation): ItemConflictHttpRepresentation {
+    return {
+      server_item: conflict.hasServerItem()
+        ? this.createItem(conflict.getServerItem() as ItemRepresentation)
+        : undefined,
+      unsaved_item: conflict.hasUnsavedItem()
+        ? this.createItemHash(conflict.getUnsavedItem() as ItemHashRepresentation)
+        : undefined,
+      type: conflict.getType(),
+    }
+  }
+
+  private createItemHash(itemHash: ItemHashRepresentation): ItemHashHttpRepresentation {
+    return {
+      uuid: itemHash.getUuid(),
+      user_uuid: itemHash.getUserUuid(),
+      content: itemHash.hasContent() ? (itemHash.getContent() as string) : undefined,
+      content_type: itemHash.hasContentType() ? (itemHash.getContentType() as string) : null,
+      deleted: itemHash.hasDeleted() ? (itemHash.getDeleted() as boolean) : false,
+      duplicate_of: itemHash.hasDuplicateOf() ? (itemHash.getDuplicateOf() as string) : null,
+      auth_hash: itemHash.hasAuthHash() ? (itemHash.getAuthHash() as string) : undefined,
+      enc_item_key: itemHash.hasEncItemKey() ? (itemHash.getEncItemKey() as string) : undefined,
+      items_key_id: itemHash.hasItemsKeyId() ? (itemHash.getItemsKeyId() as string) : undefined,
+      key_system_identifier: itemHash.hasKeySystemIdentifier() ? (itemHash.getKeySystemIdentifier() as string) : null,
+      shared_vault_uuid: itemHash.hasSharedVaultUuid() ? (itemHash.getSharedVaultUuid() as string) : null,
+      created_at: itemHash.hasCreatedAt() ? (itemHash.getCreatedAt() as string) : undefined,
+      created_at_timestamp: itemHash.hasCreatedAtTimestamp() ? (itemHash.getCreatedAtTimestamp() as number) : undefined,
+      updated_at: itemHash.hasUpdatedAt() ? (itemHash.getUpdatedAt() as string) : undefined,
+      updated_at_timestamp: itemHash.hasUpdatedAtTimestamp() ? (itemHash.getUpdatedAtTimestamp() as number) : undefined,
+    }
+  }
+
+  private createSavedItem(item: SavedItemRepresentation): SavedItemHttpRepresentation {
+    return {
+      uuid: item.getUuid(),
+      duplicate_of: item.hasDuplicateOf() ? (item.getDuplicateOf() as string) : null,
+      content_type: item.getContentType(),
+      auth_hash: item.hasAuthHash() ? (item.getAuthHash() as string) : null,
+      deleted: item.getDeleted(),
+      created_at: item.getCreatedAt(),
+      created_at_timestamp: item.getCreatedAtTimestamp(),
+      updated_at: item.getUpdatedAt(),
+      updated_at_timestamp: item.getUpdatedAtTimestamp(),
+      key_system_identifier: item.hasKeySystemIdentifier() ? (item.getKeySystemIdentifier() as string) : null,
+      shared_vault_uuid: item.hasSharedVaultUuid() ? (item.getSharedVaultUuid() as string) : null,
+      user_uuid: item.hasUserUuid() ? (item.getUserUuid() as string) : null,
+      last_edited_by_uuid: item.hasLastEditedByUuid() ? (item.getLastEditedByUuid() as string) : null,
+    }
+  }
+
+  private createItem(item: ItemRepresentation): ItemHttpRepresentation {
+    return {
+      uuid: item.getUuid(),
+      items_key_id: item.hasItemsKeyId() ? (item.getItemsKeyId() as string) : null,
+      duplicate_of: item.hasDuplicateOf() ? (item.getDuplicateOf() as string) : null,
+      enc_item_key: item.hasEncItemKey() ? (item.getEncItemKey() as string) : null,
+      content: item.hasContent() ? (item.getContent() as string) : null,
+      content_type: item.getContentType(),
+      auth_hash: item.hasAuthHash() ? (item.getAuthHash() as string) : null,
+      deleted: item.getDeleted(),
+      created_at: item.getCreatedAt(),
+      created_at_timestamp: item.getCreatedAtTimestamp(),
+      updated_at: item.getUpdatedAt(),
+      updated_at_timestamp: item.getUpdatedAtTimestamp(),
+      updated_with_session: item.hasUpdatedWithSession() ? (item.getUpdatedWithSession() as string) : null,
+      key_system_identifier: item.hasKeySystemIdentifier() ? (item.getKeySystemIdentifier() as string) : null,
+      shared_vault_uuid: item.hasSharedVaultUuid() ? (item.getSharedVaultUuid() as string) : null,
+      user_uuid: item.hasUserUuid() ? (item.getUserUuid() as string) : null,
+      last_edited_by_uuid: item.hasLastEditedByUuid() ? (item.getLastEditedByUuid() as string) : null,
+    }
+  }
+}

+ 8 - 0
packages/api-gateway/src/Mapping/Sync/Http/ItemConflictHttpRepresentation.ts

@@ -0,0 +1,8 @@
+import { ItemHttpRepresentation } from './ItemHttpRepresentation'
+import { ItemHashHttpRepresentation } from './ItemHashHttpRepresentation'
+
+export interface ItemConflictHttpRepresentation {
+  server_item?: ItemHttpRepresentation
+  unsaved_item?: ItemHashHttpRepresentation
+  type: string
+}

+ 17 - 0
packages/api-gateway/src/Mapping/Sync/Http/ItemHashHttpRepresentation.ts

@@ -0,0 +1,17 @@
+export interface ItemHashHttpRepresentation {
+  uuid: string
+  user_uuid: string
+  content?: string
+  content_type: string | null
+  deleted?: boolean
+  duplicate_of?: string | null
+  auth_hash?: string
+  enc_item_key?: string
+  items_key_id?: string
+  key_system_identifier: string | null
+  shared_vault_uuid: string | null
+  created_at?: string
+  created_at_timestamp?: number
+  updated_at?: string
+  updated_at_timestamp?: number
+}

+ 19 - 0
packages/api-gateway/src/Mapping/Sync/Http/ItemHttpRepresentation.ts

@@ -0,0 +1,19 @@
+export interface ItemHttpRepresentation {
+  uuid: string
+  items_key_id: string | null
+  duplicate_of: string | null
+  enc_item_key: string | null
+  content: string | null
+  content_type: string
+  auth_hash: string | null
+  deleted: boolean
+  created_at: string
+  created_at_timestamp: number
+  updated_at: string
+  updated_at_timestamp: number
+  updated_with_session: string | null
+  key_system_identifier: string | null
+  shared_vault_uuid: string | null
+  user_uuid: string | null
+  last_edited_by_uuid: string | null
+}

+ 9 - 0
packages/api-gateway/src/Mapping/Sync/Http/MessageHttpRepresentation.ts

@@ -0,0 +1,9 @@
+export interface MessageHttpRepresentation {
+  uuid: string
+  recipient_uuid: string
+  sender_uuid: string
+  encrypted_message: string
+  replaceability_identifier: string | null
+  created_at_timestamp: number
+  updated_at_timestamp: number
+}

+ 8 - 0
packages/api-gateway/src/Mapping/Sync/Http/NotificationHttpRepresentation.ts

@@ -0,0 +1,8 @@
+export interface NotificationHttpRepresentation {
+  uuid: string
+  user_uuid: string
+  type: string
+  payload: string
+  created_at_timestamp: number
+  updated_at_timestamp: number
+}

+ 15 - 0
packages/api-gateway/src/Mapping/Sync/Http/SavedItemHttpRepresentation.ts

@@ -0,0 +1,15 @@
+export interface SavedItemHttpRepresentation {
+  uuid: string
+  duplicate_of: string | null
+  content_type: string
+  auth_hash: string | null
+  deleted: boolean
+  created_at: string
+  created_at_timestamp: number
+  updated_at: string
+  updated_at_timestamp: number
+  key_system_identifier: string | null
+  shared_vault_uuid: string | null
+  user_uuid: string | null
+  last_edited_by_uuid: string | null
+}

+ 7 - 0
packages/api-gateway/src/Mapping/Sync/Http/SharedVaultHttpRepresentation.ts

@@ -0,0 +1,7 @@
+export interface SharedVaultHttpRepresentation {
+  uuid: string
+  user_uuid: string
+  file_upload_bytes_used: number
+  created_at_timestamp: number
+  updated_at_timestamp: number
+}

+ 10 - 0
packages/api-gateway/src/Mapping/Sync/Http/SharedVaultInviteHttpRepresentation.ts

@@ -0,0 +1,10 @@
+export interface SharedVaultInviteHttpRepresentation {
+  uuid: string
+  shared_vault_uuid: string
+  user_uuid: string
+  sender_uuid: string
+  encrypted_message: string
+  permission: string
+  created_at_timestamp: number
+  updated_at_timestamp: number
+}

+ 19 - 0
packages/api-gateway/src/Mapping/Sync/Http/SyncResponseHttpRepresentation.ts

@@ -0,0 +1,19 @@
+import { ItemConflictHttpRepresentation } from './ItemConflictHttpRepresentation'
+import { ItemHttpRepresentation } from './ItemHttpRepresentation'
+import { MessageHttpRepresentation } from './MessageHttpRepresentation'
+import { NotificationHttpRepresentation } from './NotificationHttpRepresentation'
+import { SavedItemHttpRepresentation } from './SavedItemHttpRepresentation'
+import { SharedVaultHttpRepresentation } from './SharedVaultHttpRepresentation'
+import { SharedVaultInviteHttpRepresentation } from './SharedVaultInviteHttpRepresentation'
+
+export type SyncResponseHttpRepresentation = {
+  retrieved_items: ItemHttpRepresentation[]
+  saved_items: SavedItemHttpRepresentation[]
+  conflicts: ItemConflictHttpRepresentation[]
+  sync_token: string
+  cursor_token?: string
+  messages: MessageHttpRepresentation[]
+  shared_vaults: SharedVaultHttpRepresentation[]
+  shared_vault_invites: SharedVaultInviteHttpRepresentation[]
+  notifications: NotificationHttpRepresentation[]
+}

+ 11 - 15
packages/api-gateway/src/Service/DirectCall/DirectCallServiceProxy.ts

@@ -42,7 +42,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
     }
   }
 
-  async callEmailServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
+  async callEmailServer(_request: Request, response: Response, _methodIdentifier: string): Promise<void> {
     response.status(400).send({
       error: {
         message: 'Email server is not available.',
@@ -50,13 +50,13 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
     })
   }
 
-  async callAuthServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
+  async callAuthServer(request: never, response: never, methodIdentifier: string): Promise<void> {
     const authService = this.serviceContainer.get(ServiceIdentifier.create(ServiceIdentifier.NAMES.Auth).getValue())
     if (!authService) {
       throw new Error('Auth service not found')
     }
 
-    const serviceResponse = (await authService.handleRequest(request, response, endpointOrMethodIdentifier)) as {
+    const serviceResponse = (await authService.handleRequest(request, response, methodIdentifier)) as {
       statusCode: number
       json: Record<string, unknown>
     }
@@ -67,7 +67,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
   async callAuthServerWithLegacyFormat(
     _request: Request,
     response: Response,
-    _endpointOrMethodIdentifier: string,
+    _methodIdentifier: string,
   ): Promise<void> {
     response.status(400).send({
       error: {
@@ -76,13 +76,13 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
     })
   }
 
-  async callRevisionsServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
+  async callRevisionsServer(request: never, response: never, methodIdentifier: string): Promise<void> {
     const service = this.serviceContainer.get(ServiceIdentifier.create(ServiceIdentifier.NAMES.Revisions).getValue())
     if (!service) {
       throw new Error('Revisions service not found')
     }
 
-    const serviceResponse = (await service.handleRequest(request, response, endpointOrMethodIdentifier)) as {
+    const serviceResponse = (await service.handleRequest(request, response, methodIdentifier)) as {
       statusCode: number
       json: Record<string, unknown>
     }
@@ -90,7 +90,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
     this.sendDecoratedResponse(response, serviceResponse)
   }
 
-  async callSyncingServer(request: never, response: never, endpointOrMethodIdentifier: string): Promise<void> {
+  async callSyncingServer(request: never, response: never, methodIdentifier: string): Promise<void> {
     const service = this.serviceContainer.get(
       ServiceIdentifier.create(ServiceIdentifier.NAMES.SyncingServer).getValue(),
     )
@@ -98,7 +98,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
       throw new Error('Syncing service not found')
     }
 
-    const serviceResponse = (await service.handleRequest(request, response, endpointOrMethodIdentifier)) as {
+    const serviceResponse = (await service.handleRequest(request, response, methodIdentifier)) as {
       statusCode: number
       json: Record<string, unknown>
     }
@@ -106,11 +106,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
     this.sendDecoratedResponse(response, serviceResponse)
   }
 
-  async callLegacySyncingServer(
-    _request: Request,
-    response: Response,
-    _endpointOrMethodIdentifier: string,
-  ): Promise<void> {
+  async callLegacySyncingServer(_request: Request, response: Response, _methodIdentifier: string): Promise<void> {
     response.status(400).send({
       error: {
         message: 'Legacy syncing server endpoints are no longer available.',
@@ -118,7 +114,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
     })
   }
 
-  async callPaymentsServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
+  async callPaymentsServer(_request: Request, response: Response, _methodIdentifier: string): Promise<void> {
     response.status(400).send({
       error: {
         message: 'Payments server is not available.',
@@ -126,7 +122,7 @@ export class DirectCallServiceProxy implements ServiceProxyInterface {
     })
   }
 
-  async callWebSocketServer(_request: Request, response: Response, _endpointOrMethodIdentifier: string): Promise<void> {
+  async callWebSocketServer(_request: Request, response: Response, _methodIdentifier: string): Promise<void> {
     response.status(400).send({
       error: {
         message: 'Websockets server is not available.',

+ 28 - 69
packages/api-gateway/src/Service/Http/HttpServiceProxy.ts

@@ -72,16 +72,16 @@ export class HttpServiceProxy implements ServiceProxyInterface {
   async callSyncingServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
-    await this.callServer(this.syncingServerJsUrl, request, response, endpointOrMethodIdentifier, payload)
+    await this.callServer(this.syncingServerJsUrl, request, response, endpoint, payload)
   }
 
   async callRevisionsServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
     if (!this.revisionsServerUrl) {
@@ -89,37 +89,31 @@ export class HttpServiceProxy implements ServiceProxyInterface {
 
       return
     }
-    await this.callServer(this.revisionsServerUrl, request, response, endpointOrMethodIdentifier, payload)
+    await this.callServer(this.revisionsServerUrl, request, response, endpoint, payload)
   }
 
   async callLegacySyncingServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
-    await this.callServerWithLegacyFormat(
-      this.syncingServerJsUrl,
-      request,
-      response,
-      endpointOrMethodIdentifier,
-      payload,
-    )
+    await this.callServerWithLegacyFormat(this.syncingServerJsUrl, request, response, endpoint, payload)
   }
 
   async callAuthServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
-    await this.callServer(this.authServerUrl, request, response, endpointOrMethodIdentifier, payload)
+    await this.callServer(this.authServerUrl, request, response, endpoint, payload)
   }
 
   async callEmailServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
     if (!this.emailServerUrl) {
@@ -128,13 +122,13 @@ export class HttpServiceProxy implements ServiceProxyInterface {
       return
     }
 
-    await this.callServer(this.emailServerUrl, request, response, endpointOrMethodIdentifier, payload)
+    await this.callServer(this.emailServerUrl, request, response, endpoint, payload)
   }
 
   async callWebSocketServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
     if (!this.webSocketServerUrl) {
@@ -145,22 +139,16 @@ export class HttpServiceProxy implements ServiceProxyInterface {
 
     const isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat = request.headers.connectionid !== undefined
     if (isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat) {
-      await this.callServerWithLegacyFormat(
-        this.webSocketServerUrl,
-        request,
-        response,
-        endpointOrMethodIdentifier,
-        payload,
-      )
+      await this.callServerWithLegacyFormat(this.webSocketServerUrl, request, response, endpoint, payload)
     } else {
-      await this.callServer(this.webSocketServerUrl, request, response, endpointOrMethodIdentifier, payload)
+      await this.callServer(this.webSocketServerUrl, request, response, endpoint, payload)
     }
   }
 
   async callPaymentsServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void | Response<unknown, Record<string, unknown>>> {
     if (!this.paymentsServerUrl) {
@@ -169,29 +157,23 @@ export class HttpServiceProxy implements ServiceProxyInterface {
       return
     }
 
-    await this.callServerWithLegacyFormat(
-      this.paymentsServerUrl,
-      request,
-      response,
-      endpointOrMethodIdentifier,
-      payload,
-    )
+    await this.callServerWithLegacyFormat(this.paymentsServerUrl, request, response, endpoint, payload)
   }
 
   async callAuthServerWithLegacyFormat(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
-    await this.callServerWithLegacyFormat(this.authServerUrl, request, response, endpointOrMethodIdentifier, payload)
+    await this.callServerWithLegacyFormat(this.authServerUrl, request, response, endpoint, payload)
   }
 
   private async getServerResponse(
     serverUrl: string,
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
     retryAttempt?: number,
   ): Promise<AxiosResponse | undefined> {
@@ -215,7 +197,7 @@ export class HttpServiceProxy implements ServiceProxyInterface {
       const serviceResponse = await this.httpClient.request({
         method: request.method as Method,
         headers,
-        url: `${serverUrl}/${endpointOrMethodIdentifier}`,
+        url: `${serverUrl}/${endpoint}`,
         data: this.getRequestData(payload),
         maxContentLength: Infinity,
         maxBodyLength: Infinity,
@@ -232,9 +214,7 @@ export class HttpServiceProxy implements ServiceProxyInterface {
       }
 
       if (retryAttempt) {
-        this.logger.debug(
-          `Request to ${serverUrl}/${endpointOrMethodIdentifier} succeeded after ${retryAttempt} retries`,
-        )
+        this.logger.debug(`Request to ${serverUrl}/${endpoint} succeeded after ${retryAttempt} retries`)
       }
 
       return serviceResponse
@@ -246,18 +226,9 @@ export class HttpServiceProxy implements ServiceProxyInterface {
 
         const nextRetryAttempt = retryAttempt ? retryAttempt + 1 : 1
 
-        this.logger.debug(
-          `Retrying request to ${serverUrl}/${endpointOrMethodIdentifier} for the ${nextRetryAttempt} time`,
-        )
+        this.logger.debug(`Retrying request to ${serverUrl}/${endpoint} for the ${nextRetryAttempt} time`)
 
-        return this.getServerResponse(
-          serverUrl,
-          request,
-          response,
-          endpointOrMethodIdentifier,
-          payload,
-          nextRetryAttempt,
-        )
+        return this.getServerResponse(serverUrl, request, response, endpoint, payload, nextRetryAttempt)
       }
 
       let detailedErrorMessage = (error as Error).message
@@ -267,8 +238,8 @@ export class HttpServiceProxy implements ServiceProxyInterface {
 
       this.logger.error(
         tooManyRetryAttempts
-          ? `Request to ${serverUrl}/${endpointOrMethodIdentifier} timed out after ${retryAttempt} retries`
-          : `Could not pass the request to ${serverUrl}/${endpointOrMethodIdentifier} on underlying service: ${detailedErrorMessage}`,
+          ? `Request to ${serverUrl}/${endpoint} timed out after ${retryAttempt} retries`
+          : `Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${detailedErrorMessage}`,
       )
 
       this.logger.debug(`Response error: ${JSON.stringify(error)}`)
@@ -299,16 +270,10 @@ export class HttpServiceProxy implements ServiceProxyInterface {
     serverUrl: string,
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
-    const serviceResponse = await this.getServerResponse(
-      serverUrl,
-      request,
-      response,
-      endpointOrMethodIdentifier,
-      payload,
-    )
+    const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
 
     if (!serviceResponse) {
       return
@@ -340,16 +305,10 @@ export class HttpServiceProxy implements ServiceProxyInterface {
     serverUrl: string,
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void | Response<unknown, Record<string, unknown>>> {
-    const serviceResponse = await this.getServerResponse(
-      serverUrl,
-      request,
-      response,
-      endpointOrMethodIdentifier,
-      payload,
-    )
+    const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
 
     if (!serviceResponse) {
       return

+ 38 - 69
packages/api-gateway/src/Service/gRPC/GRPCServiceProxy.ts

@@ -7,6 +7,7 @@ import * as grpc from '@grpc/grpc-js'
 
 import { CrossServiceTokenCacheInterface } from '../Cache/CrossServiceTokenCacheInterface'
 import { ServiceProxyInterface } from '../Proxy/ServiceProxyInterface'
+import { GRPCSyncingServerServiceProxy } from './GRPCSyncingServerServiceProxy'
 
 export class GRPCServiceProxy implements ServiceProxyInterface {
   constructor(
@@ -23,6 +24,7 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
     private logger: Logger,
     private timer: TimerInterface,
     private sessionsClient: ISessionsClient,
+    private gRPCSyncingServerServiceProxy: GRPCSyncingServerServiceProxy,
   ) {}
 
   async validateSession(headers: {
@@ -83,16 +85,24 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
   async callSyncingServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
-    await this.callServer(this.syncingServerJsUrl, request, response, endpointOrMethodIdentifier, payload)
+    if (endpoint === 'items/sync') {
+      const result = await this.gRPCSyncingServerServiceProxy.sync(request, response, payload)
+
+      response.status(result.status).send(result.data)
+
+      return
+    }
+
+    await this.callServer(this.syncingServerJsUrl, request, response, endpoint, payload)
   }
 
   async callRevisionsServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
     if (!this.revisionsServerUrl) {
@@ -100,37 +110,31 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
 
       return
     }
-    await this.callServer(this.revisionsServerUrl, request, response, endpointOrMethodIdentifier, payload)
+    await this.callServer(this.revisionsServerUrl, request, response, endpoint, payload)
   }
 
   async callLegacySyncingServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
-    await this.callServerWithLegacyFormat(
-      this.syncingServerJsUrl,
-      request,
-      response,
-      endpointOrMethodIdentifier,
-      payload,
-    )
+    await this.callServerWithLegacyFormat(this.syncingServerJsUrl, request, response, endpoint, payload)
   }
 
   async callAuthServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
-    await this.callServer(this.authServerUrl, request, response, endpointOrMethodIdentifier, payload)
+    await this.callServer(this.authServerUrl, request, response, endpoint, payload)
   }
 
   async callEmailServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
     if (!this.emailServerUrl) {
@@ -139,13 +143,13 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
       return
     }
 
-    await this.callServer(this.emailServerUrl, request, response, endpointOrMethodIdentifier, payload)
+    await this.callServer(this.emailServerUrl, request, response, endpoint, payload)
   }
 
   async callWebSocketServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
     if (!this.webSocketServerUrl) {
@@ -156,22 +160,16 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
 
     const isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat = request.headers.connectionid !== undefined
     if (isARequestComingFromApiGatewayAndShouldBeKeptInMinimalFormat) {
-      await this.callServerWithLegacyFormat(
-        this.webSocketServerUrl,
-        request,
-        response,
-        endpointOrMethodIdentifier,
-        payload,
-      )
+      await this.callServerWithLegacyFormat(this.webSocketServerUrl, request, response, endpoint, payload)
     } else {
-      await this.callServer(this.webSocketServerUrl, request, response, endpointOrMethodIdentifier, payload)
+      await this.callServer(this.webSocketServerUrl, request, response, endpoint, payload)
     }
   }
 
   async callPaymentsServer(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void | Response<unknown, Record<string, unknown>>> {
     if (!this.paymentsServerUrl) {
@@ -180,29 +178,23 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
       return
     }
 
-    await this.callServerWithLegacyFormat(
-      this.paymentsServerUrl,
-      request,
-      response,
-      endpointOrMethodIdentifier,
-      payload,
-    )
+    await this.callServerWithLegacyFormat(this.paymentsServerUrl, request, response, endpoint, payload)
   }
 
   async callAuthServerWithLegacyFormat(
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
-    await this.callServerWithLegacyFormat(this.authServerUrl, request, response, endpointOrMethodIdentifier, payload)
+    await this.callServerWithLegacyFormat(this.authServerUrl, request, response, endpoint, payload)
   }
 
   private async getServerResponse(
     serverUrl: string,
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
     retryAttempt?: number,
   ): Promise<AxiosResponse | undefined> {
@@ -226,7 +218,7 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
       const serviceResponse = await this.httpClient.request({
         method: request.method as Method,
         headers,
-        url: `${serverUrl}/${endpointOrMethodIdentifier}`,
+        url: `${serverUrl}/${endpoint}`,
         data: this.getRequestData(payload),
         maxContentLength: Infinity,
         maxBodyLength: Infinity,
@@ -243,9 +235,7 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
       }
 
       if (retryAttempt) {
-        this.logger.debug(
-          `Request to ${serverUrl}/${endpointOrMethodIdentifier} succeeded after ${retryAttempt} retries`,
-        )
+        this.logger.debug(`Request to ${serverUrl}/${endpoint} succeeded after ${retryAttempt} retries`)
       }
 
       return serviceResponse
@@ -257,18 +247,9 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
 
         const nextRetryAttempt = retryAttempt ? retryAttempt + 1 : 1
 
-        this.logger.debug(
-          `Retrying request to ${serverUrl}/${endpointOrMethodIdentifier} for the ${nextRetryAttempt} time`,
-        )
+        this.logger.debug(`Retrying request to ${serverUrl}/${endpoint} for the ${nextRetryAttempt} time`)
 
-        return this.getServerResponse(
-          serverUrl,
-          request,
-          response,
-          endpointOrMethodIdentifier,
-          payload,
-          nextRetryAttempt,
-        )
+        return this.getServerResponse(serverUrl, request, response, endpoint, payload, nextRetryAttempt)
       }
 
       let detailedErrorMessage = (error as Error).message
@@ -278,8 +259,8 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
 
       this.logger.error(
         tooManyRetryAttempts
-          ? `Request to ${serverUrl}/${endpointOrMethodIdentifier} timed out after ${retryAttempt} retries`
-          : `Could not pass the request to ${serverUrl}/${endpointOrMethodIdentifier} on underlying service: ${detailedErrorMessage}`,
+          ? `Request to ${serverUrl}/${endpoint} timed out after ${retryAttempt} retries`
+          : `Could not pass the request to ${serverUrl}/${endpoint} on underlying service: ${detailedErrorMessage}`,
       )
 
       this.logger.debug(`Response error: ${JSON.stringify(error)}`)
@@ -310,16 +291,10 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
     serverUrl: string,
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void> {
-    const serviceResponse = await this.getServerResponse(
-      serverUrl,
-      request,
-      response,
-      endpointOrMethodIdentifier,
-      payload,
-    )
+    const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
 
     if (!serviceResponse) {
       return
@@ -351,16 +326,10 @@ export class GRPCServiceProxy implements ServiceProxyInterface {
     serverUrl: string,
     request: Request,
     response: Response,
-    endpointOrMethodIdentifier: string,
+    endpoint: string,
     payload?: Record<string, unknown> | string,
   ): Promise<void | Response<unknown, Record<string, unknown>>> {
-    const serviceResponse = await this.getServerResponse(
-      serverUrl,
-      request,
-      response,
-      endpointOrMethodIdentifier,
-      payload,
-    )
+    const serviceResponse = await this.getServerResponse(serverUrl, request, response, endpoint, payload)
 
     if (!serviceResponse) {
       return

+ 52 - 0
packages/api-gateway/src/Service/gRPC/GRPCSyncingServerServiceProxy.ts

@@ -0,0 +1,52 @@
+import { Request, Response } from 'express'
+import { ISyncingClient, SyncRequest, SyncResponse } from '@standardnotes/grpc'
+import { MapperInterface } from '@standardnotes/domain-core'
+import { Metadata } from '@grpc/grpc-js'
+
+import { SyncResponseHttpRepresentation } from '../../Mapping/Sync/Http/SyncResponseHttpRepresentation'
+
+export class GRPCSyncingServerServiceProxy {
+  constructor(
+    private syncingClient: ISyncingClient,
+    private syncRequestGRPCMapper: MapperInterface<Record<string, unknown>, SyncRequest>,
+    private syncResponseGRPCMapper: MapperInterface<SyncResponse, SyncResponseHttpRepresentation>,
+  ) {}
+
+  async sync(
+    request: Request,
+    response: Response,
+    payload?: Record<string, unknown> | string,
+  ): Promise<{ status: number; data: unknown }> {
+    return new Promise((resolve, reject) => {
+      try {
+        const syncRequest = this.syncRequestGRPCMapper.toProjection(payload as Record<string, unknown>)
+
+        const metadata = new Metadata()
+        metadata.set('x-user-uuid', response.locals.user.uuid)
+        metadata.set('x-snjs-version', request.headers['x-snjs-version'] as string)
+        metadata.set('x-read-only-access', response.locals.readonlyAccess ? 'true' : 'false')
+        if (response.locals.session) {
+          metadata.set('x-session-uuid', response.locals.session.uuid)
+        }
+
+        this.syncingClient.syncItems(syncRequest, metadata, (error, syncResponse) => {
+          if (error) {
+            const responseCode = error.metadata.get('x-sync-error-response-code').pop()
+            if (responseCode) {
+              return resolve({
+                status: +responseCode,
+                data: { error: { message: error.metadata.get('x-sync-error-message').pop() } },
+              })
+            }
+
+            return reject(error)
+          }
+
+          return resolve({ status: 200, data: this.syncResponseGRPCMapper.toProjection(syncResponse) })
+        })
+      } catch (error) {
+        reject(error)
+      }
+    })
+  }
+}

+ 9 - 0
packages/auth/bin/server.ts

@@ -100,6 +100,8 @@ void container.load().then((container) => {
   grpcServer.bindAsync(`0.0.0.0:${gRPCPort}`, grpc.ServerCredentials.createInsecure(), (error, port) => {
     if (error) {
       logger.error(`Failed to bind gRPC server: ${error.message}`)
+
+      return
     }
 
     logger.info(`gRPC server bound on port ${port}`)
@@ -114,6 +116,13 @@ void container.load().then((container) => {
     serverInstance.close(() => {
       logger.info('HTTP server closed')
     })
+    grpcServer.tryShutdown((error?: Error) => {
+      if (error) {
+        logger.error(`Failed to shutdown gRPC server: ${error.message}`)
+      } else {
+        logger.info('gRPC server closed')
+      }
+    })
   })
 
   logger.info(`Server started on port ${process.env.PORT}`)

+ 1 - 2
packages/auth/package.json

@@ -20,7 +20,6 @@
     "lint:fix": "eslint . --fix --ext .ts",
     "pretest": "yarn lint && yarn build",
     "test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
-    "grpc": "yarn node dist/bin/grpc.js",
     "start": "yarn node dist/bin/server.js",
     "worker": "yarn node dist/bin/worker.js",
     "cleanup": "yarn node dist/bin/cleanup.js",
@@ -38,7 +37,7 @@
     "@aws-sdk/client-sqs": "^3.427.0",
     "@cbor-extract/cbor-extract-linux-arm64": "^2.1.1",
     "@cbor-extract/cbor-extract-linux-x64": "^2.1.1",
-    "@grpc/grpc-js": "^1.9.10",
+    "@grpc/grpc-js": "^1.9.11",
     "@simplewebauthn/server": "^8.1.1",
     "@simplewebauthn/typescript-types": "^8.0.0",
     "@standardnotes/api": "^1.26.26",

+ 2 - 0
packages/grpc/lib/index.d.ts

@@ -1,2 +1,4 @@
 export * from './auth_grpc_pb'
 export * from './auth_pb'
+export * from './sync_grpc_pb'
+export * from './sync_pb'

+ 2 - 0
packages/grpc/lib/index.js

@@ -16,3 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
 Object.defineProperty(exports, "__esModule", { value: true });
 __exportStar(require("./auth_grpc_pb"), exports);
 __exportStar(require("./auth_pb"), exports);
+__exportStar(require("./sync_grpc_pb"), exports);
+__exportStar(require("./sync_pb"), exports);

+ 41 - 0
packages/grpc/lib/sync_grpc_pb.d.ts

@@ -0,0 +1,41 @@
+// package: sync
+// file: sync.proto
+
+/* tslint:disable */
+/* eslint-disable */
+
+import * as grpc from "@grpc/grpc-js";
+import * as sync_pb from "./sync_pb";
+
+interface ISyncingService extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {
+    syncItems: ISyncingService_IsyncItems;
+}
+
+interface ISyncingService_IsyncItems extends grpc.MethodDefinition<sync_pb.SyncRequest, sync_pb.SyncResponse> {
+    path: "/sync.Syncing/syncItems";
+    requestStream: false;
+    responseStream: false;
+    requestSerialize: grpc.serialize<sync_pb.SyncRequest>;
+    requestDeserialize: grpc.deserialize<sync_pb.SyncRequest>;
+    responseSerialize: grpc.serialize<sync_pb.SyncResponse>;
+    responseDeserialize: grpc.deserialize<sync_pb.SyncResponse>;
+}
+
+export const SyncingService: ISyncingService;
+
+export interface ISyncingServer {
+    syncItems: grpc.handleUnaryCall<sync_pb.SyncRequest, sync_pb.SyncResponse>;
+}
+
+export interface ISyncingClient {
+    syncItems(request: sync_pb.SyncRequest, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
+    syncItems(request: sync_pb.SyncRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
+    syncItems(request: sync_pb.SyncRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
+}
+
+export class SyncingClient extends grpc.Client implements ISyncingClient {
+    constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);
+    public syncItems(request: sync_pb.SyncRequest, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
+    public syncItems(request: sync_pb.SyncRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
+    public syncItems(request: sync_pb.SyncRequest, metadata: grpc.Metadata, options: Partial<grpc.CallOptions>, callback: (error: grpc.ServiceError | null, response: sync_pb.SyncResponse) => void): grpc.ClientUnaryCall;
+}

+ 44 - 0
packages/grpc/lib/sync_grpc_pb.js

@@ -0,0 +1,44 @@
+// GENERATED CODE -- DO NOT EDIT!
+
+'use strict';
+var grpc = require('@grpc/grpc-js');
+var sync_pb = require('./sync_pb.js');
+
+function serialize_sync_SyncRequest(arg) {
+  if (!(arg instanceof sync_pb.SyncRequest)) {
+    throw new Error('Expected argument of type sync.SyncRequest');
+  }
+  return Buffer.from(arg.serializeBinary());
+}
+
+function deserialize_sync_SyncRequest(buffer_arg) {
+  return sync_pb.SyncRequest.deserializeBinary(new Uint8Array(buffer_arg));
+}
+
+function serialize_sync_SyncResponse(arg) {
+  if (!(arg instanceof sync_pb.SyncResponse)) {
+    throw new Error('Expected argument of type sync.SyncResponse');
+  }
+  return Buffer.from(arg.serializeBinary());
+}
+
+function deserialize_sync_SyncResponse(buffer_arg) {
+  return sync_pb.SyncResponse.deserializeBinary(new Uint8Array(buffer_arg));
+}
+
+
+var SyncingService = exports.SyncingService = {
+  syncItems: {
+    path: '/sync.Syncing/syncItems',
+    requestStream: false,
+    responseStream: false,
+    requestType: sync_pb.SyncRequest,
+    responseType: sync_pb.SyncResponse,
+    requestSerialize: serialize_sync_SyncRequest,
+    requestDeserialize: deserialize_sync_SyncRequest,
+    responseSerialize: serialize_sync_SyncResponse,
+    responseDeserialize: deserialize_sync_SyncResponse,
+  },
+};
+
+exports.SyncingClient = grpc.makeGenericClientConstructor(SyncingService);

+ 683 - 0
packages/grpc/lib/sync_pb.d.ts

@@ -0,0 +1,683 @@
+// package: sync
+// file: sync.proto
+
+/* tslint:disable */
+/* eslint-disable */
+
+import * as jspb from "google-protobuf";
+
+export class ItemHashRepresentation extends jspb.Message { 
+    getUuid(): string;
+    setUuid(value: string): ItemHashRepresentation;
+    getUserUuid(): string;
+    setUserUuid(value: string): ItemHashRepresentation;
+
+    hasContent(): boolean;
+    clearContent(): void;
+    getContent(): string | undefined;
+    setContent(value: string): ItemHashRepresentation;
+
+    hasContentType(): boolean;
+    clearContentType(): void;
+    getContentType(): string | undefined;
+    setContentType(value: string): ItemHashRepresentation;
+
+    hasDeleted(): boolean;
+    clearDeleted(): void;
+    getDeleted(): boolean | undefined;
+    setDeleted(value: boolean): ItemHashRepresentation;
+
+    hasDuplicateOf(): boolean;
+    clearDuplicateOf(): void;
+    getDuplicateOf(): string | undefined;
+    setDuplicateOf(value: string): ItemHashRepresentation;
+
+    hasAuthHash(): boolean;
+    clearAuthHash(): void;
+    getAuthHash(): string | undefined;
+    setAuthHash(value: string): ItemHashRepresentation;
+
+    hasEncItemKey(): boolean;
+    clearEncItemKey(): void;
+    getEncItemKey(): string | undefined;
+    setEncItemKey(value: string): ItemHashRepresentation;
+
+    hasItemsKeyId(): boolean;
+    clearItemsKeyId(): void;
+    getItemsKeyId(): string | undefined;
+    setItemsKeyId(value: string): ItemHashRepresentation;
+
+    hasKeySystemIdentifier(): boolean;
+    clearKeySystemIdentifier(): void;
+    getKeySystemIdentifier(): string | undefined;
+    setKeySystemIdentifier(value: string): ItemHashRepresentation;
+
+    hasSharedVaultUuid(): boolean;
+    clearSharedVaultUuid(): void;
+    getSharedVaultUuid(): string | undefined;
+    setSharedVaultUuid(value: string): ItemHashRepresentation;
+
+    hasCreatedAt(): boolean;
+    clearCreatedAt(): void;
+    getCreatedAt(): string | undefined;
+    setCreatedAt(value: string): ItemHashRepresentation;
+
+    hasCreatedAtTimestamp(): boolean;
+    clearCreatedAtTimestamp(): void;
+    getCreatedAtTimestamp(): number | undefined;
+    setCreatedAtTimestamp(value: number): ItemHashRepresentation;
+
+    hasUpdatedAt(): boolean;
+    clearUpdatedAt(): void;
+    getUpdatedAt(): string | undefined;
+    setUpdatedAt(value: string): ItemHashRepresentation;
+
+    hasUpdatedAtTimestamp(): boolean;
+    clearUpdatedAtTimestamp(): void;
+    getUpdatedAtTimestamp(): number | undefined;
+    setUpdatedAtTimestamp(value: number): ItemHashRepresentation;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): ItemHashRepresentation.AsObject;
+    static toObject(includeInstance: boolean, msg: ItemHashRepresentation): ItemHashRepresentation.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: ItemHashRepresentation, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): ItemHashRepresentation;
+    static deserializeBinaryFromReader(message: ItemHashRepresentation, reader: jspb.BinaryReader): ItemHashRepresentation;
+}
+
+export namespace ItemHashRepresentation {
+    export type AsObject = {
+        uuid: string,
+        userUuid: string,
+        content?: string,
+        contentType?: string,
+        deleted?: boolean,
+        duplicateOf?: string,
+        authHash?: string,
+        encItemKey?: string,
+        itemsKeyId?: string,
+        keySystemIdentifier?: string,
+        sharedVaultUuid?: string,
+        createdAt?: string,
+        createdAtTimestamp?: number,
+        updatedAt?: string,
+        updatedAtTimestamp?: number,
+    }
+}
+
+export class ItemConflictRepresentation extends jspb.Message { 
+
+    hasServerItem(): boolean;
+    clearServerItem(): void;
+    getServerItem(): ItemRepresentation | undefined;
+    setServerItem(value?: ItemRepresentation): ItemConflictRepresentation;
+
+    hasUnsavedItem(): boolean;
+    clearUnsavedItem(): void;
+    getUnsavedItem(): ItemHashRepresentation | undefined;
+    setUnsavedItem(value?: ItemHashRepresentation): ItemConflictRepresentation;
+    getType(): string;
+    setType(value: string): ItemConflictRepresentation;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): ItemConflictRepresentation.AsObject;
+    static toObject(includeInstance: boolean, msg: ItemConflictRepresentation): ItemConflictRepresentation.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: ItemConflictRepresentation, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): ItemConflictRepresentation;
+    static deserializeBinaryFromReader(message: ItemConflictRepresentation, reader: jspb.BinaryReader): ItemConflictRepresentation;
+}
+
+export namespace ItemConflictRepresentation {
+    export type AsObject = {
+        serverItem?: ItemRepresentation.AsObject,
+        unsavedItem?: ItemHashRepresentation.AsObject,
+        type: string,
+    }
+}
+
+export class ItemRepresentation extends jspb.Message { 
+    getUuid(): string;
+    setUuid(value: string): ItemRepresentation;
+
+    hasItemsKeyId(): boolean;
+    clearItemsKeyId(): void;
+    getItemsKeyId(): string | undefined;
+    setItemsKeyId(value: string): ItemRepresentation;
+
+    hasDuplicateOf(): boolean;
+    clearDuplicateOf(): void;
+    getDuplicateOf(): string | undefined;
+    setDuplicateOf(value: string): ItemRepresentation;
+
+    hasEncItemKey(): boolean;
+    clearEncItemKey(): void;
+    getEncItemKey(): string | undefined;
+    setEncItemKey(value: string): ItemRepresentation;
+
+    hasContent(): boolean;
+    clearContent(): void;
+    getContent(): string | undefined;
+    setContent(value: string): ItemRepresentation;
+    getContentType(): string;
+    setContentType(value: string): ItemRepresentation;
+
+    hasAuthHash(): boolean;
+    clearAuthHash(): void;
+    getAuthHash(): string | undefined;
+    setAuthHash(value: string): ItemRepresentation;
+    getDeleted(): boolean;
+    setDeleted(value: boolean): ItemRepresentation;
+    getCreatedAt(): string;
+    setCreatedAt(value: string): ItemRepresentation;
+    getCreatedAtTimestamp(): number;
+    setCreatedAtTimestamp(value: number): ItemRepresentation;
+    getUpdatedAt(): string;
+    setUpdatedAt(value: string): ItemRepresentation;
+    getUpdatedAtTimestamp(): number;
+    setUpdatedAtTimestamp(value: number): ItemRepresentation;
+
+    hasUpdatedWithSession(): boolean;
+    clearUpdatedWithSession(): void;
+    getUpdatedWithSession(): string | undefined;
+    setUpdatedWithSession(value: string): ItemRepresentation;
+
+    hasKeySystemIdentifier(): boolean;
+    clearKeySystemIdentifier(): void;
+    getKeySystemIdentifier(): string | undefined;
+    setKeySystemIdentifier(value: string): ItemRepresentation;
+
+    hasSharedVaultUuid(): boolean;
+    clearSharedVaultUuid(): void;
+    getSharedVaultUuid(): string | undefined;
+    setSharedVaultUuid(value: string): ItemRepresentation;
+
+    hasUserUuid(): boolean;
+    clearUserUuid(): void;
+    getUserUuid(): string | undefined;
+    setUserUuid(value: string): ItemRepresentation;
+
+    hasLastEditedByUuid(): boolean;
+    clearLastEditedByUuid(): void;
+    getLastEditedByUuid(): string | undefined;
+    setLastEditedByUuid(value: string): ItemRepresentation;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): ItemRepresentation.AsObject;
+    static toObject(includeInstance: boolean, msg: ItemRepresentation): ItemRepresentation.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: ItemRepresentation, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): ItemRepresentation;
+    static deserializeBinaryFromReader(message: ItemRepresentation, reader: jspb.BinaryReader): ItemRepresentation;
+}
+
+export namespace ItemRepresentation {
+    export type AsObject = {
+        uuid: string,
+        itemsKeyId?: string,
+        duplicateOf?: string,
+        encItemKey?: string,
+        content?: string,
+        contentType: string,
+        authHash?: string,
+        deleted: boolean,
+        createdAt: string,
+        createdAtTimestamp: number,
+        updatedAt: string,
+        updatedAtTimestamp: number,
+        updatedWithSession?: string,
+        keySystemIdentifier?: string,
+        sharedVaultUuid?: string,
+        userUuid?: string,
+        lastEditedByUuid?: string,
+    }
+}
+
+export class SavedItemRepresentation extends jspb.Message { 
+    getUuid(): string;
+    setUuid(value: string): SavedItemRepresentation;
+
+    hasDuplicateOf(): boolean;
+    clearDuplicateOf(): void;
+    getDuplicateOf(): string | undefined;
+    setDuplicateOf(value: string): SavedItemRepresentation;
+    getContentType(): string;
+    setContentType(value: string): SavedItemRepresentation;
+
+    hasAuthHash(): boolean;
+    clearAuthHash(): void;
+    getAuthHash(): string | undefined;
+    setAuthHash(value: string): SavedItemRepresentation;
+    getDeleted(): boolean;
+    setDeleted(value: boolean): SavedItemRepresentation;
+    getCreatedAt(): string;
+    setCreatedAt(value: string): SavedItemRepresentation;
+    getCreatedAtTimestamp(): number;
+    setCreatedAtTimestamp(value: number): SavedItemRepresentation;
+    getUpdatedAt(): string;
+    setUpdatedAt(value: string): SavedItemRepresentation;
+    getUpdatedAtTimestamp(): number;
+    setUpdatedAtTimestamp(value: number): SavedItemRepresentation;
+
+    hasKeySystemIdentifier(): boolean;
+    clearKeySystemIdentifier(): void;
+    getKeySystemIdentifier(): string | undefined;
+    setKeySystemIdentifier(value: string): SavedItemRepresentation;
+
+    hasSharedVaultUuid(): boolean;
+    clearSharedVaultUuid(): void;
+    getSharedVaultUuid(): string | undefined;
+    setSharedVaultUuid(value: string): SavedItemRepresentation;
+
+    hasUserUuid(): boolean;
+    clearUserUuid(): void;
+    getUserUuid(): string | undefined;
+    setUserUuid(value: string): SavedItemRepresentation;
+
+    hasLastEditedByUuid(): boolean;
+    clearLastEditedByUuid(): void;
+    getLastEditedByUuid(): string | undefined;
+    setLastEditedByUuid(value: string): SavedItemRepresentation;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): SavedItemRepresentation.AsObject;
+    static toObject(includeInstance: boolean, msg: SavedItemRepresentation): SavedItemRepresentation.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: SavedItemRepresentation, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): SavedItemRepresentation;
+    static deserializeBinaryFromReader(message: SavedItemRepresentation, reader: jspb.BinaryReader): SavedItemRepresentation;
+}
+
+export namespace SavedItemRepresentation {
+    export type AsObject = {
+        uuid: string,
+        duplicateOf?: string,
+        contentType: string,
+        authHash?: string,
+        deleted: boolean,
+        createdAt: string,
+        createdAtTimestamp: number,
+        updatedAt: string,
+        updatedAtTimestamp: number,
+        keySystemIdentifier?: string,
+        sharedVaultUuid?: string,
+        userUuid?: string,
+        lastEditedByUuid?: string,
+    }
+}
+
+export class MessageRepresentation extends jspb.Message { 
+    getUuid(): string;
+    setUuid(value: string): MessageRepresentation;
+    getRecipientUuid(): string;
+    setRecipientUuid(value: string): MessageRepresentation;
+    getSenderUuid(): string;
+    setSenderUuid(value: string): MessageRepresentation;
+    getEncryptedMessage(): string;
+    setEncryptedMessage(value: string): MessageRepresentation;
+
+    hasReplaceabilityIdentifier(): boolean;
+    clearReplaceabilityIdentifier(): void;
+    getReplaceabilityIdentifier(): string | undefined;
+    setReplaceabilityIdentifier(value: string): MessageRepresentation;
+    getCreatedAtTimestamp(): number;
+    setCreatedAtTimestamp(value: number): MessageRepresentation;
+    getUpdatedAtTimestamp(): number;
+    setUpdatedAtTimestamp(value: number): MessageRepresentation;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): MessageRepresentation.AsObject;
+    static toObject(includeInstance: boolean, msg: MessageRepresentation): MessageRepresentation.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: MessageRepresentation, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): MessageRepresentation;
+    static deserializeBinaryFromReader(message: MessageRepresentation, reader: jspb.BinaryReader): MessageRepresentation;
+}
+
+export namespace MessageRepresentation {
+    export type AsObject = {
+        uuid: string,
+        recipientUuid: string,
+        senderUuid: string,
+        encryptedMessage: string,
+        replaceabilityIdentifier?: string,
+        createdAtTimestamp: number,
+        updatedAtTimestamp: number,
+    }
+}
+
+export class SharedVaultRepresentation extends jspb.Message { 
+    getUuid(): string;
+    setUuid(value: string): SharedVaultRepresentation;
+    getUserUuid(): string;
+    setUserUuid(value: string): SharedVaultRepresentation;
+    getFileUploadBytesUsed(): number;
+    setFileUploadBytesUsed(value: number): SharedVaultRepresentation;
+    getCreatedAtTimestamp(): number;
+    setCreatedAtTimestamp(value: number): SharedVaultRepresentation;
+    getUpdatedAtTimestamp(): number;
+    setUpdatedAtTimestamp(value: number): SharedVaultRepresentation;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): SharedVaultRepresentation.AsObject;
+    static toObject(includeInstance: boolean, msg: SharedVaultRepresentation): SharedVaultRepresentation.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: SharedVaultRepresentation, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): SharedVaultRepresentation;
+    static deserializeBinaryFromReader(message: SharedVaultRepresentation, reader: jspb.BinaryReader): SharedVaultRepresentation;
+}
+
+export namespace SharedVaultRepresentation {
+    export type AsObject = {
+        uuid: string,
+        userUuid: string,
+        fileUploadBytesUsed: number,
+        createdAtTimestamp: number,
+        updatedAtTimestamp: number,
+    }
+}
+
+export class SharedVaultInviteRepresentation extends jspb.Message { 
+    getUuid(): string;
+    setUuid(value: string): SharedVaultInviteRepresentation;
+    getSharedVaultUuid(): string;
+    setSharedVaultUuid(value: string): SharedVaultInviteRepresentation;
+    getUserUuid(): string;
+    setUserUuid(value: string): SharedVaultInviteRepresentation;
+    getSenderUuid(): string;
+    setSenderUuid(value: string): SharedVaultInviteRepresentation;
+    getEncryptedMessage(): string;
+    setEncryptedMessage(value: string): SharedVaultInviteRepresentation;
+    getPermission(): string;
+    setPermission(value: string): SharedVaultInviteRepresentation;
+    getCreatedAtTimestamp(): number;
+    setCreatedAtTimestamp(value: number): SharedVaultInviteRepresentation;
+    getUpdatedAtTimestamp(): number;
+    setUpdatedAtTimestamp(value: number): SharedVaultInviteRepresentation;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): SharedVaultInviteRepresentation.AsObject;
+    static toObject(includeInstance: boolean, msg: SharedVaultInviteRepresentation): SharedVaultInviteRepresentation.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: SharedVaultInviteRepresentation, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): SharedVaultInviteRepresentation;
+    static deserializeBinaryFromReader(message: SharedVaultInviteRepresentation, reader: jspb.BinaryReader): SharedVaultInviteRepresentation;
+}
+
+export namespace SharedVaultInviteRepresentation {
+    export type AsObject = {
+        uuid: string,
+        sharedVaultUuid: string,
+        userUuid: string,
+        senderUuid: string,
+        encryptedMessage: string,
+        permission: string,
+        createdAtTimestamp: number,
+        updatedAtTimestamp: number,
+    }
+}
+
+export class NotificationRepresentation extends jspb.Message { 
+    getUuid(): string;
+    setUuid(value: string): NotificationRepresentation;
+    getUserUuid(): string;
+    setUserUuid(value: string): NotificationRepresentation;
+    getType(): string;
+    setType(value: string): NotificationRepresentation;
+    getPayload(): string;
+    setPayload(value: string): NotificationRepresentation;
+    getCreatedAtTimestamp(): number;
+    setCreatedAtTimestamp(value: number): NotificationRepresentation;
+    getUpdatedAtTimestamp(): number;
+    setUpdatedAtTimestamp(value: number): NotificationRepresentation;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): NotificationRepresentation.AsObject;
+    static toObject(includeInstance: boolean, msg: NotificationRepresentation): NotificationRepresentation.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: NotificationRepresentation, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): NotificationRepresentation;
+    static deserializeBinaryFromReader(message: NotificationRepresentation, reader: jspb.BinaryReader): NotificationRepresentation;
+}
+
+export namespace NotificationRepresentation {
+    export type AsObject = {
+        uuid: string,
+        userUuid: string,
+        type: string,
+        payload: string,
+        createdAtTimestamp: number,
+        updatedAtTimestamp: number,
+    }
+}
+
+export class ItemHash extends jspb.Message { 
+    getUuid(): string;
+    setUuid(value: string): ItemHash;
+
+    hasContent(): boolean;
+    clearContent(): void;
+    getContent(): string | undefined;
+    setContent(value: string): ItemHash;
+
+    hasContentType(): boolean;
+    clearContentType(): void;
+    getContentType(): string | undefined;
+    setContentType(value: string): ItemHash;
+
+    hasDeleted(): boolean;
+    clearDeleted(): void;
+    getDeleted(): boolean | undefined;
+    setDeleted(value: boolean): ItemHash;
+
+    hasDuplicateOf(): boolean;
+    clearDuplicateOf(): void;
+    getDuplicateOf(): string | undefined;
+    setDuplicateOf(value: string): ItemHash;
+
+    hasAuthHash(): boolean;
+    clearAuthHash(): void;
+    getAuthHash(): string | undefined;
+    setAuthHash(value: string): ItemHash;
+
+    hasEncItemKey(): boolean;
+    clearEncItemKey(): void;
+    getEncItemKey(): string | undefined;
+    setEncItemKey(value: string): ItemHash;
+
+    hasItemsKeyId(): boolean;
+    clearItemsKeyId(): void;
+    getItemsKeyId(): string | undefined;
+    setItemsKeyId(value: string): ItemHash;
+
+    hasKeySystemIdentifier(): boolean;
+    clearKeySystemIdentifier(): void;
+    getKeySystemIdentifier(): string | undefined;
+    setKeySystemIdentifier(value: string): ItemHash;
+
+    hasSharedVaultUuid(): boolean;
+    clearSharedVaultUuid(): void;
+    getSharedVaultUuid(): string | undefined;
+    setSharedVaultUuid(value: string): ItemHash;
+
+    hasCreatedAt(): boolean;
+    clearCreatedAt(): void;
+    getCreatedAt(): string | undefined;
+    setCreatedAt(value: string): ItemHash;
+
+    hasCreatedAtTimestamp(): boolean;
+    clearCreatedAtTimestamp(): void;
+    getCreatedAtTimestamp(): number | undefined;
+    setCreatedAtTimestamp(value: number): ItemHash;
+
+    hasUpdatedAt(): boolean;
+    clearUpdatedAt(): void;
+    getUpdatedAt(): string | undefined;
+    setUpdatedAt(value: string): ItemHash;
+
+    hasUpdatedAtTimestamp(): boolean;
+    clearUpdatedAtTimestamp(): void;
+    getUpdatedAtTimestamp(): number | undefined;
+    setUpdatedAtTimestamp(value: number): ItemHash;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): ItemHash.AsObject;
+    static toObject(includeInstance: boolean, msg: ItemHash): ItemHash.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: ItemHash, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): ItemHash;
+    static deserializeBinaryFromReader(message: ItemHash, reader: jspb.BinaryReader): ItemHash;
+}
+
+export namespace ItemHash {
+    export type AsObject = {
+        uuid: string,
+        content?: string,
+        contentType?: string,
+        deleted?: boolean,
+        duplicateOf?: string,
+        authHash?: string,
+        encItemKey?: string,
+        itemsKeyId?: string,
+        keySystemIdentifier?: string,
+        sharedVaultUuid?: string,
+        createdAt?: string,
+        createdAtTimestamp?: number,
+        updatedAt?: string,
+        updatedAtTimestamp?: number,
+    }
+}
+
+export class SyncResponse extends jspb.Message { 
+    clearRetrievedItemsList(): void;
+    getRetrievedItemsList(): Array<ItemRepresentation>;
+    setRetrievedItemsList(value: Array<ItemRepresentation>): SyncResponse;
+    addRetrievedItems(value?: ItemRepresentation, index?: number): ItemRepresentation;
+    clearSavedItemsList(): void;
+    getSavedItemsList(): Array<SavedItemRepresentation>;
+    setSavedItemsList(value: Array<SavedItemRepresentation>): SyncResponse;
+    addSavedItems(value?: SavedItemRepresentation, index?: number): SavedItemRepresentation;
+    clearConflictsList(): void;
+    getConflictsList(): Array<ItemConflictRepresentation>;
+    setConflictsList(value: Array<ItemConflictRepresentation>): SyncResponse;
+    addConflicts(value?: ItemConflictRepresentation, index?: number): ItemConflictRepresentation;
+    getSyncToken(): string;
+    setSyncToken(value: string): SyncResponse;
+
+    hasCursorToken(): boolean;
+    clearCursorToken(): void;
+    getCursorToken(): string | undefined;
+    setCursorToken(value: string): SyncResponse;
+    clearMessagesList(): void;
+    getMessagesList(): Array<MessageRepresentation>;
+    setMessagesList(value: Array<MessageRepresentation>): SyncResponse;
+    addMessages(value?: MessageRepresentation, index?: number): MessageRepresentation;
+    clearSharedVaultsList(): void;
+    getSharedVaultsList(): Array<SharedVaultRepresentation>;
+    setSharedVaultsList(value: Array<SharedVaultRepresentation>): SyncResponse;
+    addSharedVaults(value?: SharedVaultRepresentation, index?: number): SharedVaultRepresentation;
+    clearSharedVaultInvitesList(): void;
+    getSharedVaultInvitesList(): Array<SharedVaultInviteRepresentation>;
+    setSharedVaultInvitesList(value: Array<SharedVaultInviteRepresentation>): SyncResponse;
+    addSharedVaultInvites(value?: SharedVaultInviteRepresentation, index?: number): SharedVaultInviteRepresentation;
+    clearNotificationsList(): void;
+    getNotificationsList(): Array<NotificationRepresentation>;
+    setNotificationsList(value: Array<NotificationRepresentation>): SyncResponse;
+    addNotifications(value?: NotificationRepresentation, index?: number): NotificationRepresentation;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): SyncResponse.AsObject;
+    static toObject(includeInstance: boolean, msg: SyncResponse): SyncResponse.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: SyncResponse, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): SyncResponse;
+    static deserializeBinaryFromReader(message: SyncResponse, reader: jspb.BinaryReader): SyncResponse;
+}
+
+export namespace SyncResponse {
+    export type AsObject = {
+        retrievedItemsList: Array<ItemRepresentation.AsObject>,
+        savedItemsList: Array<SavedItemRepresentation.AsObject>,
+        conflictsList: Array<ItemConflictRepresentation.AsObject>,
+        syncToken: string,
+        cursorToken?: string,
+        messagesList: Array<MessageRepresentation.AsObject>,
+        sharedVaultsList: Array<SharedVaultRepresentation.AsObject>,
+        sharedVaultInvitesList: Array<SharedVaultInviteRepresentation.AsObject>,
+        notificationsList: Array<NotificationRepresentation.AsObject>,
+    }
+}
+
+export class SyncRequest extends jspb.Message { 
+    clearItemsList(): void;
+    getItemsList(): Array<ItemHash>;
+    setItemsList(value: Array<ItemHash>): SyncRequest;
+    addItems(value?: ItemHash, index?: number): ItemHash;
+    clearSharedVaultUuidsList(): void;
+    getSharedVaultUuidsList(): Array<string>;
+    setSharedVaultUuidsList(value: Array<string>): SyncRequest;
+    addSharedVaultUuids(value: string, index?: number): string;
+
+    hasComputeIntegrity(): boolean;
+    clearComputeIntegrity(): void;
+    getComputeIntegrity(): boolean | undefined;
+    setComputeIntegrity(value: boolean): SyncRequest;
+
+    hasSyncToken(): boolean;
+    clearSyncToken(): void;
+    getSyncToken(): string | undefined;
+    setSyncToken(value: string): SyncRequest;
+
+    hasCursorToken(): boolean;
+    clearCursorToken(): void;
+    getCursorToken(): string | undefined;
+    setCursorToken(value: string): SyncRequest;
+
+    hasLimit(): boolean;
+    clearLimit(): void;
+    getLimit(): number | undefined;
+    setLimit(value: number): SyncRequest;
+
+    hasContentType(): boolean;
+    clearContentType(): void;
+    getContentType(): string | undefined;
+    setContentType(value: string): SyncRequest;
+
+    hasApiVersion(): boolean;
+    clearApiVersion(): void;
+    getApiVersion(): string | undefined;
+    setApiVersion(value: string): SyncRequest;
+
+    serializeBinary(): Uint8Array;
+    toObject(includeInstance?: boolean): SyncRequest.AsObject;
+    static toObject(includeInstance: boolean, msg: SyncRequest): SyncRequest.AsObject;
+    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
+    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
+    static serializeBinaryToWriter(message: SyncRequest, writer: jspb.BinaryWriter): void;
+    static deserializeBinary(bytes: Uint8Array): SyncRequest;
+    static deserializeBinaryFromReader(message: SyncRequest, reader: jspb.BinaryReader): SyncRequest;
+}
+
+export namespace SyncRequest {
+    export type AsObject = {
+        itemsList: Array<ItemHash.AsObject>,
+        sharedVaultUuidsList: Array<string>,
+        computeIntegrity?: boolean,
+        syncToken?: string,
+        cursorToken?: string,
+        limit?: number,
+        contentType?: string,
+        apiVersion?: string,
+    }
+}

+ 5675 - 0
packages/grpc/lib/sync_pb.js

@@ -0,0 +1,5675 @@
+// source: sync.proto
+/**
+ * @fileoverview
+ * @enhanceable
+ * @suppress {missingRequire} reports error on implicit type usages.
+ * @suppress {messageConventions} JS Compiler reports an error if a variable or
+ *     field starts with 'MSG_' and isn't a translatable message.
+ * @public
+ */
+// GENERATED CODE -- DO NOT EDIT!
+/* eslint-disable */
+// @ts-nocheck
+
+var jspb = require('google-protobuf');
+var goog = jspb;
+var global = (function() {
+  if (this) { return this; }
+  if (typeof window !== 'undefined') { return window; }
+  if (typeof global !== 'undefined') { return global; }
+  if (typeof self !== 'undefined') { return self; }
+  return Function('return this')();
+}.call(null));
+
+goog.exportSymbol('proto.sync.ItemConflictRepresentation', null, global);
+goog.exportSymbol('proto.sync.ItemHash', null, global);
+goog.exportSymbol('proto.sync.ItemHashRepresentation', null, global);
+goog.exportSymbol('proto.sync.ItemRepresentation', null, global);
+goog.exportSymbol('proto.sync.MessageRepresentation', null, global);
+goog.exportSymbol('proto.sync.NotificationRepresentation', null, global);
+goog.exportSymbol('proto.sync.SavedItemRepresentation', null, global);
+goog.exportSymbol('proto.sync.SharedVaultInviteRepresentation', null, global);
+goog.exportSymbol('proto.sync.SharedVaultRepresentation', null, global);
+goog.exportSymbol('proto.sync.SyncRequest', null, global);
+goog.exportSymbol('proto.sync.SyncResponse', null, global);
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.ItemHashRepresentation = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.sync.ItemHashRepresentation, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.ItemHashRepresentation.displayName = 'proto.sync.ItemHashRepresentation';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.ItemConflictRepresentation = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.sync.ItemConflictRepresentation, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.ItemConflictRepresentation.displayName = 'proto.sync.ItemConflictRepresentation';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.ItemRepresentation = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.sync.ItemRepresentation, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.ItemRepresentation.displayName = 'proto.sync.ItemRepresentation';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.SavedItemRepresentation = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.sync.SavedItemRepresentation, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.SavedItemRepresentation.displayName = 'proto.sync.SavedItemRepresentation';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.MessageRepresentation = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.sync.MessageRepresentation, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.MessageRepresentation.displayName = 'proto.sync.MessageRepresentation';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.SharedVaultRepresentation = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.sync.SharedVaultRepresentation, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.SharedVaultRepresentation.displayName = 'proto.sync.SharedVaultRepresentation';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.SharedVaultInviteRepresentation = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.sync.SharedVaultInviteRepresentation, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.SharedVaultInviteRepresentation.displayName = 'proto.sync.SharedVaultInviteRepresentation';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.NotificationRepresentation = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.sync.NotificationRepresentation, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.NotificationRepresentation.displayName = 'proto.sync.NotificationRepresentation';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.ItemHash = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.sync.ItemHash, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.ItemHash.displayName = 'proto.sync.ItemHash';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.SyncResponse = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.sync.SyncResponse.repeatedFields_, null);
+};
+goog.inherits(proto.sync.SyncResponse, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.SyncResponse.displayName = 'proto.sync.SyncResponse';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.sync.SyncRequest = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.sync.SyncRequest.repeatedFields_, null);
+};
+goog.inherits(proto.sync.SyncRequest, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.sync.SyncRequest.displayName = 'proto.sync.SyncRequest';
+}
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.ItemHashRepresentation.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.ItemHashRepresentation.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.ItemHashRepresentation} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.ItemHashRepresentation.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    uuid: jspb.Message.getFieldWithDefault(msg, 1, ""),
+    userUuid: jspb.Message.getFieldWithDefault(msg, 2, ""),
+    content: jspb.Message.getFieldWithDefault(msg, 3, ""),
+    contentType: jspb.Message.getFieldWithDefault(msg, 4, ""),
+    deleted: jspb.Message.getBooleanFieldWithDefault(msg, 5, false),
+    duplicateOf: jspb.Message.getFieldWithDefault(msg, 6, ""),
+    authHash: jspb.Message.getFieldWithDefault(msg, 7, ""),
+    encItemKey: jspb.Message.getFieldWithDefault(msg, 8, ""),
+    itemsKeyId: jspb.Message.getFieldWithDefault(msg, 9, ""),
+    keySystemIdentifier: jspb.Message.getFieldWithDefault(msg, 10, ""),
+    sharedVaultUuid: jspb.Message.getFieldWithDefault(msg, 11, ""),
+    createdAt: jspb.Message.getFieldWithDefault(msg, 12, ""),
+    createdAtTimestamp: jspb.Message.getFieldWithDefault(msg, 13, 0),
+    updatedAt: jspb.Message.getFieldWithDefault(msg, 14, ""),
+    updatedAtTimestamp: jspb.Message.getFieldWithDefault(msg, 15, 0)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.ItemHashRepresentation}
+ */
+proto.sync.ItemHashRepresentation.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.ItemHashRepresentation;
+  return proto.sync.ItemHashRepresentation.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.ItemHashRepresentation} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.ItemHashRepresentation}
+ */
+proto.sync.ItemHashRepresentation.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUuid(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUserUuid(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setContent(value);
+      break;
+    case 4:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setContentType(value);
+      break;
+    case 5:
+      var value = /** @type {boolean} */ (reader.readBool());
+      msg.setDeleted(value);
+      break;
+    case 6:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setDuplicateOf(value);
+      break;
+    case 7:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setAuthHash(value);
+      break;
+    case 8:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setEncItemKey(value);
+      break;
+    case 9:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setItemsKeyId(value);
+      break;
+    case 10:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setKeySystemIdentifier(value);
+      break;
+    case 11:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setSharedVaultUuid(value);
+      break;
+    case 12:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setCreatedAt(value);
+      break;
+    case 13:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setCreatedAtTimestamp(value);
+      break;
+    case 14:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUpdatedAt(value);
+      break;
+    case 15:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setUpdatedAtTimestamp(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.ItemHashRepresentation.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.ItemHashRepresentation.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.ItemHashRepresentation} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.ItemHashRepresentation.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = message.getUserUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 4));
+  if (f != null) {
+    writer.writeString(
+      4,
+      f
+    );
+  }
+  f = /** @type {boolean} */ (jspb.Message.getField(message, 5));
+  if (f != null) {
+    writer.writeBool(
+      5,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 6));
+  if (f != null) {
+    writer.writeString(
+      6,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 7));
+  if (f != null) {
+    writer.writeString(
+      7,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 8));
+  if (f != null) {
+    writer.writeString(
+      8,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 9));
+  if (f != null) {
+    writer.writeString(
+      9,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 10));
+  if (f != null) {
+    writer.writeString(
+      10,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 11));
+  if (f != null) {
+    writer.writeString(
+      11,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 12));
+  if (f != null) {
+    writer.writeString(
+      12,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 13));
+  if (f != null) {
+    writer.writeUint64(
+      13,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 14));
+  if (f != null) {
+    writer.writeString(
+      14,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 15));
+  if (f != null) {
+    writer.writeUint64(
+      15,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string uuid = 1;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string user_uuid = 2;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getUserUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setUserUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 2, value);
+};
+
+
+/**
+ * optional string content = 3;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getContent = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setContent = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearContent = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasContent = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+/**
+ * optional string content_type = 4;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getContentType = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setContentType = function(value) {
+  return jspb.Message.setField(this, 4, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearContentType = function() {
+  return jspb.Message.setField(this, 4, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasContentType = function() {
+  return jspb.Message.getField(this, 4) != null;
+};
+
+
+/**
+ * optional bool deleted = 5;
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.getDeleted = function() {
+  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 5, false));
+};
+
+
+/**
+ * @param {boolean} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setDeleted = function(value) {
+  return jspb.Message.setField(this, 5, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearDeleted = function() {
+  return jspb.Message.setField(this, 5, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasDeleted = function() {
+  return jspb.Message.getField(this, 5) != null;
+};
+
+
+/**
+ * optional string duplicate_of = 6;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getDuplicateOf = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setDuplicateOf = function(value) {
+  return jspb.Message.setField(this, 6, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearDuplicateOf = function() {
+  return jspb.Message.setField(this, 6, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasDuplicateOf = function() {
+  return jspb.Message.getField(this, 6) != null;
+};
+
+
+/**
+ * optional string auth_hash = 7;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getAuthHash = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setAuthHash = function(value) {
+  return jspb.Message.setField(this, 7, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearAuthHash = function() {
+  return jspb.Message.setField(this, 7, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasAuthHash = function() {
+  return jspb.Message.getField(this, 7) != null;
+};
+
+
+/**
+ * optional string enc_item_key = 8;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getEncItemKey = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 8, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setEncItemKey = function(value) {
+  return jspb.Message.setField(this, 8, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearEncItemKey = function() {
+  return jspb.Message.setField(this, 8, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasEncItemKey = function() {
+  return jspb.Message.getField(this, 8) != null;
+};
+
+
+/**
+ * optional string items_key_id = 9;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getItemsKeyId = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 9, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setItemsKeyId = function(value) {
+  return jspb.Message.setField(this, 9, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearItemsKeyId = function() {
+  return jspb.Message.setField(this, 9, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasItemsKeyId = function() {
+  return jspb.Message.getField(this, 9) != null;
+};
+
+
+/**
+ * optional string key_system_identifier = 10;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getKeySystemIdentifier = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 10, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setKeySystemIdentifier = function(value) {
+  return jspb.Message.setField(this, 10, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearKeySystemIdentifier = function() {
+  return jspb.Message.setField(this, 10, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasKeySystemIdentifier = function() {
+  return jspb.Message.getField(this, 10) != null;
+};
+
+
+/**
+ * optional string shared_vault_uuid = 11;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getSharedVaultUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 11, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setSharedVaultUuid = function(value) {
+  return jspb.Message.setField(this, 11, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearSharedVaultUuid = function() {
+  return jspb.Message.setField(this, 11, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasSharedVaultUuid = function() {
+  return jspb.Message.getField(this, 11) != null;
+};
+
+
+/**
+ * optional string created_at = 12;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getCreatedAt = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 12, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setCreatedAt = function(value) {
+  return jspb.Message.setField(this, 12, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearCreatedAt = function() {
+  return jspb.Message.setField(this, 12, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasCreatedAt = function() {
+  return jspb.Message.getField(this, 12) != null;
+};
+
+
+/**
+ * optional uint64 created_at_timestamp = 13;
+ * @return {number}
+ */
+proto.sync.ItemHashRepresentation.prototype.getCreatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 13, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setCreatedAtTimestamp = function(value) {
+  return jspb.Message.setField(this, 13, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearCreatedAtTimestamp = function() {
+  return jspb.Message.setField(this, 13, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasCreatedAtTimestamp = function() {
+  return jspb.Message.getField(this, 13) != null;
+};
+
+
+/**
+ * optional string updated_at = 14;
+ * @return {string}
+ */
+proto.sync.ItemHashRepresentation.prototype.getUpdatedAt = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 14, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setUpdatedAt = function(value) {
+  return jspb.Message.setField(this, 14, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearUpdatedAt = function() {
+  return jspb.Message.setField(this, 14, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasUpdatedAt = function() {
+  return jspb.Message.getField(this, 14) != null;
+};
+
+
+/**
+ * optional uint64 updated_at_timestamp = 15;
+ * @return {number}
+ */
+proto.sync.ItemHashRepresentation.prototype.getUpdatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 15, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.setUpdatedAtTimestamp = function(value) {
+  return jspb.Message.setField(this, 15, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHashRepresentation} returns this
+ */
+proto.sync.ItemHashRepresentation.prototype.clearUpdatedAtTimestamp = function() {
+  return jspb.Message.setField(this, 15, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHashRepresentation.prototype.hasUpdatedAtTimestamp = function() {
+  return jspb.Message.getField(this, 15) != null;
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.ItemConflictRepresentation.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.ItemConflictRepresentation.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.ItemConflictRepresentation} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.ItemConflictRepresentation.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    serverItem: (f = msg.getServerItem()) && proto.sync.ItemRepresentation.toObject(includeInstance, f),
+    unsavedItem: (f = msg.getUnsavedItem()) && proto.sync.ItemHashRepresentation.toObject(includeInstance, f),
+    type: jspb.Message.getFieldWithDefault(msg, 3, "")
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.ItemConflictRepresentation}
+ */
+proto.sync.ItemConflictRepresentation.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.ItemConflictRepresentation;
+  return proto.sync.ItemConflictRepresentation.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.ItemConflictRepresentation} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.ItemConflictRepresentation}
+ */
+proto.sync.ItemConflictRepresentation.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = new proto.sync.ItemRepresentation;
+      reader.readMessage(value,proto.sync.ItemRepresentation.deserializeBinaryFromReader);
+      msg.setServerItem(value);
+      break;
+    case 2:
+      var value = new proto.sync.ItemHashRepresentation;
+      reader.readMessage(value,proto.sync.ItemHashRepresentation.deserializeBinaryFromReader);
+      msg.setUnsavedItem(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setType(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.ItemConflictRepresentation.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.ItemConflictRepresentation.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.ItemConflictRepresentation} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.ItemConflictRepresentation.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getServerItem();
+  if (f != null) {
+    writer.writeMessage(
+      1,
+      f,
+      proto.sync.ItemRepresentation.serializeBinaryToWriter
+    );
+  }
+  f = message.getUnsavedItem();
+  if (f != null) {
+    writer.writeMessage(
+      2,
+      f,
+      proto.sync.ItemHashRepresentation.serializeBinaryToWriter
+    );
+  }
+  f = message.getType();
+  if (f.length > 0) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional ItemRepresentation server_item = 1;
+ * @return {?proto.sync.ItemRepresentation}
+ */
+proto.sync.ItemConflictRepresentation.prototype.getServerItem = function() {
+  return /** @type{?proto.sync.ItemRepresentation} */ (
+    jspb.Message.getWrapperField(this, proto.sync.ItemRepresentation, 1));
+};
+
+
+/**
+ * @param {?proto.sync.ItemRepresentation|undefined} value
+ * @return {!proto.sync.ItemConflictRepresentation} returns this
+*/
+proto.sync.ItemConflictRepresentation.prototype.setServerItem = function(value) {
+  return jspb.Message.setWrapperField(this, 1, value);
+};
+
+
+/**
+ * Clears the message field making it undefined.
+ * @return {!proto.sync.ItemConflictRepresentation} returns this
+ */
+proto.sync.ItemConflictRepresentation.prototype.clearServerItem = function() {
+  return this.setServerItem(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemConflictRepresentation.prototype.hasServerItem = function() {
+  return jspb.Message.getField(this, 1) != null;
+};
+
+
+/**
+ * optional ItemHashRepresentation unsaved_item = 2;
+ * @return {?proto.sync.ItemHashRepresentation}
+ */
+proto.sync.ItemConflictRepresentation.prototype.getUnsavedItem = function() {
+  return /** @type{?proto.sync.ItemHashRepresentation} */ (
+    jspb.Message.getWrapperField(this, proto.sync.ItemHashRepresentation, 2));
+};
+
+
+/**
+ * @param {?proto.sync.ItemHashRepresentation|undefined} value
+ * @return {!proto.sync.ItemConflictRepresentation} returns this
+*/
+proto.sync.ItemConflictRepresentation.prototype.setUnsavedItem = function(value) {
+  return jspb.Message.setWrapperField(this, 2, value);
+};
+
+
+/**
+ * Clears the message field making it undefined.
+ * @return {!proto.sync.ItemConflictRepresentation} returns this
+ */
+proto.sync.ItemConflictRepresentation.prototype.clearUnsavedItem = function() {
+  return this.setUnsavedItem(undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemConflictRepresentation.prototype.hasUnsavedItem = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional string type = 3;
+ * @return {string}
+ */
+proto.sync.ItemConflictRepresentation.prototype.getType = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemConflictRepresentation} returns this
+ */
+proto.sync.ItemConflictRepresentation.prototype.setType = function(value) {
+  return jspb.Message.setProto3StringField(this, 3, value);
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.ItemRepresentation.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.ItemRepresentation.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.ItemRepresentation} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.ItemRepresentation.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    uuid: jspb.Message.getFieldWithDefault(msg, 1, ""),
+    itemsKeyId: jspb.Message.getFieldWithDefault(msg, 2, ""),
+    duplicateOf: jspb.Message.getFieldWithDefault(msg, 3, ""),
+    encItemKey: jspb.Message.getFieldWithDefault(msg, 4, ""),
+    content: jspb.Message.getFieldWithDefault(msg, 5, ""),
+    contentType: jspb.Message.getFieldWithDefault(msg, 6, ""),
+    authHash: jspb.Message.getFieldWithDefault(msg, 7, ""),
+    deleted: jspb.Message.getBooleanFieldWithDefault(msg, 8, false),
+    createdAt: jspb.Message.getFieldWithDefault(msg, 9, ""),
+    createdAtTimestamp: jspb.Message.getFieldWithDefault(msg, 10, 0),
+    updatedAt: jspb.Message.getFieldWithDefault(msg, 11, ""),
+    updatedAtTimestamp: jspb.Message.getFieldWithDefault(msg, 12, 0),
+    updatedWithSession: jspb.Message.getFieldWithDefault(msg, 13, ""),
+    keySystemIdentifier: jspb.Message.getFieldWithDefault(msg, 14, ""),
+    sharedVaultUuid: jspb.Message.getFieldWithDefault(msg, 15, ""),
+    userUuid: jspb.Message.getFieldWithDefault(msg, 16, ""),
+    lastEditedByUuid: jspb.Message.getFieldWithDefault(msg, 17, "")
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.ItemRepresentation}
+ */
+proto.sync.ItemRepresentation.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.ItemRepresentation;
+  return proto.sync.ItemRepresentation.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.ItemRepresentation} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.ItemRepresentation}
+ */
+proto.sync.ItemRepresentation.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUuid(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setItemsKeyId(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setDuplicateOf(value);
+      break;
+    case 4:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setEncItemKey(value);
+      break;
+    case 5:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setContent(value);
+      break;
+    case 6:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setContentType(value);
+      break;
+    case 7:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setAuthHash(value);
+      break;
+    case 8:
+      var value = /** @type {boolean} */ (reader.readBool());
+      msg.setDeleted(value);
+      break;
+    case 9:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setCreatedAt(value);
+      break;
+    case 10:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setCreatedAtTimestamp(value);
+      break;
+    case 11:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUpdatedAt(value);
+      break;
+    case 12:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setUpdatedAtTimestamp(value);
+      break;
+    case 13:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUpdatedWithSession(value);
+      break;
+    case 14:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setKeySystemIdentifier(value);
+      break;
+    case 15:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setSharedVaultUuid(value);
+      break;
+    case 16:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUserUuid(value);
+      break;
+    case 17:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setLastEditedByUuid(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.ItemRepresentation.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.ItemRepresentation.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.ItemRepresentation} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.ItemRepresentation.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 4));
+  if (f != null) {
+    writer.writeString(
+      4,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 5));
+  if (f != null) {
+    writer.writeString(
+      5,
+      f
+    );
+  }
+  f = message.getContentType();
+  if (f.length > 0) {
+    writer.writeString(
+      6,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 7));
+  if (f != null) {
+    writer.writeString(
+      7,
+      f
+    );
+  }
+  f = message.getDeleted();
+  if (f) {
+    writer.writeBool(
+      8,
+      f
+    );
+  }
+  f = message.getCreatedAt();
+  if (f.length > 0) {
+    writer.writeString(
+      9,
+      f
+    );
+  }
+  f = message.getCreatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      10,
+      f
+    );
+  }
+  f = message.getUpdatedAt();
+  if (f.length > 0) {
+    writer.writeString(
+      11,
+      f
+    );
+  }
+  f = message.getUpdatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      12,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 13));
+  if (f != null) {
+    writer.writeString(
+      13,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 14));
+  if (f != null) {
+    writer.writeString(
+      14,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 15));
+  if (f != null) {
+    writer.writeString(
+      15,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 16));
+  if (f != null) {
+    writer.writeString(
+      16,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 17));
+  if (f != null) {
+    writer.writeString(
+      17,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string uuid = 1;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string items_key_id = 2;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getItemsKeyId = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setItemsKeyId = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.clearItemsKeyId = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.hasItemsKeyId = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional string duplicate_of = 3;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getDuplicateOf = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setDuplicateOf = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.clearDuplicateOf = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.hasDuplicateOf = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+/**
+ * optional string enc_item_key = 4;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getEncItemKey = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setEncItemKey = function(value) {
+  return jspb.Message.setField(this, 4, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.clearEncItemKey = function() {
+  return jspb.Message.setField(this, 4, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.hasEncItemKey = function() {
+  return jspb.Message.getField(this, 4) != null;
+};
+
+
+/**
+ * optional string content = 5;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getContent = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setContent = function(value) {
+  return jspb.Message.setField(this, 5, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.clearContent = function() {
+  return jspb.Message.setField(this, 5, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.hasContent = function() {
+  return jspb.Message.getField(this, 5) != null;
+};
+
+
+/**
+ * optional string content_type = 6;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getContentType = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setContentType = function(value) {
+  return jspb.Message.setProto3StringField(this, 6, value);
+};
+
+
+/**
+ * optional string auth_hash = 7;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getAuthHash = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setAuthHash = function(value) {
+  return jspb.Message.setField(this, 7, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.clearAuthHash = function() {
+  return jspb.Message.setField(this, 7, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.hasAuthHash = function() {
+  return jspb.Message.getField(this, 7) != null;
+};
+
+
+/**
+ * optional bool deleted = 8;
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.getDeleted = function() {
+  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 8, false));
+};
+
+
+/**
+ * @param {boolean} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setDeleted = function(value) {
+  return jspb.Message.setProto3BooleanField(this, 8, value);
+};
+
+
+/**
+ * optional string created_at = 9;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getCreatedAt = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 9, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setCreatedAt = function(value) {
+  return jspb.Message.setProto3StringField(this, 9, value);
+};
+
+
+/**
+ * optional uint64 created_at_timestamp = 10;
+ * @return {number}
+ */
+proto.sync.ItemRepresentation.prototype.getCreatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 10, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setCreatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 10, value);
+};
+
+
+/**
+ * optional string updated_at = 11;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getUpdatedAt = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 11, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setUpdatedAt = function(value) {
+  return jspb.Message.setProto3StringField(this, 11, value);
+};
+
+
+/**
+ * optional uint64 updated_at_timestamp = 12;
+ * @return {number}
+ */
+proto.sync.ItemRepresentation.prototype.getUpdatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 12, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setUpdatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 12, value);
+};
+
+
+/**
+ * optional string updated_with_session = 13;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getUpdatedWithSession = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 13, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setUpdatedWithSession = function(value) {
+  return jspb.Message.setField(this, 13, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.clearUpdatedWithSession = function() {
+  return jspb.Message.setField(this, 13, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.hasUpdatedWithSession = function() {
+  return jspb.Message.getField(this, 13) != null;
+};
+
+
+/**
+ * optional string key_system_identifier = 14;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getKeySystemIdentifier = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 14, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setKeySystemIdentifier = function(value) {
+  return jspb.Message.setField(this, 14, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.clearKeySystemIdentifier = function() {
+  return jspb.Message.setField(this, 14, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.hasKeySystemIdentifier = function() {
+  return jspb.Message.getField(this, 14) != null;
+};
+
+
+/**
+ * optional string shared_vault_uuid = 15;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getSharedVaultUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 15, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setSharedVaultUuid = function(value) {
+  return jspb.Message.setField(this, 15, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.clearSharedVaultUuid = function() {
+  return jspb.Message.setField(this, 15, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.hasSharedVaultUuid = function() {
+  return jspb.Message.getField(this, 15) != null;
+};
+
+
+/**
+ * optional string user_uuid = 16;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getUserUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 16, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setUserUuid = function(value) {
+  return jspb.Message.setField(this, 16, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.clearUserUuid = function() {
+  return jspb.Message.setField(this, 16, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.hasUserUuid = function() {
+  return jspb.Message.getField(this, 16) != null;
+};
+
+
+/**
+ * optional string last_edited_by_uuid = 17;
+ * @return {string}
+ */
+proto.sync.ItemRepresentation.prototype.getLastEditedByUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 17, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.setLastEditedByUuid = function(value) {
+  return jspb.Message.setField(this, 17, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemRepresentation} returns this
+ */
+proto.sync.ItemRepresentation.prototype.clearLastEditedByUuid = function() {
+  return jspb.Message.setField(this, 17, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemRepresentation.prototype.hasLastEditedByUuid = function() {
+  return jspb.Message.getField(this, 17) != null;
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.SavedItemRepresentation.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.SavedItemRepresentation.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.SavedItemRepresentation} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.SavedItemRepresentation.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    uuid: jspb.Message.getFieldWithDefault(msg, 1, ""),
+    duplicateOf: jspb.Message.getFieldWithDefault(msg, 2, ""),
+    contentType: jspb.Message.getFieldWithDefault(msg, 3, ""),
+    authHash: jspb.Message.getFieldWithDefault(msg, 4, ""),
+    deleted: jspb.Message.getBooleanFieldWithDefault(msg, 5, false),
+    createdAt: jspb.Message.getFieldWithDefault(msg, 6, ""),
+    createdAtTimestamp: jspb.Message.getFieldWithDefault(msg, 7, 0),
+    updatedAt: jspb.Message.getFieldWithDefault(msg, 8, ""),
+    updatedAtTimestamp: jspb.Message.getFieldWithDefault(msg, 9, 0),
+    keySystemIdentifier: jspb.Message.getFieldWithDefault(msg, 10, ""),
+    sharedVaultUuid: jspb.Message.getFieldWithDefault(msg, 11, ""),
+    userUuid: jspb.Message.getFieldWithDefault(msg, 12, ""),
+    lastEditedByUuid: jspb.Message.getFieldWithDefault(msg, 13, "")
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.SavedItemRepresentation}
+ */
+proto.sync.SavedItemRepresentation.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.SavedItemRepresentation;
+  return proto.sync.SavedItemRepresentation.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.SavedItemRepresentation} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.SavedItemRepresentation}
+ */
+proto.sync.SavedItemRepresentation.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUuid(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setDuplicateOf(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setContentType(value);
+      break;
+    case 4:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setAuthHash(value);
+      break;
+    case 5:
+      var value = /** @type {boolean} */ (reader.readBool());
+      msg.setDeleted(value);
+      break;
+    case 6:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setCreatedAt(value);
+      break;
+    case 7:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setCreatedAtTimestamp(value);
+      break;
+    case 8:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUpdatedAt(value);
+      break;
+    case 9:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setUpdatedAtTimestamp(value);
+      break;
+    case 10:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setKeySystemIdentifier(value);
+      break;
+    case 11:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setSharedVaultUuid(value);
+      break;
+    case 12:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUserUuid(value);
+      break;
+    case 13:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setLastEditedByUuid(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.SavedItemRepresentation.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.SavedItemRepresentation.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.SavedItemRepresentation} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.SavedItemRepresentation.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = message.getContentType();
+  if (f.length > 0) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 4));
+  if (f != null) {
+    writer.writeString(
+      4,
+      f
+    );
+  }
+  f = message.getDeleted();
+  if (f) {
+    writer.writeBool(
+      5,
+      f
+    );
+  }
+  f = message.getCreatedAt();
+  if (f.length > 0) {
+    writer.writeString(
+      6,
+      f
+    );
+  }
+  f = message.getCreatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      7,
+      f
+    );
+  }
+  f = message.getUpdatedAt();
+  if (f.length > 0) {
+    writer.writeString(
+      8,
+      f
+    );
+  }
+  f = message.getUpdatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      9,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 10));
+  if (f != null) {
+    writer.writeString(
+      10,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 11));
+  if (f != null) {
+    writer.writeString(
+      11,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 12));
+  if (f != null) {
+    writer.writeString(
+      12,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 13));
+  if (f != null) {
+    writer.writeString(
+      13,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string uuid = 1;
+ * @return {string}
+ */
+proto.sync.SavedItemRepresentation.prototype.getUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string duplicate_of = 2;
+ * @return {string}
+ */
+proto.sync.SavedItemRepresentation.prototype.getDuplicateOf = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setDuplicateOf = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.clearDuplicateOf = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SavedItemRepresentation.prototype.hasDuplicateOf = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional string content_type = 3;
+ * @return {string}
+ */
+proto.sync.SavedItemRepresentation.prototype.getContentType = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setContentType = function(value) {
+  return jspb.Message.setProto3StringField(this, 3, value);
+};
+
+
+/**
+ * optional string auth_hash = 4;
+ * @return {string}
+ */
+proto.sync.SavedItemRepresentation.prototype.getAuthHash = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setAuthHash = function(value) {
+  return jspb.Message.setField(this, 4, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.clearAuthHash = function() {
+  return jspb.Message.setField(this, 4, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SavedItemRepresentation.prototype.hasAuthHash = function() {
+  return jspb.Message.getField(this, 4) != null;
+};
+
+
+/**
+ * optional bool deleted = 5;
+ * @return {boolean}
+ */
+proto.sync.SavedItemRepresentation.prototype.getDeleted = function() {
+  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 5, false));
+};
+
+
+/**
+ * @param {boolean} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setDeleted = function(value) {
+  return jspb.Message.setProto3BooleanField(this, 5, value);
+};
+
+
+/**
+ * optional string created_at = 6;
+ * @return {string}
+ */
+proto.sync.SavedItemRepresentation.prototype.getCreatedAt = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setCreatedAt = function(value) {
+  return jspb.Message.setProto3StringField(this, 6, value);
+};
+
+
+/**
+ * optional uint64 created_at_timestamp = 7;
+ * @return {number}
+ */
+proto.sync.SavedItemRepresentation.prototype.getCreatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setCreatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 7, value);
+};
+
+
+/**
+ * optional string updated_at = 8;
+ * @return {string}
+ */
+proto.sync.SavedItemRepresentation.prototype.getUpdatedAt = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 8, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setUpdatedAt = function(value) {
+  return jspb.Message.setProto3StringField(this, 8, value);
+};
+
+
+/**
+ * optional uint64 updated_at_timestamp = 9;
+ * @return {number}
+ */
+proto.sync.SavedItemRepresentation.prototype.getUpdatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 9, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setUpdatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 9, value);
+};
+
+
+/**
+ * optional string key_system_identifier = 10;
+ * @return {string}
+ */
+proto.sync.SavedItemRepresentation.prototype.getKeySystemIdentifier = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 10, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setKeySystemIdentifier = function(value) {
+  return jspb.Message.setField(this, 10, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.clearKeySystemIdentifier = function() {
+  return jspb.Message.setField(this, 10, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SavedItemRepresentation.prototype.hasKeySystemIdentifier = function() {
+  return jspb.Message.getField(this, 10) != null;
+};
+
+
+/**
+ * optional string shared_vault_uuid = 11;
+ * @return {string}
+ */
+proto.sync.SavedItemRepresentation.prototype.getSharedVaultUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 11, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setSharedVaultUuid = function(value) {
+  return jspb.Message.setField(this, 11, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.clearSharedVaultUuid = function() {
+  return jspb.Message.setField(this, 11, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SavedItemRepresentation.prototype.hasSharedVaultUuid = function() {
+  return jspb.Message.getField(this, 11) != null;
+};
+
+
+/**
+ * optional string user_uuid = 12;
+ * @return {string}
+ */
+proto.sync.SavedItemRepresentation.prototype.getUserUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 12, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setUserUuid = function(value) {
+  return jspb.Message.setField(this, 12, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.clearUserUuid = function() {
+  return jspb.Message.setField(this, 12, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SavedItemRepresentation.prototype.hasUserUuid = function() {
+  return jspb.Message.getField(this, 12) != null;
+};
+
+
+/**
+ * optional string last_edited_by_uuid = 13;
+ * @return {string}
+ */
+proto.sync.SavedItemRepresentation.prototype.getLastEditedByUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 13, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.setLastEditedByUuid = function(value) {
+  return jspb.Message.setField(this, 13, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SavedItemRepresentation} returns this
+ */
+proto.sync.SavedItemRepresentation.prototype.clearLastEditedByUuid = function() {
+  return jspb.Message.setField(this, 13, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SavedItemRepresentation.prototype.hasLastEditedByUuid = function() {
+  return jspb.Message.getField(this, 13) != null;
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.MessageRepresentation.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.MessageRepresentation.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.MessageRepresentation} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.MessageRepresentation.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    uuid: jspb.Message.getFieldWithDefault(msg, 1, ""),
+    recipientUuid: jspb.Message.getFieldWithDefault(msg, 2, ""),
+    senderUuid: jspb.Message.getFieldWithDefault(msg, 3, ""),
+    encryptedMessage: jspb.Message.getFieldWithDefault(msg, 4, ""),
+    replaceabilityIdentifier: jspb.Message.getFieldWithDefault(msg, 5, ""),
+    createdAtTimestamp: jspb.Message.getFieldWithDefault(msg, 6, 0),
+    updatedAtTimestamp: jspb.Message.getFieldWithDefault(msg, 7, 0)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.MessageRepresentation}
+ */
+proto.sync.MessageRepresentation.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.MessageRepresentation;
+  return proto.sync.MessageRepresentation.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.MessageRepresentation} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.MessageRepresentation}
+ */
+proto.sync.MessageRepresentation.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUuid(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setRecipientUuid(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setSenderUuid(value);
+      break;
+    case 4:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setEncryptedMessage(value);
+      break;
+    case 5:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setReplaceabilityIdentifier(value);
+      break;
+    case 6:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setCreatedAtTimestamp(value);
+      break;
+    case 7:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setUpdatedAtTimestamp(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.MessageRepresentation.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.MessageRepresentation.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.MessageRepresentation} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.MessageRepresentation.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = message.getRecipientUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = message.getSenderUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+  f = message.getEncryptedMessage();
+  if (f.length > 0) {
+    writer.writeString(
+      4,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 5));
+  if (f != null) {
+    writer.writeString(
+      5,
+      f
+    );
+  }
+  f = message.getCreatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      6,
+      f
+    );
+  }
+  f = message.getUpdatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      7,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string uuid = 1;
+ * @return {string}
+ */
+proto.sync.MessageRepresentation.prototype.getUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.MessageRepresentation} returns this
+ */
+proto.sync.MessageRepresentation.prototype.setUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string recipient_uuid = 2;
+ * @return {string}
+ */
+proto.sync.MessageRepresentation.prototype.getRecipientUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.MessageRepresentation} returns this
+ */
+proto.sync.MessageRepresentation.prototype.setRecipientUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 2, value);
+};
+
+
+/**
+ * optional string sender_uuid = 3;
+ * @return {string}
+ */
+proto.sync.MessageRepresentation.prototype.getSenderUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.MessageRepresentation} returns this
+ */
+proto.sync.MessageRepresentation.prototype.setSenderUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 3, value);
+};
+
+
+/**
+ * optional string encrypted_message = 4;
+ * @return {string}
+ */
+proto.sync.MessageRepresentation.prototype.getEncryptedMessage = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.MessageRepresentation} returns this
+ */
+proto.sync.MessageRepresentation.prototype.setEncryptedMessage = function(value) {
+  return jspb.Message.setProto3StringField(this, 4, value);
+};
+
+
+/**
+ * optional string replaceability_identifier = 5;
+ * @return {string}
+ */
+proto.sync.MessageRepresentation.prototype.getReplaceabilityIdentifier = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.MessageRepresentation} returns this
+ */
+proto.sync.MessageRepresentation.prototype.setReplaceabilityIdentifier = function(value) {
+  return jspb.Message.setField(this, 5, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.MessageRepresentation} returns this
+ */
+proto.sync.MessageRepresentation.prototype.clearReplaceabilityIdentifier = function() {
+  return jspb.Message.setField(this, 5, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.MessageRepresentation.prototype.hasReplaceabilityIdentifier = function() {
+  return jspb.Message.getField(this, 5) != null;
+};
+
+
+/**
+ * optional uint64 created_at_timestamp = 6;
+ * @return {number}
+ */
+proto.sync.MessageRepresentation.prototype.getCreatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.MessageRepresentation} returns this
+ */
+proto.sync.MessageRepresentation.prototype.setCreatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 6, value);
+};
+
+
+/**
+ * optional uint64 updated_at_timestamp = 7;
+ * @return {number}
+ */
+proto.sync.MessageRepresentation.prototype.getUpdatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.MessageRepresentation} returns this
+ */
+proto.sync.MessageRepresentation.prototype.setUpdatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 7, value);
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.SharedVaultRepresentation.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.SharedVaultRepresentation.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.SharedVaultRepresentation} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.SharedVaultRepresentation.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    uuid: jspb.Message.getFieldWithDefault(msg, 1, ""),
+    userUuid: jspb.Message.getFieldWithDefault(msg, 2, ""),
+    fileUploadBytesUsed: jspb.Message.getFieldWithDefault(msg, 3, 0),
+    createdAtTimestamp: jspb.Message.getFieldWithDefault(msg, 4, 0),
+    updatedAtTimestamp: jspb.Message.getFieldWithDefault(msg, 5, 0)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.SharedVaultRepresentation}
+ */
+proto.sync.SharedVaultRepresentation.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.SharedVaultRepresentation;
+  return proto.sync.SharedVaultRepresentation.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.SharedVaultRepresentation} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.SharedVaultRepresentation}
+ */
+proto.sync.SharedVaultRepresentation.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUuid(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUserUuid(value);
+      break;
+    case 3:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setFileUploadBytesUsed(value);
+      break;
+    case 4:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setCreatedAtTimestamp(value);
+      break;
+    case 5:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setUpdatedAtTimestamp(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.SharedVaultRepresentation.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.SharedVaultRepresentation.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.SharedVaultRepresentation} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.SharedVaultRepresentation.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = message.getUserUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = message.getFileUploadBytesUsed();
+  if (f !== 0) {
+    writer.writeUint32(
+      3,
+      f
+    );
+  }
+  f = message.getCreatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      4,
+      f
+    );
+  }
+  f = message.getUpdatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      5,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string uuid = 1;
+ * @return {string}
+ */
+proto.sync.SharedVaultRepresentation.prototype.getUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SharedVaultRepresentation} returns this
+ */
+proto.sync.SharedVaultRepresentation.prototype.setUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string user_uuid = 2;
+ * @return {string}
+ */
+proto.sync.SharedVaultRepresentation.prototype.getUserUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SharedVaultRepresentation} returns this
+ */
+proto.sync.SharedVaultRepresentation.prototype.setUserUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 2, value);
+};
+
+
+/**
+ * optional uint32 file_upload_bytes_used = 3;
+ * @return {number}
+ */
+proto.sync.SharedVaultRepresentation.prototype.getFileUploadBytesUsed = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.SharedVaultRepresentation} returns this
+ */
+proto.sync.SharedVaultRepresentation.prototype.setFileUploadBytesUsed = function(value) {
+  return jspb.Message.setProto3IntField(this, 3, value);
+};
+
+
+/**
+ * optional uint64 created_at_timestamp = 4;
+ * @return {number}
+ */
+proto.sync.SharedVaultRepresentation.prototype.getCreatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.SharedVaultRepresentation} returns this
+ */
+proto.sync.SharedVaultRepresentation.prototype.setCreatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 4, value);
+};
+
+
+/**
+ * optional uint64 updated_at_timestamp = 5;
+ * @return {number}
+ */
+proto.sync.SharedVaultRepresentation.prototype.getUpdatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.SharedVaultRepresentation} returns this
+ */
+proto.sync.SharedVaultRepresentation.prototype.setUpdatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 5, value);
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.SharedVaultInviteRepresentation.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.SharedVaultInviteRepresentation} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.SharedVaultInviteRepresentation.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    uuid: jspb.Message.getFieldWithDefault(msg, 1, ""),
+    sharedVaultUuid: jspb.Message.getFieldWithDefault(msg, 2, ""),
+    userUuid: jspb.Message.getFieldWithDefault(msg, 3, ""),
+    senderUuid: jspb.Message.getFieldWithDefault(msg, 4, ""),
+    encryptedMessage: jspb.Message.getFieldWithDefault(msg, 5, ""),
+    permission: jspb.Message.getFieldWithDefault(msg, 6, ""),
+    createdAtTimestamp: jspb.Message.getFieldWithDefault(msg, 7, 0),
+    updatedAtTimestamp: jspb.Message.getFieldWithDefault(msg, 8, 0)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.SharedVaultInviteRepresentation}
+ */
+proto.sync.SharedVaultInviteRepresentation.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.SharedVaultInviteRepresentation;
+  return proto.sync.SharedVaultInviteRepresentation.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.SharedVaultInviteRepresentation} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.SharedVaultInviteRepresentation}
+ */
+proto.sync.SharedVaultInviteRepresentation.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUuid(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setSharedVaultUuid(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUserUuid(value);
+      break;
+    case 4:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setSenderUuid(value);
+      break;
+    case 5:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setEncryptedMessage(value);
+      break;
+    case 6:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setPermission(value);
+      break;
+    case 7:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setCreatedAtTimestamp(value);
+      break;
+    case 8:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setUpdatedAtTimestamp(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.SharedVaultInviteRepresentation.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.SharedVaultInviteRepresentation} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.SharedVaultInviteRepresentation.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = message.getSharedVaultUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = message.getUserUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+  f = message.getSenderUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      4,
+      f
+    );
+  }
+  f = message.getEncryptedMessage();
+  if (f.length > 0) {
+    writer.writeString(
+      5,
+      f
+    );
+  }
+  f = message.getPermission();
+  if (f.length > 0) {
+    writer.writeString(
+      6,
+      f
+    );
+  }
+  f = message.getCreatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      7,
+      f
+    );
+  }
+  f = message.getUpdatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      8,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string uuid = 1;
+ * @return {string}
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.getUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SharedVaultInviteRepresentation} returns this
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.setUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string shared_vault_uuid = 2;
+ * @return {string}
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.getSharedVaultUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SharedVaultInviteRepresentation} returns this
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.setSharedVaultUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 2, value);
+};
+
+
+/**
+ * optional string user_uuid = 3;
+ * @return {string}
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.getUserUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SharedVaultInviteRepresentation} returns this
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.setUserUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 3, value);
+};
+
+
+/**
+ * optional string sender_uuid = 4;
+ * @return {string}
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.getSenderUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SharedVaultInviteRepresentation} returns this
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.setSenderUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 4, value);
+};
+
+
+/**
+ * optional string encrypted_message = 5;
+ * @return {string}
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.getEncryptedMessage = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SharedVaultInviteRepresentation} returns this
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.setEncryptedMessage = function(value) {
+  return jspb.Message.setProto3StringField(this, 5, value);
+};
+
+
+/**
+ * optional string permission = 6;
+ * @return {string}
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.getPermission = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SharedVaultInviteRepresentation} returns this
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.setPermission = function(value) {
+  return jspb.Message.setProto3StringField(this, 6, value);
+};
+
+
+/**
+ * optional uint64 created_at_timestamp = 7;
+ * @return {number}
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.getCreatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 7, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.SharedVaultInviteRepresentation} returns this
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.setCreatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 7, value);
+};
+
+
+/**
+ * optional uint64 updated_at_timestamp = 8;
+ * @return {number}
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.getUpdatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 8, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.SharedVaultInviteRepresentation} returns this
+ */
+proto.sync.SharedVaultInviteRepresentation.prototype.setUpdatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 8, value);
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.NotificationRepresentation.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.NotificationRepresentation.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.NotificationRepresentation} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.NotificationRepresentation.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    uuid: jspb.Message.getFieldWithDefault(msg, 1, ""),
+    userUuid: jspb.Message.getFieldWithDefault(msg, 2, ""),
+    type: jspb.Message.getFieldWithDefault(msg, 3, ""),
+    payload: jspb.Message.getFieldWithDefault(msg, 4, ""),
+    createdAtTimestamp: jspb.Message.getFieldWithDefault(msg, 5, 0),
+    updatedAtTimestamp: jspb.Message.getFieldWithDefault(msg, 6, 0)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.NotificationRepresentation}
+ */
+proto.sync.NotificationRepresentation.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.NotificationRepresentation;
+  return proto.sync.NotificationRepresentation.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.NotificationRepresentation} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.NotificationRepresentation}
+ */
+proto.sync.NotificationRepresentation.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUuid(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUserUuid(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setType(value);
+      break;
+    case 4:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setPayload(value);
+      break;
+    case 5:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setCreatedAtTimestamp(value);
+      break;
+    case 6:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setUpdatedAtTimestamp(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.NotificationRepresentation.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.NotificationRepresentation.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.NotificationRepresentation} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.NotificationRepresentation.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = message.getUserUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = message.getType();
+  if (f.length > 0) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+  f = message.getPayload();
+  if (f.length > 0) {
+    writer.writeString(
+      4,
+      f
+    );
+  }
+  f = message.getCreatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      5,
+      f
+    );
+  }
+  f = message.getUpdatedAtTimestamp();
+  if (f !== 0) {
+    writer.writeUint64(
+      6,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string uuid = 1;
+ * @return {string}
+ */
+proto.sync.NotificationRepresentation.prototype.getUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.NotificationRepresentation} returns this
+ */
+proto.sync.NotificationRepresentation.prototype.setUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string user_uuid = 2;
+ * @return {string}
+ */
+proto.sync.NotificationRepresentation.prototype.getUserUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.NotificationRepresentation} returns this
+ */
+proto.sync.NotificationRepresentation.prototype.setUserUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 2, value);
+};
+
+
+/**
+ * optional string type = 3;
+ * @return {string}
+ */
+proto.sync.NotificationRepresentation.prototype.getType = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.NotificationRepresentation} returns this
+ */
+proto.sync.NotificationRepresentation.prototype.setType = function(value) {
+  return jspb.Message.setProto3StringField(this, 3, value);
+};
+
+
+/**
+ * optional string payload = 4;
+ * @return {string}
+ */
+proto.sync.NotificationRepresentation.prototype.getPayload = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.NotificationRepresentation} returns this
+ */
+proto.sync.NotificationRepresentation.prototype.setPayload = function(value) {
+  return jspb.Message.setProto3StringField(this, 4, value);
+};
+
+
+/**
+ * optional uint64 created_at_timestamp = 5;
+ * @return {number}
+ */
+proto.sync.NotificationRepresentation.prototype.getCreatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 5, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.NotificationRepresentation} returns this
+ */
+proto.sync.NotificationRepresentation.prototype.setCreatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 5, value);
+};
+
+
+/**
+ * optional uint64 updated_at_timestamp = 6;
+ * @return {number}
+ */
+proto.sync.NotificationRepresentation.prototype.getUpdatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.NotificationRepresentation} returns this
+ */
+proto.sync.NotificationRepresentation.prototype.setUpdatedAtTimestamp = function(value) {
+  return jspb.Message.setProto3IntField(this, 6, value);
+};
+
+
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.ItemHash.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.ItemHash.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.ItemHash} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.ItemHash.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    uuid: jspb.Message.getFieldWithDefault(msg, 1, ""),
+    content: jspb.Message.getFieldWithDefault(msg, 2, ""),
+    contentType: jspb.Message.getFieldWithDefault(msg, 3, ""),
+    deleted: jspb.Message.getBooleanFieldWithDefault(msg, 4, false),
+    duplicateOf: jspb.Message.getFieldWithDefault(msg, 5, ""),
+    authHash: jspb.Message.getFieldWithDefault(msg, 6, ""),
+    encItemKey: jspb.Message.getFieldWithDefault(msg, 7, ""),
+    itemsKeyId: jspb.Message.getFieldWithDefault(msg, 8, ""),
+    keySystemIdentifier: jspb.Message.getFieldWithDefault(msg, 9, ""),
+    sharedVaultUuid: jspb.Message.getFieldWithDefault(msg, 10, ""),
+    createdAt: jspb.Message.getFieldWithDefault(msg, 11, ""),
+    createdAtTimestamp: jspb.Message.getFieldWithDefault(msg, 12, 0),
+    updatedAt: jspb.Message.getFieldWithDefault(msg, 13, ""),
+    updatedAtTimestamp: jspb.Message.getFieldWithDefault(msg, 14, 0)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.ItemHash}
+ */
+proto.sync.ItemHash.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.ItemHash;
+  return proto.sync.ItemHash.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.ItemHash} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.ItemHash}
+ */
+proto.sync.ItemHash.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUuid(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setContent(value);
+      break;
+    case 3:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setContentType(value);
+      break;
+    case 4:
+      var value = /** @type {boolean} */ (reader.readBool());
+      msg.setDeleted(value);
+      break;
+    case 5:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setDuplicateOf(value);
+      break;
+    case 6:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setAuthHash(value);
+      break;
+    case 7:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setEncItemKey(value);
+      break;
+    case 8:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setItemsKeyId(value);
+      break;
+    case 9:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setKeySystemIdentifier(value);
+      break;
+    case 10:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setSharedVaultUuid(value);
+      break;
+    case 11:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setCreatedAt(value);
+      break;
+    case 12:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setCreatedAtTimestamp(value);
+      break;
+    case 13:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setUpdatedAt(value);
+      break;
+    case 14:
+      var value = /** @type {number} */ (reader.readUint64());
+      msg.setUpdatedAtTimestamp(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.ItemHash.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.ItemHash.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.ItemHash} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.ItemHash.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getUuid();
+  if (f.length > 0) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 2));
+  if (f != null) {
+    writer.writeString(
+      2,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeString(
+      3,
+      f
+    );
+  }
+  f = /** @type {boolean} */ (jspb.Message.getField(message, 4));
+  if (f != null) {
+    writer.writeBool(
+      4,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 5));
+  if (f != null) {
+    writer.writeString(
+      5,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 6));
+  if (f != null) {
+    writer.writeString(
+      6,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 7));
+  if (f != null) {
+    writer.writeString(
+      7,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 8));
+  if (f != null) {
+    writer.writeString(
+      8,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 9));
+  if (f != null) {
+    writer.writeString(
+      9,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 10));
+  if (f != null) {
+    writer.writeString(
+      10,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 11));
+  if (f != null) {
+    writer.writeString(
+      11,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 12));
+  if (f != null) {
+    writer.writeUint64(
+      12,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 13));
+  if (f != null) {
+    writer.writeString(
+      13,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 14));
+  if (f != null) {
+    writer.writeUint64(
+      14,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string uuid = 1;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setUuid = function(value) {
+  return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * optional string content = 2;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getContent = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setContent = function(value) {
+  return jspb.Message.setField(this, 2, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearContent = function() {
+  return jspb.Message.setField(this, 2, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasContent = function() {
+  return jspb.Message.getField(this, 2) != null;
+};
+
+
+/**
+ * optional string content_type = 3;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getContentType = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setContentType = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearContentType = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasContentType = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+/**
+ * optional bool deleted = 4;
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.getDeleted = function() {
+  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 4, false));
+};
+
+
+/**
+ * @param {boolean} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setDeleted = function(value) {
+  return jspb.Message.setField(this, 4, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearDeleted = function() {
+  return jspb.Message.setField(this, 4, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasDeleted = function() {
+  return jspb.Message.getField(this, 4) != null;
+};
+
+
+/**
+ * optional string duplicate_of = 5;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getDuplicateOf = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setDuplicateOf = function(value) {
+  return jspb.Message.setField(this, 5, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearDuplicateOf = function() {
+  return jspb.Message.setField(this, 5, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasDuplicateOf = function() {
+  return jspb.Message.getField(this, 5) != null;
+};
+
+
+/**
+ * optional string auth_hash = 6;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getAuthHash = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setAuthHash = function(value) {
+  return jspb.Message.setField(this, 6, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearAuthHash = function() {
+  return jspb.Message.setField(this, 6, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasAuthHash = function() {
+  return jspb.Message.getField(this, 6) != null;
+};
+
+
+/**
+ * optional string enc_item_key = 7;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getEncItemKey = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setEncItemKey = function(value) {
+  return jspb.Message.setField(this, 7, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearEncItemKey = function() {
+  return jspb.Message.setField(this, 7, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasEncItemKey = function() {
+  return jspb.Message.getField(this, 7) != null;
+};
+
+
+/**
+ * optional string items_key_id = 8;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getItemsKeyId = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 8, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setItemsKeyId = function(value) {
+  return jspb.Message.setField(this, 8, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearItemsKeyId = function() {
+  return jspb.Message.setField(this, 8, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasItemsKeyId = function() {
+  return jspb.Message.getField(this, 8) != null;
+};
+
+
+/**
+ * optional string key_system_identifier = 9;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getKeySystemIdentifier = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 9, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setKeySystemIdentifier = function(value) {
+  return jspb.Message.setField(this, 9, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearKeySystemIdentifier = function() {
+  return jspb.Message.setField(this, 9, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasKeySystemIdentifier = function() {
+  return jspb.Message.getField(this, 9) != null;
+};
+
+
+/**
+ * optional string shared_vault_uuid = 10;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getSharedVaultUuid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 10, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setSharedVaultUuid = function(value) {
+  return jspb.Message.setField(this, 10, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearSharedVaultUuid = function() {
+  return jspb.Message.setField(this, 10, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasSharedVaultUuid = function() {
+  return jspb.Message.getField(this, 10) != null;
+};
+
+
+/**
+ * optional string created_at = 11;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getCreatedAt = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 11, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setCreatedAt = function(value) {
+  return jspb.Message.setField(this, 11, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearCreatedAt = function() {
+  return jspb.Message.setField(this, 11, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasCreatedAt = function() {
+  return jspb.Message.getField(this, 11) != null;
+};
+
+
+/**
+ * optional uint64 created_at_timestamp = 12;
+ * @return {number}
+ */
+proto.sync.ItemHash.prototype.getCreatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 12, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setCreatedAtTimestamp = function(value) {
+  return jspb.Message.setField(this, 12, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearCreatedAtTimestamp = function() {
+  return jspb.Message.setField(this, 12, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasCreatedAtTimestamp = function() {
+  return jspb.Message.getField(this, 12) != null;
+};
+
+
+/**
+ * optional string updated_at = 13;
+ * @return {string}
+ */
+proto.sync.ItemHash.prototype.getUpdatedAt = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 13, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setUpdatedAt = function(value) {
+  return jspb.Message.setField(this, 13, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearUpdatedAt = function() {
+  return jspb.Message.setField(this, 13, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasUpdatedAt = function() {
+  return jspb.Message.getField(this, 13) != null;
+};
+
+
+/**
+ * optional uint64 updated_at_timestamp = 14;
+ * @return {number}
+ */
+proto.sync.ItemHash.prototype.getUpdatedAtTimestamp = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 14, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.setUpdatedAtTimestamp = function(value) {
+  return jspb.Message.setField(this, 14, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.ItemHash} returns this
+ */
+proto.sync.ItemHash.prototype.clearUpdatedAtTimestamp = function() {
+  return jspb.Message.setField(this, 14, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.ItemHash.prototype.hasUpdatedAtTimestamp = function() {
+  return jspb.Message.getField(this, 14) != null;
+};
+
+
+
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.sync.SyncResponse.repeatedFields_ = [1,2,3,6,7,8,9];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.SyncResponse.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.SyncResponse.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.SyncResponse} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.SyncResponse.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    retrievedItemsList: jspb.Message.toObjectList(msg.getRetrievedItemsList(),
+    proto.sync.ItemRepresentation.toObject, includeInstance),
+    savedItemsList: jspb.Message.toObjectList(msg.getSavedItemsList(),
+    proto.sync.SavedItemRepresentation.toObject, includeInstance),
+    conflictsList: jspb.Message.toObjectList(msg.getConflictsList(),
+    proto.sync.ItemConflictRepresentation.toObject, includeInstance),
+    syncToken: jspb.Message.getFieldWithDefault(msg, 4, ""),
+    cursorToken: jspb.Message.getFieldWithDefault(msg, 5, ""),
+    messagesList: jspb.Message.toObjectList(msg.getMessagesList(),
+    proto.sync.MessageRepresentation.toObject, includeInstance),
+    sharedVaultsList: jspb.Message.toObjectList(msg.getSharedVaultsList(),
+    proto.sync.SharedVaultRepresentation.toObject, includeInstance),
+    sharedVaultInvitesList: jspb.Message.toObjectList(msg.getSharedVaultInvitesList(),
+    proto.sync.SharedVaultInviteRepresentation.toObject, includeInstance),
+    notificationsList: jspb.Message.toObjectList(msg.getNotificationsList(),
+    proto.sync.NotificationRepresentation.toObject, includeInstance)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.SyncResponse}
+ */
+proto.sync.SyncResponse.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.SyncResponse;
+  return proto.sync.SyncResponse.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.SyncResponse} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.SyncResponse}
+ */
+proto.sync.SyncResponse.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = new proto.sync.ItemRepresentation;
+      reader.readMessage(value,proto.sync.ItemRepresentation.deserializeBinaryFromReader);
+      msg.addRetrievedItems(value);
+      break;
+    case 2:
+      var value = new proto.sync.SavedItemRepresentation;
+      reader.readMessage(value,proto.sync.SavedItemRepresentation.deserializeBinaryFromReader);
+      msg.addSavedItems(value);
+      break;
+    case 3:
+      var value = new proto.sync.ItemConflictRepresentation;
+      reader.readMessage(value,proto.sync.ItemConflictRepresentation.deserializeBinaryFromReader);
+      msg.addConflicts(value);
+      break;
+    case 4:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setSyncToken(value);
+      break;
+    case 5:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setCursorToken(value);
+      break;
+    case 6:
+      var value = new proto.sync.MessageRepresentation;
+      reader.readMessage(value,proto.sync.MessageRepresentation.deserializeBinaryFromReader);
+      msg.addMessages(value);
+      break;
+    case 7:
+      var value = new proto.sync.SharedVaultRepresentation;
+      reader.readMessage(value,proto.sync.SharedVaultRepresentation.deserializeBinaryFromReader);
+      msg.addSharedVaults(value);
+      break;
+    case 8:
+      var value = new proto.sync.SharedVaultInviteRepresentation;
+      reader.readMessage(value,proto.sync.SharedVaultInviteRepresentation.deserializeBinaryFromReader);
+      msg.addSharedVaultInvites(value);
+      break;
+    case 9:
+      var value = new proto.sync.NotificationRepresentation;
+      reader.readMessage(value,proto.sync.NotificationRepresentation.deserializeBinaryFromReader);
+      msg.addNotifications(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.SyncResponse.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.SyncResponse.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.SyncResponse} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.SyncResponse.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getRetrievedItemsList();
+  if (f.length > 0) {
+    writer.writeRepeatedMessage(
+      1,
+      f,
+      proto.sync.ItemRepresentation.serializeBinaryToWriter
+    );
+  }
+  f = message.getSavedItemsList();
+  if (f.length > 0) {
+    writer.writeRepeatedMessage(
+      2,
+      f,
+      proto.sync.SavedItemRepresentation.serializeBinaryToWriter
+    );
+  }
+  f = message.getConflictsList();
+  if (f.length > 0) {
+    writer.writeRepeatedMessage(
+      3,
+      f,
+      proto.sync.ItemConflictRepresentation.serializeBinaryToWriter
+    );
+  }
+  f = message.getSyncToken();
+  if (f.length > 0) {
+    writer.writeString(
+      4,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 5));
+  if (f != null) {
+    writer.writeString(
+      5,
+      f
+    );
+  }
+  f = message.getMessagesList();
+  if (f.length > 0) {
+    writer.writeRepeatedMessage(
+      6,
+      f,
+      proto.sync.MessageRepresentation.serializeBinaryToWriter
+    );
+  }
+  f = message.getSharedVaultsList();
+  if (f.length > 0) {
+    writer.writeRepeatedMessage(
+      7,
+      f,
+      proto.sync.SharedVaultRepresentation.serializeBinaryToWriter
+    );
+  }
+  f = message.getSharedVaultInvitesList();
+  if (f.length > 0) {
+    writer.writeRepeatedMessage(
+      8,
+      f,
+      proto.sync.SharedVaultInviteRepresentation.serializeBinaryToWriter
+    );
+  }
+  f = message.getNotificationsList();
+  if (f.length > 0) {
+    writer.writeRepeatedMessage(
+      9,
+      f,
+      proto.sync.NotificationRepresentation.serializeBinaryToWriter
+    );
+  }
+};
+
+
+/**
+ * repeated ItemRepresentation retrieved_items = 1;
+ * @return {!Array<!proto.sync.ItemRepresentation>}
+ */
+proto.sync.SyncResponse.prototype.getRetrievedItemsList = function() {
+  return /** @type{!Array<!proto.sync.ItemRepresentation>} */ (
+    jspb.Message.getRepeatedWrapperField(this, proto.sync.ItemRepresentation, 1));
+};
+
+
+/**
+ * @param {!Array<!proto.sync.ItemRepresentation>} value
+ * @return {!proto.sync.SyncResponse} returns this
+*/
+proto.sync.SyncResponse.prototype.setRetrievedItemsList = function(value) {
+  return jspb.Message.setRepeatedWrapperField(this, 1, value);
+};
+
+
+/**
+ * @param {!proto.sync.ItemRepresentation=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.sync.ItemRepresentation}
+ */
+proto.sync.SyncResponse.prototype.addRetrievedItems = function(opt_value, opt_index) {
+  return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.sync.ItemRepresentation, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.sync.SyncResponse} returns this
+ */
+proto.sync.SyncResponse.prototype.clearRetrievedItemsList = function() {
+  return this.setRetrievedItemsList([]);
+};
+
+
+/**
+ * repeated SavedItemRepresentation saved_items = 2;
+ * @return {!Array<!proto.sync.SavedItemRepresentation>}
+ */
+proto.sync.SyncResponse.prototype.getSavedItemsList = function() {
+  return /** @type{!Array<!proto.sync.SavedItemRepresentation>} */ (
+    jspb.Message.getRepeatedWrapperField(this, proto.sync.SavedItemRepresentation, 2));
+};
+
+
+/**
+ * @param {!Array<!proto.sync.SavedItemRepresentation>} value
+ * @return {!proto.sync.SyncResponse} returns this
+*/
+proto.sync.SyncResponse.prototype.setSavedItemsList = function(value) {
+  return jspb.Message.setRepeatedWrapperField(this, 2, value);
+};
+
+
+/**
+ * @param {!proto.sync.SavedItemRepresentation=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.sync.SavedItemRepresentation}
+ */
+proto.sync.SyncResponse.prototype.addSavedItems = function(opt_value, opt_index) {
+  return jspb.Message.addToRepeatedWrapperField(this, 2, opt_value, proto.sync.SavedItemRepresentation, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.sync.SyncResponse} returns this
+ */
+proto.sync.SyncResponse.prototype.clearSavedItemsList = function() {
+  return this.setSavedItemsList([]);
+};
+
+
+/**
+ * repeated ItemConflictRepresentation conflicts = 3;
+ * @return {!Array<!proto.sync.ItemConflictRepresentation>}
+ */
+proto.sync.SyncResponse.prototype.getConflictsList = function() {
+  return /** @type{!Array<!proto.sync.ItemConflictRepresentation>} */ (
+    jspb.Message.getRepeatedWrapperField(this, proto.sync.ItemConflictRepresentation, 3));
+};
+
+
+/**
+ * @param {!Array<!proto.sync.ItemConflictRepresentation>} value
+ * @return {!proto.sync.SyncResponse} returns this
+*/
+proto.sync.SyncResponse.prototype.setConflictsList = function(value) {
+  return jspb.Message.setRepeatedWrapperField(this, 3, value);
+};
+
+
+/**
+ * @param {!proto.sync.ItemConflictRepresentation=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.sync.ItemConflictRepresentation}
+ */
+proto.sync.SyncResponse.prototype.addConflicts = function(opt_value, opt_index) {
+  return jspb.Message.addToRepeatedWrapperField(this, 3, opt_value, proto.sync.ItemConflictRepresentation, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.sync.SyncResponse} returns this
+ */
+proto.sync.SyncResponse.prototype.clearConflictsList = function() {
+  return this.setConflictsList([]);
+};
+
+
+/**
+ * optional string sync_token = 4;
+ * @return {string}
+ */
+proto.sync.SyncResponse.prototype.getSyncToken = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SyncResponse} returns this
+ */
+proto.sync.SyncResponse.prototype.setSyncToken = function(value) {
+  return jspb.Message.setProto3StringField(this, 4, value);
+};
+
+
+/**
+ * optional string cursor_token = 5;
+ * @return {string}
+ */
+proto.sync.SyncResponse.prototype.getCursorToken = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SyncResponse} returns this
+ */
+proto.sync.SyncResponse.prototype.setCursorToken = function(value) {
+  return jspb.Message.setField(this, 5, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SyncResponse} returns this
+ */
+proto.sync.SyncResponse.prototype.clearCursorToken = function() {
+  return jspb.Message.setField(this, 5, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SyncResponse.prototype.hasCursorToken = function() {
+  return jspb.Message.getField(this, 5) != null;
+};
+
+
+/**
+ * repeated MessageRepresentation messages = 6;
+ * @return {!Array<!proto.sync.MessageRepresentation>}
+ */
+proto.sync.SyncResponse.prototype.getMessagesList = function() {
+  return /** @type{!Array<!proto.sync.MessageRepresentation>} */ (
+    jspb.Message.getRepeatedWrapperField(this, proto.sync.MessageRepresentation, 6));
+};
+
+
+/**
+ * @param {!Array<!proto.sync.MessageRepresentation>} value
+ * @return {!proto.sync.SyncResponse} returns this
+*/
+proto.sync.SyncResponse.prototype.setMessagesList = function(value) {
+  return jspb.Message.setRepeatedWrapperField(this, 6, value);
+};
+
+
+/**
+ * @param {!proto.sync.MessageRepresentation=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.sync.MessageRepresentation}
+ */
+proto.sync.SyncResponse.prototype.addMessages = function(opt_value, opt_index) {
+  return jspb.Message.addToRepeatedWrapperField(this, 6, opt_value, proto.sync.MessageRepresentation, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.sync.SyncResponse} returns this
+ */
+proto.sync.SyncResponse.prototype.clearMessagesList = function() {
+  return this.setMessagesList([]);
+};
+
+
+/**
+ * repeated SharedVaultRepresentation shared_vaults = 7;
+ * @return {!Array<!proto.sync.SharedVaultRepresentation>}
+ */
+proto.sync.SyncResponse.prototype.getSharedVaultsList = function() {
+  return /** @type{!Array<!proto.sync.SharedVaultRepresentation>} */ (
+    jspb.Message.getRepeatedWrapperField(this, proto.sync.SharedVaultRepresentation, 7));
+};
+
+
+/**
+ * @param {!Array<!proto.sync.SharedVaultRepresentation>} value
+ * @return {!proto.sync.SyncResponse} returns this
+*/
+proto.sync.SyncResponse.prototype.setSharedVaultsList = function(value) {
+  return jspb.Message.setRepeatedWrapperField(this, 7, value);
+};
+
+
+/**
+ * @param {!proto.sync.SharedVaultRepresentation=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.sync.SharedVaultRepresentation}
+ */
+proto.sync.SyncResponse.prototype.addSharedVaults = function(opt_value, opt_index) {
+  return jspb.Message.addToRepeatedWrapperField(this, 7, opt_value, proto.sync.SharedVaultRepresentation, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.sync.SyncResponse} returns this
+ */
+proto.sync.SyncResponse.prototype.clearSharedVaultsList = function() {
+  return this.setSharedVaultsList([]);
+};
+
+
+/**
+ * repeated SharedVaultInviteRepresentation shared_vault_invites = 8;
+ * @return {!Array<!proto.sync.SharedVaultInviteRepresentation>}
+ */
+proto.sync.SyncResponse.prototype.getSharedVaultInvitesList = function() {
+  return /** @type{!Array<!proto.sync.SharedVaultInviteRepresentation>} */ (
+    jspb.Message.getRepeatedWrapperField(this, proto.sync.SharedVaultInviteRepresentation, 8));
+};
+
+
+/**
+ * @param {!Array<!proto.sync.SharedVaultInviteRepresentation>} value
+ * @return {!proto.sync.SyncResponse} returns this
+*/
+proto.sync.SyncResponse.prototype.setSharedVaultInvitesList = function(value) {
+  return jspb.Message.setRepeatedWrapperField(this, 8, value);
+};
+
+
+/**
+ * @param {!proto.sync.SharedVaultInviteRepresentation=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.sync.SharedVaultInviteRepresentation}
+ */
+proto.sync.SyncResponse.prototype.addSharedVaultInvites = function(opt_value, opt_index) {
+  return jspb.Message.addToRepeatedWrapperField(this, 8, opt_value, proto.sync.SharedVaultInviteRepresentation, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.sync.SyncResponse} returns this
+ */
+proto.sync.SyncResponse.prototype.clearSharedVaultInvitesList = function() {
+  return this.setSharedVaultInvitesList([]);
+};
+
+
+/**
+ * repeated NotificationRepresentation notifications = 9;
+ * @return {!Array<!proto.sync.NotificationRepresentation>}
+ */
+proto.sync.SyncResponse.prototype.getNotificationsList = function() {
+  return /** @type{!Array<!proto.sync.NotificationRepresentation>} */ (
+    jspb.Message.getRepeatedWrapperField(this, proto.sync.NotificationRepresentation, 9));
+};
+
+
+/**
+ * @param {!Array<!proto.sync.NotificationRepresentation>} value
+ * @return {!proto.sync.SyncResponse} returns this
+*/
+proto.sync.SyncResponse.prototype.setNotificationsList = function(value) {
+  return jspb.Message.setRepeatedWrapperField(this, 9, value);
+};
+
+
+/**
+ * @param {!proto.sync.NotificationRepresentation=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.sync.NotificationRepresentation}
+ */
+proto.sync.SyncResponse.prototype.addNotifications = function(opt_value, opt_index) {
+  return jspb.Message.addToRepeatedWrapperField(this, 9, opt_value, proto.sync.NotificationRepresentation, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.sync.SyncResponse} returns this
+ */
+proto.sync.SyncResponse.prototype.clearNotificationsList = function() {
+  return this.setNotificationsList([]);
+};
+
+
+
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.sync.SyncRequest.repeatedFields_ = [1,2];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.sync.SyncRequest.prototype.toObject = function(opt_includeInstance) {
+  return proto.sync.SyncRequest.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.sync.SyncRequest} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.SyncRequest.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    itemsList: jspb.Message.toObjectList(msg.getItemsList(),
+    proto.sync.ItemHash.toObject, includeInstance),
+    sharedVaultUuidsList: (f = jspb.Message.getRepeatedField(msg, 2)) == null ? undefined : f,
+    computeIntegrity: jspb.Message.getBooleanFieldWithDefault(msg, 3, false),
+    syncToken: jspb.Message.getFieldWithDefault(msg, 4, ""),
+    cursorToken: jspb.Message.getFieldWithDefault(msg, 5, ""),
+    limit: jspb.Message.getFieldWithDefault(msg, 6, 0),
+    contentType: jspb.Message.getFieldWithDefault(msg, 7, ""),
+    apiVersion: jspb.Message.getFieldWithDefault(msg, 8, "")
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.sync.SyncRequest}
+ */
+proto.sync.SyncRequest.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.sync.SyncRequest;
+  return proto.sync.SyncRequest.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.sync.SyncRequest} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.sync.SyncRequest}
+ */
+proto.sync.SyncRequest.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = new proto.sync.ItemHash;
+      reader.readMessage(value,proto.sync.ItemHash.deserializeBinaryFromReader);
+      msg.addItems(value);
+      break;
+    case 2:
+      var value = /** @type {string} */ (reader.readString());
+      msg.addSharedVaultUuids(value);
+      break;
+    case 3:
+      var value = /** @type {boolean} */ (reader.readBool());
+      msg.setComputeIntegrity(value);
+      break;
+    case 4:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setSyncToken(value);
+      break;
+    case 5:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setCursorToken(value);
+      break;
+    case 6:
+      var value = /** @type {number} */ (reader.readUint32());
+      msg.setLimit(value);
+      break;
+    case 7:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setContentType(value);
+      break;
+    case 8:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setApiVersion(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.sync.SyncRequest.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.sync.SyncRequest.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.sync.SyncRequest} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.sync.SyncRequest.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getItemsList();
+  if (f.length > 0) {
+    writer.writeRepeatedMessage(
+      1,
+      f,
+      proto.sync.ItemHash.serializeBinaryToWriter
+    );
+  }
+  f = message.getSharedVaultUuidsList();
+  if (f.length > 0) {
+    writer.writeRepeatedString(
+      2,
+      f
+    );
+  }
+  f = /** @type {boolean} */ (jspb.Message.getField(message, 3));
+  if (f != null) {
+    writer.writeBool(
+      3,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 4));
+  if (f != null) {
+    writer.writeString(
+      4,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 5));
+  if (f != null) {
+    writer.writeString(
+      5,
+      f
+    );
+  }
+  f = /** @type {number} */ (jspb.Message.getField(message, 6));
+  if (f != null) {
+    writer.writeUint32(
+      6,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 7));
+  if (f != null) {
+    writer.writeString(
+      7,
+      f
+    );
+  }
+  f = /** @type {string} */ (jspb.Message.getField(message, 8));
+  if (f != null) {
+    writer.writeString(
+      8,
+      f
+    );
+  }
+};
+
+
+/**
+ * repeated ItemHash items = 1;
+ * @return {!Array<!proto.sync.ItemHash>}
+ */
+proto.sync.SyncRequest.prototype.getItemsList = function() {
+  return /** @type{!Array<!proto.sync.ItemHash>} */ (
+    jspb.Message.getRepeatedWrapperField(this, proto.sync.ItemHash, 1));
+};
+
+
+/**
+ * @param {!Array<!proto.sync.ItemHash>} value
+ * @return {!proto.sync.SyncRequest} returns this
+*/
+proto.sync.SyncRequest.prototype.setItemsList = function(value) {
+  return jspb.Message.setRepeatedWrapperField(this, 1, value);
+};
+
+
+/**
+ * @param {!proto.sync.ItemHash=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.sync.ItemHash}
+ */
+proto.sync.SyncRequest.prototype.addItems = function(opt_value, opt_index) {
+  return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.sync.ItemHash, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.clearItemsList = function() {
+  return this.setItemsList([]);
+};
+
+
+/**
+ * repeated string shared_vault_uuids = 2;
+ * @return {!Array<string>}
+ */
+proto.sync.SyncRequest.prototype.getSharedVaultUuidsList = function() {
+  return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 2));
+};
+
+
+/**
+ * @param {!Array<string>} value
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.setSharedVaultUuidsList = function(value) {
+  return jspb.Message.setField(this, 2, value || []);
+};
+
+
+/**
+ * @param {string} value
+ * @param {number=} opt_index
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.addSharedVaultUuids = function(value, opt_index) {
+  return jspb.Message.addToRepeatedField(this, 2, value, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.clearSharedVaultUuidsList = function() {
+  return this.setSharedVaultUuidsList([]);
+};
+
+
+/**
+ * optional bool compute_integrity = 3;
+ * @return {boolean}
+ */
+proto.sync.SyncRequest.prototype.getComputeIntegrity = function() {
+  return /** @type {boolean} */ (jspb.Message.getBooleanFieldWithDefault(this, 3, false));
+};
+
+
+/**
+ * @param {boolean} value
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.setComputeIntegrity = function(value) {
+  return jspb.Message.setField(this, 3, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.clearComputeIntegrity = function() {
+  return jspb.Message.setField(this, 3, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SyncRequest.prototype.hasComputeIntegrity = function() {
+  return jspb.Message.getField(this, 3) != null;
+};
+
+
+/**
+ * optional string sync_token = 4;
+ * @return {string}
+ */
+proto.sync.SyncRequest.prototype.getSyncToken = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.setSyncToken = function(value) {
+  return jspb.Message.setField(this, 4, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.clearSyncToken = function() {
+  return jspb.Message.setField(this, 4, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SyncRequest.prototype.hasSyncToken = function() {
+  return jspb.Message.getField(this, 4) != null;
+};
+
+
+/**
+ * optional string cursor_token = 5;
+ * @return {string}
+ */
+proto.sync.SyncRequest.prototype.getCursorToken = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.setCursorToken = function(value) {
+  return jspb.Message.setField(this, 5, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.clearCursorToken = function() {
+  return jspb.Message.setField(this, 5, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SyncRequest.prototype.hasCursorToken = function() {
+  return jspb.Message.getField(this, 5) != null;
+};
+
+
+/**
+ * optional uint32 limit = 6;
+ * @return {number}
+ */
+proto.sync.SyncRequest.prototype.getLimit = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 6, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.setLimit = function(value) {
+  return jspb.Message.setField(this, 6, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.clearLimit = function() {
+  return jspb.Message.setField(this, 6, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SyncRequest.prototype.hasLimit = function() {
+  return jspb.Message.getField(this, 6) != null;
+};
+
+
+/**
+ * optional string content_type = 7;
+ * @return {string}
+ */
+proto.sync.SyncRequest.prototype.getContentType = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 7, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.setContentType = function(value) {
+  return jspb.Message.setField(this, 7, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.clearContentType = function() {
+  return jspb.Message.setField(this, 7, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SyncRequest.prototype.hasContentType = function() {
+  return jspb.Message.getField(this, 7) != null;
+};
+
+
+/**
+ * optional string api_version = 8;
+ * @return {string}
+ */
+proto.sync.SyncRequest.prototype.getApiVersion = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 8, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.setApiVersion = function(value) {
+  return jspb.Message.setField(this, 8, value);
+};
+
+
+/**
+ * Clears the field making it undefined.
+ * @return {!proto.sync.SyncRequest} returns this
+ */
+proto.sync.SyncRequest.prototype.clearApiVersion = function() {
+  return jspb.Message.setField(this, 8, undefined);
+};
+
+
+/**
+ * Returns whether this field is set.
+ * @return {boolean}
+ */
+proto.sync.SyncRequest.prototype.hasApiVersion = function() {
+  return jspb.Message.getField(this, 8) != null;
+};
+
+
+goog.object.extend(exports, proto.sync);

+ 1 - 1
packages/grpc/package.json

@@ -21,7 +21,7 @@
     "build": "tsc --build"
   },
   "dependencies": {
-    "@grpc/grpc-js": "^1.9.10",
+    "@grpc/grpc-js": "^1.9.11",
     "google-protobuf": "^3.21.2"
   },
   "devDependencies": {

+ 146 - 0
packages/grpc/proto/sync.proto

@@ -0,0 +1,146 @@
+syntax = "proto3";
+
+package sync;
+
+message ItemHashRepresentation {
+  string uuid = 1;
+  string user_uuid = 2;
+  optional string content = 3;
+  optional string content_type = 4;
+  optional bool deleted = 5;
+  optional string duplicate_of = 6;
+  optional string auth_hash = 7;
+  optional string enc_item_key = 8;
+  optional string items_key_id = 9;
+  optional string key_system_identifier = 10;
+  optional string shared_vault_uuid = 11;
+  optional string created_at = 12;
+  optional uint64 created_at_timestamp = 13;
+  optional string updated_at = 14;
+  optional uint64 updated_at_timestamp = 15;
+}
+
+
+message ItemConflictRepresentation {
+  optional ItemRepresentation server_item = 1;
+  optional ItemHashRepresentation unsaved_item = 2;
+  string type = 3;
+}
+
+message ItemRepresentation {
+  string uuid = 1;
+  optional string items_key_id = 2;
+  optional string duplicate_of = 3;
+  optional string enc_item_key = 4;
+  optional string content = 5;
+  string content_type = 6;
+  optional string auth_hash = 7;
+  bool deleted = 8;
+  string created_at = 9;
+  uint64 created_at_timestamp = 10;
+  string updated_at = 11;
+  uint64 updated_at_timestamp = 12;
+  optional string updated_with_session = 13;
+  optional string key_system_identifier = 14;
+  optional string shared_vault_uuid = 15;
+  optional string user_uuid = 16;
+  optional string last_edited_by_uuid = 17;
+}
+
+message SavedItemRepresentation {
+  string uuid = 1;
+  optional string duplicate_of = 2;
+  string content_type = 3;
+  optional string auth_hash = 4;
+  bool deleted = 5;
+  string created_at = 6;
+  uint64 created_at_timestamp = 7;
+  string updated_at = 8;
+  uint64 updated_at_timestamp = 9;
+  optional string key_system_identifier = 10;
+  optional string shared_vault_uuid = 11;
+  optional string user_uuid = 12;
+  optional string last_edited_by_uuid = 13;
+}
+
+message MessageRepresentation {
+  string uuid = 1;
+  string recipient_uuid = 2;
+  string sender_uuid = 3;
+  string encrypted_message = 4;
+  optional string replaceability_identifier = 5;
+  uint64 created_at_timestamp = 6;
+  uint64 updated_at_timestamp = 7;
+}
+
+message SharedVaultRepresentation {
+  string uuid = 1;
+  string user_uuid = 2;
+  uint32 file_upload_bytes_used = 3;
+  uint64 created_at_timestamp = 4;
+  uint64 updated_at_timestamp = 5;
+}
+
+message SharedVaultInviteRepresentation {
+  string uuid = 1;
+  string shared_vault_uuid = 2;
+  string user_uuid = 3;
+  string sender_uuid = 4;
+  string encrypted_message = 5;
+  string permission = 6;
+  uint64 created_at_timestamp = 7;
+  uint64 updated_at_timestamp = 8;
+}
+
+message NotificationRepresentation {
+  string uuid = 1;
+  string user_uuid = 2;
+  string type = 3;
+  string payload = 4;
+  uint64 created_at_timestamp = 5;
+  uint64 updated_at_timestamp = 6;
+}
+
+message ItemHash {
+  string uuid = 1;
+  optional string content = 2;
+  optional string content_type = 3;
+  optional bool deleted = 4;
+  optional string duplicate_of = 5;
+  optional string auth_hash = 6;
+  optional string enc_item_key = 7;
+  optional string items_key_id = 8;
+  optional string key_system_identifier = 9;
+  optional string shared_vault_uuid = 10;
+  optional string created_at = 11;
+  optional uint64 created_at_timestamp = 12;
+  optional string updated_at = 13;
+  optional uint64 updated_at_timestamp = 14;
+}
+
+message SyncResponse {
+  repeated ItemRepresentation retrieved_items = 1;
+  repeated SavedItemRepresentation saved_items = 2;
+  repeated ItemConflictRepresentation conflicts = 3;
+  string sync_token = 4;
+  optional string cursor_token = 5;
+  repeated MessageRepresentation messages = 6;
+  repeated SharedVaultRepresentation shared_vaults = 7;
+  repeated SharedVaultInviteRepresentation shared_vault_invites = 8;
+  repeated NotificationRepresentation notifications = 9;
+}
+
+message SyncRequest {
+  repeated ItemHash items = 1;
+  repeated string shared_vault_uuids = 2;
+  optional bool compute_integrity = 3;
+  optional string sync_token = 4;
+  optional string cursor_token = 5;
+  optional uint32 limit = 6;
+  optional string content_type = 7;
+  optional string api_version = 8;
+}
+
+service Syncing {
+  rpc syncItems(SyncRequest) returns (SyncResponse) {}
+}

+ 1 - 0
packages/syncing-server/.env.sample

@@ -6,6 +6,7 @@ VERSION=development
 AUTH_JWT_SECRET=auth_jwt_secret
 
 PORT=3000
+GRPC_PORT=50052
 
 DB_HOST=db
 DB_REPLICA_HOST=db

+ 49 - 0
packages/syncing-server/bin/server.ts

@@ -10,13 +10,20 @@ import '../src/Infra/InversifyExpressUtils/AnnotatedSharedVaultsController'
 
 import helmet from 'helmet'
 import * as cors from 'cors'
+import * as grpc from '@grpc/grpc-js'
 import { urlencoded, json, Request, Response, NextFunction } from 'express'
 import * as winston from 'winston'
 import { InversifyExpressServer } from 'inversify-express-utils'
+import { MapperInterface } from '@standardnotes/domain-core'
+import { SyncResponse, SyncingService } from '@standardnotes/grpc'
 
 import TYPES from '../src/Bootstrap/Types'
 import { Env } from '../src/Bootstrap/Env'
 import { ContainerConfigLoader } from '../src/Bootstrap/Container'
+import { SyncingServer } from '../src/Infra/gRPC/SyncingServer'
+import { SyncItems } from '../src/Domain/UseCase/Syncing/SyncItems/SyncItems'
+import { SyncResponseFactoryResolverInterface } from '../src/Domain/Item/SyncResponse/SyncResponseFactoryResolverInterface'
+import { SyncResponse20200115 } from '../src/Domain/Item/SyncResponse/SyncResponse20200115'
 
 const container = new ContainerConfigLoader()
 void container.load().then((container) => {
@@ -78,11 +85,53 @@ void container.load().then((container) => {
 
   serverInstance.keepAliveTimeout = keepAliveTimeout
 
+  const grpcKeepAliveTimeout = env.get('GRPC_KEEP_ALIVE_TIMEOUT', true)
+    ? +env.get('GRPC_KEEP_ALIVE_TIMEOUT', true)
+    : 10_000
+
+  const grpcServer = new grpc.Server({
+    'grpc.keepalive_time_ms': grpcKeepAliveTimeout * 2,
+    'grpc.keepalive_timeout_ms': grpcKeepAliveTimeout,
+  })
+
+  const gRPCPort = env.get('GRPC_PORT', true) ? +env.get('GRPC_PORT', true) : 50051
+
+  const syncingServer = new SyncingServer(
+    container.get<SyncItems>(TYPES.Sync_SyncItems),
+    container.get<SyncResponseFactoryResolverInterface>(TYPES.Sync_SyncResponseFactoryResolver),
+    container.get<MapperInterface<SyncResponse20200115, SyncResponse>>(TYPES.Sync_SyncResponseGRPCMapper),
+    container.get<winston.Logger>(TYPES.Sync_Logger),
+  )
+
+  grpcServer.addService(SyncingService, {
+    syncItems: syncingServer.syncItems.bind(syncingServer),
+  })
+  grpcServer.bindAsync(`0.0.0.0:${gRPCPort}`, grpc.ServerCredentials.createInsecure(), (error, port) => {
+    if (error) {
+      logger.error(`Failed to bind gRPC server: ${error.message}`)
+
+      return
+    }
+
+    logger.info(`gRPC server bound on port ${port}`)
+
+    grpcServer.start()
+
+    logger.info('gRPC server started')
+  })
+
   process.on('SIGTERM', () => {
     logger.info('SIGTERM signal received: closing HTTP server')
     serverInstance.close(() => {
       logger.info('HTTP server closed')
     })
+    grpcServer.tryShutdown((error?: Error) => {
+      if (error) {
+        logger.error(`Failed to shutdown gRPC server: ${error.message}`)
+      } else {
+        logger.info('gRPC server closed')
+      }
+    })
   })
 
   logger.info(`Server started on port ${process.env.PORT}`)

+ 2 - 0
packages/syncing-server/package.json

@@ -30,11 +30,13 @@
     "@aws-sdk/client-s3": "^3.427.0",
     "@aws-sdk/client-sns": "^3.427.0",
     "@aws-sdk/client-sqs": "^3.427.0",
+    "@grpc/grpc-js": "^1.9.11",
     "@standardnotes/api": "^1.26.26",
     "@standardnotes/common": "workspace:*",
     "@standardnotes/domain-core": "workspace:^",
     "@standardnotes/domain-events": "workspace:*",
     "@standardnotes/domain-events-infra": "workspace:*",
+    "@standardnotes/grpc": "workspace:^",
     "@standardnotes/responses": "^1.13.27",
     "@standardnotes/security": "workspace:*",
     "@standardnotes/settings": "workspace:*",

+ 6 - 0
packages/syncing-server/src/Bootstrap/Container.ts

@@ -157,6 +157,9 @@ import { RemoveUserFromSharedVaults } from '../Domain/UseCase/SharedVaults/Remov
 import { TransferSharedVault } from '../Domain/UseCase/SharedVaults/TransferSharedVault/TransferSharedVault'
 import { TransferSharedVaultItems } from '../Domain/UseCase/SharedVaults/TransferSharedVaultItems/TransferSharedVaultItems'
 import { DumpItem } from '../Domain/UseCase/Syncing/DumpItem/DumpItem'
+import { SyncResponse20200115 } from '../Domain/Item/SyncResponse/SyncResponse20200115'
+import { SyncResponse } from '@standardnotes/grpc'
+import { SyncResponseGRPCMapper } from '../Mapping/gRPC/SyncResponseGRPCMapper'
 
 export class ContainerConfigLoader {
   private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -351,6 +354,9 @@ export class ContainerConfigLoader {
     container
       .bind<MapperInterface<Notification, NotificationHttpRepresentation>>(TYPES.Sync_NotificationHttpMapper)
       .toConstantValue(new NotificationHttpMapper())
+    container
+      .bind<MapperInterface<SyncResponse20200115, SyncResponse>>(TYPES.Sync_SyncResponseGRPCMapper)
+      .toConstantValue(new SyncResponseGRPCMapper())
 
     // ORM
     container

+ 1 - 0
packages/syncing-server/src/Bootstrap/Types.ts

@@ -126,6 +126,7 @@ const TYPES = {
   Sync_MessagePersistenceMapper: Symbol.for('Sync_MessagePersistenceMapper'),
   Sync_MessageHttpMapper: Symbol.for('Sync_MessageHttpMapper'),
   Sync_NotificationHttpMapper: Symbol.for('Sync_NotificationHttpMapper'),
+  Sync_SyncResponseGRPCMapper: Symbol.for('Sync_SyncResponseGRPCMapper'),
   Sync_SQLItemPersistenceMapper: Symbol.for('Sync_SQLItemPersistenceMapper'),
   Sync_ItemHttpMapper: Symbol.for('Sync_ItemHttpMapper'),
   Sync_ItemHashHttpMapper: Symbol.for('Sync_ItemHashHttpMapper'),

+ 1 - 1
packages/syncing-server/src/Domain/UseCase/Syncing/GetItems/GetItems.spec.ts

@@ -184,7 +184,7 @@ describe('GetItems', () => {
     })
 
     expect(result.isFailed()).toBeTruthy()
-    expect(result.getError()).toEqual('Given value is not a valid uuid: invalid')
+    expect(result.getError()).toEqual('User uuid is invalid: Given value is not a valid uuid: invalid')
   })
 
   it('should filter shared vault uuids user wants to sync with the ones it has access to', async () => {

+ 1 - 1
packages/syncing-server/src/Domain/UseCase/Syncing/GetItems/GetItems.ts

@@ -31,7 +31,7 @@ export class GetItems implements UseCaseInterface<GetItemsResult> {
 
     const userUuidOrError = Uuid.create(dto.userUuid)
     if (userUuidOrError.isFailed()) {
-      return Result.fail(userUuidOrError.getError())
+      return Result.fail(`User uuid is invalid: ${userUuidOrError.getError()}`)
     }
     const userUuid = userUuidOrError.getValue()
 

+ 4 - 4
packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts

@@ -29,7 +29,7 @@ export class SaveNewItem implements UseCaseInterface<Item> {
   async execute(dto: SaveNewItemDTO): Promise<Result<Item>> {
     const uuidOrError = Uuid.create(dto.itemHash.props.uuid)
     if (uuidOrError.isFailed()) {
-      return Result.fail(uuidOrError.getError())
+      return Result.fail(`Item uuid is invalid: ${uuidOrError.getError()}`)
     }
     const uuid = uuidOrError.getValue()
 
@@ -37,13 +37,13 @@ export class SaveNewItem implements UseCaseInterface<Item> {
     if (dto.sessionUuid) {
       const sessionUuidOrError = Uuid.create(dto.sessionUuid)
       if (sessionUuidOrError.isFailed()) {
-        return Result.fail(sessionUuidOrError.getError())
+        return Result.fail(`Session uuid is invalid: ${sessionUuidOrError.getError()}`)
       }
       updatedWithSession = sessionUuidOrError.getValue()
     }
     const userUuidOrError = Uuid.create(dto.userUuid)
     if (userUuidOrError.isFailed()) {
-      return Result.fail(userUuidOrError.getError())
+      return Result.fail(`User uuid is invalid: ${userUuidOrError.getError()}`)
     }
     const userUuid = userUuidOrError.getValue()
 
@@ -57,7 +57,7 @@ export class SaveNewItem implements UseCaseInterface<Item> {
     if (dto.itemHash.props.duplicate_of) {
       const duplicateOfOrError = Uuid.create(dto.itemHash.props.duplicate_of)
       if (duplicateOfOrError.isFailed()) {
-        return Result.fail(duplicateOfOrError.getError())
+        return Result.fail(`Duplicate of uuid is invalid: ${duplicateOfOrError.getError()}`)
       }
       duplicateOf = duplicateOfOrError.getValue()
     }

+ 1 - 1
packages/syncing-server/src/Domain/UseCase/Syncing/SyncItems/SyncItemsDTO.ts

@@ -4,7 +4,7 @@ export type SyncItemsDTO = {
   userUuid: string
   itemHashes: Array<ItemHash>
   computeIntegrityHash: boolean
-  limit: number
+  limit?: number
   sharedVaultUuids?: string[]
   syncToken?: string | null
   cursorToken?: string | null

+ 3 - 3
packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts

@@ -40,7 +40,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
   async execute(dto: UpdateExistingItemDTO): Promise<Result<Item>> {
     const userUuidOrError = Uuid.create(dto.performingUserUuid)
     if (userUuidOrError.isFailed()) {
-      return Result.fail(userUuidOrError.getError())
+      return Result.fail(`User uuid is invalid: ${userUuidOrError.getError()}`)
     }
     const userUuid = userUuidOrError.getValue()
 
@@ -61,7 +61,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
     if (dto.sessionUuid) {
       const sessionUuidOrError = Uuid.create(dto.sessionUuid)
       if (sessionUuidOrError.isFailed()) {
-        return Result.fail(sessionUuidOrError.getError())
+        return Result.fail(`Session uuid is invalid: ${sessionUuidOrError.getError()}`)
       }
       sessionUuid = sessionUuidOrError.getValue()
     }
@@ -88,7 +88,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
     if (dto.itemHash.props.duplicate_of) {
       const duplicateOfOrError = Uuid.create(dto.itemHash.props.duplicate_of)
       if (duplicateOfOrError.isFailed()) {
-        return Result.fail(duplicateOfOrError.getError())
+        return Result.fail(`Duplicate of uuid is invalid: ${duplicateOfOrError.getError()}`)
       }
       wasMarkedAsDuplicate = dto.existingItem.props.duplicateOf === null
       dto.existingItem.props.duplicateOf = duplicateOfOrError.getValue()

+ 113 - 0
packages/syncing-server/src/Infra/gRPC/SyncingServer.ts

@@ -0,0 +1,113 @@
+import * as grpc from '@grpc/grpc-js'
+import { Status } from '@grpc/grpc-js/build/src/constants'
+import { ISyncingServer, SyncRequest, SyncResponse } from '@standardnotes/grpc'
+import { Logger } from 'winston'
+import { MapperInterface } from '@standardnotes/domain-core'
+
+import { ItemHash } from '../../Domain/Item/ItemHash'
+import { SyncItems } from '../../Domain/UseCase/Syncing/SyncItems/SyncItems'
+import { ApiVersion } from '../../Domain/Api/ApiVersion'
+import { SyncResponseFactoryResolverInterface } from '../../Domain/Item/SyncResponse/SyncResponseFactoryResolverInterface'
+import { SyncResponse20200115 } from '../../Domain/Item/SyncResponse/SyncResponse20200115'
+
+export class SyncingServer implements ISyncingServer {
+  constructor(
+    private syncItemsUseCase: SyncItems,
+    private syncResponseFactoryResolver: SyncResponseFactoryResolverInterface,
+    private mapper: MapperInterface<SyncResponse20200115, SyncResponse>,
+    private logger: Logger,
+  ) {}
+
+  async syncItems(
+    call: grpc.ServerUnaryCall<SyncRequest, SyncResponse>,
+    callback: grpc.sendUnaryData<SyncResponse>,
+  ): Promise<void> {
+    this.logger.debug('[SyncingServer] Syncing items via gRPC')
+
+    const itemHashesRPC = call.request.getItemsList()
+    const itemHashes: ItemHash[] = []
+    for (const itemHash of itemHashesRPC) {
+      const itemHashOrError = ItemHash.create({
+        uuid: itemHash.getUuid(),
+        content: itemHash.hasContent() ? itemHash.getContent() : undefined,
+        content_type: itemHash.hasContentType() ? (itemHash.getContentType() as string) : null,
+        deleted: itemHash.hasDeleted() ? itemHash.getDeleted() : undefined,
+        duplicate_of: itemHash.hasDuplicateOf() ? itemHash.getDuplicateOf() : undefined,
+        auth_hash: itemHash.hasAuthHash() ? itemHash.getAuthHash() : undefined,
+        enc_item_key: itemHash.hasEncItemKey() ? itemHash.getEncItemKey() : undefined,
+        items_key_id: itemHash.hasItemsKeyId() ? itemHash.getItemsKeyId() : undefined,
+        created_at: itemHash.hasCreatedAt() ? itemHash.getCreatedAt() : undefined,
+        created_at_timestamp: itemHash.hasCreatedAtTimestamp() ? itemHash.getCreatedAtTimestamp() : undefined,
+        updated_at: itemHash.hasUpdatedAt() ? itemHash.getUpdatedAt() : undefined,
+        updated_at_timestamp: itemHash.hasUpdatedAtTimestamp() ? itemHash.getUpdatedAtTimestamp() : undefined,
+        user_uuid: call.metadata.get('userUuid').pop() as string,
+        key_system_identifier: itemHash.hasKeySystemIdentifier() ? (itemHash.getKeySystemIdentifier() as string) : null,
+        shared_vault_uuid: itemHash.hasSharedVaultUuid() ? (itemHash.getSharedVaultUuid() as string) : null,
+      })
+
+      if (itemHashOrError.isFailed()) {
+        const metadata = new grpc.Metadata()
+        metadata.set('x-sync-error-message', itemHashOrError.getError())
+        metadata.set('x-sync-error-response-code', '400')
+
+        return callback(
+          {
+            code: Status.INVALID_ARGUMENT,
+            message: itemHashOrError.getError(),
+            name: 'INVALID_ARGUMENT',
+            metadata,
+          },
+          null,
+        )
+      }
+
+      itemHashes.push(itemHashOrError.getValue())
+    }
+
+    let sharedVaultUuids: string[] | undefined = undefined
+    const sharedVaultUuidsList = call.request.getSharedVaultUuidsList()
+    if (sharedVaultUuidsList.length > 0) {
+      sharedVaultUuids = sharedVaultUuidsList
+    }
+
+    const apiVersion = call.request.hasApiVersion() ? (call.request.getApiVersion() as string) : ApiVersion.v20161215
+
+    const syncResult = await this.syncItemsUseCase.execute({
+      userUuid: call.metadata.get('x-user-uuid').pop() as string,
+      itemHashes,
+      computeIntegrityHash: call.request.hasComputeIntegrity() ? call.request.getComputeIntegrity() === true : false,
+      syncToken: call.request.hasSyncToken() ? call.request.getSyncToken() : undefined,
+      cursorToken: call.request.getCursorToken() ? call.request.getCursorToken() : undefined,
+      limit: call.request.hasLimit() ? call.request.getLimit() : undefined,
+      contentType: call.request.hasContentType() ? call.request.getContentType() : undefined,
+      apiVersion,
+      snjsVersion: call.metadata.get('x-snjs-version').pop() as string,
+      readOnlyAccess: call.metadata.get('x-read-only-access').pop() === 'true',
+      sessionUuid: call.metadata.get('x-session-uuid').pop() as string,
+      sharedVaultUuids,
+    })
+    if (syncResult.isFailed()) {
+      const metadata = new grpc.Metadata()
+      metadata.set('x-sync-error-message', syncResult.getError())
+      metadata.set('x-sync-error-response-code', '400')
+
+      return callback(
+        {
+          code: Status.INVALID_ARGUMENT,
+          message: syncResult.getError(),
+          name: 'INVALID_ARGUMENT',
+          metadata,
+        },
+        null,
+      )
+    }
+
+    const syncResponse = await this.syncResponseFactoryResolver
+      .resolveSyncResponseFactoryVersion(apiVersion)
+      .createResponse(syncResult.getValue())
+
+    const projection = this.mapper.toProjection(syncResponse as SyncResponse20200115)
+
+    callback(null, projection)
+  }
+}

+ 250 - 0
packages/syncing-server/src/Mapping/gRPC/SyncResponseGRPCMapper.ts

@@ -0,0 +1,250 @@
+import { MapperInterface } from '@standardnotes/domain-core'
+import {
+  ItemConflictRepresentation,
+  ItemHashRepresentation,
+  ItemRepresentation,
+  MessageRepresentation,
+  NotificationRepresentation,
+  SavedItemRepresentation,
+  SharedVaultInviteRepresentation,
+  SharedVaultRepresentation,
+  SyncResponse,
+} from '@standardnotes/grpc'
+
+import { ItemHttpRepresentation } from '../Http/ItemHttpRepresentation'
+import { SyncResponse20200115 } from '../../Domain/Item/SyncResponse/SyncResponse20200115'
+import { SavedItemHttpRepresentation } from '../Http/SavedItemHttpRepresentation'
+import { ItemConflictHttpRepresentation } from '../Http/ItemConflictHttpRepresentation'
+import { ItemHashHttpRepresentation } from '../Http/ItemHashHttpRepresentation'
+import { MessageHttpRepresentation } from '../Http/MessageHttpRepresentation'
+import { SharedVaultHttpRepresentation } from '../Http/SharedVaultHttpRepresentation'
+import { SharedVaultInviteHttpRepresentation } from '../Http/SharedVaultInviteHttpRepresentation'
+import { NotificationHttpRepresentation } from '../Http/NotificationHttpRepresentation'
+
+export class SyncResponseGRPCMapper implements MapperInterface<SyncResponse20200115, SyncResponse> {
+  toDomain(_projection: SyncResponse): SyncResponse20200115 {
+    throw new Error('Method not implemented.')
+  }
+
+  toProjection(domain: SyncResponse20200115): SyncResponse {
+    const syncResponse = new SyncResponse()
+
+    const retrievedItems = domain.retrieved_items.map((item) => this.createRetrievedItem(item))
+    syncResponse.setRetrievedItemsList(retrievedItems)
+
+    const savedItems = domain.saved_items.map((item) => this.createSavedItem(item))
+    syncResponse.setSavedItemsList(savedItems)
+
+    const conflicts = domain.conflicts.map((conflict) => this.createConflict(conflict))
+    syncResponse.setConflictsList(conflicts)
+
+    syncResponse.setSyncToken(domain.sync_token)
+    if (domain.cursor_token) {
+      syncResponse.setCursorToken(domain.cursor_token)
+    }
+
+    const messages = domain.messages.map((message) => this.createMessage(message))
+    syncResponse.setMessagesList(messages)
+
+    const sharedVaults = domain.shared_vaults.map((sharedVault) => this.createSharedVault(sharedVault))
+    syncResponse.setSharedVaultsList(sharedVaults)
+
+    const sharedVaultInvites = domain.shared_vault_invites.map((sharedVaultInvite) =>
+      this.createSharedVaultInvite(sharedVaultInvite),
+    )
+    syncResponse.setSharedVaultInvitesList(sharedVaultInvites)
+
+    const notifications = domain.notifications.map((notification) => this.createNotification(notification))
+    syncResponse.setNotificationsList(notifications)
+
+    return syncResponse
+  }
+
+  private createNotification(notification: NotificationHttpRepresentation): NotificationRepresentation {
+    const notificationRepresentation = new NotificationRepresentation()
+    notificationRepresentation.setUuid(notification.uuid)
+    notificationRepresentation.setUserUuid(notification.user_uuid)
+    notificationRepresentation.setType(notification.type)
+    notificationRepresentation.setPayload(notification.payload)
+    notificationRepresentation.setCreatedAtTimestamp(notification.created_at_timestamp)
+    notificationRepresentation.setUpdatedAtTimestamp(notification.updated_at_timestamp)
+
+    return notificationRepresentation
+  }
+
+  private createSharedVaultInvite(
+    sharedVaultInvite: SharedVaultInviteHttpRepresentation,
+  ): SharedVaultInviteRepresentation {
+    const sharedVaultInviteRepresentation = new SharedVaultInviteRepresentation()
+    sharedVaultInviteRepresentation.setUuid(sharedVaultInvite.uuid)
+    sharedVaultInviteRepresentation.setSharedVaultUuid(sharedVaultInvite.shared_vault_uuid)
+    sharedVaultInviteRepresentation.setUserUuid(sharedVaultInvite.user_uuid)
+    sharedVaultInviteRepresentation.setSenderUuid(sharedVaultInvite.sender_uuid)
+    sharedVaultInviteRepresentation.setEncryptedMessage(sharedVaultInvite.encrypted_message)
+    sharedVaultInviteRepresentation.setPermission(sharedVaultInvite.permission)
+    sharedVaultInviteRepresentation.setCreatedAtTimestamp(sharedVaultInvite.created_at_timestamp)
+    sharedVaultInviteRepresentation.setUpdatedAtTimestamp(sharedVaultInvite.updated_at_timestamp)
+
+    return sharedVaultInviteRepresentation
+  }
+
+  private createSharedVault(sharedVault: SharedVaultHttpRepresentation): SharedVaultRepresentation {
+    const sharedVaultRepresentation = new SharedVaultRepresentation()
+    sharedVaultRepresentation.setUuid(sharedVault.uuid)
+    sharedVaultRepresentation.setUserUuid(sharedVault.user_uuid)
+    sharedVaultRepresentation.setFileUploadBytesUsed(sharedVault.file_upload_bytes_used)
+    sharedVaultRepresentation.setCreatedAtTimestamp(sharedVault.created_at_timestamp)
+    sharedVaultRepresentation.setUpdatedAtTimestamp(sharedVault.updated_at_timestamp)
+
+    return sharedVaultRepresentation
+  }
+
+  private createMessage(message: MessageHttpRepresentation): MessageRepresentation {
+    const messageRepresentation = new MessageRepresentation()
+    messageRepresentation.setUuid(message.uuid)
+    messageRepresentation.setRecipientUuid(message.recipient_uuid)
+    messageRepresentation.setSenderUuid(message.sender_uuid)
+    messageRepresentation.setEncryptedMessage(message.encrypted_message)
+    if (message.replaceability_identifier) {
+      messageRepresentation.setReplaceabilityIdentifier(message.replaceability_identifier)
+    }
+    messageRepresentation.setCreatedAtTimestamp(message.created_at_timestamp)
+    messageRepresentation.setUpdatedAtTimestamp(message.updated_at_timestamp)
+
+    return messageRepresentation
+  }
+
+  private createConflict(conflict: ItemConflictHttpRepresentation): ItemConflictRepresentation {
+    const itemConflictRepresentation = new ItemConflictRepresentation()
+    if (conflict.server_item) {
+      itemConflictRepresentation.setServerItem(this.createRetrievedItem(conflict.server_item))
+    }
+    if (conflict.unsaved_item) {
+      itemConflictRepresentation.setUnsavedItem(this.createItemHash(conflict.unsaved_item))
+    }
+    itemConflictRepresentation.setType(conflict.type)
+
+    return itemConflictRepresentation
+  }
+
+  private createItemHash(itemHash: ItemHashHttpRepresentation): ItemHashRepresentation {
+    const itemHashRepresentation = new ItemHashRepresentation()
+    itemHashRepresentation.setUuid(itemHash.uuid)
+    itemHashRepresentation.setUserUuid(itemHash.user_uuid)
+    if (itemHash.content) {
+      itemHashRepresentation.setContent(itemHash.content)
+    }
+    if (itemHash.content_type) {
+      itemHashRepresentation.setContentType(itemHash.content_type)
+    }
+    if (itemHash.deleted) {
+      itemHashRepresentation.setDeleted(itemHash.deleted)
+    }
+    if (itemHash.duplicate_of) {
+      itemHashRepresentation.setDuplicateOf(itemHash.duplicate_of)
+    }
+    if (itemHash.auth_hash) {
+      itemHashRepresentation.setAuthHash(itemHash.auth_hash)
+    }
+    if (itemHash.enc_item_key) {
+      itemHashRepresentation.setEncItemKey(itemHash.enc_item_key)
+    }
+    if (itemHash.items_key_id) {
+      itemHashRepresentation.setItemsKeyId(itemHash.items_key_id)
+    }
+    if (itemHash.key_system_identifier) {
+      itemHashRepresentation.setKeySystemIdentifier(itemHash.key_system_identifier)
+    }
+    if (itemHash.shared_vault_uuid) {
+      itemHashRepresentation.setSharedVaultUuid(itemHash.shared_vault_uuid)
+    }
+    if (itemHash.created_at) {
+      itemHashRepresentation.setCreatedAt(itemHash.created_at)
+    }
+    if (itemHash.created_at_timestamp) {
+      itemHashRepresentation.setCreatedAtTimestamp(itemHash.created_at_timestamp)
+    }
+    if (itemHash.updated_at) {
+      itemHashRepresentation.setUpdatedAt(itemHash.updated_at)
+    }
+    if (itemHash.updated_at_timestamp) {
+      itemHashRepresentation.setUpdatedAtTimestamp(itemHash.updated_at_timestamp)
+    }
+
+    return itemHashRepresentation
+  }
+
+  private createSavedItem(item: SavedItemHttpRepresentation): SavedItemRepresentation {
+    const savedItemRepresentation = new SavedItemRepresentation()
+    savedItemRepresentation.setUuid(item.uuid)
+    if (item.duplicate_of) {
+      savedItemRepresentation.setDuplicateOf(item.duplicate_of)
+    }
+    savedItemRepresentation.setContentType(item.content_type)
+    if (item.auth_hash) {
+      savedItemRepresentation.setAuthHash(item.auth_hash)
+    }
+    savedItemRepresentation.setDeleted(item.deleted)
+    savedItemRepresentation.setCreatedAt(item.created_at)
+    savedItemRepresentation.setCreatedAtTimestamp(item.created_at_timestamp)
+    savedItemRepresentation.setUpdatedAt(item.updated_at)
+    savedItemRepresentation.setUpdatedAtTimestamp(item.updated_at_timestamp)
+    if (item.key_system_identifier) {
+      savedItemRepresentation.setKeySystemIdentifier(item.key_system_identifier)
+    }
+    if (item.shared_vault_uuid) {
+      savedItemRepresentation.setSharedVaultUuid(item.shared_vault_uuid)
+    }
+    if (item.user_uuid) {
+      savedItemRepresentation.setUserUuid(item.user_uuid)
+    }
+    if (item.last_edited_by_uuid) {
+      savedItemRepresentation.setLastEditedByUuid(item.last_edited_by_uuid)
+    }
+
+    return savedItemRepresentation
+  }
+
+  private createRetrievedItem(item: ItemHttpRepresentation): ItemRepresentation {
+    const itemRepresentation = new ItemRepresentation()
+    itemRepresentation.setUuid(item.uuid)
+    if (item.items_key_id) {
+      itemRepresentation.setItemsKeyId(item.items_key_id)
+    }
+    if (item.duplicate_of) {
+      itemRepresentation.setDuplicateOf(item.duplicate_of)
+    }
+    if (item.enc_item_key) {
+      itemRepresentation.setEncItemKey(item.enc_item_key)
+    }
+    if (item.content) {
+      itemRepresentation.setContent(item.content)
+    }
+    itemRepresentation.setContentType(item.content_type)
+    if (item.auth_hash) {
+      itemRepresentation.setAuthHash(item.auth_hash)
+    }
+    itemRepresentation.setDeleted(item.deleted)
+    itemRepresentation.setCreatedAt(item.created_at)
+    itemRepresentation.setCreatedAtTimestamp(item.created_at_timestamp)
+    itemRepresentation.setUpdatedAt(item.updated_at)
+    itemRepresentation.setUpdatedAtTimestamp(item.updated_at_timestamp)
+    if (item.updated_with_session) {
+      itemRepresentation.setUpdatedWithSession(item.updated_with_session)
+    }
+    if (item.key_system_identifier) {
+      itemRepresentation.setKeySystemIdentifier(item.key_system_identifier)
+    }
+    if (item.shared_vault_uuid) {
+      itemRepresentation.setSharedVaultUuid(item.shared_vault_uuid)
+    }
+    if (item.user_uuid) {
+      itemRepresentation.setUserUuid(item.user_uuid)
+    }
+    if (item.last_edited_by_uuid) {
+      itemRepresentation.setLastEditedByUuid(item.last_edited_by_uuid)
+    }
+
+    return itemRepresentation
+  }
+}

+ 9 - 7
yarn.lock

@@ -2341,13 +2341,13 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@grpc/grpc-js@npm:^1.9.10":
-  version: 1.9.10
-  resolution: "@grpc/grpc-js@npm:1.9.10"
+"@grpc/grpc-js@npm:^1.9.11":
+  version: 1.9.11
+  resolution: "@grpc/grpc-js@npm:1.9.11"
   dependencies:
     "@grpc/proto-loader": "npm:^0.7.8"
     "@types/node": "npm:>=12.12.47"
-  checksum: 243cf994e6487ec9b6b24b155468c598e7f100491b3a274a963abd897b751d831149362b3c47c2d7c905df3d0fd0fa294ce8cba33efdde61f48e326b6e657e06
+  checksum: 71b8517b4ff1b3e518bc20b4366c0a7363cb925158eb8f4c99a233a3268ccf7effd0e6ec600429ff7c3f58463c612ec3bab82e40255f632c85a4de88d647e9ec
   languageName: node
   linkType: hard
 
@@ -5264,7 +5264,7 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@standardnotes/api-gateway@workspace:packages/api-gateway"
   dependencies:
-    "@grpc/grpc-js": "npm:^1.9.10"
+    "@grpc/grpc-js": "npm:^1.9.11"
     "@standardnotes/domain-core": "workspace:^"
     "@standardnotes/domain-events": "workspace:*"
     "@standardnotes/domain-events-infra": "workspace:*"
@@ -5327,7 +5327,7 @@ __metadata:
     "@aws-sdk/client-sqs": "npm:^3.427.0"
     "@cbor-extract/cbor-extract-linux-arm64": "npm:^2.1.1"
     "@cbor-extract/cbor-extract-linux-x64": "npm:^2.1.1"
-    "@grpc/grpc-js": "npm:^1.9.10"
+    "@grpc/grpc-js": "npm:^1.9.11"
     "@simplewebauthn/server": "npm:^8.1.1"
     "@simplewebauthn/typescript-types": "npm:^8.0.0"
     "@standardnotes/api": "npm:^1.26.26"
@@ -5541,7 +5541,7 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@standardnotes/grpc@workspace:packages/grpc"
   dependencies:
-    "@grpc/grpc-js": "npm:^1.9.10"
+    "@grpc/grpc-js": "npm:^1.9.11"
     "@types/google-protobuf": "npm:^3"
     google-protobuf: "npm:^3.21.2"
     grpc-tools: "npm:^1.12.4"
@@ -5798,11 +5798,13 @@ __metadata:
     "@aws-sdk/client-s3": "npm:^3.427.0"
     "@aws-sdk/client-sns": "npm:^3.427.0"
     "@aws-sdk/client-sqs": "npm:^3.427.0"
+    "@grpc/grpc-js": "npm:^1.9.11"
     "@standardnotes/api": "npm:^1.26.26"
     "@standardnotes/common": "workspace:*"
     "@standardnotes/domain-core": "workspace:^"
     "@standardnotes/domain-events": "workspace:*"
     "@standardnotes/domain-events-infra": "workspace:*"
+    "@standardnotes/grpc": "workspace:^"
     "@standardnotes/responses": "npm:^1.13.27"
     "@standardnotes/security": "workspace:*"
     "@standardnotes/settings": "workspace:*"