tsondb 0.3.0 → 0.4.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 (205) hide show
  1. package/LICENSE +385 -0
  2. package/README.md +173 -1
  3. package/lib/bin/tsondb.d.ts +8 -0
  4. package/lib/bin/tsondb.js +83 -0
  5. package/lib/index.d.ts +2 -1
  6. package/lib/index.js +1 -1
  7. package/lib/{Schema.d.ts → node/Schema.d.ts} +2 -2
  8. package/lib/{Schema.js → node/Schema.js} +1 -1
  9. package/lib/node/index.d.ts +8 -0
  10. package/lib/node/index.js +62 -0
  11. package/lib/{renderers → node/renderers}/Output.d.ts +1 -1
  12. package/lib/{renderers → node/renderers}/jsonschema/index.d.ts +2 -2
  13. package/lib/{renderers → node/renderers}/jsonschema/index.js +2 -2
  14. package/lib/{renderers → node/renderers}/jsonschema/render.d.ts +1 -1
  15. package/lib/{renderers → node/renderers}/jsonschema/render.js +9 -9
  16. package/lib/{renderers → node/renderers}/ts/index.d.ts +2 -2
  17. package/lib/{renderers → node/renderers}/ts/index.js +2 -2
  18. package/lib/{renderers → node/renderers}/ts/render.d.ts +1 -1
  19. package/lib/{renderers → node/renderers}/ts/render.js +9 -8
  20. package/lib/{schema → node/schema}/Node.d.ts +4 -4
  21. package/lib/{schema → node/schema}/Node.js +5 -5
  22. package/lib/{schema/parameters → node/schema}/TypeParameter.d.ts +3 -2
  23. package/lib/{schema/parameters → node/schema}/TypeParameter.js +2 -2
  24. package/lib/{schema → node/schema}/declarations/Declaration.d.ts +10 -11
  25. package/lib/{schema → node/schema}/declarations/Declaration.js +7 -5
  26. package/lib/{schema → node/schema}/declarations/EntityDecl.d.ts +10 -9
  27. package/lib/{schema → node/schema}/declarations/EntityDecl.js +2 -2
  28. package/lib/{schema → node/schema}/declarations/EnumDecl.d.ts +9 -7
  29. package/lib/{schema → node/schema}/declarations/EnumDecl.js +3 -3
  30. package/lib/{schema → node/schema}/declarations/TypeAliasDecl.d.ts +8 -7
  31. package/lib/{schema → node/schema}/declarations/TypeAliasDecl.js +3 -3
  32. package/lib/{schema → node/schema}/index.d.ts +4 -4
  33. package/lib/{schema → node/schema}/index.js +3 -4
  34. package/lib/node/schema/types/Type.d.ts +48 -0
  35. package/lib/{schema → node/schema}/types/Type.js +41 -25
  36. package/lib/{schema → node/schema}/types/generic/ArrayType.d.ts +5 -4
  37. package/lib/{schema → node/schema}/types/generic/ArrayType.js +3 -3
  38. package/lib/{schema → node/schema}/types/generic/EnumType.d.ts +5 -4
  39. package/lib/{schema → node/schema}/types/generic/EnumType.js +4 -2
  40. package/lib/{schema → node/schema}/types/generic/ObjectType.d.ts +8 -7
  41. package/lib/{schema → node/schema}/types/generic/ObjectType.js +8 -4
  42. package/lib/{schema → node/schema}/types/primitives/BooleanType.d.ts +4 -3
  43. package/lib/{schema → node/schema}/types/primitives/DateType.d.ts +5 -4
  44. package/lib/{schema → node/schema}/types/primitives/DateType.js +1 -1
  45. package/lib/{schema → node/schema}/types/primitives/FloatType.d.ts +5 -4
  46. package/lib/{schema → node/schema}/types/primitives/FloatType.js +1 -1
  47. package/lib/{schema → node/schema}/types/primitives/IntegerType.d.ts +5 -4
  48. package/lib/{schema → node/schema}/types/primitives/IntegerType.js +1 -1
  49. package/lib/{schema → node/schema}/types/primitives/NumericType.d.ts +2 -2
  50. package/lib/node/schema/types/primitives/PrimitiveType.d.ts +6 -0
  51. package/lib/{schema → node/schema}/types/primitives/StringType.d.ts +5 -4
  52. package/lib/{schema → node/schema}/types/primitives/StringType.js +1 -1
  53. package/lib/{schema → node/schema}/types/references/IncludeIdentifierType.d.ts +9 -8
  54. package/lib/{schema → node/schema}/types/references/IncludeIdentifierType.js +1 -1
  55. package/lib/{schema → node/schema}/types/references/NestedEntityMapType.d.ts +10 -9
  56. package/lib/{schema → node/schema}/types/references/NestedEntityMapType.js +4 -4
  57. package/lib/{schema → node/schema}/types/references/ReferenceIdentifierType.d.ts +7 -6
  58. package/lib/{schema → node/schema}/types/references/ReferenceIdentifierType.js +1 -1
  59. package/lib/node/schema/types/references/TypeArgumentType.d.ts +23 -0
  60. package/lib/node/schema/types/references/TypeArgumentType.js +19 -0
  61. package/lib/node/schema/validation/type.d.ts +4 -0
  62. package/lib/{server → node/server}/api/declarations.js +1 -1
  63. package/lib/{server → node/server}/api/git.js +14 -10
  64. package/lib/{server → node/server}/api/instanceOperations.d.ts +3 -3
  65. package/lib/{server → node/server}/api/instanceOperations.js +13 -4
  66. package/lib/{server → node/server}/api/instances.js +7 -3
  67. package/lib/node/server/index.d.ts +28 -0
  68. package/lib/{server → node/server}/index.js +6 -6
  69. package/lib/node/server/init.d.ts +5 -0
  70. package/lib/{server → node/server}/init.js +8 -8
  71. package/lib/{utils → node/utils}/error.js +4 -1
  72. package/lib/{utils → node/utils}/git.d.ts +2 -2
  73. package/lib/{utils → node/utils}/instances.d.ts +3 -3
  74. package/lib/{utils → node/utils}/instances.js +8 -6
  75. package/lib/{utils → node/utils}/references.d.ts +2 -2
  76. package/lib/{utils → node/utils}/references.js +3 -3
  77. package/lib/{utils → node/utils}/render.js +1 -1
  78. package/lib/shared/api.d.ts +8 -2
  79. package/lib/shared/utils/array.js +12 -9
  80. package/lib/shared/utils/displayName.d.ts +1 -1
  81. package/lib/shared/utils/displayName.js +4 -2
  82. package/lib/shared/utils/instances.d.ts +2 -2
  83. package/lib/{utils → shared/utils}/lazy.js +1 -1
  84. package/lib/shared/utils/markdown.js +6 -6
  85. package/lib/shared/utils/object.d.ts +3 -0
  86. package/lib/shared/utils/object.js +1 -0
  87. package/lib/{utils → shared/utils}/result.js +2 -2
  88. package/lib/shared/utils/string.js +7 -4
  89. package/lib/shared/utils/validation.js +3 -2
  90. package/lib/shared/validation/number.js +3 -3
  91. package/lib/shared/validation/object.js +4 -3
  92. package/lib/shared/validation/string.js +12 -7
  93. package/lib/{client → web}/api.d.ts +2 -2
  94. package/lib/{client → web}/api.js +4 -2
  95. package/lib/web/components/Git.d.ts +2 -0
  96. package/lib/{client → web}/components/Git.js +58 -14
  97. package/lib/{client → web}/components/Layout.d.ts +1 -1
  98. package/lib/{client → web}/components/Layout.js +1 -1
  99. package/lib/web/components/Select.d.ts +3 -0
  100. package/lib/web/components/Select.js +5 -0
  101. package/lib/web/components/typeInputs/ArrayTypeInput.d.ts +13 -0
  102. package/lib/{client → web}/components/typeInputs/ArrayTypeInput.js +7 -1
  103. package/lib/{client → web}/components/typeInputs/BooleanTypeInput.d.ts +2 -2
  104. package/lib/{client → web}/components/typeInputs/DateTypeInput.d.ts +2 -2
  105. package/lib/web/components/typeInputs/EnumTypeInput.d.ts +13 -0
  106. package/lib/{client → web}/components/typeInputs/EnumTypeInput.js +1 -1
  107. package/lib/{client → web}/components/typeInputs/FloatTypeInput.d.ts +2 -2
  108. package/lib/web/components/typeInputs/GenericTypeArgumentIdentifierTypeInput.d.ts +7 -0
  109. package/lib/{client → web}/components/typeInputs/GenericTypeArgumentIdentifierTypeInput.js +1 -1
  110. package/lib/web/components/typeInputs/IncludeIdentifierTypeInput.d.ts +13 -0
  111. package/lib/{client → web}/components/typeInputs/IntegerTypeInput.d.ts +2 -2
  112. package/lib/web/components/typeInputs/NestedEntityMapTypeInput.d.ts +13 -0
  113. package/lib/{client → web}/components/typeInputs/NestedEntityMapTypeInput.js +7 -2
  114. package/lib/{client → web}/components/typeInputs/ObjectTypeInput.d.ts +4 -4
  115. package/lib/{client → web}/components/typeInputs/ObjectTypeInput.js +4 -1
  116. package/lib/{client → web}/components/typeInputs/ReferenceIdentifierTypeInput.d.ts +3 -3
  117. package/lib/web/components/typeInputs/ReferenceIdentifierTypeInput.js +11 -0
  118. package/lib/{client → web}/components/typeInputs/StringTypeInput.d.ts +2 -2
  119. package/lib/web/components/typeInputs/TypeInput.d.ts +13 -0
  120. package/lib/{client → web}/components/typeInputs/TypeInput.js +3 -3
  121. package/lib/{client → web}/components/typeInputs/utils/Markdown.d.ts +1 -1
  122. package/lib/{client → web}/components/typeInputs/utils/Markdown.js +2 -2
  123. package/lib/{client → web}/components/typeInputs/utils/MismatchingTypeError.d.ts +1 -1
  124. package/lib/{client → web}/components/typeInputs/utils/ValidationErrors.d.ts +1 -1
  125. package/lib/web/hooks/useAPIResource.d.ts +1 -0
  126. package/lib/{client → web}/hooks/useEntityFromRoute.d.ts +1 -1
  127. package/lib/{client → web}/hooks/useEntityFromRoute.js +1 -1
  128. package/lib/{client → web}/hooks/useInstanceNamesByEntity.d.ts +1 -1
  129. package/lib/{client → web}/hooks/useInstanceNamesByEntity.js +8 -6
  130. package/lib/web/hooks/useMappedAPIResource.d.ts +1 -0
  131. package/lib/{client → web}/hooks/useMappedAPIResource.js +7 -9
  132. package/lib/{client → web}/hooks/useSecondaryDeclarations.d.ts +1 -1
  133. package/lib/{client → web}/hooks/useSecondaryDeclarations.js +4 -2
  134. package/lib/{client → web}/routes/CreateInstance.d.ts +1 -1
  135. package/lib/{client → web}/routes/CreateInstance.js +4 -2
  136. package/lib/web/routes/Entity.d.ts +2 -0
  137. package/lib/{client → web}/routes/Entity.js +5 -3
  138. package/lib/web/routes/Home.d.ts +2 -0
  139. package/lib/{client → web}/routes/Instance.d.ts +1 -1
  140. package/lib/{client → web}/routes/Instance.js +14 -8
  141. package/lib/{client → web}/routes/NotFound.d.ts +1 -1
  142. package/lib/web/utils/typeSkeleton.d.ts +3 -0
  143. package/lib/{client → web}/utils/typeSkeleton.js +4 -1
  144. package/package.json +31 -18
  145. package/lib/ModelContainer.d.ts +0 -17
  146. package/lib/ModelContainer.js +0 -65
  147. package/lib/client/components/Git.d.ts +0 -2
  148. package/lib/client/components/Select.d.ts +0 -3
  149. package/lib/client/components/Select.js +0 -2
  150. package/lib/client/components/typeInputs/ArrayTypeInput.d.ts +0 -13
  151. package/lib/client/components/typeInputs/EnumTypeInput.d.ts +0 -13
  152. package/lib/client/components/typeInputs/GenericTypeArgumentIdentifierTypeInput.d.ts +0 -7
  153. package/lib/client/components/typeInputs/IncludeIdentifierTypeInput.d.ts +0 -13
  154. package/lib/client/components/typeInputs/NestedEntityMapTypeInput.d.ts +0 -13
  155. package/lib/client/components/typeInputs/ReferenceIdentifierTypeInput.js +0 -9
  156. package/lib/client/components/typeInputs/TypeInput.d.ts +0 -13
  157. package/lib/client/hooks/useAPIResource.d.ts +0 -1
  158. package/lib/client/hooks/useMappedAPIResource.d.ts +0 -1
  159. package/lib/client/routes/Entity.d.ts +0 -2
  160. package/lib/client/routes/Home.d.ts +0 -2
  161. package/lib/client/utils/typeSkeleton.d.ts +0 -3
  162. package/lib/schema/types/Type.d.ts +0 -48
  163. package/lib/schema/types/primitives/PrimitiveType.d.ts +0 -6
  164. package/lib/schema/types/references/GenericArgumentIdentifierType.d.ts +0 -22
  165. package/lib/schema/types/references/GenericArgumentIdentifierType.js +0 -19
  166. package/lib/schema/validation/type.d.ts +0 -4
  167. package/lib/server/index.d.ts +0 -29
  168. package/lib/server/init.d.ts +0 -5
  169. package/lib/tsconfig.tsbuildinfo +0 -1
  170. package/lib/utils/object.d.ts +0 -3
  171. package/lib/utils/object.js +0 -1
  172. /package/lib/{renderers → node/renderers}/Output.js +0 -0
  173. /package/lib/{schema → node/schema}/types/primitives/BooleanType.js +0 -0
  174. /package/lib/{schema → node/schema}/types/primitives/NumericType.js +0 -0
  175. /package/lib/{schema → node/schema}/types/primitives/PrimitiveType.js +0 -0
  176. /package/lib/{schema → node/schema}/validation/options.d.ts +0 -0
  177. /package/lib/{schema → node/schema}/validation/options.js +0 -0
  178. /package/lib/{schema → node/schema}/validation/type.js +0 -0
  179. /package/lib/{server → node/server}/api/declarations.d.ts +0 -0
  180. /package/lib/{server → node/server}/api/git.d.ts +0 -0
  181. /package/lib/{server → node/server}/api/index.d.ts +0 -0
  182. /package/lib/{server → node/server}/api/index.js +0 -0
  183. /package/lib/{server → node/server}/api/instances.d.ts +0 -0
  184. /package/lib/{utils → node/utils}/error.d.ts +0 -0
  185. /package/lib/{utils → node/utils}/git.js +0 -0
  186. /package/lib/{utils → node/utils}/path.d.ts +0 -0
  187. /package/lib/{utils → node/utils}/path.js +0 -0
  188. /package/lib/{utils → node/utils}/render.d.ts +0 -0
  189. /package/lib/{utils → shared/utils}/enum.d.ts +0 -0
  190. /package/lib/{utils → shared/utils}/enum.js +0 -0
  191. /package/lib/{utils → shared/utils}/lazy.d.ts +0 -0
  192. /package/lib/{utils → shared/utils}/result.d.ts +0 -0
  193. /package/lib/{client → web}/components/typeInputs/BooleanTypeInput.js +0 -0
  194. /package/lib/{client → web}/components/typeInputs/DateTypeInput.js +0 -0
  195. /package/lib/{client → web}/components/typeInputs/FloatTypeInput.js +0 -0
  196. /package/lib/{client → web}/components/typeInputs/IncludeIdentifierTypeInput.js +0 -0
  197. /package/lib/{client → web}/components/typeInputs/IntegerTypeInput.js +0 -0
  198. /package/lib/{client → web}/components/typeInputs/StringTypeInput.js +0 -0
  199. /package/lib/{client → web}/components/typeInputs/utils/MismatchingTypeError.js +0 -0
  200. /package/lib/{client → web}/components/typeInputs/utils/ValidationErrors.js +0 -0
  201. /package/lib/{client → web}/hooks/useAPIResource.js +0 -0
  202. /package/lib/{client → web}/index.d.ts +0 -0
  203. /package/lib/{client → web}/index.js +0 -0
  204. /package/lib/{client → web}/routes/Home.js +0 -0
  205. /package/lib/{client → web}/routes/NotFound.js +0 -0
