Initial commit
This commit is contained in:
@@ -0,0 +1,687 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import type { Ref, ComputedRef } from 'vue'
|
||||
import type { FileNode, FileCollection, FileEntity } from '../types/node'
|
||||
import type { FilterCondition, SortCondition, RangeCondition } from '../types/common'
|
||||
import { isFileCollection } from '../types/node'
|
||||
import { collectionService } from '../services/collectionService'
|
||||
import { entityService } from '../services/entityService'
|
||||
import { nodeService } from '../services/nodeService'
|
||||
import { FileCollectionObject } from '../models/collection'
|
||||
import { FileEntityObject } from '../models/entity'
|
||||
|
||||
// Root collection constant
|
||||
export const ROOT_ID = '00000000-0000-0000-0000-000000000000'
|
||||
|
||||
// Store structure: provider -> service -> nodeId -> node (either collection or entity object)
|
||||
type NodeRecord = FileCollectionObject | FileEntityObject
|
||||
type ServiceNodeStore = Record<string, NodeRecord>
|
||||
type ProviderNodeStore = Record<string, ServiceNodeStore>
|
||||
type NodeStore = Record<string, ProviderNodeStore>
|
||||
|
||||
export const useNodesStore = defineStore('fileNodes', () => {
|
||||
const nodes: Ref<NodeStore> = ref({})
|
||||
const syncTokens: Ref<Record<string, Record<string, string>>> = ref({}) // provider -> service -> token
|
||||
const loading = ref(false)
|
||||
const error: Ref<string | null> = ref(null)
|
||||
|
||||
// Computed: flat list of all nodes
|
||||
const nodeList: ComputedRef<NodeRecord[]> = computed(() => {
|
||||
const result: NodeRecord[] = []
|
||||
Object.values(nodes.value).forEach(providerNodes => {
|
||||
Object.values(providerNodes).forEach(serviceNodes => {
|
||||
result.push(...Object.values(serviceNodes))
|
||||
})
|
||||
})
|
||||
return result
|
||||
})
|
||||
|
||||
// Computed: all collections (folders)
|
||||
const collectionList: ComputedRef<FileCollectionObject[]> = computed(() => {
|
||||
return nodeList.value.filter(
|
||||
(node): node is FileCollectionObject => node['@type'] === 'files.collection'
|
||||
)
|
||||
})
|
||||
|
||||
// Computed: all entities (files)
|
||||
const entityList: ComputedRef<FileEntityObject[]> = computed(() => {
|
||||
return nodeList.value.filter(
|
||||
(node): node is FileEntityObject => node['@type'] === 'files.entity'
|
||||
)
|
||||
})
|
||||
|
||||
// Get a specific node
|
||||
const getNode = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeId: string
|
||||
): NodeRecord | undefined => {
|
||||
return nodes.value[providerId]?.[serviceId]?.[nodeId]
|
||||
}
|
||||
|
||||
// Get all nodes for a service
|
||||
const getServiceNodes = (
|
||||
providerId: string,
|
||||
serviceId: string
|
||||
): NodeRecord[] => {
|
||||
return Object.values(nodes.value[providerId]?.[serviceId] || {})
|
||||
}
|
||||
|
||||
// Get children of a parent node (or root nodes if parentId is null/ROOT_ID)
|
||||
const getChildren = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
parentId: string | null
|
||||
): NodeRecord[] => {
|
||||
const serviceNodes = nodes.value[providerId]?.[serviceId] || {}
|
||||
const targetParent = parentId === ROOT_ID ? ROOT_ID : parentId
|
||||
return Object.values(serviceNodes).filter(node => node.in === targetParent)
|
||||
}
|
||||
|
||||
// Get child collections (folders)
|
||||
const getChildCollections = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
parentId: string | null
|
||||
): FileCollectionObject[] => {
|
||||
return getChildren(providerId, serviceId, parentId).filter(
|
||||
(node): node is FileCollectionObject => node['@type'] === 'files.collection'
|
||||
)
|
||||
}
|
||||
|
||||
// Get child entities (files)
|
||||
const getChildEntities = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
parentId: string | null
|
||||
): FileEntityObject[] => {
|
||||
return getChildren(providerId, serviceId, parentId).filter(
|
||||
(node): node is FileEntityObject => node['@type'] === 'files.entity'
|
||||
)
|
||||
}
|
||||
|
||||
// Get path to root (ancestors)
|
||||
const getPath = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeId: string
|
||||
): NodeRecord[] => {
|
||||
const path: NodeRecord[] = []
|
||||
let currentNode = getNode(providerId, serviceId, nodeId)
|
||||
|
||||
while (currentNode) {
|
||||
path.unshift(currentNode)
|
||||
if (currentNode.in === null || currentNode.in === ROOT_ID || currentNode.id === ROOT_ID) {
|
||||
break
|
||||
}
|
||||
currentNode = getNode(providerId, serviceId, currentNode.in)
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
// Check if a node is the root
|
||||
const isRoot = (nodeId: string): boolean => {
|
||||
return nodeId === ROOT_ID
|
||||
}
|
||||
|
||||
// Helper to hydrate a node based on its type
|
||||
const hydrateNode = (data: FileNode): NodeRecord => {
|
||||
if (isFileCollection(data)) {
|
||||
return new FileCollectionObject().fromJson(data)
|
||||
} else {
|
||||
return new FileEntityObject().fromJson(data as FileEntity)
|
||||
}
|
||||
}
|
||||
|
||||
// Set all nodes for a provider/service
|
||||
const setNodes = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
data: FileNode[]
|
||||
) => {
|
||||
if (!nodes.value[providerId]) {
|
||||
nodes.value[providerId] = {}
|
||||
}
|
||||
const hydrated: ServiceNodeStore = {}
|
||||
for (const nodeData of data) {
|
||||
hydrated[nodeData.id] = hydrateNode(nodeData)
|
||||
}
|
||||
nodes.value[providerId][serviceId] = hydrated
|
||||
}
|
||||
|
||||
// Add/update a single node
|
||||
const addNode = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
node: FileNode
|
||||
) => {
|
||||
if (!nodes.value[providerId]) {
|
||||
nodes.value[providerId] = {}
|
||||
}
|
||||
if (!nodes.value[providerId][serviceId]) {
|
||||
nodes.value[providerId][serviceId] = {}
|
||||
}
|
||||
nodes.value[providerId][serviceId][node.id] = hydrateNode(node)
|
||||
}
|
||||
|
||||
// Add multiple nodes (handles both array and object formats from API)
|
||||
const addNodes = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
data: FileNode[] | Record<string, FileNode>
|
||||
) => {
|
||||
if (!nodes.value[providerId]) {
|
||||
nodes.value[providerId] = {}
|
||||
}
|
||||
if (!nodes.value[providerId][serviceId]) {
|
||||
nodes.value[providerId][serviceId] = {}
|
||||
}
|
||||
// Handle both array and object (keyed by ID) formats
|
||||
const nodeArray = Array.isArray(data) ? data : Object.values(data)
|
||||
for (const nodeData of nodeArray) {
|
||||
nodes.value[providerId][serviceId][nodeData.id] = hydrateNode(nodeData)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a node
|
||||
const removeNode = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeId: string
|
||||
) => {
|
||||
if (nodes.value[providerId]?.[serviceId]) {
|
||||
delete nodes.value[providerId][serviceId][nodeId]
|
||||
}
|
||||
}
|
||||
|
||||
// Remove multiple nodes
|
||||
const removeNodes = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeIds: string[]
|
||||
) => {
|
||||
if (nodes.value[providerId]?.[serviceId]) {
|
||||
for (const id of nodeIds) {
|
||||
delete nodes.value[providerId][serviceId][id]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all nodes for a service
|
||||
const clearServiceNodes = (
|
||||
providerId: string,
|
||||
serviceId: string
|
||||
) => {
|
||||
if (nodes.value[providerId]) {
|
||||
delete nodes.value[providerId][serviceId]
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all nodes
|
||||
const clearNodes = () => {
|
||||
nodes.value = {}
|
||||
}
|
||||
|
||||
// Sync token management
|
||||
const getSyncToken = (
|
||||
providerId: string,
|
||||
serviceId: string
|
||||
): string | undefined => {
|
||||
return syncTokens.value[providerId]?.[serviceId]
|
||||
}
|
||||
|
||||
const setSyncToken = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
token: string
|
||||
) => {
|
||||
if (!syncTokens.value[providerId]) {
|
||||
syncTokens.value[providerId] = {}
|
||||
}
|
||||
syncTokens.value[providerId][serviceId] = token
|
||||
}
|
||||
|
||||
// ==================== API Actions ====================
|
||||
|
||||
// Fetch nodes (collections and entities) for a location
|
||||
const fetchNodes = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location?: string | null,
|
||||
recursive: boolean = false,
|
||||
filter?: FilterCondition[] | null,
|
||||
sort?: SortCondition[] | null,
|
||||
range?: RangeCondition | null
|
||||
): Promise<NodeRecord[]> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const data = await nodeService.list(
|
||||
providerId,
|
||||
serviceId,
|
||||
location,
|
||||
recursive,
|
||||
filter,
|
||||
sort,
|
||||
range
|
||||
)
|
||||
// API returns object keyed by ID, convert to array
|
||||
const nodeArray = Array.isArray(data) ? data : Object.values(data)
|
||||
addNodes(providerId, serviceId, nodeArray)
|
||||
return nodeArray.map(hydrateNode)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch nodes'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch collections (folders) for a location
|
||||
const fetchCollections = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location?: string | null,
|
||||
filter?: FilterCondition[] | null,
|
||||
sort?: SortCondition[] | null
|
||||
): Promise<FileCollectionObject[]> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const data = await collectionService.list(providerId, serviceId, location, filter, sort)
|
||||
// API returns object keyed by ID, convert to array
|
||||
const collectionArray = Array.isArray(data) ? data : Object.values(data)
|
||||
addNodes(providerId, serviceId, collectionArray)
|
||||
return collectionArray.map(c => new FileCollectionObject().fromJson(c))
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch collections'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch entities (files) for a collection
|
||||
const fetchEntities = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string,
|
||||
filter?: FilterCondition[] | null,
|
||||
sort?: SortCondition[] | null,
|
||||
range?: RangeCondition | null
|
||||
): Promise<FileEntityObject[]> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const data = await entityService.list(providerId, serviceId, collection, filter, sort, range)
|
||||
// API returns object keyed by ID, convert to array
|
||||
const entityArray = Array.isArray(data) ? data : Object.values(data)
|
||||
addNodes(providerId, serviceId, entityArray)
|
||||
return entityArray.map(e => new FileEntityObject().fromJson(e))
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch entities'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Create a collection (folder)
|
||||
const createCollection = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location: string | null,
|
||||
data: Partial<FileCollection>,
|
||||
options?: Record<string, unknown>
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const created = await collectionService.create(providerId, serviceId, location, data, options)
|
||||
addNode(providerId, serviceId, created)
|
||||
return new FileCollectionObject().fromJson(created)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to create collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Create an entity (file)
|
||||
const createEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
data: Partial<FileEntity>,
|
||||
options?: Record<string, unknown>
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const created = await entityService.create(providerId, serviceId, collection, data, options)
|
||||
addNode(providerId, serviceId, created)
|
||||
return new FileEntityObject().fromJson(created)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to create entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Modify a collection
|
||||
const modifyCollection = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string,
|
||||
data: Partial<FileCollection>
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const modified = await collectionService.modify(providerId, serviceId, identifier, data)
|
||||
addNode(providerId, serviceId, modified)
|
||||
return new FileCollectionObject().fromJson(modified)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to modify collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Modify an entity
|
||||
const modifyEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
data: Partial<FileEntity>
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const modified = await entityService.modify(providerId, serviceId, collection, identifier, data)
|
||||
addNode(providerId, serviceId, modified)
|
||||
return new FileEntityObject().fromJson(modified)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to modify entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy a collection
|
||||
const destroyCollection = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string
|
||||
): Promise<boolean> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const success = await collectionService.destroy(providerId, serviceId, identifier)
|
||||
if (success) {
|
||||
removeNode(providerId, serviceId, identifier)
|
||||
}
|
||||
return success
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to destroy collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy an entity
|
||||
const destroyEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string
|
||||
): Promise<boolean> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const success = await entityService.destroy(providerId, serviceId, collection, identifier)
|
||||
if (success) {
|
||||
removeNode(providerId, serviceId, identifier)
|
||||
}
|
||||
return success
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to destroy entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Copy a collection
|
||||
const copyCollection = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string,
|
||||
location?: string | null
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const copied = await collectionService.copy(providerId, serviceId, identifier, location)
|
||||
addNode(providerId, serviceId, copied)
|
||||
return new FileCollectionObject().fromJson(copied)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to copy collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Copy an entity
|
||||
const copyEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
destination?: string | null
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const copied = await entityService.copy(providerId, serviceId, collection, identifier, destination)
|
||||
addNode(providerId, serviceId, copied)
|
||||
return new FileEntityObject().fromJson(copied)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to copy entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Move a collection
|
||||
const moveCollection = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string,
|
||||
location?: string | null
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const moved = await collectionService.move(providerId, serviceId, identifier, location)
|
||||
addNode(providerId, serviceId, moved)
|
||||
return new FileCollectionObject().fromJson(moved)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to move collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Move an entity
|
||||
const moveEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
destination?: string | null
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const moved = await entityService.move(providerId, serviceId, collection, identifier, destination)
|
||||
addNode(providerId, serviceId, moved)
|
||||
return new FileEntityObject().fromJson(moved)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to move entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Read entity content
|
||||
const readEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string,
|
||||
identifier: string
|
||||
): Promise<string | null> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const result = await entityService.read(providerId, serviceId, collection, identifier)
|
||||
return result.content
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to read entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Write entity content
|
||||
const writeEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
content: string
|
||||
): Promise<number> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
return await entityService.write(providerId, serviceId, collection, identifier, content)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to write entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Sync delta changes
|
||||
const syncDelta = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location: string | null,
|
||||
signature: string,
|
||||
recursive: boolean = false,
|
||||
detail: 'ids' | 'full' = 'full'
|
||||
): Promise<void> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const delta = await nodeService.delta(providerId, serviceId, location, signature, recursive, detail)
|
||||
|
||||
// Handle removed nodes
|
||||
if (delta.removed.length > 0) {
|
||||
removeNodes(providerId, serviceId, delta.removed)
|
||||
}
|
||||
|
||||
// Handle added/modified nodes
|
||||
if (detail === 'full') {
|
||||
const addedNodes = delta.added as FileNode[]
|
||||
const modifiedNodes = delta.modified as FileNode[]
|
||||
if (addedNodes.length > 0) {
|
||||
addNodes(providerId, serviceId, addedNodes)
|
||||
}
|
||||
if (modifiedNodes.length > 0) {
|
||||
addNodes(providerId, serviceId, modifiedNodes)
|
||||
}
|
||||
}
|
||||
|
||||
// Update sync token
|
||||
if (delta.signature) {
|
||||
setSyncToken(providerId, serviceId, delta.signature)
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to sync delta'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
nodes,
|
||||
syncTokens,
|
||||
loading,
|
||||
error,
|
||||
// Constants
|
||||
ROOT_ID,
|
||||
// Computed
|
||||
nodeList,
|
||||
collectionList,
|
||||
entityList,
|
||||
// Getters
|
||||
getNode,
|
||||
getServiceNodes,
|
||||
getChildren,
|
||||
getChildCollections,
|
||||
getChildEntities,
|
||||
getPath,
|
||||
isRoot,
|
||||
// Setters
|
||||
setNodes,
|
||||
addNode,
|
||||
addNodes,
|
||||
removeNode,
|
||||
removeNodes,
|
||||
clearServiceNodes,
|
||||
clearNodes,
|
||||
// Sync
|
||||
getSyncToken,
|
||||
setSyncToken,
|
||||
// API Actions - Fetch
|
||||
fetchNodes,
|
||||
fetchCollections,
|
||||
fetchEntities,
|
||||
// API Actions - Create
|
||||
createCollection,
|
||||
createEntity,
|
||||
// API Actions - Modify
|
||||
modifyCollection,
|
||||
modifyEntity,
|
||||
// API Actions - Destroy
|
||||
destroyCollection,
|
||||
destroyEntity,
|
||||
// API Actions - Copy
|
||||
copyCollection,
|
||||
copyEntity,
|
||||
// API Actions - Move
|
||||
moveCollection,
|
||||
moveEntity,
|
||||
// API Actions - Content
|
||||
readEntity,
|
||||
writeEntity,
|
||||
// API Actions - Sync
|
||||
syncDelta,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user