tsondb 0.21.1 → 0.21.2

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 (51) hide show
  1. package/dist/src/node/config.d.ts +2 -2
  2. package/dist/src/node/git.d.ts +4 -4
  3. package/dist/src/node/index.d.ts +39 -4
  4. package/dist/src/node/index.js +112 -11
  5. package/dist/src/node/renderers/jsonschema/render.js +7 -0
  6. package/dist/src/node/renderers/ts/render.d.ts +4 -0
  7. package/dist/src/node/renderers/ts/render.js +6 -0
  8. package/dist/src/node/schema/dsl/declarations/Decl.d.ts +3 -2
  9. package/dist/src/node/schema/dsl/declarations/Decl.js +2 -1
  10. package/dist/src/node/schema/dsl/declarations/SingletonEntityDecl.d.ts +35 -0
  11. package/dist/src/node/schema/dsl/declarations/SingletonEntityDecl.js +23 -0
  12. package/dist/src/node/schema/dsl/index.d.ts +1 -0
  13. package/dist/src/node/schema/dsl/index.js +1 -0
  14. package/dist/src/node/schema/generatedTypeHelpers.d.ts +18 -0
  15. package/dist/src/node/schema/guards.d.ts +1 -0
  16. package/dist/src/node/schema/guards.js +1 -0
  17. package/dist/src/node/schema/helpers.js +1 -0
  18. package/dist/src/node/schema/index.d.ts +12 -1
  19. package/dist/src/node/schema/index.js +44 -0
  20. package/dist/src/node/schema/treeOperations/customConstraints.d.ts +2 -1
  21. package/dist/src/node/schema/treeOperations/customConstraints.js +10 -10
  22. package/dist/src/node/schema/treeOperations/implicitSchema.js +1 -0
  23. package/dist/src/node/schema/treeOperations/references.js +1 -0
  24. package/dist/src/node/schema/treeOperations/serialization.d.ts +4 -1
  25. package/dist/src/node/schema/treeOperations/serialization.js +8 -0
  26. package/dist/src/node/schema/treeOperations/typeResolution.d.ts +4 -1
  27. package/dist/src/node/schema/treeOperations/typeResolution.js +6 -0
  28. package/dist/src/node/schema/treeOperations/validation.js +2 -0
  29. package/dist/src/node/server/api/declarations.js +165 -23
  30. package/dist/src/node/transaction.d.ts +28 -11
  31. package/dist/src/node/transaction.js +93 -0
  32. package/dist/src/node/utils/customConstraints.d.ts +6 -5
  33. package/dist/src/node/utils/customConstraints.js +34 -15
  34. package/dist/src/node/utils/databaseInMemory.d.ts +20 -8
  35. package/dist/src/node/utils/databaseInMemory.js +67 -9
  36. package/dist/src/node/utils/databaseOnDisk.js +27 -30
  37. package/dist/src/node/utils/files.d.ts +7 -2
  38. package/dist/src/node/utils/files.js +9 -0
  39. package/dist/src/node/utils/references.d.ts +2 -0
  40. package/dist/src/node/utils/references.js +6 -1
  41. package/dist/src/shared/api.d.ts +41 -2
  42. package/dist/src/shared/schema/Node.d.ts +5 -1
  43. package/dist/src/shared/schema/Node.js +6 -0
  44. package/dist/src/shared/schema/declarations/Declaration.d.ts +3 -2
  45. package/dist/src/shared/schema/declarations/SingletonEntityDecl.d.ts +18 -0
  46. package/dist/src/shared/schema/declarations/SingletonEntityDecl.js +8 -0
  47. package/dist/src/shared/utils/childInstances.d.ts +4 -0
  48. package/dist/src/shared/utils/instances.d.ts +4 -0
  49. package/dist/src/web/web.js +573 -130
  50. package/package.json +1 -1
  51. package/public/css/styles.css +1 -0
@@ -1,6 +1,7 @@
1
- import type { DeclarationName, DefaultTSONDBTypes, EntityName } from "../index.ts";
1
+ import type { DeclarationName, DefaultTSONDBTypes, EntityName, SingletonEntityName } from "../index.ts";
2
2
  import type { Decl } from "./dsl/declarations/Decl.ts";
3
3
  import type { EntityDecl } from "./dsl/declarations/EntityDecl.ts";
