tsondb 0.5.18 → 0.6.0

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 (222) hide show
  1. package/dist/src/bin/tsondb.js +1 -1
  2. package/dist/src/index.d.ts +1 -1
  3. package/dist/src/node/config.d.ts +20 -0
  4. package/dist/src/node/config.js +5 -0
  5. package/dist/src/node/index.d.ts +3 -2
  6. package/dist/src/node/index.js +13 -8
  7. package/dist/src/node/renderers/jsonschema/render.d.ts +1 -1
  8. package/dist/src/node/renderers/jsonschema/render.js +12 -7
  9. package/dist/src/node/renderers/ts/render.d.ts +1 -1
  10. package/dist/src/node/renderers/ts/render.js +12 -6
  11. package/dist/src/node/schema/Node.d.ts +100 -29
  12. package/dist/src/node/schema/Node.js +268 -61
  13. package/dist/src/node/schema/Schema.js +96 -1
  14. package/dist/src/node/schema/TypeParameter.d.ts +7 -9
  15. package/dist/src/node/schema/TypeParameter.js +7 -5
  16. package/dist/src/node/schema/declarations/Declaration.d.ts +10 -28
  17. package/dist/src/node/schema/declarations/Declaration.js +8 -110
  18. package/dist/src/node/schema/declarations/EntityDecl.d.ts +48 -48
  19. package/dist/src/node/schema/declarations/EntityDecl.js +15 -12
  20. package/dist/src/node/schema/declarations/EnumDecl.d.ts +14 -17
  21. package/dist/src/node/schema/declarations/EnumDecl.js +12 -12
  22. package/dist/src/node/schema/declarations/TypeAliasDecl.d.ts +8 -14
  23. package/dist/src/node/schema/declarations/TypeAliasDecl.js +10 -11
  24. package/dist/src/node/schema/index.d.ts +0 -2
  25. package/dist/src/node/schema/index.js +0 -1
  26. package/dist/src/node/schema/types/Type.d.ts +19 -42
  27. package/dist/src/node/schema/types/Type.js +29 -167
  28. package/dist/src/node/schema/types/generic/ArrayType.d.ts +5 -14
  29. package/dist/src/node/schema/types/generic/ArrayType.js +15 -20
  30. package/dist/src/node/schema/types/generic/EnumType.d.ts +5 -17
  31. package/dist/src/node/schema/types/generic/EnumType.js +12 -20
  32. package/dist/src/node/schema/types/generic/ObjectType.d.ts +5 -19
  33. package/dist/src/node/schema/types/generic/ObjectType.js +11 -15
  34. package/dist/src/node/schema/types/primitives/BooleanType.d.ts +7 -8
  35. package/dist/src/node/schema/types/primitives/BooleanType.js +5 -4
  36. package/dist/src/node/schema/types/primitives/DateType.d.ts +6 -8
  37. package/dist/src/node/schema/types/primitives/DateType.js +5 -4
  38. package/dist/src/node/schema/types/primitives/FloatType.d.ts +9 -21
  39. package/dist/src/node/schema/types/primitives/FloatType.js +5 -4
  40. package/dist/src/node/schema/types/primitives/IntegerType.d.ts +9 -21
  41. package/dist/src/node/schema/types/primitives/IntegerType.js +5 -4
  42. package/dist/src/node/schema/types/primitives/StringType.d.ts +6 -10
  43. package/dist/src/node/schema/types/primitives/StringType.js +5 -4
  44. package/dist/src/node/schema/types/references/ChildEntitiesType.d.ts +18 -0
  45. package/dist/src/node/schema/types/references/ChildEntitiesType.js +18 -0
  46. package/dist/src/node/schema/types/references/IncludeIdentifierType.d.ts +7 -13
  47. package/dist/src/node/schema/types/references/IncludeIdentifierType.js +12 -12
  48. package/dist/src/node/schema/types/references/NestedEntityMapType.d.ts +7 -17
  49. package/dist/src/node/schema/types/references/NestedEntityMapType.js +11 -13
  50. package/dist/src/node/schema/types/references/ReferenceIdentifierType.d.ts +4 -10
  51. package/dist/src/node/schema/types/references/ReferenceIdentifierType.js +3 -5
  52. package/dist/src/node/schema/types/references/TypeArgumentType.d.ts +5 -10
  53. package/dist/src/node/schema/types/references/TypeArgumentType.js +10 -5
  54. package/dist/src/node/server/api/declarations.js +24 -6
  55. package/dist/src/node/server/api/index.js +11 -0
  56. package/dist/src/node/server/api/instances.js +9 -6
  57. package/dist/src/node/server/index.d.ts +6 -1
  58. package/dist/src/node/server/index.js +7 -3
  59. package/dist/src/node/server/init.d.ts +2 -1
  60. package/dist/src/node/server/init.js +11 -11
  61. package/dist/src/node/server/utils/childInstances.d.ts +4 -0
  62. package/dist/src/node/server/utils/childInstances.js +41 -0
  63. package/dist/src/node/server/utils/instanceOperations.d.ts +8 -0
  64. package/dist/src/node/server/utils/instanceOperations.js +107 -0
  65. package/dist/src/node/server/utils/locales.d.ts +2 -0
  66. package/dist/src/node/server/utils/locales.js +8 -0
  67. package/dist/src/node/utils/childInstances.d.ts +32 -0
  68. package/dist/src/node/utils/childInstances.js +164 -0
  69. package/dist/src/node/utils/displayName.d.ts +2 -1
  70. package/dist/src/node/utils/displayName.js +5 -4
  71. package/dist/src/node/utils/files.d.ts +5 -0
  72. package/dist/src/node/utils/files.js +9 -0
  73. package/dist/src/node/utils/instanceOperations.d.ts +14 -0
  74. package/dist/src/node/utils/instanceOperations.js +88 -0
  75. package/dist/src/node/utils/instances.d.ts +1 -1
  76. package/dist/src/node/utils/instances.js +12 -6
  77. package/dist/src/node/utils/references.d.ts +3 -1
  78. package/dist/src/node/utils/references.js +43 -18
  79. package/dist/src/node/utils/referencesWorker.d.ts +5 -0
  80. package/dist/src/node/utils/referencesWorker.js +22 -0
  81. package/dist/src/node/utils/workers.d.ts +15 -0
  82. package/dist/src/node/utils/workers.js +90 -0
  83. package/dist/src/shared/api.d.ts +14 -1
  84. package/dist/src/shared/schema/Node.d.ts +63 -0
  85. package/dist/src/shared/schema/Node.js +128 -0
  86. package/dist/src/shared/schema/TypeParameter.d.ts +9 -0
  87. package/dist/src/shared/schema/TypeParameter.js +2 -0
  88. package/dist/src/shared/schema/declarations/Declaration.d.ts +21 -0
  89. package/dist/src/shared/schema/declarations/Declaration.js +4 -0
  90. package/dist/src/shared/schema/declarations/EntityDecl.d.ts +32 -0
  91. package/dist/src/shared/schema/declarations/EntityDecl.js +8 -0
  92. package/dist/src/shared/schema/declarations/EnumDecl.d.ts +11 -0
  93. package/dist/src/shared/schema/declarations/EnumDecl.js +9 -0
  94. package/dist/src/shared/schema/declarations/TypeAliasDecl.d.ts +11 -0
  95. package/dist/src/shared/schema/declarations/TypeAliasDecl.js +7 -0
  96. package/dist/src/shared/schema/types/ArrayType.d.ts +11 -0
  97. package/dist/src/shared/schema/types/ArrayType.js +8 -0
  98. package/dist/src/shared/schema/types/BooleanType.d.ts +7 -0
  99. package/dist/src/shared/schema/types/BooleanType.js +2 -0
  100. package/dist/src/shared/schema/types/ChildEntitiesType.d.ts +8 -0
  101. package/dist/src/shared/schema/types/ChildEntitiesType.js +2 -0
  102. package/dist/src/shared/schema/types/DateType.d.ts +8 -0
  103. package/dist/src/shared/schema/types/DateType.js +2 -0
  104. package/dist/src/shared/schema/types/EnumType.d.ts +14 -0
  105. package/dist/src/shared/schema/types/EnumType.js +28 -0
  106. package/dist/src/shared/schema/types/FloatType.d.ts +11 -0
  107. package/dist/src/shared/schema/types/FloatType.js +2 -0
  108. package/dist/src/shared/schema/types/IncludeIdentifierType.d.ts +11 -0
  109. package/dist/src/shared/schema/types/IncludeIdentifierType.js +13 -0
  110. package/dist/src/shared/schema/types/IntegerType.d.ts +11 -0
  111. package/dist/src/shared/schema/types/IntegerType.js +2 -0
  112. package/dist/src/shared/schema/types/NestedEntityMapType.d.ts +17 -0
  113. package/dist/src/shared/schema/types/NestedEntityMapType.js +14 -0
  114. package/dist/src/shared/schema/types/ObjectType.d.ts +19 -0
  115. package/dist/src/shared/schema/types/ObjectType.js +14 -0
  116. package/dist/src/shared/schema/types/ReferenceIdentifierType.d.ts +8 -0
  117. package/dist/src/shared/schema/types/ReferenceIdentifierType.js +2 -0
  118. package/dist/src/shared/schema/types/StringType.d.ts +10 -0
  119. package/dist/src/shared/schema/types/StringType.js +2 -0
  120. package/dist/src/shared/schema/types/Type.d.ts +20 -0
  121. package/dist/src/shared/schema/types/TypeArgumentType.d.ts +12 -0
  122. package/dist/src/shared/schema/types/TypeArgumentType.js +7 -0
  123. package/dist/src/shared/utils/array.d.ts +4 -0
  124. package/dist/src/shared/utils/array.js +30 -0
  125. package/dist/src/shared/utils/async.d.ts +8 -0
  126. package/dist/src/shared/utils/async.js +35 -0
  127. package/dist/src/shared/utils/compare.js +3 -3
  128. package/dist/src/shared/utils/displayName.d.ts +6 -2
  129. package/dist/src/shared/utils/displayName.js +21 -8
  130. package/dist/src/shared/utils/instances.d.ts +2 -3
  131. package/dist/src/shared/utils/instances.js +3 -1
  132. package/dist/src/shared/utils/markdown.d.ts +4 -0
  133. package/dist/src/shared/utils/markdown.js +173 -110
  134. package/dist/src/shared/utils/object.d.ts +2 -0
  135. package/dist/src/shared/utils/object.js +2 -0
  136. package/dist/src/shared/utils/result.d.ts +8 -2
  137. package/dist/src/shared/utils/result.js +1 -1
  138. package/dist/src/web/api/declarations.d.ts +26 -0
  139. package/dist/src/web/api/declarations.js +51 -0
  140. package/dist/src/web/api/git.d.ts +14 -0
  141. package/dist/src/web/api/git.js +20 -0
  142. package/dist/src/web/api/index.d.ts +1 -0
  143. package/dist/src/web/api/index.js +2 -0
  144. package/dist/src/web/api/instances.d.ts +2 -0
  145. package/dist/src/web/api/instances.js +2 -0
  146. package/dist/src/web/components/Git.js +19 -16
  147. package/dist/src/web/components/InstanceRouteSkeleton.d.ts +42 -0
  148. package/dist/src/web/components/InstanceRouteSkeleton.js +114 -0
  149. package/dist/src/web/components/Layout.js +3 -2
  150. package/dist/src/web/components/ModalDialog.d.ts +3 -0
  151. package/dist/src/web/components/ModalDialog.js +16 -0
  152. package/dist/src/web/components/Settings.d.ts +2 -0
  153. package/dist/src/web/components/Settings.js +47 -0
  154. package/dist/src/web/components/typeInputs/ArrayTypeInput.d.ts +3 -11
  155. package/dist/src/web/components/typeInputs/ArrayTypeInput.js +5 -4
  156. package/dist/src/web/components/typeInputs/BooleanTypeInput.d.ts +3 -6
  157. package/dist/src/web/components/typeInputs/BooleanTypeInput.js +2 -2
  158. package/dist/src/web/components/typeInputs/ChildEntitiesTypeInput.d.ts +6 -0
  159. package/dist/src/web/components/typeInputs/ChildEntitiesTypeInput.js +27 -0
  160. package/dist/src/web/components/typeInputs/DateTypeInput.d.ts +3 -6
  161. package/dist/src/web/components/typeInputs/DateTypeInput.js +2 -2
  162. package/dist/src/web/components/typeInputs/EnumTypeInput.d.ts +3 -11
  163. package/dist/src/web/components/typeInputs/EnumTypeInput.js +55 -22
  164. package/dist/src/web/components/typeInputs/FloatTypeInput.d.ts +3 -6
  165. package/dist/src/web/components/typeInputs/FloatTypeInput.js +2 -2
  166. package/dist/src/web/components/typeInputs/GenericTypeArgumentIdentifierTypeInput.d.ts +3 -4
  167. package/dist/src/web/components/typeInputs/IncludeIdentifierTypeInput.d.ts +3 -11
  168. package/dist/src/web/components/typeInputs/IncludeIdentifierTypeInput.js +3 -2
  169. package/dist/src/web/components/typeInputs/IntegerTypeInput.d.ts +3 -6
  170. package/dist/src/web/components/typeInputs/IntegerTypeInput.js +2 -2
  171. package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.d.ts +3 -11
  172. package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.js +5 -4
  173. package/dist/src/web/components/typeInputs/ObjectTypeInput.d.ts +3 -11
  174. package/dist/src/web/components/typeInputs/ObjectTypeInput.js +8 -5
  175. package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.d.ts +3 -8
  176. package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.js +2 -2
  177. package/dist/src/web/components/typeInputs/StringTypeInput.d.ts +3 -6
  178. package/dist/src/web/components/typeInputs/StringTypeInput.js +3 -3
  179. package/dist/src/web/components/typeInputs/TypeInput.d.ts +12 -4
  180. package/dist/src/web/components/typeInputs/TypeInput.js +22 -17
  181. package/dist/src/web/components/typeInputs/utils/ValidationErrors.d.ts +1 -0
  182. package/dist/src/web/components/typeInputs/utils/ValidationErrors.js +1 -3
  183. package/dist/src/web/context/config.d.ts +11 -0
  184. package/dist/src/web/context/config.js +6 -0
  185. package/dist/src/web/context/entities.d.ts +8 -5
  186. package/dist/src/web/context/entities.js +1 -1
  187. package/dist/src/web/context/settings.d.ts +8 -0
  188. package/dist/src/web/context/settings.js +10 -0
  189. package/dist/src/web/hooks/useEntityFromRoute.d.ts +1 -1
  190. package/dist/src/web/hooks/useEntityFromRoute.js +2 -2
  191. package/dist/src/web/hooks/useInstanceNamesByEntity.d.ts +1 -1
  192. package/dist/src/web/hooks/useInstanceNamesByEntity.js +4 -2
  193. package/dist/src/web/hooks/useMappedAPIResource.js +2 -4
  194. package/dist/src/web/hooks/useSecondaryDeclarations.d.ts +1 -1
  195. package/dist/src/web/hooks/useSecondaryDeclarations.js +8 -4
  196. package/dist/src/web/hooks/useSettings.d.ts +10 -0
  197. package/dist/src/web/hooks/useSettings.js +51 -0
  198. package/dist/src/web/index.js +11 -5
  199. package/dist/src/web/routes/CreateInstance.js +40 -79
  200. package/dist/src/web/routes/Entity.js +42 -19
  201. package/dist/src/web/routes/Home.js +24 -5
  202. package/dist/src/web/routes/Instance.js +34 -85
  203. package/dist/src/web/utils/InlineMarkdown.d.ts +1 -1
  204. package/dist/src/web/utils/InlineMarkdown.js +13 -1
  205. package/dist/src/web/utils/api.d.ts +25 -0
  206. package/dist/src/web/utils/api.js +34 -0
  207. package/dist/src/web/utils/typeSkeleton.d.ts +1 -1
  208. package/dist/src/web/utils/typeSkeleton.js +2 -0
  209. package/package.json +2 -1
  210. package/public/css/styles.css +171 -12
  211. package/dist/src/node/schema/types/primitives/NumericType.d.ts +0 -6
  212. package/dist/src/node/schema/types/primitives/NumericType.js +0 -2
  213. package/dist/src/node/schema/types/primitives/PrimitiveType.d.ts +0 -6
  214. package/dist/src/node/schema/validation/type.d.ts +0 -4
  215. package/dist/src/node/schema/validation/type.js +0 -1
  216. package/dist/src/node/server/api/instanceOperations.d.ts +0 -6
  217. package/dist/src/node/server/api/instanceOperations.js +0 -93
  218. package/dist/src/shared/config.d.ts +0 -11
  219. package/dist/src/shared/config.js +0 -1
  220. package/dist/src/web/api.d.ts +0 -24
  221. package/dist/src/web/api.js +0 -201
  222. /package/dist/src/{node/schema/types/primitives/PrimitiveType.js → shared/schema/types/Type.js} +0 -0