@@ -1,10 +1,10 @@
1
1
  import Debug from "debug";
2
2
  import express from "express";
3
+ import { isOk } from "../../../shared/utils/result.js";
3
4
  import { serializeDecl } from "../../schema/declarations/Declaration.js";
4
5
  import { isEntityDecl } from "../../schema/declarations/EntityDecl.js";
5
6
  import { isEnumDecl } from "../../schema/declarations/EnumDecl.js";
6
7
  import { isTypeAliasDecl } from "../../schema/declarations/TypeAliasDecl.js";
7
- import { isOk } from "../../utils/result.js";
8
8
  import { createInstance, deleteInstance, updateInstance } from "./instanceOperations.js";
9
9
  const debug = Debug("tsondb:server:api:declarations");
10
10
  export const declarationsApi = express.Router();
@@ -1,9 +1,9 @@
1
1
  import Debug from "debug";
2
- import express from "express";
2
+ import express, {} from "express";
3
3
  import { join } from "node:path";
4
+ import { hasFileChanges } from "../../../shared/utils/git.js";
5
+ import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
4
6
  import { serializeEntityDecl } from "../../schema/index.js";
5
- import { hasFileChanges } from "../../shared/utils/git.js";
6
- import { getInstanceContainerOverview } from "../../shared/utils/instances.js";
7
7
  import { attachGitStatusToInstancesByEntityName } from "../../utils/instances.js";
