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.
Files changed (74) hide show
  1. package/dist/src/node/index.js +17 -20
  2. package/dist/src/node/schema/Node.d.ts +2 -2
  3. package/dist/src/node/schema/Node.js +3 -5
  4. package/dist/src/node/schema/declarations/EntityDecl.d.ts +4 -4
  5. package/dist/src/node/schema/types/primitives/FloatType.d.ts +5 -2
  6. package/dist/src/node/schema/types/primitives/FloatType.js +10 -4
  7. package/dist/src/node/schema/types/references/IncludeIdentifierType.js +0 -3
  8. package/dist/src/node/server/api/declarations.js +27 -10
  9. package/dist/src/node/server/api/git.js +5 -4
  10. package/dist/src/node/server/api/index.js +2 -0
  11. package/dist/src/node/server/api/instances.js +2 -3
  12. package/dist/src/node/server/api/search.d.ts +1 -0
  13. package/dist/src/node/server/api/search.js +50 -0
  14. package/dist/src/node/server/index.d.ts +4 -3
  15. package/dist/src/node/server/index.js +2 -2
  16. package/dist/src/node/server/init.d.ts +2 -2
  17. package/dist/src/node/server/init.js +14 -11
  18. package/dist/src/node/server/utils/childInstances.d.ts +0 -3
  19. package/dist/src/node/server/utils/childInstances.js +2 -43
  20. package/dist/src/node/server/utils/instanceOperations.d.ts +3 -4
  21. package/dist/src/node/server/utils/instanceOperations.js +44 -84
  22. package/dist/src/node/server/utils/query.d.ts +2 -0
  23. package/dist/src/node/server/utils/query.js +7 -0
  24. package/dist/src/node/utils/childInstances.d.ts +11 -10
  25. package/dist/src/node/utils/childInstances.js +66 -130
  26. package/dist/src/node/utils/databaseInMemory.d.ts +18 -0
  27. package/dist/src/node/utils/databaseInMemory.js +86 -0
  28. package/dist/src/node/utils/databaseOnDisk.d.ts +2 -0
  29. package/dist/src/node/utils/databaseOnDisk.js +62 -0
  30. package/dist/src/node/utils/databaseTransactions.d.ts +46 -0
  31. package/dist/src/node/utils/databaseTransactions.js +105 -0
  32. package/dist/src/node/utils/displayName.d.ts +3 -3
  33. package/dist/src/node/utils/error.d.ts +4 -0
  34. package/dist/src/node/utils/error.js +8 -0
  35. package/dist/src/node/utils/files.d.ts +4 -2
  36. package/dist/src/node/utils/files.js +2 -1
  37. package/dist/src/node/utils/git.d.ts +3 -1
  38. package/dist/src/node/utils/git.js +8 -2
  39. package/dist/src/node/utils/instanceTransactionSteps.d.ts +9 -0
  40. package/dist/src/node/utils/instanceTransactionSteps.js +40 -0
  41. package/dist/src/node/utils/references.d.ts +4 -3
  42. package/dist/src/node/utils/references.js +3 -2
  43. package/dist/src/shared/api.d.ts +4 -0
  44. package/dist/src/shared/schema/declarations/EntityDecl.d.ts +2 -2
  45. package/dist/src/shared/schema/declarations/EnumDecl.d.ts +8 -0
  46. package/dist/src/shared/schema/declarations/EnumDecl.js +5 -0
  47. package/dist/src/shared/schema/types/FloatType.d.ts +6 -5
  48. package/dist/src/shared/schema/types/FloatType.js +1 -0
  49. package/dist/src/shared/utils/dictionary.d.ts +17 -0
  50. package/dist/src/shared/utils/dictionary.js +76 -0
  51. package/dist/src/shared/utils/instances.d.ts +2 -2
  52. package/dist/src/shared/utils/result.d.ts +4 -0
  53. package/dist/src/shared/utils/result.js +4 -0
  54. package/dist/src/web/api/search.d.ts +2 -0
  55. package/dist/src/web/api/search.js +7 -0
  56. package/dist/src/web/components/InstanceRouteSkeleton.d.ts +9 -8
  57. package/dist/src/web/components/Layout.js +1 -1
  58. package/dist/src/web/components/git/GitStatusIndicator.js +1 -1
  59. package/dist/src/web/components/typeInputs/ChildEntitiesTypeInput.js +1 -1
  60. package/dist/src/web/components/typeInputs/FloatTypeInput.d.ts +1 -1
  61. package/dist/src/web/components/typeInputs/FloatTypeInput.js +2 -1
  62. package/dist/src/web/hooks/useGitClient.js +0 -1
  63. package/dist/src/web/hooks/useMappedAPIResource.js +2 -4
  64. package/dist/src/web/index.js +2 -1
  65. package/dist/src/web/routes/Search.d.ts +2 -0
  66. package/dist/src/web/routes/Search.js +56 -0
  67. package/dist/src/web/utils/typeSkeleton.d.ts +2 -2
  68. package/dist/src/web/utils/typeSkeleton.js +1 -1
  69. package/package.json +3 -3
  70. package/public/css/styles.css +10 -0
  71. package/dist/src/node/utils/instanceOperations.d.ts +0 -14
  72. package/dist/src/node/utils/instanceOperations.js +0 -88
  73. package/dist/src/node/utils/instances.d.ts +0 -6
  74. 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 ?? ""}`, title: getLabelForGitStatus(gitStatusForDisplay), children: 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 { SerializedFloatType } from "../../../shared/schema/types/FloatType.ts";
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: 0.01, "aria-invalid": errors.length > 0, disabled: disabled }), _jsx(ValidationErrors, { disabled: disabled, errors: errors })] }));
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
  };
@@ -59,7 +59,6 @@ export const useGitClient = () => {
59
59
  })));
60
60
  setIsDetached(branchesData.isDetached);
61
61
  setLatestCommit(statusData.latestCommit);
62
- console.log(statusData.latestCommit);
63
62
  }
64
63
  catch (error) {
65
64
  logAndAlertError(error, "Error updating git status: ");
@@ -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((err) => {
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];
@@ -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,2 @@
1
+ import type { FunctionalComponent } from "preact";
2
+ export declare const Search: FunctionalComponent;
@@ -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: SerializedType) => unknown;
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",
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.46.4"
47
+ "typescript-eslint": "^8.48.0"
48
48
  },
49
49
  "dependencies": {
50
- "@preact/signals": "^2.5.0",
50
+ "@preact/signals": "^2.5.1",
51
51
  "debug": "^4.4.3",
52
52
  "express": "^5.1.0",
53
53
  "preact": "^10.27.2",
@@ -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";