From 67c326ad3b0da9e84ccbe78585f2574b03ffdaa5 Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 27 Dec 2023 18:43:56 -0700 Subject: [PATCH] reimpl of some missing features from before the deletions of everything --- project-warstone/bun.lockb | Bin 0 -> 126517 bytes project-warstone/package.json | 2 +- .../{query syntax => query syntax.txt} | 8 +- project-warstone/src/App.tsx | 13 +- .../src/components/GameSystemEditor/index.tsx | 23 ++- .../components/SchemaBuilder/field-editor.tsx | 6 +- .../src/components/SchemaBuilder/index.tsx | 33 ++-- .../SchemaBuilder/template-editor.tsx | 4 +- .../components/SchemaBuilder/value-field.tsx | 2 +- .../src/components/schemalist.tsx | 20 +++ .../src/constants/TemplateTypes.ts | 54 ++++--- project-warstone/src/hooks/useObjectState.ts | 151 +++++++++++------- project-warstone/src/lib/accordion/index.tsx | 2 +- project-warstone/src/services/game-systems.ts | 74 ++++----- project-warstone/src/services/indexeddb.ts | 7 + project-warstone/src/utils/indexeddb.ts | 49 +++--- 16 files changed, 272 insertions(+), 176 deletions(-) create mode 100755 project-warstone/bun.lockb rename project-warstone/{query syntax => query syntax.txt} (86%) create mode 100644 project-warstone/src/components/schemalist.tsx create mode 100644 project-warstone/src/services/indexeddb.ts diff --git a/project-warstone/bun.lockb b/project-warstone/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..6da180d84d4635afa0a0642d839787300dd200a8 GIT binary patch literal 126517 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 literal 0 HcmV?d00001 diff --git a/project-warstone/package.json b/project-warstone/package.json index 9f9289e..329a745 100644 --- a/project-warstone/package.json +++ b/project-warstone/package.json @@ -17,7 +17,7 @@ "postcss": "^8.4.24", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.12.0", + "react-router-dom": "^6.21.0", "recoil": "^0.7.7", "recoilize": "^3.2.0", "tailwindcss": "^3.3.2", diff --git a/project-warstone/query syntax b/project-warstone/query syntax.txt similarity index 86% rename from project-warstone/query syntax rename to project-warstone/query syntax.txt index 2ca7f02..88e5087 100644 --- a/project-warstone/query syntax +++ b/project-warstone/query syntax.txt @@ -1,9 +1,11 @@ Start query: ? - - by default, queries search the publication + - by default, queries search the current publication + - you can specify a publication by following this with the name, by default it will use the latest release of a publication + - you can specify the version by including it in a pair of square brackets Query current object: ^ - refers specifically to the object based off of this specific type Query relative object: $ - - refers to the last relative object in the heirarchy + - refers to the last relative object in the hierarchy Access child: . - this also maps over all items in a list and returns a list of the relevant children - if the parent is a list, it will return a list of just the specified child @@ -45,6 +47,8 @@ separator: ::;; Examples: ?core.weapon_abilities[name=Rapid Fire].body - this searches the publication 'core' object for weapon_abilities that have the name + ?core[v1].weapon_abilities[name=Rapid Fire].body + - this searches version 'v1' of the publication 'core' object for weapon_abilities that have the name Sustained Hits {{?^sustained_hits}} - This formats the string with the queried values, which start the query within the same object that the field exists in. This would result in the string 'Sustained Hits 1' Anti-{{_.keyword}} {{_.value}}< + + + + + {/* */} + + {/* */} + + - - {/* */} ) } diff --git a/project-warstone/src/components/GameSystemEditor/index.tsx b/project-warstone/src/components/GameSystemEditor/index.tsx index 995f57d..771b0cf 100644 --- a/project-warstone/src/components/GameSystemEditor/index.tsx +++ b/project-warstone/src/components/GameSystemEditor/index.tsx @@ -16,19 +16,20 @@ export const GameSystemEditor: FC = () => { name: 'Asshammer 40x a day', accolades: [], }); - const [schemas, setSchemas] = useState<{ name: string, id: string }[]>([]); + const [schemas, setSchemas] = useState<[string,string][]>([]); const [lastSaved, setLastSaved] = useState(gameSystem); const fetchSchema = useCallback(async (id: string) => { - const res = await GameSystemsService.getSchema(id); + try { + const schema = await GameSystemsService.getSchema(id); - if (res.status !== 200) return; - const schema = await res.json() - - updateGameSystem({ - schema - }); + updateGameSystem({ + schema + }); + } catch (e) { + console.log('failed to fetch schema:', e) + } }, [updateGameSystem]) useEffect(() => { @@ -38,7 +39,6 @@ export const GameSystemEditor: FC = () => { useEffect(() => { GameSystemsService.getSchemaList() - .then(res => res.json()) .then(schemas => setSchemas(schemas)); }, []); @@ -48,8 +48,7 @@ export const GameSystemEditor: FC = () => { }, [gameSystem]) const fetchGameSystem = useCallback(async () => { - const res = await GameSystemsService.getGameSystem(''); - const gs = await res.json(); + const gs = await GameSystemsService.getGameSystem(''); setGameSystem(gs); setLastSaved(gs); }, [setGameSystem]); @@ -66,7 +65,7 @@ export const GameSystemEditor: FC = () => { diff --git a/project-warstone/src/components/SchemaBuilder/field-editor.tsx b/project-warstone/src/components/SchemaBuilder/field-editor.tsx index b57387c..82477c5 100644 --- a/project-warstone/src/components/SchemaBuilder/field-editor.tsx +++ b/project-warstone/src/components/SchemaBuilder/field-editor.tsx @@ -24,9 +24,9 @@ export const FieldEditor: FC = ({ update, field, fieldName, deleteField setReserved(RESERVED_FIELDS[fieldName]); }, [fieldName]) - useEffect(() => { - console.log(field.value); - }, [field]) + // useEffect(() => { + // console.log(field.value); + // }, [field]) return (
  • diff --git a/project-warstone/src/components/SchemaBuilder/index.tsx b/project-warstone/src/components/SchemaBuilder/index.tsx index 829a7bf..c063382 100644 --- a/project-warstone/src/components/SchemaBuilder/index.tsx +++ b/project-warstone/src/components/SchemaBuilder/index.tsx @@ -4,17 +4,23 @@ import { TypeEditor } from './type-editor'; import { useObjectState, useObjectStateWrapper } from '../../hooks/useObjectState'; import { FieldTypes, Schema, Template, TypeType } from '../../types/schema'; import { useInput } from '../../hooks/useInput'; -import { useRecoilState } from 'recoil'; +import { useRecoilState, useResetRecoilState } from 'recoil'; import { SchemaEditAtom } from '../../recoil/atoms/schema'; import { GameSystemsService } from '../../services/game-systems'; import { SchemaViewer } from './schema-viewer'; import { TemplateEditor } from './template-editor'; import { Icon } from '../Icon'; +import { useNavigate, useParams } from 'react-router-dom'; export const SchemaBuilder: FC = () => { const [schema, setSchema] = useRecoilState(SchemaEditAtom); - const { update: updateSchema } = useObjectStateWrapper(schema, setSchema); + const resetSchema = useResetRecoilState(SchemaEditAtom); + const { update: updateSchema, bindProperty:bindSchemaProperty } = useObjectStateWrapper(schema, setSchema); + + const navigate = useNavigate(); + + const {id} = useParams<{id: string}>() const { value: typeName, bind: bindTypeName, reset: resetTypeName } = useInput(''); @@ -23,19 +29,18 @@ export const SchemaBuilder: FC = () => { const [lastSaved, setLastSaved] = useState(schema); const fetchSchema = useCallback(async () => { - const result = await GameSystemsService.getSchema('286f4c18-d280-444b-8d7e-9a3dd09f64ef') - if (result.status !== 200) return; - const fetchedSchema = await result.json(); + if (!id) return; + if (id === 'new') return resetSchema(); + const fetchedSchema = await GameSystemsService.getSchema(id) // if (fetchedSchema.name === schema.name) return; if (!fetchedSchema.templates) fetchedSchema.templates = {} setSchema(fetchedSchema); setLastSaved(fetchedSchema); - }, [setSchema]) + }, []) useEffect(() => { fetchSchema(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) + }, [fetchSchema]) const [selectedType, setSelectedType] = useState(''); @@ -51,9 +56,10 @@ export const SchemaBuilder: FC = () => { setSelectedType(''); }, [resetTypeName, updateSchema]); - const saveSchema = useCallback(() => { - GameSystemsService.saveSchema(schema); + const saveSchema = useCallback(async () => { setLastSaved(schema); + const sid = await GameSystemsService.saveSchema(schema); + if (id === 'new') navigate('/schema/'+sid) }, [schema]) const selectTypeForEdit = useCallback((typeKey: string) => { @@ -95,6 +101,9 @@ export const SchemaBuilder: FC = () => { return (
    +
    + +

    Add a template

    @@ -103,7 +112,7 @@ export const SchemaBuilder: FC = () => {
      {Object.entries(schema.templates).map(([templateKey, template]) => ( - + ))}
    @@ -119,7 +128,7 @@ export const SchemaBuilder: FC = () => {
      {Object.keys(schema.types).map(t => ( -
    • +
    • {t}
      +
        + {schemas.map(([id,name])=>
      • {name || 'Unnamed Schema'}
      • )} +
      +
      + ) +} \ No newline at end of file diff --git a/project-warstone/src/constants/TemplateTypes.ts b/project-warstone/src/constants/TemplateTypes.ts index 610ec82..0e0fb25 100644 --- a/project-warstone/src/constants/TemplateTypes.ts +++ b/project-warstone/src/constants/TemplateTypes.ts @@ -1,4 +1,4 @@ -import { FieldTypes, TypeType } from '../types/schema'; +import { FieldTypes, TypeType } from "../types/schema"; export const TEMPLATE_TYPES: Record = { section: { @@ -7,14 +7,14 @@ export const TEMPLATE_TYPES: Record = { limit: 1, minimum: 1, type: FieldTypes.text, - value: '' + value: "", }, body: { isConstant: false, limit: 0, minimum: 1, - type: FieldTypes['long text'], - value: '' + type: FieldTypes["long text"], + value: "", }, }, steps: { @@ -23,8 +23,8 @@ export const TEMPLATE_TYPES: Record = { limit: 0, minimum: 1, type: FieldTypes.type, - value: 'section' - } + value: "section", + }, }, image: { name: { @@ -32,14 +32,14 @@ export const TEMPLATE_TYPES: Record = { limit: 1, minimum: 1, type: FieldTypes.text, - value: '' + value: "", }, link: { isConstant: false, limit: 1, minimum: 1, type: FieldTypes.text, - value: '' + value: "", }, }, list: { @@ -47,18 +47,34 @@ export const TEMPLATE_TYPES: Record = { isConstant: false, limit: 0, minimum: 1, - type: FieldTypes['long text'], - value: '' - } + type: FieldTypes["long text"], + value: "", + }, + }, + table_column: { + name: { + isConstant: false, + limit: 0, + minimum: 1, + type: FieldTypes.any, + value: "", + }, + value: { + isConstant: false, + limit: 0, + minimum: 1, + type: FieldTypes.any, + value: "", + }, }, table_row: { columns: { isConstant: false, limit: 0, minimum: 1, - type: FieldTypes.any, - value: '' - } + type: FieldTypes.type, + value: "tableColumn", + }, }, table: { rows: { @@ -66,14 +82,14 @@ export const TEMPLATE_TYPES: Record = { limit: 0, minimum: 1, type: FieldTypes.type, - value: 'tableRow' + value: "tableRow", }, header: { isConstant: false, limit: 1, minimum: 0, type: FieldTypes.type, - value: 'tableRow' - } - } -}; \ No newline at end of file + value: "tableRow", + }, + }, +}; diff --git a/project-warstone/src/hooks/useObjectState.ts b/project-warstone/src/hooks/useObjectState.ts index fcea972..08363cd 100644 --- a/project-warstone/src/hooks/useObjectState.ts +++ b/project-warstone/src/hooks/useObjectState.ts @@ -1,46 +1,58 @@ -import React, { ChangeEvent, useCallback, useState } from 'react'; -import { InputBinder } from '../types/inputBinder'; +import React, { ChangeEvent, useCallback, useState } from "react"; +import { InputBinder } from "../types/inputBinder"; type ObjectStateHookConfig = { disallowSpaces?: boolean; spaceReplacer?: string; -} +}; export const useObjectState = (initial: T) => { const [state, setState] = useState(initial || {} as T); - const bindProperty = useCallback((property: K, config: ObjectStateHookConfig) => ({ - value: state[property] ?? '', - name: property, - onChange: (event: ChangeEvent) => - setState(value => ( - { - ...value, - [event.target.name]: ( - (typeof value[property] === 'number') ? - Number(event.target.value) || 0 : - config?.disallowSpaces ? - event.target.value.replace(' ', config.spaceReplacer || '_') : - event.target.value - ) - } - )) - }), [state]) + const bindProperty = useCallback( + (property: K, config?: ObjectStateHookConfig) => ({ + value: state[property] ?? "", + name: property, + onChange: ( + event: ChangeEvent< + HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement + >, + ) => + setState((value) => ( + { + ...value, + [event.target.name]: ( + (typeof value[property] === "number") + ? Number(event.target.value) || 0 + : config?.disallowSpaces + ? event.target.value.replace(" ", config.spaceReplacer || "_") + : event.target.value + ), + } + )), + }), + [state], + ); const bindPropertyCheck = useCallback((property: K) => ({ checked: !!state[property], name: property, - onChange: (event: ChangeEvent) => setState(value => ({ - ...value, [event.target.name]: (event.target.checked) - })), - readOnly: true - }), [state]) + onChange: (event: ChangeEvent) => + setState((value) => ({ + ...value, + [event.target.name]: (event.target.checked), + })), + readOnly: true, + }), [state]); - const update = useCallback((updates: Partial) => setState(s => ({ ...s, ...updates })), []) + const update = useCallback( + (updates: Partial) => setState((s) => ({ ...s, ...updates })), + [], + ); const reset = useCallback(() => { setState(initial); - }, [initial]) + }, [initial]); return { bindProperty, @@ -48,48 +60,67 @@ export const useObjectState = (initial: T) => { update, state, setState, - reset - } -} + reset, + }; +}; -export const useObjectStateWrapper = (state: T, setState: React.Dispatch>) => { - - const bindProperty = useCallback((property: K, config?: ObjectStateHookConfig): InputBinder => ({ - value: state[property] ?? '', - name: property.toString(), - onChange: (event: ChangeEvent) => - setState(value => ( - { - ...value, - [event.target.name]: ( - (typeof value[property] === 'number') ? - Number(event.target.value) || 0 : - config?.disallowSpaces ? - event.target.value.replace(' ', config.spaceReplacer || '_') : - event.target.value - ) - } - )) - }), [setState, state]) +export const useObjectStateWrapper = ( + state: T, + setState: React.Dispatch>, +) => { + const bindProperty = useCallback( + ( + property: K, + config?: ObjectStateHookConfig, + ): InputBinder => ({ + value: state[property]?.toString() ?? "", + name: property.toString(), + onChange: ( + event: ChangeEvent< + HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement + >, + ) => + setState((value) => ( + { + ...value, + [event.target.name]: ( + (typeof value[property] === "number") + ? Number(event.target.value) || 0 + : config?.disallowSpaces + ? event.target.value.replace(" ", config.spaceReplacer || "_") + : event.target.value + ), + } + )), + }), + [setState, state], + ); const bindPropertyCheck = useCallback((property: K) => ({ checked: !!state[property], name: property, - onChange: (event: ChangeEvent) => setState(value => ({ - ...value, [event.target.name]: (event.target.checked) - })), - readOnly: true - }), [setState, state]) + onChange: (event: ChangeEvent) => + setState((value) => ({ + ...value, + [event.target.name]: (event.target.checked), + })), + readOnly: true, + }), [setState, state]); - const update = useCallback((updates: Partial | ((arg: T) => Partial)) => - setState(s => ({ ...s, ...(typeof updates === 'function' ? updates(s) : updates) } - )), [setState]) + const update = useCallback( + (updates: Partial | ((arg: T) => Partial)) => + setState((s) => ({ + ...s, + ...(typeof updates === "function" ? updates(s) : updates), + })), + [setState], + ); return { bindProperty, bindPropertyCheck, update, state, - setState - } -} \ No newline at end of file + setState, + }; +}; diff --git a/project-warstone/src/lib/accordion/index.tsx b/project-warstone/src/lib/accordion/index.tsx index 8925b57..893fb7e 100644 --- a/project-warstone/src/lib/accordion/index.tsx +++ b/project-warstone/src/lib/accordion/index.tsx @@ -13,7 +13,7 @@ export const Accordion: FCC = ({ children, expandOnHover, expanded, titl return (
      !title && !expandOnHover && setOpen(!open)} > diff --git a/project-warstone/src/services/game-systems.ts b/project-warstone/src/services/game-systems.ts index ec95e1d..131d08c 100644 --- a/project-warstone/src/services/game-systems.ts +++ b/project-warstone/src/services/game-systems.ts @@ -1,51 +1,45 @@ -import { GameSystem } from '../types/gameSystem'; -import { Schema } from '../types/schema'; +import { GameSystem } from "../types/gameSystem"; +import { Schema } from "../types/schema"; +import { idb } from "./indexeddb"; -const emptySchema = { name: '', types: {}, templates: {}, id: crypto.randomUUID() }; +const emptySchema = { + name: "", + types: {}, + templates: {}, + id: crypto.randomUUID(), +}; export const GameSystemsService = { // todo - connect to service to save schema for game saveSchema: async (schema: Schema) => { - localStorage.setItem('schema ' + schema.id, JSON.stringify(schema)); - - return { status: 200 } + // localStorage.setItem('schema ' + schema.id, JSON.stringify(schema)); + try { + return await idb.createOrUpdate({ + storeName: "schema", + ...schema, + }); + } catch (e) { + console.log(e); + } }, // todo - connect to service to fetch schema for game - getSchema: async (id: string): Promise<{ status: 200 | 404, json: () => Promise }> => { - const schema = localStorage.getItem('schema ' + id); - - if (schema) - return { - status: 200, - json: async () => JSON.parse(schema) - } - - return { - status: 404, - json: async () => (emptySchema) - } - }, - getSchemaList: async () => { - return { - status: 200, json: async () => [{ - name: 'Test Schema', - id: '286f4c18-d280-444b-8d7e-9a3dd09f64ef' - }] + getSchema: async ( + id: string, + ): Promise => { + try { + const schema = await idb.read("schema", id); + return schema; + } catch (e) { + throw e; } }, + getSchemaList: async () => await idb.listAll("schema", "name"), + saveGameSystem: async (gs: GameSystem) => { - localStorage.setItem('game-system ' + gs.id, JSON.stringify(gs)); - return { status: 200 } + localStorage.setItem("game-system " + gs.id, JSON.stringify(gs)); + return { status: 200 }; }, - getGameSystem: async (id: string): Promise<{ status: 200 | 404, json: () => Promise }> => { - const gs = localStorage.getItem('game-system ' + id); - if (!gs) return {status: 404, json:async () => ({ - accolades: [], - name: '', - id: crypto.randomUUID(), - schema: emptySchema, - schemaId: '' - })} - return { status: 200, json:async () => (JSON.parse(gs))} - } -} \ No newline at end of file + getGameSystem: async ( + id: string, + ): Promise => await idb.read("game-system", id), +}; diff --git a/project-warstone/src/services/indexeddb.ts b/project-warstone/src/services/indexeddb.ts new file mode 100644 index 0000000..b8eb002 --- /dev/null +++ b/project-warstone/src/services/indexeddb.ts @@ -0,0 +1,7 @@ +import { IndexedDBService } from "../utils/indexeddb"; + +export const idb = new IndexedDBService("commander", 1, [ + "game-system", + "publication", + "schema", +]); diff --git a/project-warstone/src/utils/indexeddb.ts b/project-warstone/src/utils/indexeddb.ts index 419023c..3406e81 100644 --- a/project-warstone/src/utils/indexeddb.ts +++ b/project-warstone/src/utils/indexeddb.ts @@ -1,4 +1,6 @@ -class IndexedDBService { +type Data = Record & { storeName: string }; + +export class IndexedDBService { private dbName: string; private dbVersion: number; private storeNames: string[]; @@ -17,7 +19,7 @@ class IndexedDBService { const request = indexedDB.open(this.dbName, this.dbVersion); request.onerror = () => { - reject(new Error('Failed to open the database.')); + reject(new Error("Failed to open the database.")); }; request.onsuccess = () => { @@ -31,13 +33,13 @@ class IndexedDBService { // Create all the required object stores during the upgrade for (const storeName of this.storeNames) { if (!db.objectStoreNames.contains(storeName)) { - db.createObjectStore(storeName, { keyPath: 'uuid' }); + db.createObjectStore(storeName, { keyPath: "uuid" }); } } }; request.onblocked = () => { - reject(new Error('Database is blocked and cannot be accessed.')); + reject(new Error("Database is blocked and cannot be accessed.")); }; }); } @@ -46,11 +48,11 @@ class IndexedDBService { return crypto.randomUUID(); } - public async create(data: any): Promise { + public async create(data: Data): Promise { const db = await this.openDB(); const uuid = this.generateUUID(); return new Promise((resolve, reject) => { - const transaction = db.transaction(data.storeName, 'readwrite'); + const transaction = db.transaction(data.storeName, "readwrite"); const objectStore = transaction.objectStore(data.storeName); const request = objectStore.add({ ...data, uuid }); @@ -60,7 +62,7 @@ class IndexedDBService { }; request.onerror = () => { - reject(new Error('Failed to add data to IndexedDB.')); + reject(new Error("Failed to add data to IndexedDB.")); }; }); } @@ -68,7 +70,7 @@ class IndexedDBService { public async read(storeName: string, uuid: string): Promise { const db = await this.openDB(); return new Promise((resolve, reject) => { - const transaction = db.transaction(storeName, 'readonly'); + const transaction = db.transaction(storeName, "readonly"); const objectStore = transaction.objectStore(storeName); const request = objectStore.get(uuid); @@ -78,25 +80,29 @@ class IndexedDBService { }; request.onerror = () => { - reject(new Error('Failed to read data from IndexedDB.')); + reject(new Error("Failed to read data from IndexedDB.")); }; }); } - public async update(storeName: string, uuid: string, newData: any): Promise { + public async update( + storeName: string, + uuid: string, + newData: any, + ): Promise { const db = await this.openDB(); return new Promise((resolve, reject) => { - const transaction = db.transaction(storeName, 'readwrite'); + const transaction = db.transaction(storeName, "readwrite"); const objectStore = transaction.objectStore(storeName); - const request = objectStore.put({ ...newData, uuid }, uuid); + const request = objectStore.put({ ...newData, uuid }); request.onsuccess = () => { resolve(); }; request.onerror = () => { - reject(new Error('Failed to update data in IndexedDB.')); + reject(new Error("Failed to update data in IndexedDB.")); }; }); } @@ -104,7 +110,7 @@ class IndexedDBService { public async delete(storeName: string, uuid: string): Promise { const db = await this.openDB(); return new Promise((resolve, reject) => { - const transaction = db.transaction(storeName, 'readwrite'); + const transaction = db.transaction(storeName, "readwrite"); const objectStore = transaction.objectStore(storeName); const request = objectStore.delete(uuid); @@ -114,16 +120,19 @@ class IndexedDBService { }; request.onerror = () => { - reject(new Error('Failed to delete data from IndexedDB.')); + reject(new Error("Failed to delete data from IndexedDB.")); }; }); } - public async listAll(storeName: string, fieldName: string): Promise<[string, any][]> { + public async listAll( + storeName: string, + fieldName: string, + ): Promise<[string, T][]> { const db = await this.openDB(); return new Promise((resolve, reject) => { - const transaction = db.transaction(storeName, 'readonly'); + const transaction = db.transaction(storeName, "readonly"); const objectStore = transaction.objectStore(storeName); const request = objectStore.openCursor(); @@ -143,14 +152,12 @@ class IndexedDBService { }; request.onerror = () => { - reject(new Error('Failed to list data from IndexedDB.')); + reject(new Error("Failed to list data from IndexedDB.")); }; }); } - public async createOrUpdate(data: any): Promise { - const db = await this.openDB(); - + public async createOrUpdate(data: Data): Promise { // If the provided data already has a UUID, check if it exists in the database if (data.uuid) { const existingData = await this.read(data.storeName, data.uuid);