8
8
  import { reinit } from "../init.js";
9
9
  const debug = Debug("tsondb:server:api:git");
@@ -19,7 +19,9 @@ gitApi.use((req, res, next) => {
19
19
  gitApi.get("/status", async (req, res) => {
20
20
  const locales = (Array.isArray(req.query["locales"]) ? req.query["locales"] : [req.query["locales"]]).filter(locale => typeof locale === "string");
21
21
  const status = await req.git.status();
22
- attachGitStatusToInstancesByEntityName(req.instancesByEntityName, req.dataRoot, req.gitRoot, status);
22
+ attachGitStatusToInstancesByEntityName(req.instancesByEntityName, req.dataRoot,
23
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
24
+ req.gitRoot, status);
23
25
  const body = {
24
26
  commitsAhead: status.ahead,
25
27
  commitsBehind: status.behind,
@@ -27,7 +29,9 @@ gitApi.get("/status", async (req, res) => {
27
29
  entityName,
28
30
  instances
29
31
  .filter(instance => hasFileChanges(instance.gitStatus))
30
- .map(instance => getInstanceContainerOverview(serializeEntityDecl(req.entitiesByName[entityName]), instance, locales)),
32
+ .map(instance => getInstanceContainerOverview(
33
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
34
+ serializeEntityDecl(req.entitiesByName[entityName]), instance, locales)),
31
35
  ])),
32
36
  };
33
37
  res.json(body);
@@ -118,7 +122,7 @@ gitApi.post("/commit", async (req, res) => {
118
122
  await req.git.commit(message);
119
123
  res.status(200).send("Commit successful");
120
124
  }
121
- catch (error) {
125
+ catch {
122
126
  res.status(500).send("Commit failed");
123
127
  }
124
128
  });
@@ -127,7 +131,7 @@ gitApi.post("/push", async (req, res) => {
127
131
  await req.git.push();
128
132
  res.status(200).send("Push successful");
129
133
  }
130
- catch (error) {
134
+ catch {
131
135
  res.status(500).send("Push failed");
132
136
  }
133
137
  });
@@ -136,7 +140,7 @@ gitApi.post("/pull", async (req, res) => {
136
140
  await req.git.pull();
137
141
  res.status(200).send("Pull successful");
138
142
  }
139
- catch (error) {
143
+ catch {
140
144
  res.status(500).send("Pull failed");
141
145
  }
142
146
  });
@@ -159,7 +163,7 @@ gitApi.post("/branch", async (req, res) => {
159
163
  await reinit(req);
160
164
  res.status(200).send(`Creation of branch "${branchName}" successful`);
161
165
  }
162
- catch (error) {
166
+ catch {
163
167
  res.status(500).send(`Creation of branch "${branchName}" failed`);
164
168
  }
165
169
  });
@@ -168,7 +172,7 @@ gitApi.post("/branch/:branchName", async (req, res) => {
168
172
  await req.git.checkout(req.params.branchName);
169
173
  res.status(200).send(`Switch to branch "${req.params.branchName}" successful`);
170
174
  }
171
- catch (error) {
175
+ catch {
172
176
  res.status(500).send(`Switch to branch "${req.params.branchName}" failed`);
173
177
  }
174
178
  });
@@ -1,6 +1,6 @@
1
- import { InstanceContainer } from "../../shared/utils/instances.js";
2
- import { Result } from "../../utils/result.js";
3
- import { TSONDBRequestLocals } from "../index.js";
1
+ import type { InstanceContainer } from "../../../shared/utils/instances.js";
2
+ import type { Result } from "../../../shared/utils/result.js";
3
+ import type { TSONDBRequestLocals } from "../index.js";
4
4
  export declare const createInstance: (locals: TSONDBRequestLocals, entityName: string, instance: unknown, idQueryParam: unknown) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
5
5
  export declare const updateInstance: (locals: TSONDBRequestLocals, entityName: string, instanceId: string, instance: unknown) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
6
6
  export declare const deleteInstance: (locals: TSONDBRequestLocals, entityName: string, instanceId: string) => Promise<Result<InstanceContainer, [code: number, message: string]>>;
@@ -1,16 +1,19 @@
1
1
  import { rm, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { v4 as uuidv4 } from "uuid";
4
+ import { removeAt } from "../../../shared/utils/array.js";
5
+ import { error, ok } from "../../../shared/utils/result.js";
4
6
  import { validateEntityDecl } from "../../schema/declarations/EntityDecl.js";
5
7
  import { formatValue } from "../../schema/index.js";
6
8
  import { createValidators } from "../../schema/Node.js";
7
- import { removeAt } from "../../shared/utils/array.js";
8
9
  import { getErrorMessageForDisplay } from "../../utils/error.js";
9
10
  import { getGitFileStatusFromStatusResult } from "../../utils/git.js";
10
11
  import { updateReferencesToInstances } from "../../utils/references.js";
11
- import { error, ok } from "../../utils/result.js";
12
12
  export const createInstance = async (locals, entityName, instance, idQueryParam) => {
13
13
  const entity = locals.entitiesByName[entityName];
14
+ if (entity === undefined) {
15
+ return error([400, "Entity not found"]);
16
+ }
14
17
  const validationErrors = validateEntityDecl(createValidators(locals.instancesByEntityName), entity, instance);
15
18
  if (validationErrors.length > 0) {
16
19
  return error([400, validationErrors.map(getErrorMessageForDisplay).join("\n\n")]);
@@ -20,7 +23,7 @@ export const createInstance = async (locals, entityName, instance, idQueryParam)
20
23
  }
21
24
  const id = locals.localeEntity === entity ? idQueryParam : uuidv4();
22
25
  if (locals.localeEntity === entity &&
23
- locals.instancesByEntityName[entity.name].some(instance => instance.id === id)) {
26
+ locals.instancesByEntityName[entity.name]?.some(instance => instance.id === id)) {
24
27
  return error([400, `Duplicate id "${id}" for locale entity`]);
25
28
  }
26
29
  const fileName = `${id}.json`;
@@ -46,6 +49,9 @@ export const updateInstance = async (locals, entityName, instanceId, instance) =
46
49
  return error([404, "Instance not found"]);
47
50
  }
48
51
  const entity = locals.entitiesByName[entityName];
52
+ if (entity === undefined) {
53
+ return error([400, "Entity not found"]);
54
+ }
49
55
  const validationErrors = validateEntityDecl(createValidators(locals.instancesByEntityName), entity, instance);
50
56
  if (validationErrors.length > 0) {
51
57
  return error([400, validationErrors.map(getErrorMessageForDisplay).join("\n\n")]);
@@ -77,6 +83,9 @@ export const deleteInstance = async (locals, entityName, instanceId) => {
77
83
  return ok(instanceContainer);
78
84
  }
79
85
  catch (err) {
80
- return error([500, `Failed to delete instance: ${err}`]);
86
+ return error([
87
+ 500,
88
+ `Failed to delete instance: ${err instanceof Error ? err.toString() : String(err)}`,
89
+ ]);
81
90
  }
82
91
  };
@@ -1,7 +1,7 @@
1
1
  import Debug from "debug";
2
2
  import express from "express";
3
+ import { getDisplayNameFromEntityInstance } from "../../../shared/utils/displayName.js";
3
4
  import { serializeEntityDecl } from "../../schema/declarations/EntityDecl.js";
4
- import { getDisplayNameFromEntityInstance } from "../../shared/utils/displayName.js";
5
5
  const debug = Debug("tsondb:server:api:instances");
6
6
  export const instancesApi = express.Router();
7
7
  instancesApi.use((req, _res, next) => {
@@ -11,11 +11,15 @@ instancesApi.use((req, _res, next) => {
11
11
  instancesApi.get("/", (req, res) => {
12
12
  const locales = (Array.isArray(req.query["locales"]) ? req.query["locales"] : [req.query["locales"]]).filter(locale => typeof locale === "string");
13
13
  const body = {
14
- instances: Object.fromEntries(Object.entries(req.instancesByEntityName).map(([entityName, instances]) => [
14
+ instances: Object.fromEntries(Object.entries(req.instancesByEntityName)
15
+ .filter(([entityName]) => Object.hasOwn(req.entitiesByName, entityName))
16
+ .map(([entityName, instances]) => [
15
17
  entityName,
16
18
  instances.map(instance => ({
17
19
  id: instance.id,
18
- name: getDisplayNameFromEntityInstance(serializeEntityDecl(req.entitiesByName[entityName]), instance.content, instance.id, locales),
20
+ name: getDisplayNameFromEntityInstance(
21
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
22
+ serializeEntityDecl(req.entitiesByName[entityName]), instance.content, instance.id, locales),
19
23
  })),
20
24
  ])),
21
25
  };
@@ -0,0 +1,28 @@
1
+ import type { SimpleGit } from "simple-git";
2
+ import type { InstancesByEntityName } from "../../shared/utils/instances.js";
3
+ import type { Schema } from "../Schema.ts";
4
+ import type { Decl } from "../schema/declarations/Declaration.js";
5
+ import type { EntityDecl } from "../schema/declarations/EntityDecl.js";
6
+ import type { ReferencesToInstances } from "../utils/references.js";
7
+ export type ServerOptions = {
8
+ name: string;
9
+ port: number;
10
+ };
11
+ export interface TSONDBRequestLocals {
12
+ git: SimpleGit;
13
+ gitRoot: string | undefined;
14
+ dataRoot: string;
15
+ declarations: readonly Decl[];
16
+ entities: readonly EntityDecl[];
17
+ instancesByEntityName: InstancesByEntityName;
18
+ entitiesByName: Record<string, EntityDecl>;
19
+ localeEntity?: EntityDecl;
20
+ referencesToInstances: ReferencesToInstances;
21
+ }
22
+ declare global {
23
+ namespace Express {
24
+ interface Request extends TSONDBRequestLocals {
25
+ }
26
+ }
27
+ }
28
+ export declare const createServer: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName, options?: Partial<ServerOptions>) => Promise<void>;
@@ -8,7 +8,7 @@ const defaultOptions = {
8
8
  name: "tsondb server",
9
9
  port: 3000,
10
10
  };
11
- export const createServer = async (modelContainer, instancesByEntityName, options) => {
11
+ export const createServer = async (schema, dataRootPath, instancesByEntityName, options) => {
12
12
  const { name, port } = { ...defaultOptions, ...options };
13
13
  const app = express();
14
14
  app.use(express.static(join(import.meta.dirname, "../../public")));
@@ -16,7 +16,7 @@ export const createServer = async (modelContainer, instancesByEntityName, option
16
16
  app.use("/js/client", express.static(join(import.meta.dirname, "../../lib/client")));
17
17
  app.use("/js/shared", express.static(join(import.meta.dirname, "../../lib/shared")));
18
18
  app.use(express.json());
19
- const requestLocals = await init(modelContainer, Object.assign({}, instancesByEntityName));
19
+ const requestLocals = await init(schema, dataRootPath, Object.assign({}, instancesByEntityName));
20
20
  app.use((req, _res, next) => {
21
21
  Object.assign(req, requestLocals);
22
22
  next();
@@ -30,9 +30,6 @@ export const createServer = async (modelContainer, instancesByEntityName, option
30
30
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
31
31
  <title>TSONDB</title>
32
32
  <link rel="stylesheet" href="/css/styles.css">
33
- </head>
34
- <body>
35
- <div id="app"></div>
36
33
  <script type="importmap">
37
34
  {
38
35
  "imports": {
@@ -43,11 +40,14 @@ export const createServer = async (modelContainer, instancesByEntityName, option
43
40
  }
44
41
  }
45
42
  </script>
43
+ </head>
44
+ <body>
45
+ <div id="app"></div>
46
46
  <script type="module" src="/js/client/index.js"></script>
47
47
  </body>
48
48
  </html>`);
49
49
  });
50
50
  app.listen(port, () => {
51
- debug(`${name} listening on http://localhost:${port}`);
51
+ debug(`${name} listening on http://localhost:${port.toString()}`);
52
52
  });
53
53
  };
@@ -0,0 +1,5 @@
1
+ import type { InstancesByEntityName } from "../../shared/utils/instances.js";
2
+ import type { Schema } from "../Schema.ts";
3
+ import type { TSONDBRequestLocals } from "./index.js";
4
+ export declare const init: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName) => Promise<TSONDBRequestLocals>;
5
+ export declare const reinit: (locals: TSONDBRequestLocals) => Promise<void>;
@@ -3,8 +3,8 @@ import { isEntityDecl } from "../schema/declarations/EntityDecl.js";
3
3
  import { resolveTypeArgumentsInDecls } from "../schema/index.js";
4
4
  import { attachGitStatusToInstancesByEntityName, getInstancesByEntityName, } from "../utils/instances.js";
5
5
  import { getReferencesToInstances } from "../utils/references.js";
6
- const getGit = async (modelContainer) => {
7
- const git = simpleGit({ baseDir: modelContainer.dataRootPath });
6
+ const getGit = async (dataRootPath) => {
7
+ const git = simpleGit({ baseDir: dataRootPath });
8
8
  if (await git.checkIsRepo()) {
9
9
  try {
10
10
  const root = await git.revparse({ "--show-toplevel": null });
@@ -19,25 +19,25 @@ const getGit = async (modelContainer) => {
19
19
  return { git };
20
20
  }
21
21
  };
22
- export const init = async (modelContainer, instancesByEntityName) => {
23
- const { git, root: gitRoot, status: gitStatus } = await getGit(modelContainer);
24
- const declarations = resolveTypeArgumentsInDecls(modelContainer.schema.declarations);
22
+ export const init = async (schema, dataRootPath, instancesByEntityName) => {
23
+ const { git, root: gitRoot, status: gitStatus } = await getGit(dataRootPath);
24
+ const declarations = resolveTypeArgumentsInDecls(schema.declarations);
25
25
  const entities = declarations.filter(isEntityDecl);
26
26
  const entitiesByName = Object.fromEntries(entities.map(entity => [entity.name, entity]));
27
27
  const instancesByEntityNameInMemory = Object.assign({}, instancesByEntityName);
28
28
  const referencesToInstances = getReferencesToInstances(instancesByEntityName, entitiesByName);
29
29
  if (gitStatus) {
30
- attachGitStatusToInstancesByEntityName(instancesByEntityName, modelContainer.dataRootPath, gitRoot, gitStatus);
30
+ attachGitStatusToInstancesByEntityName(instancesByEntityName, dataRootPath, gitRoot, gitStatus);
31
31
  }
32
32
  const requestLocals = {
33
33
  git: git,
34
34
  gitRoot: gitRoot,
35
- dataRoot: modelContainer.dataRootPath,
35
+ dataRoot: dataRootPath,
36
36
  declarations: declarations,
37
37
  entities: entities,
38
38
  instancesByEntityName: instancesByEntityNameInMemory,
39
39
  entitiesByName: entitiesByName,
40
- localeEntity: modelContainer.schema.localeEntity,
40
+ localeEntity: schema.localeEntity,
41
41
  referencesToInstances,
42
42
  };
43
43
  return requestLocals;
@@ -1,7 +1,10 @@
1
1
  import { applyIndentation } from "./render.js";
2
2
  export const getErrorMessageForDisplay = (error) => {
3
3
  if (error instanceof AggregateError) {
4
- return `${error.message}\n${applyIndentation(1, error.errors.map(subError => getErrorMessageForDisplay(subError)).join("\n"), 2)}`;
4
+ return `${error.message}\n${applyIndentation(1, error.errors
5
+ .filter(subError => subError instanceof Error)
6
+ .map(subError => getErrorMessageForDisplay(subError))
7
+ .join("\n"), 2)}`;
5
8
  }
6
9
  else if (error.cause instanceof Error) {
7
10
  return `${error.message}\n${applyIndentation(1, getErrorMessageForDisplay(error.cause), 2)}`;
@@ -1,3 +1,3 @@
1
- import { StatusResult } from "simple-git";
2
- import { GitFileStatus } from "../shared/utils/git.js";
1
+ import type { StatusResult } from "simple-git";
2
+ import type { GitFileStatus } from "../../shared/utils/git.js";
3
3
  export declare const getGitFileStatusFromStatusResult: (statusResult: StatusResult, repoRoot: string, dataRoot: string, entityName: string, fileName: string) => GitFileStatus | undefined;
@@ -1,5 +1,5 @@
1
- import { StatusResult } from "simple-git";
2
- import { EntityDecl } from "../schema/declarations/EntityDecl.js";
3
- import { InstancesByEntityName } from "../shared/utils/instances.js";
1
+ import type { StatusResult } from "simple-git";
2
+ import type { InstancesByEntityName } from "../../shared/utils/instances.js";
3
+ import type { EntityDecl } from "../schema/declarations/EntityDecl.js";
4
4
  export declare const getInstancesByEntityName: (dataRoot: string, entities: readonly EntityDecl[]) => Promise<InstancesByEntityName>;
5
5
  export declare const attachGitStatusToInstancesByEntityName: (instancesByEntityName: InstancesByEntityName, dataRoot: string, gitRoot: string, gitStatus: StatusResult) => void;
@@ -11,9 +11,11 @@ export const getInstancesByEntityName = async (dataRoot, entities) => Object.fro
11
11
  })));
12
12
  return [entity.name, instances];
13
13
  })));
14
- export const attachGitStatusToInstancesByEntityName = (instancesByEntityName, dataRoot, gitRoot, gitStatus) => Object.entries(instancesByEntityName).forEach(([entityName, instances]) => {
15
- instancesByEntityName[entityName] = instances.map(instanceContainer => ({
16
- ...instanceContainer,
17
- gitStatus: getGitFileStatusFromStatusResult(gitStatus, gitRoot, dataRoot, entityName, instanceContainer.fileName),
18
- }));
19
- });
14
+ export const attachGitStatusToInstancesByEntityName = (instancesByEntityName, dataRoot, gitRoot, gitStatus) => {
15
+ Object.entries(instancesByEntityName).forEach(([entityName, instances]) => {
16
+ instancesByEntityName[entityName] = instances.map(instanceContainer => ({
17
+ ...instanceContainer,
18
+ gitStatus: getGitFileStatusFromStatusResult(gitStatus, gitRoot, dataRoot, entityName, instanceContainer.fileName),
19
+ }));
20
+ });
21
+ };
@@ -1,5 +1,5 @@
1
- import { EntityDecl } from "../schema/declarations/EntityDecl.js";
2
- import { InstanceContainer } from "../shared/utils/instances.js";
1
+ import type { InstanceContainer } from "../../shared/utils/instances.js";
2
+ import type { EntityDecl } from "../schema/declarations/EntityDecl.js";
3
3
  export type ReferencesToInstances = {
4
4
  [instanceId: string]: string[];
5
5
  };
@@ -1,5 +1,5 @@
1
+ import { difference, removeAt } from "../../shared/utils/array.js";
1
2
  import { getReferencesForEntityDecl } from "../schema/declarations/EntityDecl.js";
2
- import { difference, removeAt } from "../shared/utils/array.js";
3
3
  const addReference = (acc, reference, instanceId) => ({
4
4
  ...acc,
5
5
  [reference]: [...(acc[reference] ?? []), instanceId],
@@ -31,8 +31,8 @@ export const updateReferencesToInstances = (entitiesByName, referencesToInstance
31
31
  if (newInstance === undefined) {
32
32
  return removeReferences(referencesToInstances, getReferencesForEntityDecl(entity, oldInstance), instanceId);
33
33
  }
34
- const oldReferences = oldInstance === undefined ? [] : getReferencesForEntityDecl(entity, oldInstance);
35
- const newReferences = newInstance === undefined ? [] : getReferencesForEntityDecl(entity, newInstance);
34
+ const oldReferences = getReferencesForEntityDecl(entity, oldInstance);
35
+ const newReferences = getReferencesForEntityDecl(entity, newInstance);
36
36
  const { added, removed } = difference(oldReferences, newReferences);
37
37
  return removeReferences(addReferences(referencesToInstances, added, instanceId), removed, instanceId);
38
38
  }
@@ -1,5 +1,5 @@
1
1
  import { EOL } from "node:os";
2
- import { mergeObjects } from "../shared/utils/object.js";
2
+ import { mergeObjects } from "../../shared/utils/object.js";
3
3
  export const prefixLines = (prefix, text, includeEmptyLines = false) => text
4
4
  .split(EOL)
5
5
  .map(line => (includeEmptyLines || line.length > 0 ? prefix + line : line))
@@ -1,5 +1,5 @@
1
- import { SerializedDecl } from "../schema/declarations/Declaration.js";
2
- import { InstanceContainer, InstanceContainerOverview } from "./utils/instances.js";
1
+ import type { SerializedDecl } from "../node/schema/declarations/Declaration.js";
2
+ import type { InstanceContainer, InstanceContainerOverview } from "./utils/instances.js";
3
3
  export interface GetAllDeclarationsResponseBody<D extends SerializedDecl = SerializedDecl> {
4
4
  declarations: {
5
5
  declaration: D;
@@ -51,3 +51,9 @@ export interface GetAllGitBranchesResponseBody {
51
51
  allBranches: string[];
52
52
  currentBranch: string;
53
53
  }
54
+ export interface CreateCommitRequestBody {
55
+ message: string;
56
+ }
57
+ export interface CreateBranchRequestBody {
58
+ branchName: string;
59
+ }
@@ -1,12 +1,15 @@
1
- export const removeAt = (arr, index) => [
2
- ...arr.slice(0, index),
3
- ...arr.slice(index + 1),
4
- ];
5
- export const insertAt = (arr, index, item) => [
6
- ...arr.slice(0, index),
7
- item,
8
- ...arr.slice(index),
9
- ];
1
+ export const removeAt = (arr, index) => {
2
+ if (index < 0 || index >= arr.length) {
3
+ throw new RangeError(`index ${index.toString()} is out of bounds for array of length ${arr.length.toString()}`);
4
+ }
5
+ return [...arr.slice(0, index), ...arr.slice(index + 1)];
6
+ };
7
+ export const insertAt = (arr, index, item) => {
8
+ if (index < 0 || index > arr.length) {
9
+ throw new RangeError(`index ${index.toString()} is out of bounds for array of length ${arr.length.toString()}`);
10
+ }
11
+ return [...arr.slice(0, index), item, ...arr.slice(index)];
12
+ };
10
13
  /**
11
14
  * Calculates the difference between two arrays, including duplicated values.
12
15
  * @param oldArr - The original array.
@@ -1,2 +1,2 @@
1
- import { SerializedEntityDecl } from "../../schema/index.js";
1
+ import type { SerializedEntityDecl } from "../../node/schema/index.js";
2
2
  export declare const getDisplayNameFromEntityInstance: (entity: SerializedEntityDecl, instance: unknown, defaultName: string, locales?: string[]) => string;
@@ -23,9 +23,11 @@ export const getDisplayNameFromEntityInstance = (entity, instance, defaultName,
23
23
  const availableLocales = Object.keys(localeMap ?? {});
24
24
  return availableLocales.length === 0
25
25
  ? defaultName
26
- : locales.reduce((name, locale) => name ??
26
+ : (locales.reduce((name, locale) => name ??
27
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
27
28
  getValueAtPath(localeMap[locale], pathInLocaleMap), undefined) ??
29
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
28
30
  getValueAtPath(localeMap[availableLocales[0]], pathInLocaleMap) ??
29
- defaultName;
31
+ defaultName);
30
32
  }
31
33
  };
@@ -1,5 +1,5 @@
1
- import { SerializedEntityDecl } from "../../schema/index.js";
2
- import { GitFileStatus } from "./git.js";
1
+ import type { SerializedEntityDecl } from "../../node/schema/index.js";
2
+ import type { GitFileStatus } from "./git.js";
3
3
  export interface InstanceContainer {
4
4
  fileName: string;
5
5
  id: string;
@@ -1,4 +1,4 @@
1
- import { assertExhaustive } from "../shared/utils/typeSafety.js";
1
+ import { assertExhaustive } from "./typeSafety.js";
2
2
  /**
3
3
  * A lazy value that is only evaluated when it is needed.
4
4
  */
@@ -1,18 +1,18 @@
1
1
  const boldWithItalicRule = {
2
2
  pattern: /\*\*(.*?\*.+?\*.*?)\*\*/,
3
- map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1]) }),
3
+ map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1] ?? "") }),
4
4
  };
5
5
  const italicWithBoldRule = {
6
6
  pattern: /\*(.*?\*\*.+?\*\*.*?)\*/,
7
- map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1]) }),
7
+ map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1] ?? "") }),
8
8
  };
9
9
  const boldRule = {
10
10
  pattern: /\*\*(.+?)\*\*/,
11
- map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1]) }),
11
+ map: (result, parseInside) => ({ kind: "bold", content: parseInside(result[1] ?? "") }),
12
12
  };
