feat: add models package
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { Copy } from '@standardnotes/utils'
|
||||
import { SyncResolvedParams, SyncResolvedPayload } from '../../../Runtime/Deltas/Utilities/SyncResolvedPayload'
|
||||
import { FillItemContent, ItemContent } from '../../Content/ItemContent'
|
||||
import { ContentReference } from '../../Reference/ContentReference'
|
||||
import { DecryptedTransferPayload } from '../../TransferPayload/Interfaces/DecryptedTransferPayload'
|
||||
import { DecryptedPayloadInterface } from '../Interfaces/DecryptedPayload'
|
||||
import { PayloadSource } from '../Types/PayloadSource'
|
||||
import { PurePayload } from './PurePayload'
|
||||
|
||||
export class DecryptedPayload<
|
||||
C extends ItemContent = ItemContent,
|
||||
T extends DecryptedTransferPayload<C> = DecryptedTransferPayload<C>,
|
||||
>
|
||||
extends PurePayload<T>
|
||||
implements DecryptedPayloadInterface<C>
|
||||
{
|
||||
override readonly content: C
|
||||
override readonly deleted: false
|
||||
|
||||
constructor(rawPayload: T, source = PayloadSource.Constructor) {
|
||||
super(rawPayload, source)
|
||||
|
||||
this.content = Copy(FillItemContent<C>(rawPayload.content))
|
||||
this.deleted = false
|
||||
}
|
||||
|
||||
get references(): ContentReference[] {
|
||||
return this.content.references || []
|
||||
}
|
||||
|
||||
public getReference(uuid: Uuid): ContentReference {
|
||||
const result = this.references.find((ref) => ref.uuid === uuid)
|
||||
|
||||
if (!result) {
|
||||
throw new Error('Reference not found')
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override ejected(): DecryptedTransferPayload<C> {
|
||||
return {
|
||||
...super.ejected(),
|
||||
content: this.content,
|
||||
deleted: this.deleted,
|
||||
}
|
||||
}
|
||||
|
||||
copy(override?: Partial<T>, source = this.source): this {
|
||||
const result = new DecryptedPayload(
|
||||
{
|
||||
...this.ejected(),
|
||||
...override,
|
||||
},
|
||||
source,
|
||||
)
|
||||
return result as this
|
||||
}
|
||||
|
||||
copyAsSyncResolved(override?: Partial<T> & SyncResolvedParams, source = this.source): SyncResolvedPayload {
|
||||
const result = new DecryptedPayload(
|
||||
{
|
||||
...this.ejected(),
|
||||
...override,
|
||||
},
|
||||
source,
|
||||
)
|
||||
return result as SyncResolvedPayload
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { DeletedTransferPayload } from './../../TransferPayload/Interfaces/DeletedTransferPayload'
|
||||
import { DeletedPayloadInterface } from '../Interfaces/DeletedPayload'
|
||||
import { PayloadSource } from '../Types/PayloadSource'
|
||||
import { PurePayload } from './PurePayload'
|
||||
import { SyncResolvedParams, SyncResolvedPayload } from '../../../Runtime/Deltas/Utilities/SyncResolvedPayload'
|
||||
|
||||
export class DeletedPayload extends PurePayload<DeletedTransferPayload> implements DeletedPayloadInterface {
|
||||
override readonly deleted: true
|
||||
override readonly content: undefined
|
||||
|
||||
constructor(rawPayload: DeletedTransferPayload, source = PayloadSource.Constructor) {
|
||||
super(rawPayload, source)
|
||||
|
||||
this.deleted = true
|
||||
this.content = undefined
|
||||
}
|
||||
|
||||
get discardable(): boolean | undefined {
|
||||
return !this.dirty
|
||||
}
|
||||
|
||||
override ejected(): DeletedTransferPayload {
|
||||
return {
|
||||
...super.ejected(),
|
||||
deleted: this.deleted,
|
||||
content: undefined,
|
||||
}
|
||||
}
|
||||
|
||||
copy(override?: Partial<DeletedTransferPayload>, source = this.source): this {
|
||||
const result = new DeletedPayload(
|
||||
{
|
||||
...this.ejected(),
|
||||
...override,
|
||||
},
|
||||
source,
|
||||
)
|
||||
return result as this
|
||||
}
|
||||
|
||||
copyAsSyncResolved(
|
||||
override?: Partial<DeletedTransferPayload> & SyncResolvedParams,
|
||||
source = this.source,
|
||||
): SyncResolvedPayload {
|
||||
const result = new DeletedPayload(
|
||||
{
|
||||
...this.ejected(),
|
||||
...override,
|
||||
},
|
||||
source,
|
||||
)
|
||||
return result as SyncResolvedPayload
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { ProtocolVersion, protocolVersionFromEncryptedString } from '@standardnotes/common'
|
||||
import { SyncResolvedParams, SyncResolvedPayload } from '../../../Runtime/Deltas/Utilities/SyncResolvedPayload'
|
||||
import { EncryptedTransferPayload } from '../../TransferPayload/Interfaces/EncryptedTransferPayload'
|
||||
import { EncryptedPayloadInterface } from '../Interfaces/EncryptedPayload'
|
||||
import { PayloadSource } from '../Types/PayloadSource'
|
||||
import { PurePayload } from './PurePayload'
|
||||
|
||||
export class EncryptedPayload extends PurePayload<EncryptedTransferPayload> implements EncryptedPayloadInterface {
|
||||
override readonly content: string
|
||||
override readonly deleted: false
|
||||
readonly auth_hash?: string
|
||||
readonly enc_item_key: string
|
||||
readonly errorDecrypting: boolean
|
||||
readonly items_key_id: string | undefined
|
||||
readonly version: ProtocolVersion
|
||||
readonly waitingForKey: boolean
|
||||
|
||||
constructor(rawPayload: EncryptedTransferPayload, source = PayloadSource.Constructor) {
|
||||
super(rawPayload, source)
|
||||
|
||||
this.auth_hash = rawPayload.auth_hash
|
||||
this.content = rawPayload.content
|
||||
this.deleted = false
|
||||
this.enc_item_key = rawPayload.enc_item_key
|
||||
this.errorDecrypting = rawPayload.errorDecrypting
|
||||
this.items_key_id = rawPayload.items_key_id
|
||||
this.version = protocolVersionFromEncryptedString(this.content)
|
||||
this.waitingForKey = rawPayload.waitingForKey
|
||||
}
|
||||
|
||||
override ejected(): EncryptedTransferPayload {
|
||||
return {
|
||||
...super.ejected(),
|
||||
enc_item_key: this.enc_item_key,
|
||||
items_key_id: this.items_key_id,
|
||||
auth_hash: this.auth_hash,
|
||||
errorDecrypting: this.errorDecrypting,
|
||||
waitingForKey: this.waitingForKey,
|
||||
content: this.content,
|
||||
deleted: this.deleted,
|
||||
}
|
||||
}
|
||||
|
||||
copy(override?: Partial<EncryptedTransferPayload>, source = this.source): this {
|
||||
const result = new EncryptedPayload(
|
||||
{
|
||||
...this.ejected(),
|
||||
...override,
|
||||
},
|
||||
source,
|
||||
)
|
||||
return result as this
|
||||
}
|
||||
|
||||
copyAsSyncResolved(
|
||||
override?: Partial<EncryptedTransferPayload> & SyncResolvedParams,
|
||||
source = this.source,
|
||||
): SyncResolvedPayload {
|
||||
const result = new EncryptedPayload(
|
||||
{
|
||||
...this.ejected(),
|
||||
...override,
|
||||
},
|
||||
source,
|
||||
)
|
||||
return result as SyncResolvedPayload
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { deepFreeze, useBoolean } from '@standardnotes/utils'
|
||||
import { PayloadInterface } from '../Interfaces/PayloadInterface'
|
||||
import { PayloadSource } from '../Types/PayloadSource'
|
||||
import { TransferPayload } from '../../TransferPayload/Interfaces/TransferPayload'
|
||||
import { ItemContent } from '../../Content/ItemContent'
|
||||
import { SyncResolvedParams, SyncResolvedPayload } from '../../../Runtime/Deltas/Utilities/SyncResolvedPayload'
|
||||
|
||||
type RequiredKeepUndefined<T> = { [K in keyof T]-?: [T[K]] } extends infer U
|
||||
? U extends Record<keyof U, [unknown]>
|
||||
? { [K in keyof U]: U[K][0] }
|
||||
: never
|
||||
: never
|
||||
|
||||
export abstract class PurePayload<T extends TransferPayload<C>, C extends ItemContent = ItemContent>
|
||||
implements PayloadInterface<T>
|
||||
{
|
||||
readonly source: PayloadSource
|
||||
readonly uuid: string
|
||||
readonly content_type: ContentType
|
||||
readonly deleted: boolean
|
||||
readonly content: C | string | undefined
|
||||
|
||||
readonly created_at: Date
|
||||
readonly updated_at: Date
|
||||
readonly created_at_timestamp: number
|
||||
readonly updated_at_timestamp: number
|
||||
readonly dirtyIndex?: number
|
||||
readonly globalDirtyIndexAtLastSync?: number
|
||||
readonly dirty?: boolean
|
||||
|
||||
readonly lastSyncBegan?: Date
|
||||
readonly lastSyncEnd?: Date
|
||||
|
||||
readonly duplicate_of?: string
|
||||
|
||||
constructor(rawPayload: T, source = PayloadSource.Constructor) {
|
||||
this.source = source
|
||||
this.uuid = rawPayload.uuid
|
||||
|
||||
if (!this.uuid) {
|
||||
throw Error(
|
||||
`Attempting to construct payload with null uuid
|
||||
Content type: ${rawPayload.content_type}`,
|
||||
)
|
||||
}
|
||||
|
||||
this.content = rawPayload.content
|
||||
this.content_type = rawPayload.content_type
|
||||
this.deleted = useBoolean(rawPayload.deleted, false)
|
||||
this.dirty = rawPayload.dirty
|
||||
this.duplicate_of = rawPayload.duplicate_of
|
||||
|
||||
this.created_at = new Date(rawPayload.created_at || new Date())
|
||||
this.updated_at = new Date(rawPayload.updated_at || 0)
|
||||
|
||||
this.created_at_timestamp = rawPayload.created_at_timestamp || 0
|
||||
this.updated_at_timestamp = rawPayload.updated_at_timestamp || 0
|
||||
|
||||
this.lastSyncBegan = rawPayload.lastSyncBegan ? new Date(rawPayload.lastSyncBegan) : undefined
|
||||
this.lastSyncEnd = rawPayload.lastSyncEnd ? new Date(rawPayload.lastSyncEnd) : undefined
|
||||
|
||||
this.dirtyIndex = rawPayload.dirtyIndex
|
||||
this.globalDirtyIndexAtLastSync = rawPayload.globalDirtyIndexAtLastSync
|
||||
|
||||
const timeToAllowSubclassesToFinishConstruction = 0
|
||||
setTimeout(() => {
|
||||
deepFreeze(this)
|
||||
}, timeToAllowSubclassesToFinishConstruction)
|
||||
}
|
||||
|
||||
ejected(): TransferPayload {
|
||||
const comprehensive: RequiredKeepUndefined<TransferPayload> = {
|
||||
uuid: this.uuid,
|
||||
content: this.content,
|
||||
deleted: this.deleted,
|
||||
content_type: this.content_type,
|
||||
created_at: this.created_at,
|
||||
updated_at: this.updated_at,
|
||||
created_at_timestamp: this.created_at_timestamp,
|
||||
updated_at_timestamp: this.updated_at_timestamp,
|
||||
dirty: this.dirty,
|
||||
duplicate_of: this.duplicate_of,
|
||||
dirtyIndex: this.dirtyIndex,
|
||||
globalDirtyIndexAtLastSync: this.globalDirtyIndexAtLastSync,
|
||||
lastSyncBegan: this.lastSyncBegan,
|
||||
lastSyncEnd: this.lastSyncEnd,
|
||||
}
|
||||
|
||||
return comprehensive
|
||||
}
|
||||
|
||||
public get serverUpdatedAt(): Date {
|
||||
return this.updated_at
|
||||
}
|
||||
|
||||
public get serverUpdatedAtTimestamp(): number {
|
||||
return this.updated_at_timestamp
|
||||
}
|
||||
|
||||
abstract copy(override?: Partial<TransferPayload>, source?: PayloadSource): this
|
||||
|
||||
abstract copyAsSyncResolved(override?: Partial<T> & SyncResolvedParams, source?: PayloadSource): SyncResolvedPayload
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { DecryptedTransferPayload } from './../../TransferPayload/Interfaces/DecryptedTransferPayload'
|
||||
import { ItemContent } from '../../Content/ItemContent'
|
||||
import { ContentReference } from '../../Reference/ContentReference'
|
||||
import { PayloadInterface } from './PayloadInterface'
|
||||
|
||||
export interface DecryptedPayloadInterface<C extends ItemContent = ItemContent>
|
||||
extends PayloadInterface<DecryptedTransferPayload> {
|
||||
readonly content: C
|
||||
deleted: false
|
||||
|
||||
ejected(): DecryptedTransferPayload<C>
|
||||
get references(): ContentReference[]
|
||||
getReference(uuid: Uuid): ContentReference
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { DeletedTransferPayload } from '../../TransferPayload'
|
||||
import { PayloadInterface } from './PayloadInterface'
|
||||
|
||||
export interface DeletedPayloadInterface extends PayloadInterface<DeletedTransferPayload> {
|
||||
readonly deleted: true
|
||||
readonly content: undefined
|
||||
|
||||
/**
|
||||
* Whether a payload can be discarded and removed from storage.
|
||||
* This value is true if a payload is marked as deleted and not dirty.
|
||||
*/
|
||||
discardable: boolean | undefined
|
||||
|
||||
ejected(): DeletedTransferPayload
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import { EncryptedTransferPayload } from '../../TransferPayload/Interfaces/EncryptedTransferPayload'
|
||||
import { PayloadInterface } from './PayloadInterface'
|
||||
|
||||
export interface EncryptedPayloadInterface extends PayloadInterface<EncryptedTransferPayload> {
|
||||
readonly content: string
|
||||
readonly deleted: false
|
||||
readonly enc_item_key: string
|
||||
readonly items_key_id: string | undefined
|
||||
readonly errorDecrypting: boolean
|
||||
readonly waitingForKey: boolean
|
||||
readonly version: ProtocolVersion
|
||||
|
||||
/** @deprecated */
|
||||
readonly auth_hash?: string
|
||||
|
||||
ejected(): EncryptedTransferPayload
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { SyncResolvedParams, SyncResolvedPayload } from './../../../Runtime/Deltas/Utilities/SyncResolvedPayload'
|
||||
import { ContentType, Uuid } from '@standardnotes/common'
|
||||
import { ItemContent } from '../../Content/ItemContent'
|
||||
import { TransferPayload } from '../../TransferPayload/Interfaces/TransferPayload'
|
||||
import { PayloadSource } from '../Types/PayloadSource'
|
||||
|
||||
export interface PayloadInterface<T extends TransferPayload = TransferPayload, C extends ItemContent = ItemContent> {
|
||||
readonly source: PayloadSource
|
||||
readonly uuid: Uuid
|
||||
readonly content_type: ContentType
|
||||
content: C | string | undefined
|
||||
deleted: boolean
|
||||
|
||||
/** updated_at is set by the server only, and not the client.*/
|
||||
readonly updated_at: Date
|
||||
readonly created_at: Date
|
||||
readonly created_at_timestamp: number
|
||||
readonly updated_at_timestamp: number
|
||||
get serverUpdatedAt(): Date
|
||||
get serverUpdatedAtTimestamp(): number
|
||||
|
||||
readonly dirtyIndex?: number
|
||||
readonly globalDirtyIndexAtLastSync?: number
|
||||
readonly dirty?: boolean
|
||||
|
||||
readonly lastSyncBegan?: Date
|
||||
readonly lastSyncEnd?: Date
|
||||
|
||||
readonly duplicate_of?: Uuid
|
||||
|
||||
/**
|
||||
* "Ejected" means a payload for
|
||||
* generic, non-contextual consumption, such as saving to a backup file or syncing
|
||||
* with a server.
|
||||
*/
|
||||
ejected(): TransferPayload
|
||||
|
||||
copy(override?: Partial<T>, source?: PayloadSource): this
|
||||
|
||||
copyAsSyncResolved(override?: Partial<T> & SyncResolvedParams, source?: PayloadSource): SyncResolvedPayload
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { ItemContent } from '../../Content/ItemContent'
|
||||
import {
|
||||
isDecryptedTransferPayload,
|
||||
isDeletedTransferPayload,
|
||||
isEncryptedTransferPayload,
|
||||
isErrorDecryptingTransferPayload,
|
||||
} from '../../TransferPayload'
|
||||
import { DecryptedPayloadInterface } from './DecryptedPayload'
|
||||
import { DeletedPayloadInterface } from './DeletedPayload'
|
||||
import { EncryptedPayloadInterface } from './EncryptedPayload'
|
||||
import { PayloadInterface } from './PayloadInterface'
|
||||
|
||||
export function isDecryptedPayload<C extends ItemContent = ItemContent>(
|
||||
payload: PayloadInterface,
|
||||
): payload is DecryptedPayloadInterface<C> {
|
||||
return isDecryptedTransferPayload(payload)
|
||||
}
|
||||
|
||||
export function isEncryptedPayload(payload: PayloadInterface): payload is EncryptedPayloadInterface {
|
||||
return isEncryptedTransferPayload(payload)
|
||||
}
|
||||
|
||||
export function isDeletedPayload(payload: PayloadInterface): payload is DeletedPayloadInterface {
|
||||
return isDeletedTransferPayload(payload)
|
||||
}
|
||||
|
||||
export function isErrorDecryptingPayload(payload: PayloadInterface): payload is EncryptedPayloadInterface {
|
||||
return isErrorDecryptingTransferPayload(payload)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ItemContent } from '../../Content/ItemContent'
|
||||
import { DecryptedPayloadInterface } from './DecryptedPayload'
|
||||
import { DeletedPayloadInterface } from './DeletedPayload'
|
||||
import { EncryptedPayloadInterface } from './EncryptedPayload'
|
||||
|
||||
export type FullyFormedPayloadInterface<C extends ItemContent = ItemContent> =
|
||||
| DecryptedPayloadInterface<C>
|
||||
| EncryptedPayloadInterface
|
||||
| DeletedPayloadInterface
|
||||
|
||||
export type AnyNonDecryptedPayloadInterface = EncryptedPayloadInterface | DeletedPayloadInterface
|
||||
@@ -0,0 +1,43 @@
|
||||
export enum PayloadEmitSource {
|
||||
/** When an observer registers to stream items, the items are pushed immediately to the observer */
|
||||
InitialObserverRegistrationPush = 1,
|
||||
|
||||
/**
|
||||
* Payload when a client modifies item property then maps it to update UI.
|
||||
* This also indicates that the item was dirtied
|
||||
*/
|
||||
LocalChanged,
|
||||
LocalInserted,
|
||||
LocalDatabaseLoaded,
|
||||
/** The payload returned by offline sync operation */
|
||||
OfflineSyncSaved,
|
||||
LocalRetrieved,
|
||||
|
||||
FileImport,
|
||||
|
||||
ComponentRetrieved,
|
||||
/** Payloads received from an external component with the intention of creating a new item */
|
||||
ComponentCreated,
|
||||
|
||||
/**
|
||||
* When the payloads are about to sync, they are emitted by the sync service with updated
|
||||
* values of lastSyncBegan. Payloads emitted from this source indicate that these payloads
|
||||
* have been saved to disk, and are about to be synced
|
||||
*/
|
||||
PreSyncSave,
|
||||
|
||||
RemoteRetrieved,
|
||||
RemoteSaved,
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the changed payload represents only an internal change that shouldn't
|
||||
* require a UI refresh
|
||||
*/
|
||||
export function isPayloadSourceInternalChange(source: PayloadEmitSource): boolean {
|
||||
return [PayloadEmitSource.RemoteSaved, PayloadEmitSource.PreSyncSave].includes(source)
|
||||
}
|
||||
|
||||
export function isPayloadSourceRetrieved(source: PayloadEmitSource): boolean {
|
||||
return [PayloadEmitSource.RemoteRetrieved, PayloadEmitSource.ComponentRetrieved].includes(source)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export enum PayloadSource {
|
||||
/**
|
||||
* Payloads with a source of Constructor means that the payload was created
|
||||
* in isolated space by the caller, and does not yet have any app-related affiliation.
|
||||
*/
|
||||
Constructor = 1,
|
||||
|
||||
RemoteRetrieved,
|
||||
|
||||
RemoteSaved,
|
||||
|
||||
FileImport,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export function PayloadTimestampDefaults() {
|
||||
return {
|
||||
updated_at: new Date(0),
|
||||
created_at: new Date(),
|
||||
updated_at_timestamp: 0,
|
||||
created_at_timestamp: 0,
|
||||
}
|
||||
}
|
||||
13
packages/models/src/Domain/Abstract/Payload/index.ts
Normal file
13
packages/models/src/Domain/Abstract/Payload/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export * from './Implementations/PurePayload'
|
||||
export * from './Implementations/DecryptedPayload'
|
||||
export * from './Implementations/EncryptedPayload'
|
||||
export * from './Implementations/DeletedPayload'
|
||||
export * from './Interfaces/DecryptedPayload'
|
||||
export * from './Interfaces/DeletedPayload'
|
||||
export * from './Interfaces/EncryptedPayload'
|
||||
export * from './Interfaces/PayloadInterface'
|
||||
export * from './Interfaces/TypeCheck'
|
||||
export * from './Interfaces/UnionTypes'
|
||||
export * from './Types/PayloadSource'
|
||||
export * from './Types/EmitSource'
|
||||
export * from './Types/TimestampDefaults'
|
||||
Reference in New Issue
Block a user