tsondb 0.12.3 → 0.12.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/node/index.js +17 -20
- package/dist/src/node/schema/Node.d.ts +2 -2
- package/dist/src/node/schema/Node.js +3 -5
- package/dist/src/node/schema/declarations/EntityDecl.d.ts +4 -4
- package/dist/src/node/schema/types/primitives/FloatType.d.ts +5 -2
- package/dist/src/node/schema/types/primitives/FloatType.js +10 -4
- package/dist/src/node/schema/types/references/IncludeIdentifierType.js +0 -3
- package/dist/src/node/server/api/declarations.js +27 -10
- package/dist/src/node/server/api/git.js +5 -4
- package/dist/src/node/server/api/index.js +2 -0
- package/dist/src/node/server/api/instances.js +2 -3
- package/dist/src/node/server/api/search.d.ts +1 -0
- package/dist/src/node/server/api/search.js +50 -0
- package/dist/src/node/server/index.d.ts +4 -3
- package/dist/src/node/server/index.js +2 -2
- package/dist/src/node/server/init.d.ts +2 -2
- package/dist/src/node/server/init.js +14 -11
- package/dist/src/node/server/utils/childInstances.d.ts +0 -3
- package/dist/src/node/server/utils/childInstances.js +2 -43
- package/dist/src/node/server/utils/instanceOperations.d.ts +3 -4
- package/dist/src/node/server/utils/instanceOperations.js +44 -84
- package/dist/src/node/server/utils/query.d.ts +2 -0
- package/dist/src/node/server/utils/query.js +7 -0
- package/dist/src/node/utils/childInstances.d.ts +11 -10
- package/dist/src/node/utils/childInstances.js +66 -130
- package/dist/src/node/utils/databaseInMemory.d.ts +18 -0
- package/dist/src/node/utils/databaseInMemory.js +86 -0
- package/dist/src/node/utils/databaseOnDisk.d.ts +2 -0
- package/dist/src/node/utils/databaseOnDisk.js +62 -0
- package/dist/src/node/utils/databaseTransactions.d.ts +46 -0
- package/dist/src/node/utils/databaseTransactions.js +105 -0
- package/dist/src/node/utils/displayName.d.ts +3 -3
- package/dist/src/node/utils/error.d.ts +4 -0
- package/dist/src/node/utils/error.js +8 -0
- package/dist/src/node/utils/files.d.ts +4 -2
- package/dist/src/node/utils/files.js +2 -1
- package/dist/src/node/utils/git.d.ts +3 -1
- package/dist/src/node/utils/git.js +8 -2
- package/dist/src/node/utils/instanceTransactionSteps.d.ts +9 -0
- package/dist/src/node/utils/instanceTransactionSteps.js +40 -0
- package/dist/src/node/utils/references.d.ts +4 -3
- package/dist/src/node/utils/references.js +3 -2
- package/dist/src/shared/api.d.ts +4 -0
- package/dist/src/shared/schema/declarations/EntityDecl.d.ts +2 -2
- package/dist/src/shared/schema/declarations/EnumDecl.d.ts +8 -0
- package/dist/src/shared/schema/declarations/EnumDecl.js +5 -0
- package/dist/src/shared/schema/types/FloatType.d.ts +6 -5
- package/dist/src/shared/schema/types/FloatType.js +1 -0
- package/dist/src/shared/utils/dictionary.d.ts +17 -0
- package/dist/src/shared/utils/dictionary.js +76 -0
- package/dist/src/shared/utils/instances.d.ts +2 -2
- package/dist/src/shared/utils/result.d.ts +4 -0
- package/dist/src/shared/utils/result.js +4 -0
- package/dist/src/web/api/search.d.ts +2 -0
- package/dist/src/web/api/search.js +7 -0
- package/dist/src/web/components/InstanceRouteSkeleton.d.ts +9 -8
- package/dist/src/web/components/Layout.js +1 -1
- package/dist/src/web/components/git/GitStatusIndicator.js +1 -1
- package/dist/src/web/components/typeInputs/ChildEntitiesTypeInput.js +1 -1
- package/dist/src/web/components/typeInputs/FloatTypeInput.d.ts +1 -1
- package/dist/src/web/components/typeInputs/FloatTypeInput.js +2 -1
- package/dist/src/web/hooks/useGitClient.js +0 -1
- package/dist/src/web/hooks/useMappedAPIResource.js +2 -4
- package/dist/src/web/index.js +2 -1
- package/dist/src/web/routes/Search.d.ts +2 -0
- package/dist/src/web/routes/Search.js +56 -0
- package/dist/src/web/utils/typeSkeleton.d.ts +2 -2
- package/dist/src/web/utils/typeSkeleton.js +1 -1
- package/package.json +3 -3
- package/public/css/styles.css +10 -0
- package/dist/src/node/utils/instanceOperations.d.ts +0 -14
- package/dist/src/node/utils/instanceOperations.js +0 -88
- package/dist/src/node/utils/instances.d.ts +0 -6
- package/dist/src/node/utils/instances.js +0 -29
|
@@ -6,7 +6,7 @@ import { Settings } from "./Settings.js";
|
|
|
6
6
|
export const Layout = ({ breadcrumbs, children }) => {
|
|
7
7
|
const [_1, setIsGitOpen] = useContext(GitContext);
|
|
8
8
|
const [isGitAlwaysOpen, _2] = useSetting("gitSidebar");
|
|
9
|
-
return (_jsxs(_Fragment, { children: [_jsxs("header", { children: [_jsx("nav", { children: _jsx("ol", { children: breadcrumbs.map(({ url, label }) => (_jsx("li", { children: _jsx("a", { href: url, children: label }) }, url))) }) }), _jsxs("div", { class: "nav-buttons", children: [_jsx("button", { class: `git-toggle${!isGitAlwaysOpen ? " git-toggle--no-sidebar" : ""}`, onClick: () => {
|
|
9
|
+
return (_jsxs(_Fragment, { children: [_jsxs("header", { children: [_jsx("nav", { children: _jsx("ol", { children: breadcrumbs.map(({ url, label }) => (_jsx("li", { children: _jsx("a", { href: url, children: label }) }, url))) }) }), _jsxs("div", { class: "nav-buttons", children: [_jsx("a", { href: "/search", class: "btn", children: "Search" }), _jsx("button", { class: `git-toggle${!isGitAlwaysOpen ? " git-toggle--no-sidebar" : ""}`, onClick: () => {
|
|
10
10
|
setIsGitOpen(b => !b);
|
|
11
11
|
}, children: "File changes" }), _jsx(Settings, {})] })] }), _jsx("main", { children: children })] }));
|
|
12
12
|
};
|
|
@@ -2,5 +2,5 @@ import { jsx as _jsx } from "preact/jsx-runtime";
|
|
|
2
2
|
import { getGitStatusForDisplay, getLabelForGitStatus, } from "../../../shared/utils/git.js";
|
|
3
3
|
export const GitStatusIndicator = ({ status }) => {
|
|
4
4
|
const gitStatusForDisplay = getGitStatusForDisplay(status);
|
|
5
|
-
return (_jsx("span", { class: `git-status git-status--${gitStatusForDisplay
|
|
5
|
+
return gitStatusForDisplay === undefined ? null : (_jsx("span", { class: `git-status git-status--${gitStatusForDisplay}`, title: getLabelForGitStatus(gitStatusForDisplay), children: gitStatusForDisplay }));
|
|
6
6
|
};
|
|
@@ -42,7 +42,7 @@ export const ChildEntitiesTypeInput = props => {
|
|
|
42
42
|
return (_jsxs("div", { class: "field field--container field--array", children: [childInstancesForEntity.length > 0 ? (_jsx("ol", { children: childInstancesForEntity.map(([item, originalIndex], i) => (_jsxs("li", { class: "container-item array-item", children: [_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [i + 1, "."] }), _jsx("button", { class: "destructive", onClick: () => {
|
|
43
43
|
onChildRemove(i);
|
|
44
44
|
}, disabled: disabled, children: "Delete Item" })] }), _jsx(TypeInput, { ...props, type: childEntity.type, value: item.content, parentKey: childEntity.parentReferenceKey, onChange: newItem => {
|
|
45
|
-
onChildChange(originalIndex, newItem);
|
|
45
|
+
onChildChange(originalIndex, newItem); // guaranteed to be an object because of the ObjectType in the child entity
|
|
46
46
|
}, childInstances: item.childInstances, setChildInstances: newChildInstances => {
|
|
47
47
|
onGrandChildrenChange(originalIndex, newChildInstances);
|
|
48
48
|
} })] }, i))) })) : (_jsx("p", { class: "empty", children: "No child entities" })), _jsx("div", { class: "add-item-container", children: _jsx("button", { onClick: () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FunctionComponent } from "preact";
|
|
2
|
-
import type
|
|
2
|
+
import { type SerializedFloatType } from "../../../shared/schema/types/FloatType.ts";
|
|
3
3
|
import type { TypeInputProps } from "./TypeInput.tsx";
|
|
4
4
|
type Props = TypeInputProps<SerializedFloatType, number>;
|
|
5
5
|
export declare const FloatTypeInput: FunctionComponent<Props>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { useState } from "preact/hooks";
|
|
3
|
+
import { DEFAULT_FRACTION_DIGITS, } from "../../../shared/schema/types/FloatType.js";
|
|
3
4
|
import { validateNumberConstraints } from "../../../shared/validation/number.js";
|
|
4
5
|
import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
|
|
5
6
|
import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
@@ -15,5 +16,5 @@ export const FloatTypeInput = ({ type, value, disabled, onChange }) => {
|
|
|
15
16
|
if (!Number.isNaN(numericValue)) {
|
|
16
17
|
onChange(numericValue);
|
|
17
18
|
}
|
|
18
|
-
}, step:
|
|
19
|
+
}, step: 1 / Math.pow(10, type.fractionDigits ?? DEFAULT_FRACTION_DIGITS), "aria-invalid": errors.length > 0, disabled: disabled }), _jsx(ValidationErrors, { disabled: disabled, errors: errors })] }));
|
|
19
20
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "preact/hooks";
|
|
2
|
+
import { logAndAlertError } from "../utils/debug.js";
|
|
2
3
|
export const useMappedAPIResource = (apiFn, mapFn, ...args) => {
|
|
3
4
|
const [data, setData] = useState();
|
|
4
5
|
const fetchData = useCallback(() => apiFn(...args).then(result => {
|
|
@@ -7,10 +8,7 @@ export const useMappedAPIResource = (apiFn, mapFn, ...args) => {
|
|
|
7
8
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
8
9
|
[apiFn, mapFn, ...args]);
|
|
9
10
|
useEffect(() => {
|
|
10
|
-
fetchData().catch(
|
|
11
|
-
console.log(err);
|
|
12
|
-
alert(err);
|
|
13
|
-
});
|
|
11
|
+
fetchData().catch(logAndAlertError);
|
|
14
12
|
}, [fetchData]);
|
|
15
13
|
const reload = useCallback(fetchData, [fetchData]);
|
|
16
14
|
return [data, reload];
|
package/dist/src/web/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { Entity } from "./routes/Entity.js";
|
|
|
20
20
|
import { Home } from "./routes/Home.js";
|
|
21
21
|
import { Instance } from "./routes/Instance.js";
|
|
22
22
|
import { NotFound } from "./routes/NotFound.js";
|
|
23
|
+
import { Search } from "./routes/Search.js";
|
|
23
24
|
const mapEntities = (data) => data.declarations
|
|
24
25
|
.map(decl => ({ ...decl, isLocaleEntity: decl.declaration.name === data.localeEntity }))
|
|
25
26
|
.sort((a, b) => a.declaration.name.localeCompare(b.declaration.name));
|
|
@@ -33,7 +34,7 @@ const App = ({ config }) => {
|
|
|
33
34
|
alert("Error reloading entities: " + String(error));
|
|
34
35
|
});
|
|
35
36
|
}, [location.path, reloadEntities]);
|
|
36
|
-
return (_jsx(ConfigContext.Provider, { value: config, children: _jsx(SettingsContext.Provider, { value: settingsContext, children: _jsx(GitContext.Provider, { value: [isGitOpen, setIsGitOpen], children: _jsx(LocationProvider, { children: _jsx(EntitiesContext.Provider, { value: { entities: entities ?? [], reloadEntities }, children: _jsxs(ContextProviderWrapper, { context: GitClientContext, useValue: useGitClient, children: [_jsx(LoadingOverlay, {}), _jsxs(Router, { children: [_jsx(Route, { path: "/", component: Home }), _jsx(Route, { path: "/entities/:name", component: Entity }), _jsx(Route, { path: "/entities/:name/instances/create", component: CreateInstance }), _jsx(Route, { path: "/entities/:name/instances/:id", component: Instance }), _jsx(Route, { default: true, component: NotFound })] }), _jsx(Git, {})] }) }) }) }) }) }));
|
|
37
|
+
return (_jsx(ConfigContext.Provider, { value: config, children: _jsx(SettingsContext.Provider, { value: settingsContext, children: _jsx(GitContext.Provider, { value: [isGitOpen, setIsGitOpen], children: _jsx(LocationProvider, { children: _jsx(EntitiesContext.Provider, { value: { entities: entities ?? [], reloadEntities }, children: _jsxs(ContextProviderWrapper, { context: GitClientContext, useValue: useGitClient, children: [_jsx(LoadingOverlay, {}), _jsxs(Router, { children: [_jsx(Route, { path: "/", component: Home }), _jsx(Route, { path: "/search", component: Search }), _jsx(Route, { path: "/entities/:name", component: Entity }), _jsx(Route, { path: "/entities/:name/instances/create", component: CreateInstance }), _jsx(Route, { path: "/entities/:name/instances/:id", component: Instance }), _jsx(Route, { default: true, component: NotFound })] }), _jsx(Git, {})] }) }) }) }) }) }));
|
|
37
38
|
};
|
|
38
39
|
const config = await getWebConfig();
|
|
39
40
|
const root = document.getElementById("app");
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useState } from "preact/hooks";
|
|
3
|
+
import { getGitStatusForDisplay } from "../../shared/utils/git.js";
|
|
4
|
+
import { toTitleCase } from "../../shared/utils/string.js";
|
|
5
|
+
import { deleteInstanceByEntityNameAndId } from "../api/declarations.js";
|
|
6
|
+
import { searchInstances } from "../api/search.js";
|
|
7
|
+
import { GitStatusIndicator } from "../components/git/GitStatusIndicator.js";
|
|
8
|
+
import { Layout } from "../components/Layout.js";
|
|
9
|
+
import { useSetting } from "../hooks/useSettings.js";
|
|
10
|
+
import { logAndAlertError } from "../utils/debug.js";
|
|
11
|
+
import { homeTitle } from "./Home.js";
|
|
12
|
+
const MIN_CHARACTERS = 3;
|
|
13
|
+
export const Search = () => {
|
|
14
|
+
const [locales] = useSetting("displayedLocales");
|
|
15
|
+
const [query, setQuery] = useState(() => new URLSearchParams(window.location.search).get("q") ?? "");
|
|
16
|
+
const [results, setResults] = useState();
|
|
17
|
+
const search = useCallback(() => {
|
|
18
|
+
const url = new URL(window.location.href);
|
|
19
|
+
if (url.searchParams.get("q") !== query) {
|
|
20
|
+
if (query.length === 0) {
|
|
21
|
+
url.searchParams.delete("q");
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
url.searchParams.set("q", query);
|
|
25
|
+
}
|
|
26
|
+
window.history.pushState({}, "", url);
|
|
27
|
+
}
|
|
28
|
+
if (query.length >= MIN_CHARACTERS) {
|
|
29
|
+
searchInstances(locales, query)
|
|
30
|
+
.then(res => {
|
|
31
|
+
setResults(res.results);
|
|
32
|
+
})
|
|
33
|
+
.catch(logAndAlertError);
|
|
34
|
+
}
|
|
35
|
+
}, [locales, query]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
document.title = "Search — TSONDB";
|
|
38
|
+
search();
|
|
39
|
+
}, [search]);
|
|
40
|
+
return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: homeTitle }], children: [_jsx("h1", { children: "Search" }), _jsx("input", { type: "search", name: "q", value: query, onInput: event => {
|
|
41
|
+
setQuery(event.currentTarget.value);
|
|
42
|
+
} }), query.length < MIN_CHARACTERS ? (_jsx("p", { class: "help", children: "Provide at least 3 characters in the search field to start the search" })) : (results && (_jsxs("section", { class: "search-results", children: [_jsx("h2", { children: "Results" }), results.length === 0 ? (_jsx("p", { class: "empty", children: "No results" })) : (_jsx("ul", { class: "entries entries--instances", children: results.map(([entityName, instance]) => {
|
|
43
|
+
const gitStatusForDisplay = getGitStatusForDisplay(instance.gitStatus);
|
|
44
|
+
return (_jsxs("li", { id: `instance-${instance.id}`, class: `entries-item${gitStatusForDisplay === undefined
|
|
45
|
+
? ""
|
|
46
|
+
: ` git-status--${gitStatusForDisplay}`}`, children: [_jsxs("h2", { class: "entries-item__title", children: [instance.displayName, _jsx("span", { "aria-hidden": true, class: "entries-item__title-entity", children: toTitleCase(entityName) })] }), _jsx("p", { "aria-hidden": true, class: "entries-item__subtitle entries-item__subtitle--id", children: instance.id }), _jsxs("div", { class: "entries-item__side", children: [_jsx(GitStatusIndicator, { status: instance.gitStatus }), _jsxs("div", { class: "btns", children: [_jsx("a", { href: `/entities/${entityName}/instances/${instance.id}`, class: "btn", children: "Edit" }), _jsx("button", { class: "destructive", onClick: () => {
|
|
47
|
+
if (confirm("Are you sure you want to delete this instance?")) {
|
|
48
|
+
deleteInstanceByEntityNameAndId(locales, entityName, instance.id)
|
|
49
|
+
.then(() => {
|
|
50
|
+
search();
|
|
51
|
+
})
|
|
52
|
+
.catch(logAndAlertError);
|
|
53
|
+
}
|
|
54
|
+
}, children: "Delete" })] })] })] }, instance.id));
|
|
55
|
+
}) }))] })))] }));
|
|
56
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { SerializedType } from "../../shared/schema/types/Type.ts";
|
|
1
|
+
import type { SerializedAsType, SerializedType } from "../../shared/schema/types/Type.ts";
|
|
2
2
|
import type { GetDeclFromDeclName } from "../hooks/useSecondaryDeclarations.ts";
|
|
3
|
-
export declare const createTypeSkeleton: (getDeclFromDeclName: GetDeclFromDeclName, type:
|
|
3
|
+
export declare const createTypeSkeleton: <T extends SerializedType>(getDeclFromDeclName: GetDeclFromDeclName, type: T) => SerializedAsType<T>;
|
|
@@ -4,7 +4,7 @@ export const createTypeSkeleton = (getDeclFromDeclName, type) => {
|
|
|
4
4
|
case "BooleanType":
|
|
5
5
|
return false;
|
|
6
6
|
case "DateType":
|
|
7
|
-
return type.time === true ? new Date().toISOString() : new Date().toDateString();
|
|
7
|
+
return (type.time === true ? new Date().toISOString() : new Date().toDateString());
|
|
8
8
|
case "FloatType":
|
|
9
9
|
return 0.0;
|
|
10
10
|
case "IntegerType":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsondb",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.6",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "Lukas Obermann",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"prettier": "^3.6.2",
|
|
45
45
|
"tsx": "^4.20.6",
|
|
46
46
|
"typescript": "^5.9.3",
|
|
47
|
-
"typescript-eslint": "^8.
|
|
47
|
+
"typescript-eslint": "^8.48.0"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@preact/signals": "^2.5.
|
|
50
|
+
"@preact/signals": "^2.5.1",
|
|
51
51
|
"debug": "^4.4.3",
|
|
52
52
|
"express": "^5.1.0",
|
|
53
53
|
"preact": "^10.27.2",
|
package/public/css/styles.css
CHANGED
|
@@ -589,6 +589,12 @@ select {
|
|
|
589
589
|
}
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
+
.entries-item__title-entity {
|
|
593
|
+
font-weight: 400;
|
|
594
|
+
color: var(--color-foreground-secondary);
|
|
595
|
+
padding-left: 0.5em;
|
|
596
|
+
}
|
|
597
|
+
|
|
592
598
|
.entries-item__subtitle--id {
|
|
593
599
|
font-family: var(--font-mono);
|
|
594
600
|
}
|
|
@@ -1205,3 +1211,7 @@ dialog[open]:has(> .loading-overlay--open)::backdrop {
|
|
|
1205
1211
|
.object-item--translation button {
|
|
1206
1212
|
align-self: center;
|
|
1207
1213
|
}
|
|
1214
|
+
|
|
1215
|
+
section.search-results {
|
|
1216
|
+
margin-top: 3.6rem;
|
|
1217
|
+
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
|
|
2
|
-
import { type Result } from "../../shared/utils/result.ts";
|
|
3
|
-
import type { EntityDecl } from "../schema/declarations/EntityDecl.js";
|
|
4
|
-
import { type ReferencesToInstances } from "./references.ts";
|
|
5
|
-
export declare const createNewId: () => `${string}-${string}-${string}-${string}-${string}`;
|
|
6
|
-
export declare const checkCreateInstancePossible: (localeEntity: EntityDecl | undefined, instancesByEntityName: InstancesByEntityName, entity: EntityDecl, instanceContent: unknown, customId: unknown) => Result<string, [code: number, message: string]>;
|
|
7
|
-
export declare const createInstance: (dataRoot: string, localeEntity: EntityDecl | undefined, instancesByEntityName: InstancesByEntityName, entity: EntityDecl, instanceContent: unknown, customId: unknown) => Promise<Result<string, [code: number, message: string]>>;
|
|
8
|
-
export declare const checkUpdateInstancePossible: (instancesByEntityName: InstancesByEntityName, entity: EntityDecl, instanceContent: unknown) => Result<void, [code: number, message: string]>;
|
|
9
|
-
export declare const checkCreateNonLocaleInstancePossible: (instancesByEntityName: InstancesByEntityName, entity: EntityDecl, instanceContent: unknown) => Result<void, [code: number, message: string]>;
|
|
10
|
-
export declare const unsafeWriteInstance: (dataRoot: string, entity: EntityDecl, instanceId: string, instanceContent: unknown) => Promise<Result<string, [code: number, message: string]>>;
|
|
11
|
-
export declare const updateInstance: (dataRoot: string, instancesByEntityName: InstancesByEntityName, entity: EntityDecl, instanceId: string, instanceContent: unknown) => Promise<Result<string, [code: number, message: string]>>;
|
|
12
|
-
export declare const checkDeleteInstancePossible: (referencesToInstances: ReferencesToInstances, instanceId: string) => Result<void, [code: number, message: string]>;
|
|
13
|
-
export declare const unsafeDeleteInstance: (dataRoot: string, entityName: string, instanceId: string) => Promise<Result<string, [code: number, message: string]>>;
|
|
14
|
-
export declare const deleteInstance: (dataRoot: string, referencesToInstances: ReferencesToInstances, entityName: string, instanceId: string) => Promise<Result<string, [code: number, message: string]>>;
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { error, isOk, map, ok } from "../../shared/utils/result.js";
|
|
3
|
-
import { createValidators, validateEntityDecl } from "../schema/index.js";
|
|
4
|
-
import { getErrorMessageForDisplay } from "./error.js";
|
|
5
|
-
import * as Files from "./files.js";
|
|
6
|
-
import { isReferencedByOtherInstances } from "./references.js";
|
|
7
|
-
export const createNewId = () => randomUUID();
|
|
8
|
-
export const checkCreateInstancePossible = (localeEntity, instancesByEntityName, entity, instanceContent, customId) => {
|
|
9
|
-
const newInstanceId = entity === localeEntity ? customId : createNewId();
|
|
10
|
-
if (typeof newInstanceId !== "string") {
|
|
11
|
-
return error([400, `New identifier "${String(newInstanceId)}" is not a string`]);
|
|
12
|
-
}
|
|
13
|
-
if (localeEntity === entity &&
|
|
14
|
-
instancesByEntityName[entity.name]?.some(instance => instance.id === newInstanceId)) {
|
|
15
|
-
return error([400, `Duplicate id "${newInstanceId}" for locale entity`]);
|
|
16
|
-
}
|
|
17
|
-
return map(checkUpdateInstancePossible(instancesByEntityName, entity, instanceContent), () => newInstanceId);
|
|
18
|
-
};
|
|
19
|
-
export const createInstance = async (dataRoot, localeEntity, instancesByEntityName, entity, instanceContent, customId) => {
|
|
20
|
-
const prerequisiteCheckResult = checkCreateInstancePossible(localeEntity, instancesByEntityName, entity, instanceContent, customId);
|
|
21
|
-
if (isOk(prerequisiteCheckResult)) {
|
|
22
|
-
const newInstanceId = prerequisiteCheckResult.value;
|
|
23
|
-
return unsafeWriteInstance(dataRoot, entity, newInstanceId, instanceContent);
|
|
24
|
-
}
|
|
25
|
-
else {
|
|
26
|
-
return prerequisiteCheckResult;
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
export const checkUpdateInstancePossible = (instancesByEntityName, entity, instanceContent) => {
|
|
30
|
-
const validationErrors = validateEntityDecl(createValidators(instancesByEntityName, false), [], entity, instanceContent);
|
|
31
|
-
if (validationErrors.length > 0) {
|
|
32
|
-
return error([400, validationErrors.map(getErrorMessageForDisplay).join("\n\n")]);
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
return ok();
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
export const checkCreateNonLocaleInstancePossible = checkUpdateInstancePossible;
|
|
39
|
-
export const unsafeWriteInstance = async (dataRoot, entity, instanceId, instanceContent) => {
|
|
40
|
-
try {
|
|
41
|
-
await Files.writeInstance(dataRoot, entity, instanceId, instanceContent);
|
|
42
|
-
return ok(instanceId);
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
return error([
|
|
46
|
-
500,
|
|
47
|
-
`Failed to write instance: ${err instanceof Error ? err.toString() : String(err)}`,
|
|
48
|
-
]);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
export const updateInstance = async (dataRoot, instancesByEntityName, entity, instanceId, instanceContent) => {
|
|
52
|
-
const prerequisiteCheckResult = checkUpdateInstancePossible(instancesByEntityName, entity, instanceContent);
|
|
53
|
-
if (isOk(prerequisiteCheckResult)) {
|
|
54
|
-
return unsafeWriteInstance(dataRoot, entity, instanceId, instanceContent);
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
return prerequisiteCheckResult;
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
export const checkDeleteInstancePossible = (referencesToInstances, instanceId) => {
|
|
61
|
-
if (isReferencedByOtherInstances(referencesToInstances, instanceId)) {
|
|
62
|
-
return error([400, "Cannot delete instance that is referenced by other instances"]);
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
return ok();
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
export const unsafeDeleteInstance = async (dataRoot, entityName, instanceId) => {
|
|
69
|
-
try {
|
|
70
|
-
await Files.deleteInstance(dataRoot, entityName, instanceId);
|
|
71
|
-
return ok(instanceId);
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
return error([
|
|
75
|
-
500,
|
|
76
|
-
`Failed to delete instance: ${err instanceof Error ? err.toString() : String(err)}`,
|
|
77
|
-
]);
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
export const deleteInstance = async (dataRoot, referencesToInstances, entityName, instanceId) => {
|
|
81
|
-
const prerequisiteCheckResult = checkDeleteInstancePossible(referencesToInstances, instanceId);
|
|
82
|
-
if (isOk(prerequisiteCheckResult)) {
|
|
83
|
-
return unsafeDeleteInstance(dataRoot, entityName, instanceId);
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
return prerequisiteCheckResult;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { StatusResult } from "simple-git";
|
|
2
|
-
import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
|
|
3
|
-
import type { EntityDecl } from "../schema/declarations/EntityDecl.js";
|
|
4
|
-
export declare const getInstancesByEntityName: (dataRoot: string, entities: readonly EntityDecl[]) => Promise<InstancesByEntityName>;
|
|
5
|
-
export declare const attachGitStatusToInstancesByEntityName: (instancesByEntityName: InstancesByEntityName, dataRoot: string, gitRoot: string, gitStatus: StatusResult) => void;
|
|
6
|
-
export declare const formatInstance: (entity: EntityDecl, instanceContent: unknown) => string;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import child_process from "node:child_process";
|
|
2
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
-
import { basename, extname, join } from "node:path";
|
|
4
|
-
import { platform } from "node:process";
|
|
5
|
-
import { promisify } from "node:util";
|
|
6
|
-
import { mapAsync } from "../../shared/utils/async.js";
|
|
7
|
-
import { formatValue } from "../schema/index.js";
|
|
8
|
-
import { getFileNameForId } from "./files.js";
|
|
9
|
-
import { getGitFileStatusFromStatusResult } from "./git.js";
|
|
10
|
-
const exec = promisify(child_process.exec);
|
|
11
|
-
const ulimit = platform === "win32" ? 2048 : Number.parseInt((await exec("ulimit -n")).stdout);
|
|
12
|
-
export const getInstancesByEntityName = async (dataRoot, entities) => Object.fromEntries((await mapAsync(entities, async (entity) => {
|
|
13
|
-
const entityDir = join(dataRoot, entity.name);
|
|
14
|
-
const instanceFileNames = await readdir(entityDir);
|
|
15
|
-
const instances = await mapAsync(instanceFileNames, async (instanceFileName) => ({
|
|
16
|
-
id: basename(instanceFileName, extname(instanceFileName)),
|
|
17
|
-
content: JSON.parse(await readFile(join(entityDir, instanceFileName), "utf-8")),
|
|
18
|
-
}), ulimit);
|
|
19
|
-
return [entity.name, instances];
|
|
20
|
-
}, 1)).toSorted(([a], [b]) => a.localeCompare(b)));
|
|
21
|
-
export const attachGitStatusToInstancesByEntityName = (instancesByEntityName, dataRoot, gitRoot, gitStatus) => {
|
|
22
|
-
Object.entries(instancesByEntityName).forEach(([entityName, instances]) => {
|
|
23
|
-
instancesByEntityName[entityName] = instances.map(instanceContainer => ({
|
|
24
|
-
...instanceContainer,
|
|
25
|
-
gitStatus: getGitFileStatusFromStatusResult(gitStatus, gitRoot, dataRoot, entityName, getFileNameForId(instanceContainer.id)),
|
|
26
|
-
}));
|
|
27
|
-
});
|
|
28
|
-
};
|
|
29
|
-
export const formatInstance = (entity, instanceContent) => JSON.stringify(formatValue(entity.type.value, instanceContent), undefined, 2) + "\n";
|