tsondb 0.7.6 → 0.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/src/node/schema/Node.d.ts +8 -1
  2. package/dist/src/node/schema/Node.js +34 -18
  3. package/dist/src/node/schema/Schema.js +14 -10
  4. package/dist/src/node/schema/TypeParameter.js +1 -1
  5. package/dist/src/node/schema/declarations/EntityDecl.js +1 -1
  6. package/dist/src/node/schema/declarations/EnumDecl.js +1 -1
  7. package/dist/src/node/schema/declarations/TypeAliasDecl.js +1 -1
  8. package/dist/src/node/schema/types/generic/ArrayType.js +1 -1
  9. package/dist/src/node/schema/types/generic/EnumType.js +1 -1
  10. package/dist/src/node/schema/types/generic/ObjectType.js +1 -1
  11. package/dist/src/node/schema/types/references/ChildEntitiesType.js +1 -1
  12. package/dist/src/node/schema/types/references/IncludeIdentifierType.js +2 -2
  13. package/dist/src/node/schema/types/references/NestedEntityMapType.d.ts +12 -2
  14. package/dist/src/node/schema/types/references/NestedEntityMapType.js +2 -2
  15. package/dist/src/node/schema/types/references/ReferenceIdentifierType.js +2 -2
  16. package/dist/src/node/server/api/git.js +156 -24
  17. package/dist/src/node/server/index.js +4 -0
  18. package/dist/src/node/utils/instanceOperations.d.ts +1 -1
  19. package/dist/src/node/utils/instanceOperations.js +2 -2
  20. package/dist/src/shared/api.d.ts +17 -1
  21. package/dist/src/shared/schema/declarations/EntityDecl.d.ts +2 -0
  22. package/dist/src/shared/schema/declarations/EntityDecl.js +2 -0
  23. package/dist/src/shared/schema/declarations/EnumDecl.d.ts +2 -1
  24. package/dist/src/shared/schema/declarations/EnumDecl.js +2 -0
  25. package/dist/src/shared/schema/declarations/TypeAliasDecl.d.ts +2 -1
  26. package/dist/src/shared/schema/declarations/TypeAliasDecl.js +2 -1
  27. package/dist/src/shared/utils/git.d.ts +4 -0
  28. package/dist/src/shared/utils/git.js +6 -1
  29. package/dist/src/shared/utils/markdown.d.ts +36 -17
  30. package/dist/src/shared/utils/markdown.js +79 -23
  31. package/dist/src/shared/utils/object.d.ts +1 -0
  32. package/dist/src/shared/utils/object.js +1 -0
  33. package/dist/src/web/api/git.d.ts +5 -1
  34. package/dist/src/web/api/git.js +6 -2
  35. package/dist/src/web/components/ContextProviderWrapper.d.ts +7 -0
  36. package/dist/src/web/components/ContextProviderWrapper.js +5 -0
  37. package/dist/src/web/components/InstanceRouteSkeleton.d.ts +6 -0
  38. package/dist/src/web/components/InstanceRouteSkeleton.js +12 -8
  39. package/dist/src/web/components/LoadingOverlay.d.ts +1 -0
  40. package/dist/src/web/components/LoadingOverlay.js +3 -0
  41. package/dist/src/web/components/ModalDialog.js +4 -3
  42. package/dist/src/web/components/git/Git.js +47 -0
  43. package/dist/src/web/components/git/GitBranchManager.d.ts +7 -0
  44. package/dist/src/web/components/git/GitBranchManager.js +17 -0
  45. package/dist/src/web/components/git/GitFileList.d.ts +17 -0
  46. package/dist/src/web/components/git/GitFileList.js +11 -0
  47. package/dist/src/web/components/git/GitFileManager.d.ts +8 -0
  48. package/dist/src/web/components/git/GitFileManager.js +34 -0
  49. package/dist/src/web/components/git/GitStatusIndicator.d.ts +7 -0
  50. package/dist/src/web/components/git/GitStatusIndicator.js +6 -0
  51. package/dist/src/web/components/typeInputs/StringTypeInput.js +1 -1
  52. package/dist/src/web/components/typeInputs/utils/MismatchingTypeError.js +2 -2
  53. package/dist/src/web/context/entities.d.ts +6 -5
  54. package/dist/src/web/context/git.d.ts +2 -1
  55. package/dist/src/web/context/gitClient.d.ts +2 -0
  56. package/dist/src/web/context/gitClient.js +2 -0
  57. package/dist/src/web/hooks/useGitClient.d.ts +37 -0
  58. package/dist/src/web/hooks/useGitClient.js +313 -0
  59. package/dist/src/web/hooks/useSecondaryDeclarations.js +6 -3
  60. package/dist/src/web/hooks/useSettings.js +0 -1
  61. package/dist/src/web/index.js +6 -2
  62. package/dist/src/web/routes/CreateInstance.js +29 -23
  63. package/dist/src/web/routes/Entity.js +10 -4
  64. package/dist/src/web/routes/Instance.js +21 -8
  65. package/dist/src/web/signals/loading.d.ts +2 -0
  66. package/dist/src/web/signals/loading.js +11 -0
  67. package/dist/src/web/utils/BlockMarkdown.js +1 -1
  68. package/dist/src/web/utils/debug.d.ts +1 -0
  69. package/dist/src/web/utils/debug.js +4 -0
  70. package/dist/src/web/utils/typeSkeleton.js +1 -1
  71. package/package.json +3 -3
  72. package/public/css/styles.css +132 -70
  73. package/dist/src/web/components/Git.js +0 -164
  74. /package/dist/src/web/components/{Git.d.ts → git/Git.d.ts} +0 -0
