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
@@ -21,6 +21,8 @@ export const createServer = async (schema, dataRootPath, instancesByEntityName,
21
21
  const app = express();
22
22
  app.use(express.static(join(import.meta.dirname, "../../../../public")));
23
23
  app.use("/js/node_modules/preact", staticNodeModule("preact"));
24
+ app.use("/js/node_modules/@preact/signals", staticNodeModule("@preact/signals"));
25
+ app.use("/js/node_modules/@preact/signals-core", staticNodeModule("@preact/signals-core"));
24
26
  app.use("/js/node_modules/preact-iso", staticNodeModule("preact-iso"));
25
27
  app.use("/js/client", express.static(join(import.meta.dirname, "../../../../dist/src/web")));
26
28
  app.use("/js/shared", express.static(join(import.meta.dirname, "../../../../dist/src/shared")));
@@ -42,6 +44,8 @@ export const createServer = async (schema, dataRootPath, instancesByEntityName,
42
44
  "preact/hooks": "/js/node_modules/preact/hooks/dist/hooks.module.js",
43
45
  "preact/jsx-runtime": "/js/node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js",
44
46
  "preact-iso": "/js/node_modules/preact-iso/src/index.js",
47
+ "@preact/signals": "/js/node_modules/@preact/signals/dist/signals.module.js",
48
+ "@preact/signals-core": "/js/node_modules/@preact/signals-core/dist/signals-core.module.js",
45
49
  },
46
50
  }, null, 2);
47
51
  const customStylesheetLinkHeader = customStylesheetPath
@@ -2,7 +2,7 @@ import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
2
2
  import { type Result } from "../../shared/utils/result.ts";
3
3
  import type { EntityDecl } from "../schema/declarations/EntityDecl.js";
4
4
  import { type ReferencesToInstances } from "./references.ts";
5
- export declare const createNewId: () => string;
5
+ export declare const createNewId: () => `${string}-${string}-${string}-${string}-${string}`;
6
6
  export declare const checkCreateInstancePossible: (localeEntity: EntityDecl | undefined, instancesByEntityName: InstancesByEntityName, entity: EntityDecl, instanceContent: unknown, customId: unknown) => Result<string, [code: number, message: string]>;
7
7
  export declare const createInstance: (dataRoot: string, localeEntity: EntityDecl | undefined, instancesByEntityName: InstancesByEntityName, entity: EntityDecl, instanceContent: unknown, customId: unknown) => Promise<Result<string, [code: number, message: string]>>;
8
8
  export declare const checkUpdateInstancePossible: (instancesByEntityName: InstancesByEntityName, entity: EntityDecl, instanceContent: unknown) => Result<void, [code: number, message: string]>;
@@ -1,10 +1,10 @@
1
- import { v4 as uuidv4 } from "uuid";
1
+ import { randomUUID } from "node:crypto";
2
2
  import { error, isOk, map, ok } from "../../shared/utils/result.js";
3
3
  import { createValidators, validateEntityDecl } from "../schema/index.js";
4
4
  import { getErrorMessageForDisplay } from "./error.js";
5
5
  import * as Files from "./files.js";
6
6
  import { isReferencedByOtherInstances } from "./references.js";
7
- export const createNewId = () => uuidv4();
7
+ export const createNewId = () => randomUUID();
8
8
  export const checkCreateInstancePossible = (localeEntity, instancesByEntityName, entity, instanceContent, customId) => {
9
9
  const newInstanceId = entity === localeEntity ? customId : createNewId();
10
10
  if (typeof newInstanceId !== "string") {
@@ -53,16 +53,32 @@ export interface GetAllInstancesResponseBody {
53
53
  }[];
54
54
  };
55
55
  }
56
+ export interface IsRepoResponseBody {
57
+ isRepo: boolean;
58
+ }
56
59
  export interface GitStatusResponseBody {
60
+ currentBranch: string | null;
61
+ trackingBranch: string | null;
57
62
  commitsAhead: number;
58
63
  commitsBehind: number;
59
64
  instances: {
60
65
  [entity: string]: InstanceContainerOverview[];
61
66
  };
62
67
  }
68
+ export interface GetAllGitBranchesResponseBodyBranchSummary {
69
+ current: boolean;
70
+ name: string;
71
+ commit: string;
72
+ label: string;
73
+ linkedWorkTree: boolean;
74
+ }
63
75
  export interface GetAllGitBranchesResponseBody {
64
- allBranches: string[];
76
+ isDetached: boolean;
65
77
  currentBranch: string;
78
+ allBranches: string[];
79
+ branches: {
80
+ [key: string]: GetAllGitBranchesResponseBodyBranchSummary;
81
+ };
66
82
  }
67
83
  export interface CreateCommitRequestBody {
68
84
  message: string;
@@ -27,6 +27,8 @@ export interface SerializedEntityDecl<Name extends string = string, T extends TC
27
27
  isDeprecated?: boolean;
28
28
  }
29
29
  export declare const isSerializedEntityDecl: (node: SerializedNode) => node is SerializedEntityDecl;