13
13
  const italicRule = {
14
14
  pattern: /\*(.+?)\*/,
15
- map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1]) }),
15
+ map: (result, parseInside) => ({ kind: "italic", content: parseInside(result[1] ?? "") }),
16
16
  };
17
17
  const rules = [boldWithItalicRule, italicWithBoldRule, boldRule, italicRule];
18
18
  export const parseBlockMarkdown = (text) => text.split(/\n{2,}/).map(par => ({ kind: "paragraph", content: parseInlineMarkdown(par) }));
@@ -20,10 +20,10 @@ const parseForRules = (rules, text) => {
20
20
  if (text.length === 0) {
21
21
  return [];
22
22
  }
23
- if (rules.length === 0) {
23
+ const activeRule = rules[0];
24
+ if (activeRule === undefined) {
24
25
  return [{ kind: "text", content: text }];
25
26
  }
26
- const activeRule = rules[0];
27
27
  const res = activeRule.pattern.exec(text);
28
28
  if (res && (activeRule.predicate?.(res) ?? true)) {
29
29
  const { index } = res;
@@ -1,3 +1,6 @@
1
1
  export declare const sortObjectKeys: (obj: Record<string, unknown>, keys: string[]) => Record<string, unknown>;
2
2
  export declare const sortObjectKeysAlphabetically: (obj: Record<string, unknown>) => Record<string, unknown>;
3
3
  export declare const mergeObjects: <T>(obj1: Record<string, T>, obj2: Record<string, T>, solveConflict: (a: T, b: T) => T) => Record<string, T>;
4
+ export type Leaves<T> = T extends object ? {
5
+ [K in keyof T]: T[K] extends unknown[] ? never : `${Exclude<K, symbol>}${Leaves<T[K]> extends never ? "" : `.${Leaves<T[K]>}`}`;
6
+ }[keyof T] : never;
@@ -2,5 +2,6 @@ export const sortObjectKeys = (obj, keys) => Object.fromEntries(keys.flatMap(key
2
2
  export const sortObjectKeysAlphabetically = (obj) => Object.fromEntries(Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)));
3
3
  export const mergeObjects = (obj1, obj2, solveConflict) => Object.entries(obj2).reduce((acc, [key, value]) => ({
4
4
  ...acc,
5
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
5
6
  [key]: Object.hasOwn(acc, key) ? solveConflict(acc[key], value) : value,
6
7
  }), obj1);
@@ -24,11 +24,11 @@ export const reduce = (result, fok, ferror) => (isOk(result) ? fok(result.value)
24
24
  /**
25
25
  * Maps the value of a result to a new value.
26
26
  */
27
- export const map = (result, f) => (isOk(result) ? ok(f(result.value)) : result);
27
+ export const map = (result, f) => isOk(result) ? ok(f(result.value)) : result;
28
28
  /**
29
29
  * Maps an error to a new error.
30
30
  */
31
- export const mapError = (result, f) => (isError(result) ? error(f(result.error)) : result);
31
+ export const mapError = (result, f) => isError(result) ? error(f(result.error)) : result;
32
32
  export const combine = (result1, result2, fok, ferror) => isOk(result1)
33
33
  ? isOk(result2)
34
34
  ? ok(fok(result1.value, result2.value))
@@ -5,14 +5,17 @@ const separator = /[^a-z0-9]/;
5
5
  const lastChar = (str) => str[str.length - 1];
6
6
  const lastElement = (arr) => arr[arr.length - 1];
7
7
  const isAllUppercase = (str) => str === str.toUpperCase();
8
- export const splitStringParts = (str) => [...str].reduce((acc, char, i, strArr) => {
8
+ export const splitStringParts = (str) => [...new Intl.Segmenter().segment(str)].reduce((acc, segment, i, strArr) => {
9
+ const char = segment.segment;
9
10
  if (acc.length === 0) {
10
11
  return letterOrDigit.test(char) ? [char] : acc;
11
12
  }
13
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
12
14
  const lastPart = lastElement(acc);
13
15
  if (uppercase.test(char)) {
14
16
  const lastCharOfLastPart = lastChar(lastPart);
15
- const nextChar = strArr[i + 1];
17
+ const nextSegment = strArr[i + 1];
18
+ const nextChar = nextSegment?.segment;
16
19
  if (lastCharOfLastPart === undefined ||
17
20
  (uppercase.test(lastCharOfLastPart) && (nextChar === undefined || separator.test(nextChar)))) {
18
21
  return [...acc.slice(0, -1), lastPart + char];
@@ -47,8 +50,8 @@ export const toKebabCase = (str) => splitStringParts(str)
47
50
  export const toSnakeCase = (str) => splitStringParts(str)
48
51
  .map(part => part.toLowerCase())
49
52
  .join("_");
50
- export const toTitleCase = (str) => splitStringParts(str)
51
- .map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
53
+ export const toTitleCase = (str) => splitStringParts(isAllUppercase(str) ? str.toLowerCase() : str)
54
+ .map(part => isAllUppercase(part) ? part : part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
52
55
  .join(" ");
53
56
  export const commonPrefix = (...strs) => {
54
57
  if (strs.length === 0) {
@@ -1,14 +1,15 @@
1
1
  import { gte, lte } from "./compare.js";
2
2
  export const parallelizeErrors = (errors) => errors.filter(error => error !== undefined);
3
+ const normalizeLabel = (label) => Array.isArray(label) ? label : [label, label + "s"];
3
4
  export const validateLengthRangeBound = (end, label, rangeBound, value) => {
4
5
  if (rangeBound === undefined) {
5
6
  return;
6
7
  }
7
8
  const [operator, description] = end === "lower" ? [gte, "least"] : [lte, "most"];
8
9
  const length = value.length;
9
- const normalizedLabel = Array.isArray(label) ? label : [label, label + "s"];
10
+ const normalizedLabel = normalizeLabel(label);
10
11
  if (!operator(length, rangeBound)) {
11
- return RangeError(`Expected at ${description} ${rangeBound} ${normalizedLabel[rangeBound === 1 ? 0 : 1]}, but got ${length} ${normalizedLabel[length === 1 ? 0 : 1]}`);
12
+ return RangeError(`expected at ${description} ${rangeBound.toString()} ${normalizedLabel[rangeBound === 1 ? 0 : 1]}, but got ${length.toString()} ${normalizedLabel[length === 1 ? 0 : 1]}`);
12
13
  }
13
14
  return;
14
15
  };
@@ -18,8 +18,8 @@ export const validateRangeBound = (end, rangeBound, value) => {
18
18
  : normalizedRangeBound.isExclusive
19
19
  ? [lt, "less than"]
20
20
  : [lte, "less than or equal"];
21
- if (operator(value, normalizedRangeBound.value)) {
22
- return RangeError(`expected a value ${description} ${normalizedRangeBound.value}, but got ${value}`);
21
+ if (!operator(value, normalizedRangeBound.value)) {
22
+ return RangeError(`expected a value ${description} ${normalizedRangeBound.value.toString()}, but got ${value.toString()}`);
23
23
  }
24
24
  return;
25
25
  };
@@ -28,7 +28,7 @@ export const validateMultipleOf = (multipleOf, value) => {
28
28
  return;
29
29
  }
30
30
  if (value % multipleOf !== 0) {
31
- return RangeError(`expected a value that is a multiple of ${multipleOf}, but got ${value}`);
31
+ return RangeError(`expected a value that is a multiple of ${multipleOf.toString()}, but got ${value.toString()}`);
32
32
  }
33
33
  return;
34
34
  };
@@ -1,11 +1,12 @@
1
1
  import { parallelizeErrors, validateLengthRangeBound } from "../utils/validation.js";
2
2
  export const validateObjectConstraints = (constraints, expectedKeys, value) => {
3
3
  const label = ["property", "properties"];
4
+ const actualKeys = Object.keys(value);
4
5
  return parallelizeErrors([
5
- validateLengthRangeBound("lower", label, constraints.minProperties, expectedKeys),
6
- validateLengthRangeBound("upper", label, constraints.maxProperties, expectedKeys),
6
+ validateLengthRangeBound("lower", label, constraints.minProperties, actualKeys),
7
+ validateLengthRangeBound("upper", label, constraints.maxProperties, actualKeys),
7
8
  ...(constraints.additionalProperties !== true
8
- ? Object.keys(value).flatMap(valueKey => expectedKeys.includes(valueKey)
9
+ ? actualKeys.flatMap(valueKey => expectedKeys.includes(valueKey)
9
10
  ? []
10
11
  : [TypeError(`object does not allow unknown keys and key "${valueKey}" is not known`)])
11
12
  : []),