@@ -39,6 +39,13 @@ import type { Type } from "./types/Type.ts";
39
39
  export type { BaseNode } from "../../shared/schema/Node.ts";
40
40
  export { NodeKind };
41
41
  export type Node = Decl | Type | TypeParameter;
42
+ export type NestedDecl = Decl | NestedEntity;
43
+ export type NestedEntity = {
44
+ kind: "NestedEntity";
45
+ sourceUrl: string;
46
+ name: string;
47
+ type: NestedEntityMapType;
48
+ };
42
49
  export declare const reduceNodes: <R>(reducer: (parentNodes: Node[], node: Node, collectedResults: R[]) => R[], nodes: readonly Node[], options?: {
43
50
  initialResults?: R[];
44
51
  followIncludes?: boolean;
@@ -55,7 +62,7 @@ export interface Validators {
55
62
  }
56
63
  export declare const createValidators: (instancesByEntityName: InstancesByEntityName, useStyling: boolean, checkReferentialIntegrity?: boolean) => Validators;
57
64
  export type Predicate<T extends Node> = (node: Node) => node is T;
58
- export type GetNestedDeclarations<T extends Node = Node> = (addedDecls: Decl[], node: T) => Decl[];
65
+ export type GetNestedDeclarations<T extends Node = Node> = (addedDecls: NestedDecl[], node: T, parentDecl: Decl | undefined) => NestedDecl[];
59
66
  export declare const getNestedDeclarations: GetNestedDeclarations;
60
67
  export type Validator<T extends Node = Node> = (helpers: Validators, node: T, value: unknown) => Error[];
61
68
  export type ValidatorOfParamDecl<T extends Node = Node> = (helpers: Validators, node: T, typeArgs: Type[], value: unknown) => Error[];
@@ -91,42 +91,58 @@ export const createValidators = (instancesByEntityName, useStyling, checkReferen
91
91
  ]
92
92
  : () => [],
93
93
  });
94
- export const getNestedDeclarations = (addedDecls, node) => {
94
+ export const getNestedDeclarations = (addedDecls, node, parentDecl) => {
95
95
  switch (node.kind) {
96
96
  case NodeKind.EntityDecl:
97
- return getNestedDeclarationsInEntityDecl(addedDecls, node);
97
+ return addedDecls.includes(node)
98
+ ? addedDecls
99
+ : getNestedDeclarationsInEntityDecl([...addedDecls, node], node, parentDecl);
98
100
  case NodeKind.EnumDecl:
99
- return getNestedDeclarationsInEnumDecl(addedDecls, node);
101
+ return addedDecls.includes(node)
102
+ ? addedDecls
103
+ : getNestedDeclarationsInEnumDecl([...addedDecls, node], node, parentDecl);
100
104
  case NodeKind.TypeAliasDecl:
101
- return getNestedDeclarationsInTypeAliasDecl(addedDecls, node);
105
+ return addedDecls.includes(node)
106
+ ? addedDecls
107
+ : getNestedDeclarationsInTypeAliasDecl([...addedDecls, node], node, parentDecl);
102
108
  case NodeKind.ArrayType:
103
- return getNestedDeclarationsInArrayType(addedDecls, node);
109
+ return getNestedDeclarationsInArrayType(addedDecls, node, parentDecl);
104
110
  case NodeKind.ObjectType:
105
- return getNestedDeclarationsInObjectType(addedDecls, node);
111
+ return getNestedDeclarationsInObjectType(addedDecls, node, parentDecl);
106
112
  case NodeKind.BooleanType:
107
- return getNestedDeclarationsInBooleanType(addedDecls, node);
113
+ return getNestedDeclarationsInBooleanType(addedDecls, node, parentDecl);
108
114
  case NodeKind.DateType:
109
- return getNestedDeclarationsInDateType(addedDecls, node);
115
+ return getNestedDeclarationsInDateType(addedDecls, node, parentDecl);
110
116
  case NodeKind.FloatType:
111
- return getNestedDeclarationsInFloatType(addedDecls, node);
117
+ return getNestedDeclarationsInFloatType(addedDecls, node, parentDecl);
112
118
  case NodeKind.IntegerType:
113
- return getNestedDeclarationsInIntegerType(addedDecls, node);
119
+ return getNestedDeclarationsInIntegerType(addedDecls, node, parentDecl);
114
120
  case NodeKind.StringType:
115
- return getNestedDeclarationsInStringType(addedDecls, node);
121
+ return getNestedDeclarationsInStringType(addedDecls, node, parentDecl);
116
122
  case NodeKind.TypeArgumentType:
117
- return getNestedDeclarationsInTypeArgumentType(addedDecls, node);
123
+ return getNestedDeclarationsInTypeArgumentType(addedDecls, node, parentDecl);
118
124
  case NodeKind.ReferenceIdentifierType:
119
- return getNestedDeclarationsInReferenceIdentifierType(addedDecls, node);
125
+ return getNestedDeclarationsInReferenceIdentifierType(addedDecls, node, parentDecl);
120
126
  case NodeKind.IncludeIdentifierType:
121
- return getNestedDeclarationsInIncludeIdentifierType(addedDecls, node);
127
+ return getNestedDeclarationsInIncludeIdentifierType(addedDecls, node, parentDecl);
122
128
  case NodeKind.NestedEntityMapType:
123
- return getNestedDeclarationsInNestedEntityMapType(addedDecls, node);
129
+ return addedDecls.some(addedDecl => addedDecl.kind === "NestedEntity" && addedDecl.type === node)
130
+ ? addedDecls
131
+ : getNestedDeclarationsInNestedEntityMapType([
132
+ ...addedDecls,
133
+ {
134
+ kind: "NestedEntity",
135
+ sourceUrl: parentDecl?.sourceUrl ?? "",
136
+ name: node.name,
137
+ type: node,
138
+ },
139
+ ], node, parentDecl);
124
140
  case NodeKind.EnumType:
125
- return getNestedDeclarationsInEnumType(addedDecls, node);
141
+ return getNestedDeclarationsInEnumType(addedDecls, node, parentDecl);
126
142
  case NodeKind.TypeParameter:
127
- return getNestedDeclarationsInTypeParameter(addedDecls, node);
143
+ return getNestedDeclarationsInTypeParameter(addedDecls, node, parentDecl);
128
144
  case NodeKind.ChildEntitiesType:
129
- return getNestedDeclarationsInChildEntitiesType(addedDecls, node);
145
+ return getNestedDeclarationsInChildEntitiesType(addedDecls, node, parentDecl);
130
146
  default:
131
147
  return assertExhaustive(node);
132
148
  }
@@ -152,29 +152,33 @@ const checkChildEntitiesProvideCorrectPathToParentReferenceIdentifierType = (dec
152
152
  }
153
153
  }
154
154
  };