4
+ import type { SingletonEntityDecl } from "./dsl/index.ts";
4
5
  export declare class Schema<T extends DefaultTSONDBTypes = DefaultTSONDBTypes> {
5
6
  #private;
6
7
  constructor(declarations: Decl[], localeEntity?: EntityDecl);
@@ -8,6 +9,10 @@ export declare class Schema<T extends DefaultTSONDBTypes = DefaultTSONDBTypes> {
8
9
  get resolvedEntities(): EntityDecl<EntityName<T>>[];
9
10
  getEntity<E extends EntityName<T>>(name: E): EntityDecl<E> | undefined;
10
11
  getResolvedEntity<E extends EntityName<T>>(name: E): EntityDecl<E> | undefined;
12
+ get singletonEntities(): SingletonEntityDecl<SingletonEntityName<T>>[];
13
+ get resolvedSingletonEntities(): SingletonEntityDecl<SingletonEntityName<T>>[];
14
+ getSingletonEntity<E extends SingletonEntityName<T>>(name: E): SingletonEntityDecl<E> | undefined;
15
+ getResolvedSingletonEntity<E extends SingletonEntityName<T>>(name: E): SingletonEntityDecl<E> | undefined;
11
16
  get declarations(): Decl[];
12
17
  get resolvedDeclarations(): Decl[];
13
18
  getDeclaration<E extends DeclarationName<T>>(name: E): Decl<E> | undefined;
@@ -19,6 +24,12 @@ export declare class Schema<T extends DefaultTSONDBTypes = DefaultTSONDBTypes> {
19
24
  * This includes all entity declarations, but not nested entity declarations, as they cannot be referenced directly by their name.
20
25
  */
21
26
  isEntityName(name: string): name is EntityName<T>;
27
+ /**
28
+ * Checks if the given name is a valid singleton entity name in the schema.
29
+ *
30
+ * This includes all entity declarations, but not nested entity declarations, as they cannot be referenced directly by their name.
31
+ */
32
+ isSingletonEntityName(name: string): name is SingletonEntityName<T>;
22
33
  /**
23
34
  * Checks if the given name is a valid declaration name in the schema. This includes entity declarations, enum declarations and type alias declarations.
24
35
  *
@@ -8,6 +8,7 @@ import { renderKeyPath } from "../../shared/schema/utils/keyPath.js";
8
8
  import { getParameterNames, resolveTypeArgumentsInDecls, walkNodeTree, } from "./dsl/declarations/Decl.js";
9
9
  import { isEntityDecl } from "./dsl/declarations/EntityDecl.js";
10
10
  import { cases, isEnumDecl } from "./dsl/declarations/EnumDecl.js";
11
+ import { isSingletonEntityDecl } from "./dsl/declarations/SingletonEntityDecl.js";
11
12
  import { isChildEntitiesType } from "./dsl/types/ChildEntitiesType.js";
12
13
  import { isFloatType } from "./dsl/types/FloatType.js";
13
14
  import { isIncludeIdentifierType } from "./dsl/types/IncludeIdentifierType.js";
@@ -23,8 +24,10 @@ const debug = Debug("tsondb:schema");
23
24
  export class Schema {
24
25
  #declarations;
25
26
  #entities;
27
+ #singletonEntities;
26
28
  #resolvedDeclarations;
27
29
  #resolvedEntities;
30
+ #resolvedSingletonEntities;
28
31
  #localeEntity;
29
32
  #declarationMap;
30
33
  #resolvedDeclarationMap;
@@ -58,6 +61,8 @@ export class Schema {
58
61
  this.#resolvedDeclarations = resolveTypeArgumentsInDecls(allDeclsWithoutNestedEntities);
59
62
  this.#entities = allDeclsWithoutNestedEntities.filter(isEntityDecl);
60
63
  this.#resolvedEntities = this.#resolvedDeclarations.filter(isEntityDecl);
64
+ this.#singletonEntities = allDeclsWithoutNestedEntities.filter(isSingletonEntityDecl);
65
+ this.#resolvedSingletonEntities = this.#resolvedDeclarations.filter(isSingletonEntityDecl);
61
66
  this.#declarationMap = Dictionary.fromEntries(allDeclsWithoutNestedEntities.map(decl => [decl.name, decl]));
62
67
  this.#resolvedDeclarationMap = Dictionary.fromEntries(this.#resolvedDeclarations.map(decl => [decl.name, decl]));
63
68
  debug("created schema, no integrity violations found");
@@ -76,6 +81,20 @@ export class Schema {
76
81
  const decl = this.#resolvedDeclarationMap.get(name);
77
82
  return decl && isEntityDecl(decl) ? decl : undefined;
78
83
  }
84
+ get singletonEntities() {
85
+ return this.#singletonEntities;
86
+ }
87
+ get resolvedSingletonEntities() {
88
+ return this.#resolvedSingletonEntities;
89
+ }
90
+ getSingletonEntity(name) {
91
+ const decl = this.#declarationMap.get(name);
92
+ return decl && isSingletonEntityDecl(decl) ? decl : undefined;
93
+ }
94
+ getResolvedSingletonEntity(name) {
95
+ const decl = this.#resolvedDeclarationMap.get(name);
96
+ return decl && isSingletonEntityDecl(decl) ? decl : undefined;
97
+ }
79
98
  get declarations() {
80
99
  return this.#declarations;
81
100
  }
@@ -99,6 +118,14 @@ export class Schema {
99
118
  isEntityName(name) {
100
119
  return this.#entities.some(entity => entity.name === name);
101
120
  }
121
+ /**
122
+ * Checks if the given name is a valid singleton entity name in the schema.
123
+ *
124
+ * This includes all entity declarations, but not nested entity declarations, as they cannot be referenced directly by their name.
125
+ */
126
+ isSingletonEntityName(name) {
127
+ return this.#singletonEntities.some(entity => entity.name === name);
128
+ }
102
129
  /**
103
130
  * Checks if the given name is a valid declaration name in the schema. This includes entity declarations, enum declarations and type alias declarations.
104
131
  *
@@ -234,6 +261,19 @@ const checkChildEntityTypeInEntityDecl = (checkedDecls, entityDecl, decl) => {
234
261
  }, decl);
235
262
  }
236
263
  };
264
+ const checkChildEntityTypeInSingletonEntityDecl = (checkedDecls, entityDecl, decl) => {
265
+ if (!checkedDecls.has(decl)) {
266
+ checkedDecls.add(decl);
267
+ walkNodeTree(node => {
268
+ if (isIncludeIdentifierType(node)) {
269
+ checkChildEntityTypeInSingletonEntityDecl(checkedDecls, entityDecl, node.reference);
270
+ }
271
+ else if (isChildEntitiesType(node)) {
272
+ throw new Error(`Child entities type for entity "${node.entity.name}" in entity declaration "${entityDecl.name}" is not allowed in a singleton entity. Use a regular entity instead.`);
273
+ }
274
+ }, decl);
275
+ }
276
+ };
237
277
  const checkChildEntityTypes = (localeEntity, decls) => {
238
278
  if (localeEntity && localeEntity.parentReferenceKey !== undefined) {
239
279
  throw new TypeError(`The locale entity "${localeEntity.name}" cannot be used as a child entity to other entities.`);
@@ -246,6 +286,9 @@ const checkChildEntityTypes = (localeEntity, decls) => {
246
286
  if (isEntityDecl(decl)) {
247
287
  checkChildEntityTypeInEntityDecl(new Set(), decl, decl);
248
288
  }
289
+ if (isSingletonEntityDecl(decl)) {
290
+ checkChildEntityTypeInSingletonEntityDecl(new Set(), decl, decl);
291
+ }
249
292
  }
250
293
  };
251
294
  const checkChildEntitiesProvideCorrectPathToParentReferenceIdentifierType = (decls) => {
@@ -269,6 +312,7 @@ const isDeclarationRecursive = (declToCheck) => {
269
312
  const isDeclarationIncludedInNode = (visitedDecls, node) => {
270
313
  switch (node.kind) {
271
314
  case NodeKind.EntityDecl:
315
+ case NodeKind.SingletonEntityDecl:
272
316
  case NodeKind.EnumDecl:
273
317
  case NodeKind.TypeAliasDecl:
274
318
  return visitedDecls.includes(node)
@@ -1,5 +1,6 @@
1
1
  import type { InstanceContent } from "../../../shared/utils/instances.ts";
2
2
  import type { CustomConstraintHelpers } from "../../utils/customConstraints.ts";
3
3
  import type { EntityDecl } from "../dsl/declarations/EntityDecl.ts";
4
+ import type { SingletonEntityDecl } from "../dsl/index.ts";
4
5
  export declare const checkCustomConstraintsInEntityDecl: (node: EntityDecl, value: [id: string, content: InstanceContent], helpers: CustomConstraintHelpers) => string[];
5
- export { checkCustomConstraintsInEntityDecl as checkCustomConstraints };
6
+ export declare const checkCustomConstraintsInSingletonEntityDecl: (node: SingletonEntityDecl, value: InstanceContent, helpers: CustomConstraintHelpers) => string[];
@@ -1,31 +1,31 @@
1
1
  import { assertExhaustive } from "@elyukai/utils/typeSafety";
2
2
  import { ENUM_DISCRIMINATOR_KEY } from "../../../shared/schema/declarations/EnumDecl.js";
3
3
  import { NodeKind } from "../../../shared/schema/Node.js";
4
- export const checkCustomConstraintsInEntityDecl = (node, value, helpers) => (node.customConstraints?.({ ...helpers, instanceId: value[0], instanceContent: value[1] }) ?? []).concat(checkCustomConstraints(node.type.value, value, helpers));
5
- export { checkCustomConstraintsInEntityDecl as checkCustomConstraints };
6
- const checkCustomConstraints = (node, value, helpers) => {
4
+ export const checkCustomConstraintsInEntityDecl = (node, value, helpers) => (node.customConstraints?.({ ...helpers, instanceId: value[0], instanceContent: value[1] }) ?? []).concat(checkNestedCustomConstraints(node.type.value, value, helpers));
5
+ export const checkCustomConstraintsInSingletonEntityDecl = (node, value, helpers) => (node.customConstraints?.({ ...helpers, instanceId: node.name, instanceContent: value }) ?? []).concat(checkNestedCustomConstraints(node.type.value, value, helpers));
6
+ const checkNestedCustomConstraints = (node, value, helpers) => {
7
7
  switch (node.kind) {
8
8
  case NodeKind.EnumDecl:
9
- return (node.customConstraints?.({ ...helpers, value }) ?? []).concat(checkCustomConstraints(node.type.value, value, helpers));
9
+ return (node.customConstraints?.({ ...helpers, value }) ?? []).concat(checkNestedCustomConstraints(node.type.value, value, helpers));
10
10
  case NodeKind.TypeAliasDecl:
11
- return (node.customConstraints?.({ ...helpers, value }) ?? []).concat(checkCustomConstraints(node.type.value, value, helpers));
11
+ return (node.customConstraints?.({ ...helpers, value }) ?? []).concat(checkNestedCustomConstraints(node.type.value, value, helpers));
12
12
  case NodeKind.ArrayType:
13
13
  return Array.isArray(value)
14
- ? value.flatMap(item => checkCustomConstraints(node.items, item, helpers))
14
+ ? value.flatMap(item => checkNestedCustomConstraints(node.items, item, helpers))
15
15
  : [];
16
16
  case NodeKind.ObjectType:
17
17
  return typeof value === "object" && value !== null
18
18
  ? Object.entries(value).flatMap(([key, propValue]) => node.properties[key]
19
- ? checkCustomConstraints(node.properties[key].type, propValue, helpers)
19
+ ? checkNestedCustomConstraints(node.properties[key].type, propValue, helpers)
20
20
  : [])
21
21
  : [];
22
22
  case NodeKind.TypeArgumentType: {
23
23
  throw new TypeError(`type argument "${node.argument.name}" is unresolved. Make sure to resolve all type arguments before checking for nested custom constraints.`);
24
24
  }
25
25
  case NodeKind.IncludeIdentifierType:
26
- return checkCustomConstraints(node.reference, value, helpers);
26
+ return checkNestedCustomConstraints(node.reference, value, helpers);
27
27
  case NodeKind.NestedEntityMapType:
28
- return checkCustomConstraints(node.type.value, value, helpers);
28
+ return checkNestedCustomConstraints(node.type.value, value, helpers);
29
29
  case NodeKind.EnumType: {
30
30
  if (typeof value !== "object" ||
31
31
  value === null ||
@@ -39,7 +39,7 @@ const checkCustomConstraints = (node, value, helpers) => {
39
39
  node.values[enumCase] !== undefined &&
40
40
  node.values[enumCase].type !== null &&
41
41
  enumCase in value
42
- ? checkCustomConstraints(node.values[enumCase].type, value[enumCase], helpers)
42
+ ? checkNestedCustomConstraints(node.values[enumCase].type, value[enumCase], helpers)
43
43
  : [];
44
44
  }
45
45
  case NodeKind.BooleanType:
@@ -3,6 +3,7 @@ import { NodeKind } from "../../../shared/schema/Node.js";
3
3
  export const getNestedDeclarations = (addedDecls, node, parentDecl) => {
4
4
  switch (node.kind) {
5
5
  case NodeKind.EntityDecl:
6
+ case NodeKind.SingletonEntityDecl:
6
7
  case NodeKind.EnumDecl:
7
8
  case NodeKind.TypeAliasDecl:
8
9
  return addedDecls.includes(node)
@@ -6,6 +6,7 @@ import { resolveTypeArguments } from "./typeResolution.js";
6
6
  export const getReferences = (node, value, inDecl) => {
7
7
  switch (node.kind) {
8
8
  case NodeKind.EntityDecl:
9
+ case NodeKind.SingletonEntityDecl:
9
10
  case NodeKind.EnumDecl:
10
11
  case NodeKind.TypeAliasDecl:
11
12
  return getReferences(node.type.value, value, [...inDecl, node]);
@@ -1,5 +1,6 @@
1
1
  import type { SerializedEntityDecl } from "../../../shared/schema/declarations/EntityDecl.ts";
2
2
  import type { SerializedEnumDecl } from "../../../shared/schema/declarations/EnumDecl.ts";
3
+ import type { SerializedSingletonEntityDecl } from "../../../shared/schema/declarations/SingletonEntityDecl.ts";
3
4
  import type { SerializedTypeAliasDecl } from "../../../shared/schema/declarations/TypeAliasDecl.ts";
4
5
  import { NodeKind } from "../../../shared/schema/Node.js";
5
6
  import type { SerializedTypeParameter } from "../../../shared/schema/TypeParameter.ts";
@@ -19,6 +20,7 @@ import type { SerializedTranslationObjectType } from "../../../shared/schema/typ
19
20
  import type { SerializedTypeArgumentType } from "../../../shared/schema/types/TypeArgumentType.ts";
20
21
  import { type EntityDecl } from "../dsl/declarations/EntityDecl.ts";
21
22
  import { type EnumDecl } from "../dsl/declarations/EnumDecl.ts";
23
+ import type { SingletonEntityDecl } from "../dsl/declarations/SingletonEntityDecl.ts";
22
24
  import { type TypeAliasDecl } from "../dsl/declarations/TypeAliasDecl.ts";
23
25
  import type { Node, Type } from "../dsl/index.ts";
24
26
  import { type TypeParameter } from "../dsl/TypeParameter.ts";
@@ -38,6 +40,7 @@ import { type TranslationObjectType } from "../dsl/types/TranslationObjectType.t
38
40
  import { type TypeArgumentType } from "../dsl/types/TypeArgumentType.ts";
39
41
  export type SerializedNodeMap = {
40
42
  [NodeKind.EntityDecl]: [EntityDecl, SerializedEntityDecl];
43
+ [NodeKind.SingletonEntityDecl]: [EntityDecl, SerializedSingletonEntityDecl];
41
44
  [NodeKind.EnumDecl]: [EnumDecl, SerializedEnumDecl];
42
45
  [NodeKind.TypeAliasDecl]: [TypeAliasDecl, SerializedTypeAliasDecl];
43
46
  [NodeKind.ArrayType]: [ArrayType, SerializedArrayType];
@@ -65,7 +68,7 @@ export type SerializedMemberDeclObject<T extends Record<string, MemberDecl>> = {
65
68
  export type SerializedEnumCaseDeclObject<T extends Record<string, EnumCaseDecl>> = {
66
69
  [K in keyof T]: T[K] extends EnumCaseDecl<infer CT> ? SerializedEnumCaseDecl<CT extends Type ? Serialized<CT> : null> : never;
67
70
  };
68
- export type Serialized<T extends Node> = T extends EntityDecl<infer Name, infer T, infer FK> ? SerializedEntityDecl<Name, SerializedMemberDeclObject<T>, FK> : T extends EnumDecl<infer Name, infer T, infer Params> ? SerializedEnumDecl<Name, SerializedEnumCaseDeclObject<T>, SerializedTypeParameters<Params>> : T extends TypeAliasDecl<infer Name, infer T, infer Params> ? SerializedTypeAliasDecl<Name, Serialized<T>, SerializedTypeParameters<Params>> : T extends ArrayType<infer T> ? SerializedArrayType<Serialized<T>> : T extends ObjectType<infer T> ? SerializedObjectType<SerializedMemberDeclObject<T>> : T extends BooleanType ? SerializedBooleanType : T extends DateType ? SerializedDateType : T extends FloatType ? SerializedFloatType : T extends IntegerType ? SerializedIntegerType : T extends StringType ? SerializedStringType : T extends TypeArgumentType<infer T> ? SerializedTypeArgumentType<Serialized<T>> : T extends ReferenceIdentifierType ? SerializedReferenceIdentifierType : T extends IncludeIdentifierType<infer Params> ? SerializedIncludeIdentifierType<SerializedTypeParameters<Params>> : T extends NestedEntityMapType<infer Name, infer T> ? SerializedNestedEntityMapType<Name, SerializedMemberDeclObject<T>> : T extends EnumType<infer T> ? SerializedEnumType<SerializedEnumCaseDeclObject<T>> : T extends TypeParameter<infer N, infer C> ? SerializedTypeParameter<N, C extends Type ? Serialized<C> : undefined> : T extends ChildEntitiesType ? SerializedChildEntitiesType : T extends TranslationObjectType<infer E> ? SerializedTranslationObjectType<E> : never;
71
+ export type Serialized<T extends Node> = T extends EntityDecl<infer Name, infer T, infer FK> ? SerializedEntityDecl<Name, SerializedMemberDeclObject<T>, FK> : T extends SingletonEntityDecl<infer Name, infer T> ? SerializedSingletonEntityDecl<Name, SerializedMemberDeclObject<T>> : T extends EnumDecl<infer Name, infer T, infer Params> ? SerializedEnumDecl<Name, SerializedEnumCaseDeclObject<T>, SerializedTypeParameters<Params>> : T extends TypeAliasDecl<infer Name, infer T, infer Params> ? SerializedTypeAliasDecl<Name, Serialized<T>, SerializedTypeParameters<Params>> : T extends ArrayType<infer T> ? SerializedArrayType<Serialized<T>> : T extends ObjectType<infer T> ? SerializedObjectType<SerializedMemberDeclObject<T>> : T extends BooleanType ? SerializedBooleanType : T extends DateType ? SerializedDateType : T extends FloatType ? SerializedFloatType : T extends IntegerType ? SerializedIntegerType : T extends StringType ? SerializedStringType : T extends TypeArgumentType<infer T> ? SerializedTypeArgumentType<Serialized<T>> : T extends ReferenceIdentifierType ? SerializedReferenceIdentifierType : T extends IncludeIdentifierType<infer Params> ? SerializedIncludeIdentifierType<SerializedTypeParameters<Params>> : T extends NestedEntityMapType<infer Name, infer T> ? SerializedNestedEntityMapType<Name, SerializedMemberDeclObject<T>> : T extends EnumType<infer T> ? SerializedEnumType<SerializedEnumCaseDeclObject<T>> : T extends TypeParameter<infer N, infer C> ? SerializedTypeParameter<N, C extends Type ? Serialized<C> : undefined> : T extends ChildEntitiesType ? SerializedChildEntitiesType : T extends TranslationObjectType<infer E> ? SerializedTranslationObjectType<E> : never;
69
72
  export type SerializedOf<T extends Node> = SerializedNodeMap[T["kind"]][1];
70
73
  export type Serializer<T extends Node = Node> = (node: T) => Serialized<T>;
71
74
  export declare const serializeNode: <T extends Node>(node: T) => Serialized<T>;
@@ -30,6 +30,14 @@ export const serializeNode = (node) => {
30
30
  };
31
31
  return serializedNode;
32
32
  }
33
+ case NodeKind.SingletonEntityDecl: {
34
+ const serializedNode = {
35
+ ...node,
36
+ type: serializeNode(node.type.value),
37
+ customConstraints: node.customConstraints !== undefined,
38
+ };
39
+ return serializedNode;
40
+ }
33
41
  case NodeKind.EnumDecl: {
34
42
  const serializedNode = {
35
43
  ...node,
@@ -1,6 +1,7 @@
1
1
  import { type Decl, type IncludableDeclP } from "../dsl/declarations/Decl.ts";
2
2
  import { EntityDecl } from "../dsl/declarations/EntityDecl.ts";
3
3
  import { EnumDecl } from "../dsl/declarations/EnumDecl.ts";
4
+ import { SingletonEntityDecl } from "../dsl/declarations/SingletonEntityDecl.ts";
4
5
  import { TypeAliasDecl } from "../dsl/declarations/TypeAliasDecl.ts";
5
6
  import type { Node, Type } from "../dsl/index.ts";
6
7
  import type { TypeParameter } from "../dsl/TypeParameter.ts";
@@ -22,7 +23,9 @@ import type { TranslationObjectType } from "../dsl/types/TranslationObjectType.t
22
23
  import { type TypeArgumentType } from "../dsl/types/TypeArgumentType.ts";
23
24
  export type NodeWithResolvedTypeArguments<T extends Node | null> = T extends BooleanType | DateType | FloatType | IntegerType | StringType | ReferenceIdentifierType | ChildEntitiesType | TranslationObjectType ? T : T extends EntityDecl<infer N, infer P, infer FK> ? EntityDecl<N, {
24
25
  [K in keyof P]: P[K] extends MemberDecl<infer PT, infer R> ? MemberDecl<NodeWithResolvedTypeArguments<PT>, R> : never;
25
- }, FK> : T extends EnumDecl<infer N, infer V, TypeParameter[]> ? EnumDecl<N, {
26
+ }, FK> : T extends SingletonEntityDecl<infer N, infer P> ? SingletonEntityDecl<N, {
27
+ [K in keyof P]: P[K] extends MemberDecl<infer PT, infer R> ? MemberDecl<NodeWithResolvedTypeArguments<PT>, R> : never;
28
+ }> : T extends EnumDecl<infer N, infer V, TypeParameter[]> ? EnumDecl<N, {
26
29
  [K in keyof V]: V[K] extends EnumCaseDecl<infer CT> ? EnumCaseDecl<NodeWithResolvedTypeArguments<CT>> : never;
27
30
  }, [
28
31
  ]> : T extends TypeAliasDecl<infer N, infer U, TypeParameter[]> ? TypeAliasDecl<N, NodeWithResolvedTypeArguments<U>, []> : T extends ArrayType<infer I> ? ArrayType<NodeWithResolvedTypeArguments<I>> : T extends EnumType<infer V> ? EnumType<{
@@ -3,6 +3,7 @@ import { NodeKind } from "../../../shared/schema/Node.js";
3
3
  import { getTypeArgumentsRecord, isDeclWithoutTypeParameters, } from "../dsl/declarations/Decl.js";
4
4
  import { EntityDecl } from "../dsl/declarations/EntityDecl.js";
5
5
  import { EnumDecl } from "../dsl/declarations/EnumDecl.js";
6
+ import { SingletonEntityDecl } from "../dsl/declarations/SingletonEntityDecl.js";
6
7
  import { isTypeAliasDecl, TypeAliasDecl } from "../dsl/declarations/TypeAliasDecl.js";
7
8
  import { ArrayType } from "../dsl/types/ArrayType.js";
8
9
  import {} from "../dsl/types/BooleanType.js";
@@ -24,6 +25,11 @@ export const resolveTypeArguments = (args, node, inDecl) => {
24
25
  ...node,
25
26
  type: () => resolveTypeArguments({}, node.type.value, [...inDecl, node]),
26
27
  });
28
+ case NodeKind.SingletonEntityDecl:
29
+ return SingletonEntityDecl(node.sourceUrl, {
30
+ ...node,
31
+ type: () => resolveTypeArguments({}, node.type.value, [...inDecl, node]),
32
+ });
27
33
  case NodeKind.EnumDecl:
28
34
  return EnumDecl(node.sourceUrl, {
29
35
  name: node.name,
@@ -34,6 +34,7 @@ export const createReferenceValidator = (isEntityName, databaseInMemory, useStyl
34
34
  export const validateDeclStructuralIntegrity = (helpers, inDecls, decl, typeArgs, value) => {
35
35
  switch (decl.kind) {
36
36
  case NodeKind.EntityDecl:
37
+ case NodeKind.SingletonEntityDecl:
37
38
  return validateTypeStructuralIntegrity(helpers, inDecls, decl.type.value, value);
38
39
  case NodeKind.EnumDecl:
39
40
  case NodeKind.TypeAliasDecl:
@@ -286,6 +287,7 @@ export const validateTypeStructuralIntegrity = (helpers, inDecls, type, value) =
286
287
  export const validateDeclReferentialIntegrity = (helpers, checkEntityName, checkReferentialIntegrity, inDecls, decl, typeArgs, value) => {
287
288
  switch (decl.kind) {
288
289
  case NodeKind.EntityDecl:
290
+ case NodeKind.SingletonEntityDecl:
289
291
  return validateTypeReferentialIntegrity(helpers, checkEntityName, checkReferentialIntegrity, inDecls, decl.type.value, value);
290
292
  case NodeKind.EnumDecl:
291
293
  case NodeKind.TypeAliasDecl:
@@ -1,8 +1,11 @@
1
+ import { assertExhaustive } from "@elyukai/utils/typeSafety";
1
2
  import Debug from "debug";
2
3
  import express from "express";
4
+ import { NodeKind } from "../../../shared/schema/Node.js";
3
5
  import { isEntityDecl } from "../../schema/dsl/declarations/EntityDecl.js";
4
6
  import { isEnumDecl } from "../../schema/dsl/declarations/EnumDecl.js";
5
7
  import { isTypeAliasDecl } from "../../schema/dsl/declarations/TypeAliasDecl.js";
8
+ import { isSingletonEntityDecl } from "../../schema/guards.js";
6
9
  import { serializeNode } from "../../schema/treeOperations/serialization.js";
7
10
  import { getChildInstances } from "../../utils/childInstances.js";
8
11
  import { sendErrorResponse } from "../../utils/error.js";
@@ -14,28 +17,58 @@ declarationsApi.use((req, _res, next) => {
14
17
  next();
15
18
  });
16
19
  declarationsApi.get("/", (req, res) => {
17
- let filteredEntities;
18
20
  switch (req.query["kind"]) {
19
- case "Entity":
20
- filteredEntities = req.db.schema.resolvedEntities;
21
+ case "Entity": {
22
+ const body = {
23
+ declarations: req.db.schema.resolvedEntities.map(decl => ({
24
+ declaration: serializeNode(decl),
25
+ instanceCount: req.db.countInstancesOfEntity(decl.name),
26
+ })),
27
+ localeEntity: req.db.schema.localeEntity?.name,
28
+ };
29
+ res.json(body);
21
30
  break;
22
- case "TypeAlias":
23
- filteredEntities = req.db.schema.resolvedDeclarations.filter(isTypeAliasDecl);
31
+ }
32
+ case "SingletonEntity": {
33
+ const body = {
34
+ declarations: req.db.schema.resolvedSingletonEntities.map(decl => ({
35
+ declaration: serializeNode(decl),
36
+ hasInstance: req.db.hasInstanceOfSingletonEntity(decl.name),
37
+ })),
38
+ };
39
+ res.json(body);
24
40
  break;
25
- case "Enum":
26
- filteredEntities = req.db.schema.resolvedDeclarations.filter(isEnumDecl);
41
+ }
42
+ case "TypeAlias": {
43
+ const body = {
44
+ declarations: req.db.schema.resolvedDeclarations.filter(isTypeAliasDecl).map(decl => ({
45
+ declaration: serializeNode(decl),
46
+ })),
47
+ localeEntity: req.db.schema.localeEntity?.name,
48
+ };
49
+ res.json(body);
27
50
  break;
28
- default:
29
- filteredEntities = req.db.schema.resolvedDeclarations;
51
+ }
52
+ case "Enum": {
53
+ const body = {
54
+ declarations: req.db.schema.resolvedDeclarations.filter(isEnumDecl).map(decl => ({
55
+ declaration: serializeNode(decl),
56
+ })),
57
+ localeEntity: req.db.schema.localeEntity?.name,
58
+ };
59
+ res.json(body);
60
+ break;
61
+ }
62
+ default: {
63
+ const body = {
64
+ declarations: req.db.schema.resolvedDeclarations.map(decl => ({
65
+ declaration: serializeNode(decl),
66
+ })),
67
+ localeEntity: req.db.schema.localeEntity?.name,
68
+ };
69
+ res.json(body);
70
+ }
30
71
  }
31
- const body = {
32
- declarations: filteredEntities.map(decl => ({
33
- declaration: serializeNode(decl),
34
- instanceCount: req.db.countInstancesOfEntity(decl.name),
35
- })),
36
- localeEntity: req.db.schema.localeEntity?.name,
37
- };
38
- res.json(body);
39
72
  });
40
73
  declarationsApi.get("/:name", (req, res) => {
41
74
  const decl = req.db.schema.getResolvedDeclaration(req.params.name);
@@ -43,12 +76,36 @@ declarationsApi.get("/:name", (req, res) => {
43
76
  res.status(404).send(`Declaration "${req.params.name}" not found`);
44
77
  return;
45
78
  }
46
- const body = {
47
- declaration: serializeNode(decl),
48
- instanceCount: req.db.countInstancesOfEntity(decl.name),
49
- isLocaleEntity: decl === req.db.schema.localeEntity,
50
- };
51
- res.json(body);
79
+ switch (decl.kind) {
80
+ case NodeKind.EntityDecl: {
81
+ const body = {
82
+ declaration: serializeNode(decl),
83
+ instanceCount: req.db.countInstancesOfEntity(decl.name),
84
+ isLocaleEntity: decl === req.db.schema.localeEntity,
85
+ };
86
+ res.json(body);
87
+ break;
88
+ }
89
+ case NodeKind.SingletonEntityDecl: {
90
+ const body = {
91
+ declaration: serializeNode(decl),
92
+ hasInstance: req.db.hasInstanceOfSingletonEntity(decl.name),
93
+ };
94
+ res.json(body);
95
+ break;
96
+ }
97
+ case NodeKind.TypeAliasDecl:
98
+ case NodeKind.EnumDecl: {
99
+ const body = {
100
+ declaration: serializeNode(decl),
101
+ };
102
+ res.json(body);
103
+ break;
104
+ }
105
+ default: {
106
+ return assertExhaustive(decl);
107
+ }
108
+ }
52
109
  });
53
110
  declarationsApi.get("/:name/instances", (req, res) => {
54
111
  const decl = req.db.schema.getResolvedDeclaration(req.params.name);
@@ -172,3 +229,88 @@ declarationsApi.get("/:name/instances/:id/children", (req, res) => {
172
229
  };
173
230
  res.json(body);
174
231
  });
232
+ declarationsApi.get("/:name/instance", (req, res) => {
233
+ const decl = req.db.schema.getResolvedDeclaration(req.params.name);
234
+ if (decl === undefined) {
235
+ res.status(404).send(`Declaration "${req.params.name}" not found`);
236
+ return;
237
+ }
238
+ if (!isSingletonEntityDecl(decl)) {
239
+ res.status(400).send(`Declaration "${decl.name}" is not a singleton entity`);
240
+ return;
241
+ }
242
+ const instance = req.db.getSingletonInstanceContainerOfEntity(decl.name);
243
+ if (instance === undefined) {
244
+ res.status(404).send(`Singleton instance of entity ${decl.name} not found`);
245
+ return;
246
+ }
247
+ const body = {
248
+ instance: instance,
249
+ };
250
+ res.json(body);
251
+ });
252
+ declarationsApi.post("/:name/instance", async (req, res) => {
253
+ const decl = req.db.schema.getResolvedDeclaration(req.params.name);
254
+ if (decl === undefined) {
255
+ res.status(404).send(`Declaration "${req.params.name}" not found`);
256
+ return;
257
+ }
258
+ if (!isSingletonEntityDecl(decl)) {
259
+ res.status(400).send(`Declaration "${decl.name}" is not a singleton entity`);
260
+ return;
261
+ }
262
+ const requestBody = req.body;
263
+ try {
264
+ const result = await req.db.createSingletonInstance(requestBody.instance.entityName, requestBody.instance.content);
265
+ const body = {
266
+ instance: result,
267
+ };
268
+ res.json(body);
269
+ }
270
+ catch (err) {
271
+ sendErrorResponse(res, err);
272
+ }
273
+ });
274
+ declarationsApi.put("/:name/instance", async (req, res) => {
275
+ const decl = req.db.schema.getResolvedDeclaration(req.params.name);
276
+ if (decl === undefined) {
277
+ res.status(404).send(`Declaration "${req.params.name}" not found`);
278
+ return;
279
+ }
280
+ if (!isSingletonEntityDecl(decl)) {
281
+ res.status(400).send(`Declaration "${decl.name}" is not a singleton entity`);
282
+ return;
283
+ }
284
+ const requestBody = req.body;
285
+ try {
286
+ const result = await req.db.updateSingletonInstance(requestBody.instance.entityName, requestBody.instance.content);
287
+ const body = {
288
+ instance: result,
289
+ };
290
+ res.json(body);
291
+ }
292
+ catch (err) {
293
+ sendErrorResponse(res, err);
294
+ }
295
+ });
296
+ declarationsApi.delete("/:name/instance", async (req, res) => {
297
+ const decl = req.db.schema.getResolvedDeclaration(req.params.name);
298
+ if (decl === undefined) {
299
+ res.status(404).send(`Declaration "${req.params.name}" not found`);
300
+ return;
301
+ }
302
+ if (!isSingletonEntityDecl(decl)) {
303
+ res.status(400).send(`Declaration "${decl.name}" is not a singleton entity`);
304
+ return;
305
+ }
306
+ try {
307
+ const result = await req.db.deleteSingletonInstance(req.params.name);
308
+ const body = {
309
+ instance: { content: result },
310
+ };
311
+ res.json(body);
312
+ }
313
+ catch (err) {
314
+ sendErrorResponse(res, err);
315
+ }
316
+ });
@@ -1,6 +1,6 @@
1
- import type { InstanceContainer, InstanceContent } from "../shared/utils/instances.ts";
2
- import { type EntityDecl } from "./schema/dsl/index.ts";
3
- import type { AnyEntityMap, GetEntityByName, RegisteredEntityMap } from "./schema/generatedTypeHelpers.ts";
1
+ import type { InstanceContainer, InstanceContent, SingletonInstanceContainer } from "../shared/utils/instances.ts";
2
+ import { type EntityDecl, type SingletonEntityDecl } from "./schema/dsl/index.ts";
3
+ import type { AnyEntityMap, AnySingletonEntityMap, GetEntityByName, GetSingletonEntityByName, RegisteredEntityMap, RegisteredSingletonEntityMap } from "./schema/generatedTypeHelpers.ts";
4
4
  import { type DatabaseInMemory } from "./utils/databaseInMemory.ts";
5
5
  import { type ReferencesToInstances } from "./utils/references.ts";
6
6
  export type TransactionStep = {
@@ -19,22 +19,39 @@ export type TransactionStep = {
19
19
  entity: EntityDecl;
20
20
  instanceId: string;
21
21
  oldInstance: InstanceContent;
22
+ } | {
23
+ kind: "createSingleton";
24
+ entity: SingletonEntityDecl;
25
+ instanceContent: InstanceContent;
26
+ } | {
27
+ kind: "updateSingleton";
28
+ entity: SingletonEntityDecl;
29
+ instanceContent: InstanceContent;
30
+ oldInstance: InstanceContent;
31
+ } | {
32
+ kind: "deleteSingleton";
33
+ entity: SingletonEntityDecl;
34
+ oldInstance: InstanceContent;
22
35
  };
23
- type TransactionShared<EM extends AnyEntityMap = RegisteredEntityMap> = {
24
- data: DatabaseInMemory<EM>;
36
+ type TransactionShared<EM extends AnyEntityMap = RegisteredEntityMap, SEM extends AnySingletonEntityMap = RegisteredSingletonEntityMap> = {
37
+ data: DatabaseInMemory<EM, SEM>;
25
38
  referencesToInstances: ReferencesToInstances;
26
39
  steps: TransactionStep[];
27
40
  getEntity: GetEntityByName<EM>;
28
- validate: (entity: EntityDecl, instanceContent: InstanceContent) => Error[];
41
+ getSingletonEntity: GetSingletonEntityByName<EM>;
42
+ validate: (entity: EntityDecl | SingletonEntityDecl, instanceContent: InstanceContent) => Error[];
29
43
  localeEntity: EntityDecl | undefined;
30
44
  };
31
45
  export declare const createNewId: () => `${string}-${string}-${string}-${string}-${string}`;
32
- export declare class Transaction<EM extends AnyEntityMap = RegisteredEntityMap> {
46
+ export declare class Transaction<EM extends AnyEntityMap = RegisteredEntityMap, SEM extends AnySingletonEntityMap = RegisteredSingletonEntityMap> {
33
47
  #private;
34
- constructor(values: TransactionShared<EM>);
35
- createInstance(entity: EntityDecl<Extract<keyof EM, string>>, instanceContent: InstanceContent, instanceId?: string): [Transaction<EM>, InstanceContainer];
36
- updateInstance(entity: EntityDecl<Extract<keyof EM, string>>, instanceId: string, instanceContent: InstanceContent): [Transaction<EM>, InstanceContainer];
37
- deleteInstance(entity: EntityDecl<Extract<keyof EM, string>>, instanceId: string): [Transaction<EM>, InstanceContainer];
48
+ constructor(values: TransactionShared<EM, SEM>);
49
+ createInstance(entity: EntityDecl<Extract<keyof EM, string>>, instanceContent: InstanceContent, instanceId?: string): [Transaction<EM, SEM>, InstanceContainer];
50
+ updateInstance(entity: EntityDecl<Extract<keyof EM, string>>, instanceId: string, instanceContent: InstanceContent): [Transaction<EM, SEM>, InstanceContainer];
51
+ deleteInstance(entity: EntityDecl<Extract<keyof EM, string>>, instanceId: string): [Transaction<EM, SEM>, InstanceContainer];
52
+ createSingletonInstance(entity: SingletonEntityDecl<Extract<keyof SEM, string>>, instanceContent: InstanceContent): [Transaction<EM, SEM>, SingletonInstanceContainer];
53
+ updateSingletonInstance(entity: SingletonEntityDecl<Extract<keyof SEM, string>>, instanceContent: InstanceContent): [Transaction<EM, SEM>, SingletonInstanceContainer];
54
+ deleteSingletonInstance(entity: SingletonEntityDecl<Extract<keyof SEM, string>>): [Transaction<EM, SEM>, SingletonInstanceContainer];
38
55
  /**
39
56
  * @package
40
57
  */