@@ -2,11 +2,12 @@ import Debug from "debug";
2
2
  import express from "express";
3
3
  import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
4
4
  import { isOk } from "../../../shared/utils/result.js";
5
- import { serializeDecl } from "../../schema/declarations/Declaration.js";
6
5
  import { isEntityDecl } from "../../schema/declarations/EntityDecl.js";
7
6
  import { isEnumDecl } from "../../schema/declarations/EnumDecl.js";
8
7
  import { isTypeAliasDecl } from "../../schema/declarations/TypeAliasDecl.js";
9
- import { createInstance, deleteInstance, updateInstance } from "./instanceOperations.js";
8
+ import { serializeNode } from "../../schema/index.js";
9
+ import { getChildInstances } from "../../utils/childInstances.js";
10
+ import { createInstance, deleteInstance, updateInstance } from "../utils/instanceOperations.js";
10
11
  const debug = Debug("tsondb:server:api:declarations");
11
12
  export const declarationsApi = express.Router();
12
13
  declarationsApi.use((req, _res, next) => {
@@ -30,7 +31,7 @@ declarationsApi.get("/", (req, res) => {
30
31
  }
31
32
  const body = {
32
33
  declarations: filteredEntities.map(decl => ({
33
- declaration: serializeDecl(decl),
34
+ declaration: serializeNode(decl),
34
35
  instanceCount: req.instancesByEntityName[decl.name]?.length ?? 0,
35
36
  })),
36
37
  localeEntity: req.localeEntity?.name,
@@ -44,7 +45,7 @@ declarationsApi.get("/:name", (req, res) => {
44
45
  return;
45
46
  }
46
47
  const body = {
47
- declaration: serializeDecl(decl),
48
+ declaration: serializeNode(decl),
48
49
  instanceCount: req.instancesByEntityName[decl.name]?.length ?? 0,
49
50
  isLocaleEntity: decl === req.localeEntity,
50
51
  };
@@ -78,7 +79,8 @@ declarationsApi.post("/:name/instances", async (req, res) => {
78
79
  res.status(400).send(`Declaration "${decl.name}" is not an entity`);
79
80
  return;
80
81
  }
81
- const result = await createInstance(req, req.params.name, req.body, req.query["id"]);
82
+ const requestBody = req.body;
83
+ const result = await createInstance(req, requestBody.instance, req.query["id"]);
82
84
  if (isOk(result)) {
83
85
  const body = {
84
86
  instance: result.value,
@@ -121,7 +123,8 @@ declarationsApi.put("/:name/instances/:id", async (req, res) => {
121
123
  res.status(400).send(`Declaration "${decl.name}" is not an entity`);
122
124
  return;
123
125
  }
124
- const result = await updateInstance(req, req.params.name, req.params.id, req.body);
126
+ const requestBody = req.body;
127
+ const result = await updateInstance(req, requestBody.instance);
125
128
  if (isOk(result)) {
126
129
  const body = {
127
130
  instance: result.value,
@@ -155,3 +158,18 @@ declarationsApi.delete("/:name/instances/:id", async (req, res) => {
155
158
  res.status(result.error[0]).send(result.error[1]);
156
159
  }
157
160
  });
161
+ declarationsApi.get("/:name/instances/:id/children", (req, res) => {
162
+ const decl = req.declarations.find(decl => decl.name === req.params.name);
163
+ if (decl === undefined) {
164
+ res.status(404).send(`Declaration "${req.params.name}" not found`);
165
+ return;
166
+ }
167
+ if (!isEntityDecl(decl)) {
168
+ res.status(400).send(`Declaration "${decl.name}" is not an entity`);
169
+ return;
170
+ }
171
+ const body = {
172
+ instances: getChildInstances(req.instancesByEntityName, decl, req.params.id),
173
+ };
174
+ res.json(body);
175
+ });
@@ -6,3 +6,14 @@ export const api = express.Router();
6
6
  api.use("/declarations", declarationsApi);
7
7
  api.use("/instances", instancesApi);
8
8
  api.use("/git", gitApi);
9
+ api.get("/config", (req, res) => {
10
+ const body = {
11
+ localeEntityName: req.localeEntity?.name,
12
+ defaultLocales: req.defaultLocales,
13
+ homeLayoutSections: req.homeLayoutSections?.map(section => ({
14
+ ...section,
15
+ entities: section.entities.map(entity => entity.name),
16
+ })),
17
+ };
18
+ res.json(body);
19
+ });
@@ -8,18 +8,21 @@ instancesApi.use((req, _res, next) => {
8
8
  next();
9
9
  });
10
10
  instancesApi.get("/", (req, res) => {
11
- const locales = (Array.isArray(req.query["locales"]) ? req.query["locales"] : [req.query["locales"]]).filter(locale => typeof locale === "string");
12
11
  const body = {
13
12
  instances: Object.fromEntries(Object.entries(req.instancesByEntityName)
14
13
  .filter(([entityName]) => Object.hasOwn(req.entitiesByName, entityName))
15
14
  .map(([entityName, instances]) => [
16
15
  entityName,
17
- instances.map(instance => ({
18
- id: instance.id,
19
- name: getDisplayNameFromEntityInstance(
16
+ instances.map(instance => {
17
+ const { name, localeId } = getDisplayNameFromEntityInstance(
20
18
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
21
- req.entitiesByName[entityName], instance.content, req.getInstanceById, locales),
22
- })),
19
+ req.entitiesByName[entityName], instance.content, req.getInstanceById, req.locales);
20
+ return {
21
+ id: instance.id,
22
+ name,
23
+ displayNameLocaleId: localeId,
24
+ };
25
+ }),
23
26
  ])),
24
27
  };
25
28
  res.json(body);
@@ -1,5 +1,7 @@
1
1
  import type { SimpleGit } from "simple-git";
2
+ import type { SerializedDecl } from "../../shared/schema/declarations/Declaration.ts";
2
3
  import type { InstanceContainer, InstancesByEntityName } from "../../shared/utils/instances.ts";
4
+ import type { HomeLayoutSection } from "../config.ts";
3
5
  import type { Decl } from "../schema/declarations/Declaration.ts";
4
6
  import type { EntityDecl } from "../schema/declarations/EntityDecl.ts";
5
7
  import type { Schema } from "../schema/Schema.ts";
@@ -15,9 +17,12 @@ export interface TSONDBRequestLocals {
15
17
  entities: readonly EntityDecl[];
16
18
  instancesByEntityName: InstancesByEntityName;
17
19
  entitiesByName: Record<string, EntityDecl>;
20
+ serializedDeclarationsByName: Record<string, SerializedDecl>;
18
21
  localeEntity?: EntityDecl;
19
22
  referencesToInstances: ReferencesToInstances;
23
+ defaultLocales: string[];
20
24
  locales: string[];
25
+ homeLayoutSections?: HomeLayoutSection[];
21
26
  getInstanceById: GetInstanceById;
22
27
  }
23
28
  export type GetInstanceById = (id: string) => {
@@ -30,4 +35,4 @@ declare global {
30
35
  }
31
36
  }
32
37
  }
33
- export declare const createServer: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName, options?: Partial<ServerOptions>) => Promise<void>;
38
+ export declare const createServer: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName, defaultLocales: string[], homeLayoutSections?: HomeLayoutSection[], options?: Partial<ServerOptions>) => Promise<void>;
@@ -4,6 +4,7 @@ import { findPackageJSON } from "node:module";
4
4
  import { dirname, join } from "node:path";
5
5
  import { api } from "./api/index.js";
6
6
  import { init } from "./init.js";
7
+ import { getLocalesFromRequest } from "./utils/locales.js";
7
8
  const debug = Debug("tsondb:server");
8
9
  const defaultOptions = {
9
10
  port: 3000,
@@ -15,7 +16,7 @@ const staticNodeModule = (moduleName) => {
15
16
  }
16
17
  return express.static(dirname(pathToPackageJson));
17
18
  };
18
- export const createServer = async (schema, dataRootPath, instancesByEntityName, options) => {
19
+ export const createServer = async (schema, dataRootPath, instancesByEntityName, defaultLocales, homeLayoutSections, options) => {
19
20
  const { port } = { ...defaultOptions, ...options };
20
21
  const app = express();
21
22
  app.use(express.static(join(import.meta.dirname, "../../../../public")));
@@ -24,18 +25,21 @@ export const createServer = async (schema, dataRootPath, instancesByEntityName,
24
25
  app.use("/js/client", express.static(join(import.meta.dirname, "../../../../dist/src/web")));
25
26
  app.use("/js/shared", express.static(join(import.meta.dirname, "../../../../dist/src/shared")));
26
27
  app.use(express.json());
27
- const requestLocals = await init(schema, dataRootPath, Object.assign({}, instancesByEntityName));
28
+ const requestLocals = await init(schema, dataRootPath, Object.assign({}, instancesByEntityName), defaultLocales, homeLayoutSections);
28
29
  app.use((req, _res, next) => {
29
30
  debug("%s %s", req.method, req.originalUrl);
30
31
  Object.assign(req, requestLocals);
32
+ req.locales = getLocalesFromRequest(req) ?? defaultLocales;
31
33
  next();
32
34
  });
33
35
  app.use("/api", api);
34
36
  const importMap = JSON.stringify({
35
37
  imports: {
36
38
  preact: "/js/node_modules/preact/dist/preact.module.js",
37
- "preact/hooks": "/js/node_modules/preact/hooks/dist/hooks.module.js",
38
39
  "preact/compat": "/js/node_modules/preact/compat/dist/compat.module.js",
40
+ "preact/debug": "/js/node_modules/preact/debug/dist/debug.module.js",
41
+ "preact/devtools": "/js/node_modules/preact/devtools/dist/devtools.module.js",
42
+ "preact/hooks": "/js/node_modules/preact/hooks/dist/hooks.module.js",
39
43
  "preact/jsx-runtime": "/js/node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js",
40
44
  "preact-iso": "/js/node_modules/preact-iso/src/index.js",
41
45
  },
@@ -1,5 +1,6 @@
1
1
  import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
2
+ import type { HomeLayoutSection } from "../config.ts";
2
3
  import type { Schema } from "../schema/Schema.ts";
3
4
  import type { TSONDBRequestLocals } from "./index.ts";
4
- export declare const init: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName) => Promise<TSONDBRequestLocals>;
5
+ export declare const init: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName, defaultLocales: string[], homeLayoutSections?: HomeLayoutSection[]) => Promise<TSONDBRequestLocals>;
5
6
  export declare const reinit: (locals: TSONDBRequestLocals) => Promise<void>;
@@ -1,7 +1,7 @@
1
1
  import Debug from "debug";
2
2
  import { simpleGit } from "simple-git";
3
3
  import { isEntityDecl } from "../schema/declarations/EntityDecl.js";
4
- import { resolveTypeArgumentsInDecls } from "../schema/index.js";
4
+ import { resolveTypeArgumentsInDecls, serializeNode } from "../schema/index.js";
5
5
  import { attachGitStatusToInstancesByEntityName, getInstancesByEntityName, } from "../utils/instances.js";
6
6
  import { getReferencesToInstances } from "../utils/references.js";
7
7
  const debug = Debug("tsondb:server:init");
@@ -21,15 +21,16 @@ const getGit = async (dataRootPath) => {
21
21
  return { git };
22
22
  }
23
23
  };
24
- export const init = async (schema, dataRootPath, instancesByEntityName) => {
24
+ export const init = async (schema, dataRootPath, instancesByEntityName, defaultLocales, homeLayoutSections) => {
25
25
  const { git, root: gitRoot, status: gitStatus } = await getGit(dataRootPath);
26
26
  const declarations = resolveTypeArgumentsInDecls(schema.declarations);
27
27
  debug("resolved type arguments in declarations");
28
28
  const entities = declarations.filter(isEntityDecl);
29
29
  debug("found %d entities in declarations", entities.length);
30
30
  const entitiesByName = Object.fromEntries(entities.map(entity => [entity.name, entity]));
31
+ const serializedDeclarationsByName = Object.fromEntries(declarations.map(decl => [decl.name, serializeNode(decl)]));
31
32
  const instancesByEntityNameInMemory = Object.assign({}, instancesByEntityName);
32
- const referencesToInstances = getReferencesToInstances(instancesByEntityName, entitiesByName);
33
+ const referencesToInstances = await getReferencesToInstances(instancesByEntityName, serializedDeclarationsByName);
33
34
  debug("created references cache");
34
35
  if (gitStatus) {
35
36
  attachGitStatusToInstancesByEntityName(instancesByEntityName, dataRootPath, gitRoot, gitStatus);
@@ -52,22 +53,21 @@ export const init = async (schema, dataRootPath, instancesByEntityName) => {
52
53
  entities: entities,
53
54
  instancesByEntityName: instancesByEntityNameInMemory,
54
55
  entitiesByName: entitiesByName,
56
+ serializedDeclarationsByName,
55
57
  localeEntity: schema.localeEntity,
56
58
  getInstanceById,
57
59
  referencesToInstances,
58
- locales: ["de-DE", "en-US"], // TODO: Make this configurable
60
+ defaultLocales,
61
+ locales: defaultLocales,
62
+ homeLayoutSections,
59
63
  };
60
64
  return requestLocals;
61
65
  };
62
66
  export const reinit = async (locals) => {
67
+ locals.instancesByEntityName = await getInstancesByEntityName(locals.dataRoot, locals.entities);
68
+ locals.referencesToInstances = await getReferencesToInstances(locals.instancesByEntityName, locals.serializedDeclarationsByName);
63
69
  const gitStatus = (await locals.git.checkIsRepo()) ? await locals.git.status() : undefined;
64
- const instancesByEntityName = await getInstancesByEntityName(locals.dataRoot, locals.entities);
65
- const referencesToInstances = getReferencesToInstances(instancesByEntityName, locals.entitiesByName);
66
70
  if (locals.gitRoot && gitStatus) {
67
- attachGitStatusToInstancesByEntityName(instancesByEntityName, locals.dataRoot, locals.gitRoot, gitStatus);
71
+ attachGitStatusToInstancesByEntityName(locals.instancesByEntityName, locals.dataRoot, locals.gitRoot, gitStatus);
68
72
  }
69
- Object.assign(locals, {
70
- instancesByEntityName,
71
- referencesToInstances,
72
- });
73
73
  };
@@ -0,0 +1,4 @@
1
+ import { type Result } from "../../../shared/utils/result.ts";
2
+ import type { EntityTaggedInstanceContainerWithChildInstances, UnsafeEntityTaggedInstanceContainerWithChildInstances } from "../../utils/childInstances.ts";
3
+ import type { TSONDBRequestLocals } from "../index.ts";
4
+ export declare const updateLocalsAfterInstanceTreeChangeToReflectDiskState: (locals: TSONDBRequestLocals, parentId: string | undefined, parentEntityName: string, oldChildInstances: EntityTaggedInstanceContainerWithChildInstances[], childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[]) => Promise<Result<void, [code: number, message: string]>>;
@@ -0,0 +1,41 @@
1
+ import { error, ok } from "../../../shared/utils/result.js";
2
+ import { updateLocalsAfterInstanceChangeToReflectDiskState } from "./instanceOperations.js";
3
+ export const updateLocalsAfterInstanceTreeChangeToReflectDiskState = async (locals, parentId, parentEntityName, oldChildInstances, childInstances) => {
4
+ const parentEntity = locals.entitiesByName[parentEntityName];
5
+ if (parentEntity === undefined) {
6
+ return error([400, `Unknown entity "${parentEntityName}"`]);
7
+ }
8
+ if (parentId !== undefined) {
9
+ // existing parent, some child instances may already exist
10
+ for (const oldChildInstance of oldChildInstances) {
11
+ const newChildInstance = childInstances.find(ci => ci.id === oldChildInstance.id);
12
+ if (newChildInstance === undefined) {
13
+ await updateLocalsAfterInstanceChangeToReflectDiskState(locals, oldChildInstance.entityName, oldChildInstance.id, undefined);
14
+ }
15
+ else {
16
+ await updateLocalsAfterInstanceChangeToReflectDiskState(locals, oldChildInstance.entityName, oldChildInstance.id, newChildInstance.content);
17
+ }
18
+ }
19
+ for (const newChildInstance of childInstances.filter(predicate => predicate.id === undefined)) {
20
+ if (newChildInstance.id !== undefined) {
21
+ await updateLocalsAfterInstanceChangeToReflectDiskState(locals, newChildInstance.entityName, newChildInstance.id, newChildInstance.content);
22
+ }
23
+ }
24
+ }
25
+ else {
26
+ // new parent, all child instances are new
27
+ for (const newChildInstance of childInstances) {
28
+ if (newChildInstance.id !== undefined) {
29
+ await updateLocalsAfterInstanceChangeToReflectDiskState(locals, newChildInstance.entityName, newChildInstance.id, newChildInstance.content);
30
+ }
31
+ }
32
+ }
33
+ // check recursively for child instances of child instances
34
+ for (const childInstance of childInstances) {
35
+ const oldChildInstance = childInstance.id
36
+ ? oldChildInstances.find(ci => ci.id === childInstance.id)
37
+ : undefined;
38
+ await updateLocalsAfterInstanceTreeChangeToReflectDiskState(locals, childInstance.id, childInstance.entityName, oldChildInstance ? oldChildInstance.childInstances : [], childInstance.childInstances);
39
+ }
40
+ return ok();
41
+ };
@@ -0,0 +1,8 @@
1
+ import type { InstanceContainer } from "../../../shared/utils/instances.ts";
2
+ import { type Result } from "../../../shared/utils/result.ts";
3
+ import { type CreatedEntityTaggedInstanceContainerWithChildInstances, type UpdatedEntityTaggedInstanceContainerWithChildInstances } from "../../utils/childInstances.ts";
4
+ import type { TSONDBRequestLocals } from "../index.ts";
5
+ export declare const updateLocalsAfterInstanceChangeToReflectDiskState: (locals: TSONDBRequestLocals, entityName: string, instanceId: string, newInstanceContent: unknown) => Promise<InstanceContainer>;
6
+ export declare const createInstance: (locals: TSONDBRequestLocals, instance: CreatedEntityTaggedInstanceContainerWithChildInstances, idQueryParam: unknown) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
7
+ export declare const updateInstance: (locals: TSONDBRequestLocals, instance: UpdatedEntityTaggedInstanceContainerWithChildInstances) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
8
+ export declare const deleteInstance: (locals: TSONDBRequestLocals, entityName: string, instanceId: string) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
@@ -0,0 +1,107 @@
1
+ import { removeAt } from "../../../shared/utils/array.js";
2
+ import { error, isError, ok } from "../../../shared/utils/result.js";
3
+ import { checkWriteInstanceTreePossible, getChildInstances, unsafeApplyInstanceTree, } from "../../utils/childInstances.js";
4
+ import { getFileNameForId } from "../../utils/files.js";
5
+ import { getGitFileStatusFromStatusResult } from "../../utils/git.js";
6
+ import * as Instances from "../../utils/instanceOperations.js";
7
+ import { updateReferencesToInstances } from "../../utils/references.js";
8
+ import { updateLocalsAfterInstanceTreeChangeToReflectDiskState } from "./childInstances.js";
9
+ export const updateLocalsAfterInstanceChangeToReflectDiskState = async (locals, entityName, instanceId, newInstanceContent) => {
10
+ const oldInstances = locals.instancesByEntityName[entityName] ?? [];
11
+ const oldInstanceContainerIndex = oldInstances.findIndex(instance => instance.id === instanceId);
12
+ const oldInstanceContainer = oldInstanceContainerIndex > -1 ? oldInstances[oldInstanceContainerIndex] : undefined;
13
+ const instanceContainer = oldInstanceContainer ?? {
14
+ id: instanceId,
15
+ content: undefined,
16
+ };
17
+ // old content as alternative if instance is deleted to restore old instance container
18
+ instanceContainer.content = newInstanceContent ?? instanceContainer.content;
19
+ instanceContainer.gitStatus =
20
+ locals.gitRoot === undefined
21
+ ? undefined
22
+ : getGitFileStatusFromStatusResult(await locals.git.status(), locals.gitRoot, locals.dataRoot, entityName, getFileNameForId(instanceId));
23
+ if (oldInstanceContainer === undefined) {
24
+ locals.instancesByEntityName[entityName] = [...oldInstances, instanceContainer];
25
+ }
26
+ else if (newInstanceContent === undefined) {
27
+ locals.instancesByEntityName[entityName] = removeAt(oldInstances, oldInstanceContainerIndex);
28
+ }
29
+ Object.assign(locals.referencesToInstances, updateReferencesToInstances(locals.entitiesByName, locals.referencesToInstances, entityName, instanceId, oldInstanceContainer?.content, newInstanceContent));
30
+ return instanceContainer;
31
+ };
32
+ export const createInstance = async (locals, instance, idQueryParam) => {
33
+ const entity = locals.entitiesByName[instance.entityName];
34
+ if (entity === undefined) {
35
+ return error([400, "Entity not found"]);
36
+ }
37
+ const checkTreeResult = checkWriteInstanceTreePossible(locals.entitiesByName, locals.instancesByEntityName, locals.referencesToInstances, instance.id, entity.name, [], instance.childInstances);
38
+ if (isError(checkTreeResult)) {
39
+ return checkTreeResult;
40
+ }
41
+ const res = await Instances.createInstance(locals.dataRoot, locals.localeEntity, locals.instancesByEntityName, entity, instance.content, idQueryParam);
42
+ if (isError(res)) {
43
+ return res;
44
+ }
45
+ const treeRes = await unsafeApplyInstanceTree(locals.dataRoot, locals.entitiesByName, locals.instancesByEntityName, instance.id, instance.entityName, [], instance.childInstances);
46
+ if (isError(treeRes)) {
47
+ return treeRes;
48
+ }
49
+ const newInstanceId = res.value;
50
+ const instanceContainer = await updateLocalsAfterInstanceChangeToReflectDiskState(locals, entity.name, newInstanceId, instance.content);
51
+ await updateLocalsAfterInstanceTreeChangeToReflectDiskState(locals, newInstanceId, entity.name, [], instance.childInstances);
52
+ return ok(instanceContainer);
53
+ };
54
+ export const updateInstance = async (locals, instance) => {
55
+ const instanceContainer = locals.instancesByEntityName[instance.entityName]?.find(instance => instance.id === instance.id);
56
+ if (instanceContainer === undefined) {
57
+ return error([404, "Instance not found"]);
58
+ }
59
+ const entity = locals.entitiesByName[instance.entityName];
60
+ if (entity === undefined) {
61
+ return error([400, "Entity not found"]);
62
+ }
63
+ const oldChildInstances = getChildInstances(locals.instancesByEntityName, entity, instance.id);
64
+ const checkTreeResult = checkWriteInstanceTreePossible(locals.entitiesByName, locals.instancesByEntityName, locals.referencesToInstances, instance.id, entity.name, oldChildInstances, instance.childInstances);
65
+ if (isError(checkTreeResult)) {
66
+ return checkTreeResult;
67
+ }
68
+ const res = await Instances.updateInstance(locals.dataRoot, locals.instancesByEntityName, entity, instance.id, instance.content);
69
+ if (isError(res)) {
70
+ return res;
71
+ }
72
+ const treeRes = await unsafeApplyInstanceTree(locals.dataRoot, locals.entitiesByName, locals.instancesByEntityName, instance.id, instance.entityName, oldChildInstances, instance.childInstances);
73
+ if (isError(treeRes)) {
74
+ return treeRes;
75
+ }
76
+ const newInstanceContainer = await updateLocalsAfterInstanceChangeToReflectDiskState(locals, entity.name, instance.id, instance.content);
77
+ await updateLocalsAfterInstanceTreeChangeToReflectDiskState(locals, instance.id, instance.entityName, oldChildInstances, instance.childInstances);
78
+ return ok(newInstanceContainer);
79
+ };
80
+ export const deleteInstance = async (locals, entityName, instanceId) => {
81
+ const instances = locals.instancesByEntityName[entityName] ?? [];
82
+ const instanceContainerIndex = instances.findIndex(instance => instance.id === instanceId);
83
+ const instanceContainer = instances[instanceContainerIndex];
84
+ if (instanceContainer === undefined) {
85
+ return error([404, "Instance not found"]);
86
+ }
87
+ const entity = locals.entitiesByName[entityName];
88
+ if (entity === undefined) {
89
+ return error([400, "Entity not found"]);
90
+ }
91
+ const oldChildInstances = getChildInstances(locals.instancesByEntityName, entity, instanceContainer.id);
92
+ const checkTreeResult = checkWriteInstanceTreePossible(locals.entitiesByName, locals.instancesByEntityName, locals.referencesToInstances, instanceContainer.id, entityName, oldChildInstances, []);
93
+ if (isError(checkTreeResult)) {
94
+ return checkTreeResult;
95
+ }
96
+ const res = await Instances.deleteInstance(locals.dataRoot, locals.referencesToInstances, entityName, instanceId);
97
+ if (isError(res)) {
98
+ return res;
99
+ }
100
+ const treeRes = await unsafeApplyInstanceTree(locals.dataRoot, locals.entitiesByName, locals.instancesByEntityName, instanceContainer.id, entityName, oldChildInstances, []);
101
+ if (isError(treeRes)) {
102
+ return treeRes;
103
+ }
104
+ const oldInstanceContainer = await updateLocalsAfterInstanceChangeToReflectDiskState(locals, entity.name, instanceContainer.id, undefined);
105
+ await updateLocalsAfterInstanceTreeChangeToReflectDiskState(locals, instanceContainer.id, entityName, oldChildInstances, []);
106
+ return ok(oldInstanceContainer);
107
+ };
@@ -0,0 +1,2 @@
1
+ import type { Request } from "express";
2
+ export declare const getLocalesFromRequest: (req: Request) => string[] | undefined;
@@ -0,0 +1,8 @@
1
+ export const getLocalesFromRequest = (req) => {
2
+ const requestLocales = req.query["locales"];
3
+ return Array.isArray(requestLocales) && requestLocales.every(locale => typeof locale === "string")
4
+ ? requestLocales
5
+ : typeof requestLocales === "string"
6
+ ? [requestLocales]
7
+ : undefined;
8
+ };
@@ -0,0 +1,32 @@
1
+ import type { GitFileStatus } from "../../shared/utils/git.ts";
2
+ import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
3
+ import { type Result } from "../../shared/utils/result.ts";
4
+ import type { EntityDecl } from "../schema/declarations/EntityDecl.ts";
5
+ import { type ReferencesToInstances } from "./references.ts";
6
+ export interface ChildInstanceContainer {
7
+ id?: string;
8
+ content: unknown;
9
+ gitStatus?: GitFileStatus;
10
+ }
11
+ export interface EntityTaggedInstanceContainer {
12
+ entityName: string;
13
+ id: string;
14
+ content: unknown;
15
+ }
16
+ export interface CreatedEntityTaggedInstanceContainerWithChildInstances extends GenEntityTaggedInstanceContainerWithChildInstances<undefined, UnsafeEntityTaggedInstanceContainerWithChildInstances> {
17
+ }
18
+ export interface UpdatedEntityTaggedInstanceContainerWithChildInstances extends GenEntityTaggedInstanceContainerWithChildInstances<string, UnsafeEntityTaggedInstanceContainerWithChildInstances> {
19
+ }
20
+ export interface UnsafeEntityTaggedInstanceContainerWithChildInstances extends GenEntityTaggedInstanceContainerWithChildInstances<string | undefined, UnsafeEntityTaggedInstanceContainerWithChildInstances> {
21
+ }
22
+ export interface EntityTaggedInstanceContainerWithChildInstances extends GenEntityTaggedInstanceContainerWithChildInstances<string, EntityTaggedInstanceContainerWithChildInstances> {
23
+ }
24
+ export interface GenEntityTaggedInstanceContainerWithChildInstances<ID extends string | undefined, C> {
25
+ entityName: string;
26
+ id: ID;
27
+ content: unknown;
28
+ childInstances: C[];
29
+ }
30
+ export declare const getChildInstances: (instancesByEntityName: InstancesByEntityName, parentEntity: EntityDecl, parentId: string) => EntityTaggedInstanceContainerWithChildInstances[];
31
+ export declare const checkWriteInstanceTreePossible: (entitiesByName: Record<string, EntityDecl>, instancesByEntityName: InstancesByEntityName, referencesToInstances: ReferencesToInstances, parentId: string | undefined, parentEntityName: string, oldChildInstances: EntityTaggedInstanceContainerWithChildInstances[], childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[]) => Result<void, [code: number, message: string]>;
32
+ export declare const unsafeApplyInstanceTree: (dataRoot: string, entitiesByName: Record<string, EntityDecl>, instancesByEntityName: InstancesByEntityName, parentId: string | undefined, parentEntityName: string, oldChildInstances: EntityTaggedInstanceContainerWithChildInstances[], childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[]) => Promise<Result<void, [code: number, message: string]>>;
@@ -0,0 +1,164 @@
1
+ import { hasKey } from "../../shared/utils/object.js";
2
+ import { error, isError, ok } from "../../shared/utils/result.js";
3
+ import { reduceNodes } from "../schema/index.js";
4
+ import { isChildEntitiesType } from "../schema/types/references/ChildEntitiesType.js";
5
+ import { checkCreateNonLocaleInstancePossible, checkDeleteInstancePossible, checkUpdateInstancePossible, createNewId, unsafeDeleteInstance, unsafeWriteInstance, } from "./instanceOperations.js";
6
+ import {} from "./references.js";
7
+ const isParentReferenceReferencingParent = (value, parentEntityName, parentId) => {
8
+ if (typeof value === "object" && value !== null && hasKey(value, "kind")) {
9
+ return (value.kind === parentEntityName &&
10
+ hasKey(value, parentEntityName) &&
11
+ value[parentEntityName] === parentId);
12
+ }
13
+ else if (typeof value === "string") {
14
+ return value === parentId;
15
+ }
16
+ else {
17
+ return false;
18
+ }
19
+ };
20
+ export const getChildInstances = (instancesByEntityName, parentEntity, parentId) => {
21
+ const childEntities = reduceNodes((_parentNodes, node, collectedResults) => isChildEntitiesType(node) ? [...collectedResults, node.entity] : collectedResults, [parentEntity], { followIncludes: true });
22
+ return childEntities.flatMap(childEntity => instancesByEntityName[childEntity.name]
23
+ ?.filter(instanceContainer => typeof instanceContainer.content === "object" &&
24
+ instanceContainer.content !== null &&
25
+ hasKey(instanceContainer.content, childEntity.parentReferenceKey) &&
26
+ isParentReferenceReferencingParent(instanceContainer.content[childEntity.parentReferenceKey], parentEntity.name, parentId))
27
+ .map(container => ({
28
+ ...container,
29
+ entityName: childEntity.name,
30
+ childInstances: getChildInstances(instancesByEntityName, childEntity, container.id),
31
+ })) ?? []);
32
+ };
33
+ export const checkWriteInstanceTreePossible = (entitiesByName, instancesByEntityName, referencesToInstances, parentId, parentEntityName, oldChildInstances, childInstances) => {
34
+ const parentEntity = entitiesByName[parentEntityName];
35
+ if (parentEntity === undefined) {
36
+ return error([400, `Unknown entity "${parentEntityName}"`]);
37
+ }
38
+ if (parentId !== undefined) {
39
+ // existing parent, some child instances may already exist
40
+ for (const oldChildInstance of oldChildInstances) {
41
+ const newChildInstance = childInstances.find(ci => ci.id === oldChildInstance.id);
42
+ if (newChildInstance === undefined) {
43
+ const prerequisiteCheckResult = checkDeleteInstancePossible(referencesToInstances, oldChildInstance.id);
44
+ if (isError(prerequisiteCheckResult)) {
45
+ return prerequisiteCheckResult;
46
+ }
47
+ }
48
+ else {
49
+ const entity = entitiesByName[newChildInstance.entityName];
50
+ if (entity === undefined) {
51
+ return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
52
+ }
53
+ const prerequisiteCheckResult = checkUpdateInstancePossible(instancesByEntityName, entity, newChildInstance.content);
54
+ if (isError(prerequisiteCheckResult)) {
55
+ return prerequisiteCheckResult;
56
+ }
57
+ }
58
+ }
59
+ for (const newChildInstance of childInstances.filter(predicate => predicate.id === undefined)) {
60
+ const entity = entitiesByName[newChildInstance.entityName];
61
+ if (entity === undefined) {
62
+ return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
63
+ }
64
+ const prerequisiteCheckResult = checkCreateNonLocaleInstancePossible(instancesByEntityName, entity, newChildInstance.content);
65
+ if (isError(prerequisiteCheckResult)) {
66
+ return prerequisiteCheckResult;
67
+ }
68
+ }
69
+ }
70
+ else {
71
+ // new parent, all child instances are new
72
+ for (const newChildInstance of childInstances) {
73
+ const entity = entitiesByName[newChildInstance.entityName];
74
+ if (entity === undefined) {
75
+ return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
76
+ }
77
+ const prerequisiteCheckResult = checkCreateNonLocaleInstancePossible(instancesByEntityName, entity, newChildInstance.content);
78
+ if (isError(prerequisiteCheckResult)) {
79
+ return prerequisiteCheckResult;
80
+ }
81
+ }
82
+ }
83
+ // check recursively for child instances of child instances
84
+ for (const childInstance of childInstances) {
85
+ const oldChildInstance = childInstance.id
86
+ ? oldChildInstances.find(ci => ci.id === childInstance.id)
87
+ : undefined;
88
+ const prerequisiteCheckResult = checkWriteInstanceTreePossible(entitiesByName, instancesByEntityName, referencesToInstances, childInstance.id, childInstance.entityName, oldChildInstance ? oldChildInstance.childInstances : [], childInstance.childInstances);
89
+ if (isError(prerequisiteCheckResult)) {
90
+ return prerequisiteCheckResult;
91
+ }
92
+ }
93
+ return ok();
94
+ };
95
+ export const unsafeApplyInstanceTree = async (dataRoot, entitiesByName, instancesByEntityName, parentId, parentEntityName, oldChildInstances, childInstances) => {
96
+ const parentEntity = entitiesByName[parentEntityName];
97
+ if (parentEntity === undefined) {
98
+ return error([400, `Unknown entity "${parentEntityName}"`]);
99
+ }
100
+ if (parentId !== undefined) {
101
+ // existing parent, some child instances may already exist
102
+ for (const oldChildInstance of oldChildInstances) {
103
+ const newChildInstance = childInstances.find(ci => ci.id === oldChildInstance.id);
104
+ if (newChildInstance === undefined) {
105
+ const oldGrandChildInstancesResult = await unsafeApplyInstanceTree(dataRoot, entitiesByName, instancesByEntityName, oldChildInstance.id, oldChildInstance.entityName, oldChildInstance.childInstances, []);
106
+ if (isError(oldGrandChildInstancesResult)) {
107
+ return oldGrandChildInstancesResult;
108
+ }
109
+ const prerequisiteCheckResult = await unsafeDeleteInstance(dataRoot, oldChildInstance.entityName, oldChildInstance.id);
110
+ if (isError(prerequisiteCheckResult)) {
111
+ return prerequisiteCheckResult;
112
+ }
113
+ }
114
+ else {
115
+ const entity = entitiesByName[newChildInstance.entityName];
116
+ if (entity === undefined) {
117
+ return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
118
+ }
119
+ const prerequisiteCheckResult = await unsafeWriteInstance(dataRoot, entity, oldChildInstance.id, newChildInstance.content);
120
+ if (isError(prerequisiteCheckResult)) {
121
+ return prerequisiteCheckResult;
122
+ }
123
+ }
124
+ }
125
+ for (const newChildInstance of childInstances.filter(predicate => predicate.id === undefined)) {
126
+ const entity = entitiesByName[newChildInstance.entityName];
127
+ if (entity === undefined) {
128
+ return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
129
+ }
130
+ const newInstanceID = createNewId();
131
+ const prerequisiteCheckResult = await unsafeWriteInstance(dataRoot, entity, newInstanceID, newChildInstance.content);
132
+ if (isError(prerequisiteCheckResult)) {
133
+ return prerequisiteCheckResult;
134
+ }
135
+ newChildInstance.id = newInstanceID;
136
+ }
137
+ }
138
+ else {
139
+ // new parent, all child instances are new
140
+ for (const newChildInstance of childInstances) {
141
+ const entity = entitiesByName[newChildInstance.entityName];
142
+ if (entity === undefined) {
143
+ return error([400, `Unknown entity "${newChildInstance.entityName}"`]);
144
+ }
145
+ const newInstanceID = createNewId();
146
+ const prerequisiteCheckResult = await unsafeWriteInstance(dataRoot, entity, newInstanceID, newChildInstance.content);
147
+ if (isError(prerequisiteCheckResult)) {
148
+ return prerequisiteCheckResult;
149
+ }
150
+ newChildInstance.id = newInstanceID;
151
+ }
152
+ }
153
+ // check recursively for child instances of child instances
154
+ for (const childInstance of childInstances) {
155
+ const oldChildInstance = childInstance.id
156
+ ? oldChildInstances.find(ci => ci.id === childInstance.id)
157
+ : undefined;
158
+ const prerequisiteCheckResult = await unsafeApplyInstanceTree(dataRoot, entitiesByName, instancesByEntityName, childInstance.id, childInstance.entityName, oldChildInstance ? oldChildInstance.childInstances : [], childInstance.childInstances);
159
+ if (isError(prerequisiteCheckResult)) {
160
+ return prerequisiteCheckResult;
161
+ }
162
+ }
163
+ return ok();
164
+ };
@@ -1,3 +1,4 @@
1
1
  import { type EntityDecl } from "../../node/schema/index.ts";
2
2
  import type { GetInstanceById } from "../../node/server/index.ts";
3
- export declare const getDisplayNameFromEntityInstance: (entity: EntityDecl, instance: unknown, getInstanceById: GetInstanceById, locales?: string[], defaultName?: string, useCustomizer?: boolean) => string;
3
+ import { type DisplayNameResult } from "../../shared/utils/displayName.ts";
4
+ export declare const getDisplayNameFromEntityInstance: (entity: EntityDecl, instance: unknown, getInstanceById: GetInstanceById, locales: string[], defaultName?: string, useCustomizer?: boolean) => DisplayNameResult;