155
- const addDeclarations = (existingDecls, declsToAdd, nested) => declsToAdd.reduce((accDecls, decl) => {
155
+ const addDeclarations = (existingDecls, declsToAdd) => declsToAdd.reduce((accDecls, decl) => {
156
156
  if (!accDecls.includes(decl)) {
157
- checkDuplicateIdentifier(accDecls, decl);
158
- const nestedDecls = nested ? getNestedDeclarations(accDecls, decl) : [];
159
- return addDeclarations([...accDecls, decl], nestedDecls, false);
157
+ return getNestedDeclarations(accDecls, decl.kind === "NestedEntity" ? decl.type : decl, undefined);
160
158
  }
161
159
  return accDecls;
162
160
  }, existingDecls);
163
161
  export const Schema = (declarations, localeEntity) => {
164
162
  debug("creating schema from %d declarations", declarations.length);
165
163
  debug("collecting nested declarations ...");
166
- const allDecls = addDeclarations([], localeEntity ? declarations.concat(localeEntity) : declarations, true);
164
+ const allDecls = addDeclarations([], localeEntity ? declarations.concat(localeEntity) : declarations);
165
+ debug("found %d nested declarations", allDecls.length);
166
+ debug("checking for duplicate identifiers ...");
167
+ allDecls.forEach((decl, declIndex) => {
168
+ checkDuplicateIdentifier(allDecls.slice(0, declIndex), decl);
169
+ });
170
+ const allDeclsWithoutNestedEntities = allDecls.filter(decl => decl.kind !== "NestedEntity");
167
171
  debug("checking name shadowing ...");
168
- checkParameterNamesShadowing(allDecls);
172
+ checkParameterNamesShadowing(allDeclsWithoutNestedEntities);
169
173
  debug("checking entity display name paths ...");
170
- checkEntityDisplayNamePaths(allDecls, localeEntity);
174
+ checkEntityDisplayNamePaths(allDeclsWithoutNestedEntities, localeEntity);
171
175
  debug("checking child entities ...");
172
- checkChildEntitiesProvideCorrectPathToParentReferenceIdentifierType(allDecls);
176
+ checkChildEntitiesProvideCorrectPathToParentReferenceIdentifierType(allDeclsWithoutNestedEntities);
173
177
  debug("checking child entity types ...");
174
- checkChildEntityTypes(localeEntity, allDecls);
178
+ checkChildEntityTypes(localeEntity, allDeclsWithoutNestedEntities);
175
179
  debug("created schema, no integrity violations found");
176
180
  return {
177
- declarations: allDecls,
181
+ declarations: allDeclsWithoutNestedEntities,
178
182
  localeEntity,
179
183
  };
180
184
  };
@@ -4,7 +4,7 @@ export const Param = (name, constraint) => ({
4
4
  name,
5
5
  constraint,
6
6
  });
7
- export const getNestedDeclarationsInTypeParameter = (addedDecls, param) => (param.constraint ? getNestedDeclarations(addedDecls, param.constraint) : addedDecls);
7
+ export const getNestedDeclarationsInTypeParameter = (addedDecls, param, parentDecl) => param.constraint ? getNestedDeclarations(addedDecls, param.constraint, parentDecl) : addedDecls;
8
8
  export const resolveTypeArgumentsInTypeParameter = (_args, param) => param;
9
9
  export const serializeTypeParameter = type => ({
10
10
  ...type,
@@ -26,7 +26,7 @@ export const EntityDecl = (sourceUrl, options) => {
26
26
  export { EntityDecl as Entity };
27
27
  export const isEntityDecl = node => node.kind === NodeKind.EntityDecl;
28
28
  export const isEntityDeclWithParentReference = (decl) => decl.parentReferenceKey !== undefined;
29
- export const getNestedDeclarationsInEntityDecl = (isDeclAdded, decl) => getNestedDeclarationsInObjectType(isDeclAdded, decl.type.value);
29
+ export const getNestedDeclarationsInEntityDecl = (isDeclAdded, decl) => getNestedDeclarationsInObjectType(isDeclAdded, decl.type.value, decl);
30
30
  export const validateEntityDecl = (helpers, decl, value) => validateType(helpers, decl.type.value, value);
31
31
  export const resolveTypeArgumentsInEntityDecl = (_args, decl) => EntityDecl(decl.sourceUrl, {
32
32
  ...decl,
@@ -28,7 +28,7 @@ export const EnumDecl = (sourceUrl, options) => {
28
28
  };
29
29
  export { EnumDecl as Enum };
30
30
  export const isEnumDecl = node => node.kind === NodeKind.EnumDecl;
31
- export const getNestedDeclarationsInEnumDecl = (addedDecls, decl) => getNestedDeclarationsInEnumType(addedDecls, decl.type.value);
31
+ export const getNestedDeclarationsInEnumDecl = (addedDecls, decl) => getNestedDeclarationsInEnumType(addedDecls, decl.type.value, decl);
32
32
  export const validateEnumDecl = (helpers, decl, args, value) => validateEnumType(helpers, resolveTypeArgumentsInEnumType(getTypeArgumentsRecord(decl, args), decl.type.value), value);
33
33
  export const resolveTypeArgumentsInEnumDecl = (args, decl) => EnumDecl(decl.sourceUrl, {
34
34
  name: decl.name,
@@ -26,7 +26,7 @@ export const TypeAliasDecl = (sourceUrl, options) => {
26
26
  };
27
27
  export { TypeAliasDecl as TypeAlias };
28
28
  export const isTypeAliasDecl = node => node.kind === NodeKind.TypeAliasDecl;
29
- export const getNestedDeclarationsInTypeAliasDecl = (addedDecls, decl) => getNestedDeclarations(addedDecls, decl.type.value);
29
+ export const getNestedDeclarationsInTypeAliasDecl = (addedDecls, decl) => getNestedDeclarations(addedDecls, decl.type.value, decl);
30
30
  export const validateTypeAliasDecl = ((helpers, decl, args, value) => validateType(helpers, resolveTypeArguments(getTypeArgumentsRecord(decl, args), decl.type.value), value));
31
31
  export const resolveTypeArgumentsInTypeAliasDecl = (args, decl) => TypeAliasDecl(decl.sourceUrl, {
32
32
  ...decl,
@@ -14,7 +14,7 @@ export const ArrayType = (items, options = {}) => ({
14
14
  });
15
15
  export { ArrayType as Array };
16
16
  export const isArrayType = node => node.kind === NodeKind.ArrayType;
17
- export const getNestedDeclarationsInArrayType = (addedDecls, type) => getNestedDeclarations(addedDecls, type.items);
17
+ export const getNestedDeclarationsInArrayType = (addedDecls, type, parentDecl) => getNestedDeclarations(addedDecls, type.items, parentDecl);
18
18
  export const validateArrayType = (helpers, type, value) => {
19
19
  if (!Array.isArray(value)) {
20
20
  return [TypeError(`expected an array, but got ${json(value, helpers.useStyling)}`)];
@@ -9,7 +9,7 @@ export const EnumType = (values) => ({
9
9
  values,
10
10
  });
11
11
  export const isEnumType = node => node.kind === NodeKind.EnumType;
12
- export const getNestedDeclarationsInEnumType = (addedDecls, type) => Object.values(type.values).reduce((acc, caseMember) => caseMember.type === null ? acc : getNestedDeclarations(acc, caseMember.type), addedDecls);
12
+ export const getNestedDeclarationsInEnumType = (addedDecls, type, parentDecl) => Object.values(type.values).reduce((acc, caseMember) => caseMember.type === null ? acc : getNestedDeclarations(acc, caseMember.type, parentDecl), addedDecls);
13
13
  export const validateEnumType = (helpers, type, value) => {
14
14
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
15
15
  return [TypeError(`expected an object, but got ${json(value, helpers.useStyling)}`)];
@@ -25,7 +25,7 @@ export const ObjectType = (properties, options = {}) => {
25
25
  };
26
26
  export { ObjectType as Object };
27
27
  export const isObjectType = node => node.kind === NodeKind.ObjectType;
28
- export const getNestedDeclarationsInObjectType = (addedDecls, type) => Object.values(type.properties).reduce((acc, prop) => getNestedDeclarations(acc, prop.type), addedDecls);
28
+ export const getNestedDeclarationsInObjectType = (addedDecls, type, parentDecl) => Object.values(type.properties).reduce((acc, prop) => getNestedDeclarations(acc, prop.type, parentDecl), addedDecls);
29
29
  export const validateObjectType = (helpers, type, value) => {
30
30
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
31
31
  return [TypeError(`expected an object, but got ${json(value, helpers.useStyling)}`)];
@@ -7,7 +7,7 @@ export const ChildEntitiesType = (entity) => ({
7
7
  });
8
8
  export { ChildEntitiesType as ChildEntities };
9
9
  export const isChildEntitiesType = node => node.kind === NodeKind.ChildEntitiesType;
10
- export const getNestedDeclarationsInChildEntitiesType = (addedDecls, type) => getNestedDeclarations([type.entity, ...addedDecls], type.entity);
10
+ export const getNestedDeclarationsInChildEntitiesType = (addedDecls, type, parentDecl) => getNestedDeclarations(addedDecls, type.entity, parentDecl);
11
11
  export const validateChildEntitiesType = () => [];
12
12
  export const resolveTypeArgumentsInChildEntitiesType = (_args, type) => type;
13
13
  export const serializeChildEntitiesType = type => ({
@@ -16,9 +16,9 @@ export const IncludeIdentifierType = (reference) => ({
16
16
  export { IncludeIdentifierType as IncludeIdentifier };
17
17
  export const isIncludeIdentifierType = node => node.kind === NodeKind.IncludeIdentifierType;
18
18
  const isNoGenericIncludeIdentifierType = (node) => node.args.length === 0 && node.reference.parameters.length === 0;
19
- export const getNestedDeclarationsInIncludeIdentifierType = (addedDecls, type) => type.args.reduce((accAddedDecls, arg) => getNestedDeclarations(accAddedDecls, arg), addedDecls.includes(type.reference)
19
+ export const getNestedDeclarationsInIncludeIdentifierType = (addedDecls, type, parentDecl) => type.args.reduce((accAddedDecls, arg) => getNestedDeclarations(accAddedDecls, arg, parentDecl), addedDecls.includes(type.reference)
20
20
  ? addedDecls
21
- : getNestedDeclarations([type.reference, ...addedDecls], type.reference));
21
+ : getNestedDeclarations(addedDecls, type.reference, parentDecl));
22
22
  export const validateIncludeIdentifierType = (helpers, type, value) => validateDecl(helpers, type.reference, type.args, value);
23
23
  export const resolveTypeArgumentsInIncludeIdentifierType = ((args, type) => (isNoGenericIncludeIdentifierType(type)
24
24
  ? type
@@ -1,6 +1,8 @@
1
1
  import { Lazy } from "../../../../shared/utils/lazy.ts";
2
- import type { EntityDecl } from "../../declarations/EntityDecl.ts";
3
- import type { TypeAliasDecl } from "../../declarations/TypeAliasDecl.ts";
2
+ import type { DisplayNameCustomizer } from "../../../utils/displayName.ts";
3
+ import type { EntityDecl } from "../../declarations/EntityDecl.js";
4
+ import { type EntityDisplayName } from "../../declarations/EntityDecl.js";
5
+ import type { TypeAliasDecl } from "../../declarations/TypeAliasDecl.js";
4
6
  import type { GetNestedDeclarations, GetReferences, Predicate, Serializer, TypeArgumentsResolver, Validator } from "../../Node.ts";
5
7
  import { NodeKind } from "../../Node.ts";
6
8
  import type { MemberDecl, ObjectType } from "../generic/ObjectType.ts";
@@ -11,15 +13,23 @@ type PossibleType<T extends TConstraint> = ObjectType<T> | IncludeIdentifier<[],
11
13
  export interface NestedEntityMapType<Name extends string = string, T extends TConstraint = TConstraint> extends BaseType {
12
14
  kind: NodeKind["NestedEntityMapType"];
13
15
  name: Name;
16
+ namePlural: string;
14
17
  comment?: string;
15
18
  secondaryEntity: EntityDecl;
16
19
  type: Lazy<PossibleType<T>>;
17
20
  }
18
21
  export declare const NestedEntityMapType: <Name extends string, T extends TConstraint>(options: {
19
22
  name: Name;
23
+ namePlural: string;
20
24
  comment?: string;
21
25
  secondaryEntity: EntityDecl;
22
26
  type: PossibleType<T>;
27
+ /**
28
+ * @default "name"
29
+ */
30
+ displayName?: EntityDisplayName<T>;
31
+ displayNameCustomizer?: DisplayNameCustomizer<ObjectType<T>>;
32
+ isDeprecated?: boolean;
23
33
  }) => NestedEntityMapType<Name, T>;
24
34
  export { NestedEntityMapType as NestedEntityMap };
25
35
  export declare const isNestedEntityMapType: Predicate<NestedEntityMapType>;
@@ -3,7 +3,7 @@ import { sortObjectKeysAlphabetically } from "../../../../shared/utils/object.js
3
3
  import { parallelizeErrors } from "../../../../shared/utils/validation.js";
4
4
  import { wrapErrorsIfAny } from "../../../utils/error.js";
5
5
  import { entity, json, key as keyColor } from "../../../utils/errorFormatting.js";
6
- import {} from "../../declarations/Declaration.js";
6
+ import {} from "../../declarations/EntityDecl.js";
7
7
  import { getNestedDeclarations, NodeKind, resolveTypeArguments, validateType } from "../../Node.js";
8
8
  import { getReferencesForObjectType, isObjectType, serializeObjectType, } from "../generic/ObjectType.js";
9
9
  import { formatValue } from "../Type.js";
@@ -26,7 +26,7 @@ const _NestedEntityMapType = (options) => {
26
26
  return nestedEntityMapType;
27
27
  };
28
28
  export const isNestedEntityMapType = node => node.kind === NodeKind.NestedEntityMapType;
29
- export const getNestedDeclarationsInNestedEntityMapType = (addedDecls, type) => getNestedDeclarations(addedDecls.includes(type.secondaryEntity) ? addedDecls : [type.secondaryEntity, ...addedDecls], type.type.value);
29
+ export const getNestedDeclarationsInNestedEntityMapType = (addedDecls, type, parentDecl) => getNestedDeclarations(getNestedDeclarations(addedDecls, type.secondaryEntity, parentDecl), type.type.value, parentDecl);
30
30
  export const validateNestedEntityMapType = (helpers, type, value) => {
31
31
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
32
32
  return [TypeError(`expected an object, but got ${json(value, helpers.useStyling)}`)];
@@ -6,9 +6,9 @@ export const ReferenceIdentifierType = (entity) => ({
6
6
  });
7
7
  export { ReferenceIdentifierType as ReferenceIdentifier };
8
8
  export const isReferenceIdentifierType = (node) => node.kind === NodeKind.ReferenceIdentifierType;
9
- export const getNestedDeclarationsInReferenceIdentifierType = (addedDecls, type) => addedDecls.includes(type.entity)
9
+ export const getNestedDeclarationsInReferenceIdentifierType = (addedDecls, type, parentDecl) => addedDecls.includes(type.entity)
10
10
  ? addedDecls
11
- : getNestedDeclarations([...addedDecls, type.entity], type.entity);
11
+ : getNestedDeclarations(addedDecls, type.entity, parentDecl);
12
12
  export const validateReferenceIdentifierType = (helpers, type, value) => validateType(helpers, createEntityIdentifierType(), value).concat(helpers.checkReferentialIntegrity({
13
13
  name: type.entity.name,
14
14
  value: value,
@@ -1,7 +1,7 @@
1
1
  import Debug from "debug";
2
2
  import express, {} from "express";
3
3
  import { join } from "node:path";
4
- import { hasFileChanges } from "../../../shared/utils/git.js";
4
+ import { hasFileChanges, splitBranchName } from "../../../shared/utils/git.js";
5
5
  import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
6
6
  import { attachGitStatusToInstancesByEntityName } from "../../utils/instances.js";
7
7
  import { reinit } from "../init.js";
@@ -10,12 +10,18 @@ const debug = Debug("tsondb:server:api:git");
10
10
  export const gitApi = express.Router();
11
11
  gitApi.use((req, res, next) => {
12
12
  debug(req.path);
13
- if (req.gitRoot === undefined) {
14
- res.status(500).send("Git repository not found");
13
+ if (req.path !== "/" && req.gitRoot === undefined) {
14
+ res.status(400).send("Git repository not found");
15
15
  return;
16
16
  }
17
17
  next();
18
18
  });
19
+ gitApi.get("/", (req, res) => {
20
+ const body = {
21
+ isRepo: req.gitRoot !== undefined,
22
+ };
23
+ res.json(body);
24
+ });
19
25
  gitApi.get("/status", async (req, res) => {
20
26
  const status = await req.git.status();
21
27
  attachGitStatusToInstancesByEntityName(req.instancesByEntityName, req.dataRoot,
@@ -23,6 +29,8 @@ gitApi.get("/status", async (req, res) => {
23
29
  req.gitRoot, status);
24
30
  const getChildInstancesForInstanceId = createChildInstancesForInstanceIdGetter(req);
25
31
  const body = {
32
+ currentBranch: status.current,
33
+ trackingBranch: status.tracking,
26
34
  commitsAhead: status.ahead,
27
35
  commitsBehind: status.behind,
28
36
  instances: Object.fromEntries(Object.entries(req.instancesByEntityName).map(([entityName, instances]) => [
@@ -36,62 +44,94 @@ gitApi.get("/status", async (req, res) => {
36
44
  };
37
45
  res.json(body);
38
46
  });
47
+ gitApi.post("/fetch", async (req, res) => {
48
+ try {
49
+ await req.git.fetch(["--all", "-p"]);
50
+ res.set("Content-Type", "text/plain");
51
+ res.status(200).send("Fetched all remotes");
52
+ }
53
+ catch (error) {
54
+ debug(`${req.path}: ${error.message}`);
55
+ res.set("Content-Type", "text/plain");
56
+ res.status(500).send(error instanceof Error ? error.message : "Fetching all remotes failed");
57
+ }
58
+ });
39
59
  gitApi.post("/stage", async (req, res) => {
40
60
  try {
41
61
  await req.git.add(req.dataRoot);
62
+ res.set("Content-Type", "text/plain");
42
63
  res.status(200).send("Added all database files to index");
43
64
  }
44
65
  catch (error) {
45
66
  debug(`${req.path}: ${error.message}`);
46
- res.status(500).send("Adding all database files to index failed");
67
+ res.set("Content-Type", "text/plain");
68
+ res
69
+ .status(500)
70
+ .send(error instanceof Error ? error.message : "Adding all database files to index failed");
47
71
  }
48
72
  });
49
73
  gitApi.post("/stage/:entityName", async (req, res) => {
50
74
  try {
51
75
  await req.git.add(join(req.dataRoot, req.params.entityName));
76
+ res.set("Content-Type", "text/plain");
52
77
  res.status(200).send(`Added all database files for entity ${req.params.entityName} to index`);
53
78
  }
54
79
  catch (error) {
55
80
  debug(`${req.path}: ${error.message}`);
81
+ res.set("Content-Type", "text/plain");
56
82
  res
57
83
  .status(500)
58
- .send(`Adding all database files for entity ${req.params.entityName} to index failed`);
84
+ .send(error instanceof Error
85
+ ? error.message
86
+ : `Adding all database files for entity ${req.params.entityName} to index failed`);
59
87
  }
60
88
  });
61
89
  gitApi.post("/stage/:entityName/:instanceId", async (req, res) => {
62
90
  try {
63
91
  await req.git.add(join(req.dataRoot, req.params.entityName, `${req.params.instanceId}.json`));
92
+ res.set("Content-Type", "text/plain");
64
93
  res
65
94
  .status(200)
66
95
  .send(`Added database file ${req.params.instanceId} for entity ${req.params.entityName} to index`);
67
96
  }
68
97
  catch (error) {
69
98
  debug(`${req.path}: ${error.message}`);
99
+ res.set("Content-Type", "text/plain");
70
100
  res
71
101
  .status(500)
72
- .send(`Adding database file ${req.params.instanceId} for entity ${req.params.entityName} to index failed`);
102
+ .send(error instanceof Error
103
+ ? error.message
104
+ : `Adding database file ${req.params.instanceId} for entity ${req.params.entityName} to index failed`);
73
105
  }
74
106
  });
75
107
  gitApi.post("/unstage", async (req, res) => {
76
108
  try {
77
109
  await req.git.reset(["HEAD", "--", req.dataRoot]);
110
+ res.set("Content-Type", "text/plain");
78
111
  res.status(200).send("Removed all database files to index");
79
112
  }
80
113
  catch (error) {
81
114
  debug(`${req.path}: ${error.message}`);
82
- res.status(500).send("Removing all database files to index failed");
115
+ res.set("Content-Type", "text/plain");
116
+ res
117
+ .status(500)
118
+ .send(error instanceof Error ? error.message : "Removing all database files to index failed");
83
119
  }
84
120
  });
85
121
  gitApi.post("/unstage/:entityName", async (req, res) => {
86
122
  try {
87
123
  await req.git.reset(["HEAD", "--", join(req.dataRoot, req.params.entityName)]);
124
+ res.set("Content-Type", "text/plain");
88
125
  res.status(200).send(`Removed all database files for entity ${req.params.entityName} to index`);
89
126
  }
90
127
  catch (error) {
91
128
  debug(`${req.path}: ${error.message}`);
129
+ res.set("Content-Type", "text/plain");
92
130
  res
93
131
  .status(500)
94
- .send(`Removing all database files for entity ${req.params.entityName} to index failed`);
132
+ .send(error instanceof Error
133
+ ? error.message
134
+ : `Removing all database files for entity ${req.params.entityName} to index failed`);
95
135
  }
96
136
  });
97
137
  gitApi.post("/unstage/:entityName/:instanceId", async (req, res) => {
@@ -101,78 +141,170 @@ gitApi.post("/unstage/:entityName/:instanceId", async (req, res) => {
101
141
  "--",
102
142
  join(req.dataRoot, req.params.entityName, `${req.params.instanceId}.json`),
103
143
  ]);
144
+ res.set("Content-Type", "text/plain");
104
145
  res
105
146
  .status(200)
106
147
  .send(`Removed database file ${req.params.instanceId} for entity ${req.params.entityName} to index`);
107
148
  }
108
149
  catch (error) {
109
150
  debug(`${req.path}: ${error.message}`);
151
+ res.set("Content-Type", "text/plain");
110
152
  res
111
153
  .status(500)
112
- .send(`Removing database file ${req.params.instanceId} for entity ${req.params.entityName} to index failed`);
154
+ .send(error instanceof Error
155
+ ? error.message
156
+ : `Removing database file ${req.params.instanceId} for entity ${req.params.entityName} to index failed`);
157
+ }
158
+ });
159
+ gitApi.post("/reset", async (req, res) => {
160
+ try {
161
+ await req.git.raw(["restore", "--", req.dataRoot]);
162
+ res.set("Content-Type", "text/plain");
163
+ res.status(200).send("Removed all database files to index");
164
+ }
165
+ catch (error) {
166
+ debug(`${req.path}: ${error.message}`);
167
+ res.set("Content-Type", "text/plain");
168
+ res
169
+ .status(500)
170
+ .send(error instanceof Error ? error.message : "Removing all database files to index failed");
171
+ }
172
+ });
173
+ gitApi.post("/reset/:entityName/:instanceId", async (req, res) => {
174
+ try {
175
+ await req.git.raw([
176
+ "restore",
177
+ "--",
178
+ join(req.dataRoot, req.params.entityName, `${req.params.instanceId}.json`),
179
+ ]);
180
+ await reinit(req);
181
+ res.set("Content-Type", "text/plain");
182
+ res
183
+ .status(200)
184
+ .send(`Removed database file ${req.params.instanceId} for entity ${req.params.entityName} to index`);
185
+ }
186
+ catch (error) {
187
+ debug(`${req.path}: ${error.message}`);
188
+ res.set("Content-Type", "text/plain");
189
+ res
190
+ .status(500)
191
+ .send(error instanceof Error
192
+ ? error.message
193
+ : `Removing database file ${req.params.instanceId} for entity ${req.params.entityName} to index failed`);
113
194
  }
114
195
  });
115
196
  gitApi.post("/commit", async (req, res) => {
116
197
  const message = req.body.message;
117
198
  if (typeof message !== "string" || message.length === 0) {
199
+ res.set("Content-Type", "text/plain");
118
200
  res.status(400).send("Invalid commit message");
119
201
  return;
120
202
  }
121
203
  try {
122
204
  await req.git.commit(message);
205
+ res.set("Content-Type", "text/plain");
123
206
  res.status(200).send("Commit successful");
124
207
  }
125
- catch {
126
- res.status(500).send("Commit failed");
208
+ catch (error) {
209
+ res.set("Content-Type", "text/plain");
210
+ res.status(500).send(error instanceof Error ? error.message : "Commit failed");
127
211
  }
128
212
  });
129
213
  gitApi.post("/push", async (req, res) => {
130
214
  try {
131
- await req.git.push();
215
+ const status = await req.git.status();
216
+ const remotes = await req.git.getRemotes();
217
+ await req.git.push(remotes[0]?.name, status.current ?? undefined);
218
+ res.set("Content-Type", "text/plain");
132
219
  res.status(200).send("Push successful");
133
220
  }
134
- catch {
135
- res.status(500).send("Push failed");
221
+ catch (error) {
222
+ res.set("Content-Type", "text/plain");
223
+ res.status(500).send(error instanceof Error ? error.message : "Push failed");
136
224
  }
137
225
  });
138
226
  gitApi.post("/pull", async (req, res) => {
139
227
  try {
140
- await req.git.pull();
228
+ const status = await req.git.status();
229
+ const remotes = await req.git.getRemotes();
230
+ await req.git.pull(remotes[0]?.name, status.current ?? undefined);
231
+ res.set("Content-Type", "text/plain");
141
232
  res.status(200).send("Pull successful");
142
233
  }
143
- catch {
144
- res.status(500).send("Pull failed");
234
+ catch (error) {
235
+ res.set("Content-Type", "text/plain");
236
+ res.status(500).send(error instanceof Error ? error.message : "Pull failed");
145
237
  }
146
238
  });
147
239
  gitApi.get("/branch", async (req, res) => {
148
- const branchSummary = await req.git.branchLocal();
240
+ const branchSummary = await req.git.branch();
149
241
  const body = {
150
242
  allBranches: branchSummary.all,
151
243
  currentBranch: branchSummary.current,
244
+ isDetached: branchSummary.detached,
245
+ branches: branchSummary.branches,
152
246
  };
153
247
  res.json(body);
154
248
  });
155
249
  gitApi.post("/branch", async (req, res) => {
156
250
  const branchName = req.body.branchName;
157
251
  if (typeof branchName !== "string" || branchName.length === 0) {
252
+ res.set("Content-Type", "text/plain");
158
253
  res.status(400).send("Invalid branch name");
159
254
  return;
160
255
  }
161
256
  try {
162
257
  await req.git.checkoutLocalBranch(branchName);
163
258
  await reinit(req);
259
+ res.set("Content-Type", "text/plain");
164
260
  res.status(200).send(`Creation of branch "${branchName}" successful`);
165
261
  }
166
- catch {
167
- res.status(500).send(`Creation of branch "${branchName}" failed`);
262
+ catch (error) {
263
+ res.set("Content-Type", "text/plain");
264
+ res
265
+ .status(500)
266
+ .send(error instanceof Error ? error.message : `Creation of branch "${branchName}" failed`);
168
267
  }
169
268
  });
170
269
  gitApi.post("/branch/:branchName", async (req, res) => {
270
+ const branchName = decodeURIComponent(req.params.branchName);
171
271
  try {
172
- await req.git.checkout(req.params.branchName);
173
- res.status(200).send(`Switch to branch "${req.params.branchName}" successful`);
272
+ const { remote, name: actualBranch } = splitBranchName(branchName);
273
+ if (remote) {
274
+ debug(`Switch to remote branch "${remote}/${actualBranch}"`);
275
+ }
276
+ else {
277
+ debug(`Switch to local branch "${actualBranch}"`);
278
+ }
279
+ await req.git.raw("switch", actualBranch);
280
+ await reinit(req);
281
+ res.set("Content-Type", "text/plain");
282
+ res.status(200).send(`Switch to branch "${branchName}" successful`);
283
+ }
284
+ catch (error) {
285
+ res.set("Content-Type", "text/plain");
286
+ res
287
+ .status(500)
288
+ .send(error instanceof Error ? error.message : `Switch to branch "${branchName}" failed`);
174
289
  }
175
- catch {
176
- res.status(500).send(`Switch to branch "${req.params.branchName}" failed`);
290
+ });
291
+ gitApi.delete("/branch/:branchName", async (req, res) => {
292
+ const branchName = decodeURIComponent(req.params.branchName);
293
+ try {
294
+ const branchSummary = await req.git.branchLocal();
295
+ if (branchSummary.current === branchName) {
296
+ res.set("Content-Type", "text/plain");
297
+ res.status(400).send("Cannot delete the branch currently checked out");
298
+ return;
299
+ }
300
+ await req.git.deleteLocalBranch(branchName, true);
301
+ res.set("Content-Type", "text/plain");
302
+ res.status(200).send(`Deletion of branch "${branchName}" successful`);
303
+ }
304
+ catch (error) {
305
+ res.set("Content-Type", "text/plain");
306
+ res
307
+ .status(500)
308
+ .send(error instanceof Error ? error.message : `Deletion of branch "${branchName}" failed`);
177
309
  }
178
310
  });