30
+ export declare const isSerializedEntityDeclWithParentReference: <Name extends string, T extends TConstraint, FK extends (keyof T & string) | undefined>(decl: SerializedEntityDecl<Name, T, FK>) => decl is SerializedEntityDecl<Name, T, NonNullable<FK>>;
31
+ export declare const isSerializedEntityDeclWithoutParentReference: <Name extends string, T extends TConstraint>(decl: SerializedEntityDecl<Name, T>) => decl is SerializedEntityDecl<Name, T, undefined>;
30
32
  export declare const resolveTypeArgumentsInSerializedEntityDecl: SerializedTypeArgumentsResolver<SerializedEntityDecl>;
31
33
  export declare const getReferencesForSerializedEntityDecl: GetReferencesSerialized<SerializedEntityDecl>;
32
34
  export {};
@@ -1,6 +1,8 @@
1
1
  import { NodeKind, resolveSerializedTypeArguments, } from "../Node.js";
2
2
  import { getReferencesForSerializedObjectType, } from "../types/ObjectType.js";
3
3
  export const isSerializedEntityDecl = (node) => node.kind === NodeKind.EntityDecl;
4
+ export const isSerializedEntityDeclWithParentReference = (decl) => decl.parentReferenceKey !== undefined;
5
+ export const isSerializedEntityDeclWithoutParentReference = (decl) => decl.parentReferenceKey === undefined;
4
6
  export const resolveTypeArgumentsInSerializedEntityDecl = (decls, _args, decl) => ({
5
7
  ...decl,
6
8
  type: resolveSerializedTypeArguments(decls, {}, decl.type),
@@ -1,4 +1,4 @@
1
- import type { GetReferencesSerialized, NodeKind, SerializedTypeArgumentsResolver } from "../Node.ts";
1
+ import { NodeKind, type GetReferencesSerialized, type SerializedNode, type SerializedTypeArgumentsResolver } from "../Node.js";
2
2
  import type { SerializedTypeParameter } from "../TypeParameter.ts";
3
3
  import { type SerializedEnumCaseDecl, type SerializedEnumType } from "../types/EnumType.ts";
4
4
  import type { SerializedBaseDecl } from "./Declaration.ts";
@@ -7,5 +7,6 @@ export interface SerializedEnumDecl<Name extends string = string, T extends Reco
7
7
  type: SerializedEnumType<T>;
8
8
  isDeprecated?: boolean;
9
9
  }
10
+ export declare const isSerializedEnumDecl: (node: SerializedNode) => node is SerializedEnumDecl;
10
11
  export declare const resolveTypeArgumentsInSerializedEnumDecl: SerializedTypeArgumentsResolver<SerializedEnumDecl>;
11
12
  export declare const getReferencesForSerializedEnumDecl: GetReferencesSerialized<SerializedEnumDecl>;
@@ -1,4 +1,6 @@
1
+ import { NodeKind, } from "../Node.js";
1
2
  import { getReferencesForSerializedEnumType, resolveTypeArgumentsInSerializedEnumType, } from "../types/EnumType.js";
3
+ export const isSerializedEnumDecl = (node) => node.kind === NodeKind.EnumDecl;
2
4
  export const resolveTypeArgumentsInSerializedEnumDecl = (decls, args, decl) => {
3
5
  return {
4
6
  ...decl,
@@ -1,4 +1,4 @@
1
- import { type GetReferencesSerialized, type NodeKind, type SerializedTypeArgumentsResolver } from "../Node.ts";
1
+ import { NodeKind, type GetReferencesSerialized, type SerializedNode, type SerializedTypeArgumentsResolver } from "../Node.ts";
2
2
  import type { SerializedTypeParameter } from "../TypeParameter.ts";
3
3
  import type { SerializedType } from "../types/Type.ts";
4
4
  import type { SerializedBaseDecl } from "./Declaration.ts";
@@ -7,5 +7,6 @@ export interface SerializedTypeAliasDecl<Name extends string = string, T extends
7
7
  type: T;
8
8
  isDeprecated?: boolean;
9
9
  }
10
+ export declare const isSerializedTypeAliasDecl: (node: SerializedNode) => node is SerializedTypeAliasDecl;
10
11
  export declare const resolveTypeArgumentsInSerializedTypeAliasDecl: SerializedTypeArgumentsResolver<SerializedTypeAliasDecl>;
11
12
  export declare const getReferencesForSerializedTypeAliasDecl: GetReferencesSerialized<SerializedTypeAliasDecl>;
@@ -1,4 +1,5 @@
1
- import { getReferencesSerialized, resolveSerializedTypeArguments, } from "../Node.js";
1
+ import { getReferencesSerialized, NodeKind, resolveSerializedTypeArguments, } from "../Node.js";
2
+ export const isSerializedTypeAliasDecl = (node) => node.kind === NodeKind.TypeAliasDecl;
2
3
  export const resolveTypeArgumentsInSerializedTypeAliasDecl = (decls, args, decl) => ({
3
4
  ...decl,
4
5
  parameters: [],
@@ -10,3 +10,7 @@ export type GitFileStatusForDisplay = "U" | "M" | "A" | "D" | "R" | undefined;
10
10
  export declare const hasFileChanges: (fileStatus: GitFileStatus | undefined) => boolean;
11
11
  export declare const getGitStatusForDisplay: (fileStatus: GitFileStatus | undefined) => GitFileStatusForDisplay;
12
12
  export declare const getLabelForGitStatus: (status: GitFileStatusForDisplay) => string;
13
+ export declare const splitBranchName: (branch: string) => {
14
+ remote?: string;
15
+ name: string;
16
+ };
@@ -87,7 +87,7 @@ export const getLabelForGitStatus = (status) => {
87
87
  case "M":
88
88
  return "modified";
89
89
  case "A":
90
- return "sdded";
90
+ return "added";
91
91
  case "D":
92
92
  return "deleted";
93
93
  case "R":
@@ -96,3 +96,8 @@ export const getLabelForGitStatus = (status) => {
96
96
  return "";
97
97
  }
98
98
  };
99
+ const remotePattern = /^remotes\/(\w+?)\/(.+)$/;
100
+ export const splitBranchName = (branch) => {
101
+ const [_, remote, actualBranch] = branch.match(remotePattern) ?? ["", undefined, branch];
102
+ return { remote, name: actualBranch };
103
+ };
@@ -2,50 +2,69 @@ type TextNode = {
2
2
  kind: "text";
3
3
  content: string;
4
4
  };
5
- export type InlineMarkdownNode = {
6
- kind: "bold" | "italic";
5
+ type BoldMarkdownNode = {
6
+ kind: "bold";
7
7
  content: InlineMarkdownNode[];
8
- } | {
8
+ };
9
+ type ItalicMarkdownNode = {
10
+ kind: "italic";
11
+ content: InlineMarkdownNode[];
12
+ };
13
+ type CodeMarkdownNode = {
9
14
  kind: "code";
10
15
  content: string;
11
- } | {
16
+ };
17
+ type LinkMarkdownNode = {
12
18
  kind: "link";
13
19
  href: string;
14
20
  content: InlineMarkdownNode[];
15
- } | {
21
+ };
22
+ type AttributedStringMarkdownNode = {
16
23
  kind: "attributed";
17
24
  attributes: Record<string, string | number | boolean>;
18
25
  content: InlineMarkdownNode[];
19
- } | TextNode;
20
- export type BlockMarkdownNode = {
26
+ };
27
+ export type InlineMarkdownNode = BoldMarkdownNode | ItalicMarkdownNode | CodeMarkdownNode | LinkMarkdownNode | AttributedStringMarkdownNode | TextNode;
28
+ type ParagraphBlockNode = {
21
29
  kind: "paragraph";
22
30
  content: InlineMarkdownNode[];
23
- } | {
31
+ };
32
+ type HeadingBlockNode = {
24
33
  kind: "heading";
25
34
  level: number;
26
35
  content: InlineMarkdownNode[];
27
- } | {
36
+ };
37
+ type ListBlockNode = {
28
38
  kind: "list";
29
39
  ordered: boolean;
30
- content: {
31
- kind: "listitem";
32
- content: InlineMarkdownNode[];
33
- }[];
34
- } | {
40
+ content: ListItemNode[];
41
+ };
42
+ type ListItemNode = {
43
+ kind: "listitem";
44
+ content: InlineMarkdownNode[];
45
+ };
46
+ type TableBlockNode = {
35
47
  kind: "table";
48
+ caption?: InlineMarkdownNode[];
36
49
  header: InlineMarkdownNode[][];
37
50
  rows: InlineMarkdownNode[][][];
38
51
  };
39
- export type BlockSyntaxMarkdownNode = InlineMarkdownNode | {
52
+ export type BlockMarkdownNode = ParagraphBlockNode | HeadingBlockNode | ListBlockNode | TableBlockNode;
53
+ type ListItemMarkerSyntaxNode = {
40
54
  kind: "listitemmarker";
41
55
  content: string;
42
- } | {
56
+ };
57
+ type TableMarkerSyntaxNode = {
43
58
  kind: "tablemarker";
44
59
  content: string;
45
- } | {
60
+ };
61
+ type HeadingMarkerSyntaxNode = {
46
62
  kind: "headingmarker";
47
63
  content: string;
48
64
  };
65
+ type SyntaxNode = ListItemMarkerSyntaxNode | TableMarkerSyntaxNode | HeadingMarkerSyntaxNode;
66
+ export type BlockSyntaxMarkdownNode = InlineMarkdownNode | SyntaxNode;
49
67
  export declare const parseBlockMarkdown: (text: string) => BlockMarkdownNode[];
50
68
  export declare const parseBlockMarkdownForSyntaxHighlighting: (text: string) => BlockSyntaxMarkdownNode[];
69
+ export declare const reduceSyntaxNodes: <T extends BlockSyntaxMarkdownNode>(nodes: T[]) => T[];
51
70
  export {};
@@ -1,3 +1,5 @@
1
+ import { omitUndefinedKeys } from "./object.js";
2
+ import { assertExhaustive } from "./typeSafety.js";
1
3
  const codeRule = {
2
4
  pattern: /`(.*?)`/,
3
5
  map: result => ({
@@ -122,13 +124,12 @@ const parsedAttributesLength = (rawAttributes) => rawAttributes.reduce((sum, att
122
124
  : attr.name.length + attr.separator.length + attr.rawValue.length), 0);
123
125
  const attributedRule = {
124
126
  pattern: /(?<!\\)\^\[(.*?[^\\])\]\(((?:\w+: *(?:true|false|\d+(?:\.\d+)?|"(.*?)(?<!\\)"|'(.*?)(?<!\\)'))(?:, *\w+: *(?:true|false|\d+(?:\.\d+)?|"(.*?)(?<!\\)"|'(.*?)(?<!\\)'))*)\)/,
125
- map: (result, parseInside) => ({
127
+ map: ([_res, content = "", attributesText = ""], parseInside) => ({
126
128
  kind: "attributed",
127
- attributes: mapAttributesToObject(parseAttributes(result[2] ?? "")),
128
- content: parseInside(result[1] ?? ""),
129
+ attributes: mapAttributesToObject(parseAttributes(attributesText)),
130
+ content: parseInside(content),
129
131
  }),
130
- mapHighlighting: (result, parseInside) => {
131
- const attributesText = result[2] ?? "";
132
+ mapHighlighting: ([_res, content = "", attributesText = ""], parseInside) => {
132
133
  const attributes = parseAttributes(attributesText);
133
134
  const length = parsedAttributesLength(attributes);
134
135
  const unparsedText = attributesText.length > length
@@ -139,7 +140,7 @@ const attributedRule = {
139
140
  attributes: mapAttributesToObject(attributes),
140
141
  content: [
141
142
  textNode("^["),
142
- ...parseInside(result[1] ?? ""),
143
+ ...parseInside(content),
143
144
  textNode("]("),
144
145
  ...mapAttributesToNodes(attributes),
145
146
  ...unparsedText,
@@ -154,7 +155,7 @@ const textNode = (content) => ({
154
155
  });
155
156
  const parseEscapedCharacters = (text) => text.replace(/\\([*_`[\]()\\])/g, "$1");
156
157
  const textRule = {
157
- pattern: /.+/,
158
+ pattern: /.+/s,
158
159
  map: result => ({
159
160
  kind: "text",
160
161
  content: parseEscapedCharacters(result[0]),
@@ -225,13 +226,13 @@ const listRule = {
225
226
  };
226
227
  const paragraphRule = {
227
228
  pattern: /^((?:[^\n]+?)(?:\n[^\n]+?)*)(\n{2,}|\s*$)/,
228
- map: result => ({
229
+ map: ([_res, content = "", _trailingWhitespace]) => ({
229
230
  kind: "paragraph",
230
- content: parseInlineMarkdown(result[1] ?? "", false),
231
+ content: parseInlineMarkdown(content, false),
231
232
  }),
232
- mapHighlighting: result => [
233
- ...parseInlineMarkdown(result[1] ?? "", true),
234
- ...nodesForTrailingWhitespace(result[2]),
233
+ mapHighlighting: ([_res, content = "", trailingWhitespace]) => [
234
+ ...parseInlineMarkdown(content, true),
235
+ ...nodesForTrailingWhitespace(trailingWhitespace),
235
236
  ],
236
237
  };
237
238
  const headingRule = {
@@ -253,26 +254,35 @@ const tableMarker = (text) => ({
253
254
  content: text,
254
255
  });
255
256
  const tableRule = {
256
- pattern: /^(\| *)?(.+?(?: *(?<!\\)\| *.+?)+)( *\|)?\n((?:\| *)?(?:-{3,}|:-{2,}|-{2,}:|:-+:)(?: *\| *(?:-{3,}|:-{2,}|-{2,}:|:-+:))*(?: *\|)?)((?:\n\|? *.+?(?: *(?<!\\)\| *.+?)* *(?<!\\)\|?)+)(\n{2,}|$)/,
257
- map: result => ({
257
+ pattern: /^(?:(\|#)(.+?)(#\|)\n)?(\|)?(.+?(?:(?<!\\)\|.+?)+)((?<!\\)\|)?\n((?:\| *)?(?:-{3,}|:-{2,}|-{2,}:|:-+:)(?: *\| *(?:-{3,}|:-{2,}|-{2,}:|:-+:))*(?: *\|)?)((?:\n\|?.+?(?:(?<!\\)\|.+?)*(?<!\\)\|?)+)(\n{2,}|$)/,
258
+ map: ([_res, _captionMarkerStart, caption, _captionMarkerEnd, _headerMarkerStart, headers, _headerMarkerEnd, _bodySeparators, body, _trailingWhitespace,]) => omitUndefinedKeys({
258
259
  kind: "table",
259
- header: result[2]?.split("|").map(th => parseInlineMarkdown(th.trim(), false)) ?? [],
260
- rows: result[5]
260
+ caption: caption !== undefined ? parseInlineMarkdown(caption.trim(), false) : undefined,
261
+ header: headers?.split("|").map(th => parseInlineMarkdown(th.trim(), false)) ?? [],
262
+ rows: body
261
263
  ?.split("\n")
262
264
  .slice(1)
263
265
  .map(tr => removeSurroundingPipes(tr)
264
266
  .split("|")
265
267
  .map(tc => parseInlineMarkdown(tc.trim(), false))) ?? [],
266
268
  }),
267
- mapHighlighting: result => [
268
- tableMarker(result[1] ?? ""),
269
- ...(result[2]
269
+ mapHighlighting: ([_res, captionMarkerStart, caption, captionMarkerEnd, headerMarkerStart, headers, headerMarkerEnd, bodySeparators, body, trailingWhitespace,]) => [
270
+ ...(caption !== undefined
271
+ ? [
272
+ tableMarker(captionMarkerStart ?? ""),
273
+ ...parseInlineMarkdown(caption, true),
274
+ tableMarker(captionMarkerEnd ?? ""),
275
+ textNode("\n"),
276
+ ]
277
+ : []),
278
+ tableMarker(headerMarkerStart ?? ""),
279
+ ...(headers
270
280
  ?.split("|")
271
281
  .flatMap((th, i) => i === 0
272
282
  ? parseInlineMarkdown(th, true)
273
283
  : [tableMarker("|"), ...parseInlineMarkdown(th, true)]) ?? []),
274
- tableMarker((result[3] ?? "") + "\n" + (result[4] ?? "")),
275
- ...(result[5]
284
+ tableMarker((headerMarkerEnd ?? "") + "\n" + (bodySeparators ?? "")),
285
+ ...(body
276
286
  ?.split("\n")
277
287
  .slice(1)
278
288
  .flatMap((tr) => [
@@ -283,7 +293,7 @@ const tableRule = {
283
293
  ? parseInlineMarkdown(tc, true)
284
294
  : [tableMarker("|"), ...parseInlineMarkdown(tc, true)]),
285
295
  ]) ?? []),
286
- ...nodesForTrailingWhitespace(result[6]),
296
+ ...nodesForTrailingWhitespace(trailingWhitespace),
287
297
  ],
288
298
  };
289
299
  const blockRules = [headingRule, tableRule, listRule, paragraphRule];
@@ -310,4 +320,50 @@ const parseForBlockRules = (rules, text, ruleParser, remainingRules = rules) =>
310
320
  }
311
321
  };
312
322
  export const parseBlockMarkdown = (text) => parseForBlockRules(blockRules, text, parseActiveBlockRule);
313
- export const parseBlockMarkdownForSyntaxHighlighting = (text) => parseForBlockRules(blockRules, text, parseActiveBlockSyntaxRule);
323
+ export const parseBlockMarkdownForSyntaxHighlighting = (text) => reduceSyntaxNodes(parseForBlockRules(blockRules, text, parseActiveBlockSyntaxRule));
324
+ export const reduceSyntaxNodes = (nodes) => nodes.reduce((reducedNodes, node, index) => {
325
+ const lastNode = index > 0 ? reducedNodes[reducedNodes.length - 1] : undefined;
326
+ const newLastNode = lastNode ? mergeSyntaxNodes(lastNode, node) : null;
327
+ if (newLastNode) {
328
+ reducedNodes[reducedNodes.length - 1] = reduceSyntaxNode(newLastNode);
329
+ }
330
+ else {
331
+ reducedNodes.push(reduceSyntaxNode(node));
332
+ }
333
+ return reducedNodes;
334
+ }, []);
335
+ const reduceSyntaxNode = (node) => {
336
+ switch (node.kind) {
337
+ case "bold":
338
+ case "italic":
339
+ return { ...node, content: reduceSyntaxNodes(node.content) };
340
+ case "code":
341
+ case "link":
342
+ case "attributed":
343
+ case "text":
344
+ case "listitemmarker":
345
+ case "tablemarker":
346
+ case "headingmarker":
347
+ return node;
348
+ default:
349
+ return assertExhaustive(node);
350
+ }
351
+ };
352
+ const syntaxNodeMergeRules = {
353
+ bold: (a, b) => ({ ...a, content: [...a.content, ...b.content] }),
354
+ italic: (a, b) => ({ ...a, content: [...a.content, ...b.content] }),
355
+ code: (a, b) => ({ ...a, content: a.content + b.content }),
356
+ text: (a, b) => ({ ...a, content: a.content + b.content }),
357
+ listitemmarker: (a, b) => ({ ...a, content: a.content + b.content }),
358
+ tablemarker: (a, b) => ({ ...a, content: a.content + b.content }),
359
+ headingmarker: (a, b) => ({ ...a, content: a.content + b.content }),
360
+ link: null,
361
+ attributed: null,
362
+ };
363
+ const mergeSyntaxNodes = (lastNode, node) => {
364
+ if (lastNode.kind !== node.kind) {
365
+ return null;
366
+ }
367
+ const mergeFn = syntaxNodeMergeRules[lastNode.kind];
368
+ return mergeFn?.(lastNode, node) ?? null;
369
+ };
@@ -6,3 +6,4 @@ export type Leaves<T> = T extends object ? {
6
6
  }[keyof T] : never;
7
7
  export declare const onlyKeys: <T extends object, K extends keyof T>(obj: T, ...keys: K[]) => Pick<T, K>;
8
8
  export declare const hasKey: <T extends object, K extends PropertyKey>(obj: T, key: K) => obj is T & { [k in K]: unknown; };
9
+ export declare const omitUndefinedKeys: <T extends object>(obj: T) => T;
@@ -10,3 +10,4 @@ export const mergeObjects = (obj1, obj2, solveConflict) => Object.entries(obj2).
10
10
  }), obj1);
11
11
  export const onlyKeys = (obj, ...keys) => Object.fromEntries(Object.entries(obj).filter(([key]) => keys.includes(key)));
12
12
  export const hasKey = (obj, key) => Object.hasOwn(obj, key);
13
+ export const omitUndefinedKeys = (obj) => Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined));
@@ -1,14 +1,18 @@
1
- import type { GetAllGitBranchesResponseBody, GitStatusResponseBody } from "../../shared/api.ts";
1
+ import type { GetAllGitBranchesResponseBody, GitStatusResponseBody, IsRepoResponseBody } from "../../shared/api.ts";
2
+ export declare const isRepo: (locales: string[]) => Promise<IsRepoResponseBody>;
2
3
  export declare const getStatus: (locales: string[]) => Promise<GitStatusResponseBody>;
4
+ export declare const fetch: (locales: string[]) => Promise<void>;
3
5
  export declare const stageAllFiles: (locales: string[]) => Promise<void>;
4
6
  export declare const stageAllFilesOfEntity: (locales: string[], entityName: string) => Promise<void>;
5
7
  export declare const stageFileOfEntity: (locales: string[], entityName: string, id: string) => Promise<void>;
6
8
  export declare const unstageAllFiles: (locales: string[]) => Promise<void>;
7
9
  export declare const unstageAllFilesOfEntity: (locales: string[], entityName: string) => Promise<void>;
8
10
  export declare const unstageFileOfEntity: (locales: string[], entityName: string, id: string) => Promise<void>;
11
+ export declare const resetFileOfEntity: (locales: string[], entityName: string, id: string) => Promise<void>;
9
12
  export declare const commitStagedFiles: (locales: string[], message: string) => Promise<void>;
10
13
  export declare const pushCommits: (locales: string[]) => Promise<void>;
11
14
  export declare const pullCommits: (locales: string[]) => Promise<void>;
12
15
  export declare const getBranches: (locales: string[]) => Promise<GetAllGitBranchesResponseBody>;
13
16
  export declare const createBranch: (locales: string[], branchName: string) => Promise<void>;
14
17
  export declare const switchBranch: (locales: string[], branchName: string) => Promise<void>;
18
+ export declare const deleteBranch: (locales: string[], branchName: string) => Promise<void>;
@@ -1,11 +1,14 @@
1
- import { getResource, postResource } from "../utils/api.js";
1
+ import { deleteResource, getResource, postResource } from "../utils/api.js";
2
+ export const isRepo = async (locales) => getResource("/api/git", { locales });
2
3
  export const getStatus = async (locales) => getResource("/api/git/status", { locales });
4
+ export const fetch = async (locales) => postResource("/api/git/fetch", { locales });
3
5
  export const stageAllFiles = async (locales) => postResource("/api/git/stage", { locales });
4
6
  export const stageAllFilesOfEntity = async (locales, entityName) => postResource(`/api/git/stage/${entityName}`, { locales });
5
7
  export const stageFileOfEntity = async (locales, entityName, id) => postResource(`/api/git/stage/${entityName}/${id}`, { locales });
6
8
  export const unstageAllFiles = async (locales) => postResource(`/api/git/unstage`, { locales });
7
9
  export const unstageAllFilesOfEntity = async (locales, entityName) => postResource(`/api/git/unstage/${entityName}`, { locales });
8
10
  export const unstageFileOfEntity = async (locales, entityName, id) => postResource(`/api/git/unstage/${entityName}/${id}`, { locales });
11
+ export const resetFileOfEntity = async (locales, entityName, id) => postResource(`/api/git/reset/${entityName}/${id}`, { locales });
9
12
  export const commitStagedFiles = async (locales, message) => {
10
13
  const body = { message };
11
14
  return postResource(`/api/git/commit`, { locales, body });
@@ -17,4 +20,5 @@ export const createBranch = async (locales, branchName) => {
17
20
  const body = { branchName };
18
21
  return postResource(`/api/git/branch`, { locales, body });
19
22
  };
20
- export const switchBranch = async (locales, branchName) => postResource(`/api/git/branch/${branchName}`, { locales });
23
+ export const switchBranch = async (locales, branchName) => postResource(`/api/git/branch/${encodeURIComponent(branchName)}`, { locales });
24
+ export const deleteBranch = async (locales, branchName) => deleteResource(`/api/git/branch/${encodeURIComponent(branchName)}`, { locales });
@@ -0,0 +1,7 @@
1
+ import type { ComponentChildren, Context } from "preact";
2
+ export type ContextProviderWrapper = <T>(props: {
3
+ children?: ComponentChildren;
4
+ context: Context<T>;
5
+ useValue: () => T;
6
+ }) => ComponentChildren;
7
+ export declare const ContextProviderWrapper: ContextProviderWrapper;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ export const ContextProviderWrapper = props => {
3
+ const { children, context, useValue } = props;
4
+ return _jsx(context.Provider, { value: useValue(), children: children });
5
+ };
@@ -25,6 +25,7 @@ export type InstanceRouteSkeletonOnSubmitHandler = (values: {
25
25
  setInstanceContent: Dispatch<SetStateAction<unknown>>;
26
26
  setCustomId: Dispatch<SetStateAction<string>>;
27
27
  getDeclFromDeclName: GetDeclFromDeclName;
28
+ updateLocalGitState?: () => Promise<void>;
28
29
  }) => Promise<void>;
29
30
  export type InstanceRouteSkeletonTitleBuilder = (values: {
30
31
  locales: string[];
@@ -34,6 +35,11 @@ export type InstanceRouteSkeletonTitleBuilder = (values: {
34
35
  }) => string | undefined;
35
36
  type Props = {
36
37
  mode: "create" | "edit";
38
+ buttons: {
39
+ label: string;
40
+ name: string;
41
+ primary?: boolean;
42
+ }[];
37
43
  init: InstanceRouteSkeletonInitializer;
38
44
  titleBuilder: InstanceRouteSkeletonTitleBuilder;
39
45
  onSubmit: InstanceRouteSkeletonOnSubmitHandler;
@@ -1,21 +1,23 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { useLocation, useRoute } from "preact-iso";
3
- import { useCallback, useEffect, useState } from "preact/hooks";
3
+ import { useCallback, useContext, useEffect, useState } from "preact/hooks";
4
4
  import { removeAt } from "../../shared/utils/array.js";
5
5
  import { getSerializedDisplayNameFromEntityInstance } from "../../shared/utils/displayName.js";
6
6
  import { toTitleCase } from "../../shared/utils/string.js";
7
7
  import { validateLocaleIdentifier } from "../../shared/validation/identifier.js";
8
8
  import { deleteInstanceByEntityNameAndId, getChildInstancesForInstanceByEntityName, } from "../api/declarations.js";
9
+ import { GitClientContext } from "../context/gitClient.js";
9
10
  import { useEntityFromRoute } from "../hooks/useEntityFromRoute.js";
10
11
  import { useInstanceNamesByEntity } from "../hooks/useInstanceNamesByEntity.js";
11
12
  import { useGetDeclFromDeclName, } from "../hooks/useSecondaryDeclarations.js";
12
13
  import { useSetting } from "../hooks/useSettings.js";
13
14
  import { homeTitle } from "../routes/Home.js";
14
15
  import { NotFound } from "../routes/NotFound.js";
16
+ import { runWithLoading } from "../signals/loading.js";
15
17
  import { Layout } from "./Layout.js";
16
18
  import { TypeInput } from "./typeInputs/TypeInput.js";
17
19
  import { ValidationErrors } from "./typeInputs/utils/ValidationErrors.js";
18
- export const InstanceRouteSkeleton = ({ mode, init, titleBuilder, onSubmit, }) => {
20
+ export const InstanceRouteSkeleton = ({ mode, buttons, init, titleBuilder, onSubmit, }) => {
19
21
  const { params: { name, id }, } = useRoute();
20
22
  const [locales] = useSetting("displayedLocales");
21
23
  const [getDeclFromDeclName, declsLoaded] = useGetDeclFromDeclName();
@@ -24,6 +26,7 @@ export const InstanceRouteSkeleton = ({ mode, init, titleBuilder, onSubmit, }) =
24
26
  const [instanceContent, setInstanceContent] = useState();
25
27
  const [childInstances, setChildInstances] = useState([]);
26
28
  const [customId, setCustomId] = useState("");
29
+ const client = useContext(GitClientContext);
27
30
  const { route } = useLocation();
28
31
  useEffect(() => {
29
32
  document.title =
@@ -31,8 +34,8 @@ export const InstanceRouteSkeleton = ({ mode, init, titleBuilder, onSubmit, }) =
31
34
  "Not found — TSONDB";
32
35
  }, [entity, id, instanceContent, locales, titleBuilder]);
33
36
  useEffect(() => {
34
- if (entity && instanceContent === undefined) {
35
- init({ locales, entity, instanceId: id, setInstanceContent, getDeclFromDeclName })
37
+ if (entity && instanceContent === undefined && declsLoaded) {
38
+ runWithLoading(() => init({ locales, entity, instanceId: id, setInstanceContent, getDeclFromDeclName }))
36
39
  .then(() => id
37
40
  ? getChildInstancesForInstanceByEntityName(locales, entity.name, id).then(result => {
38
41
  setChildInstances(result.instances);
@@ -42,12 +45,12 @@ export const InstanceRouteSkeleton = ({ mode, init, titleBuilder, onSubmit, }) =
42
45
  console.error("Error initializing instance route skeleton:", error);
43
46
  });
44
47
  }
45
- }, [entity, getDeclFromDeclName, id, init, instanceContent, locales, name]);
48
+ }, [entity, declsLoaded, getDeclFromDeclName, id, init, instanceContent, locales, name]);
46
49
  const handleSubmit = (event) => {
47
50
  event.preventDefault();
48
51
  if (entity && instanceContent !== undefined) {
49
52
  const buttonName = event.submitter?.getAttribute("name") ?? undefined;
50
- onSubmit({
53
+ runWithLoading(() => onSubmit({
51
54
  locales,
52
55
  entity,
53
56
  instanceId: id,
@@ -60,7 +63,8 @@ export const InstanceRouteSkeleton = ({ mode, init, titleBuilder, onSubmit, }) =
60
63
  setCustomId,
61
64
  setInstanceContent,
62
65
  childInstances,
63
- }).catch((error) => {
66
+ updateLocalGitState: client?.updateLocalState,
67
+ })).catch((error) => {
64
68
  console.error("Error submitting instance data:", error);
65
69
  });
66
70
  }
@@ -110,5 +114,5 @@ export const InstanceRouteSkeleton = ({ mode, init, titleBuilder, onSubmit, }) =
110
114
  }
111
115
  }, children: "Delete" }))] }), !id && isLocaleEntity && (_jsxs("div", { class: "field field--id", children: [_jsx("label", { htmlFor: "id", children: "ID" }), _jsx("p", { className: "comment", children: "The instance\u2019s identifier. An IETF language tag (BCP47)." }), _jsx("input", { type: "text", id: "id", value: customId, required: true, pattern: "[a-z]{2,3}(-[A-Z]{2,3})?", placeholder: "en-US, de-DE, \u2026", onInput: event => {
112
116
  setCustomId(event.currentTarget.value);
113
- }, "aria-invalid": idErrors.length > 0 }), _jsx(ValidationErrors, { errors: idErrors })] })), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entity.type, value: instanceContent, path: undefined, instanceNamesByEntity: instanceNamesByEntity, childInstances: childInstances, getDeclFromDeclName: getDeclFromDeclName, onChange: setInstanceContent, onChildChange: handleOnChildChange, onChildAdd: handleOnChildAdd, onChildRemove: handleOnChildRemove }), _jsx("div", { class: "form-footer btns", children: _jsx("button", { type: "submit", name: "save", class: "primary", children: "Save" }) })] })] }));
117
+ }, "aria-invalid": idErrors.length > 0 }), _jsx(ValidationErrors, { errors: idErrors })] })), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entity.type, value: instanceContent, path: undefined, instanceNamesByEntity: instanceNamesByEntity, childInstances: childInstances, getDeclFromDeclName: getDeclFromDeclName, onChange: setInstanceContent, onChildChange: handleOnChildChange, onChildAdd: handleOnChildAdd, onChildRemove: handleOnChildRemove }), _jsx("div", { class: "form-footer btns", children: buttons.map(button => (_jsx("button", { type: "submit", name: button.name, class: button.primary ? "primary" : undefined, children: button.label }, button.name))) })] })] }));
114
118
  };
@@ -0,0 +1 @@
1
+ export declare const LoadingOverlay: () => import("preact").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import { jsx as _jsx } from "preact/jsx-runtime";
2
+ import { loading } from "../signals/loading.js";
3
+ export const LoadingOverlay = () => (_jsx("div", { class: "loading-overlay" + (loading.value ? " loading-overlay--open" : ""), "aria-hidden": !loading.value, children: "Loading\u2026" }));
@@ -1,6 +1,7 @@
1
- import { jsx as _jsx } from "preact/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { useEffect, useRef } from "preact/hooks";
3
- export const ModalDialog = props => {
3
+ import { LoadingOverlay } from "./LoadingOverlay.js";
4
+ export const ModalDialog = ({ children, ...props }) => {
4
5
  const ref = useRef(null);
5
6
  useEffect(() => {
6
7
  if (ref.current) {
@@ -12,5 +13,5 @@ export const ModalDialog = props => {
12
13
  }
13
14
  }
14
15
  }, [props.open]);
15
- return _jsx("dialog", { ...props, open: undefined, ref: ref });
16
+ return (_jsxs("dialog", { ...props, open: undefined, ref: ref, children: [_jsx(LoadingOverlay, {}), children] }));
16
17
  };