tsondb 0.5.8 → 0.5.12

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 (68) hide show
  1. package/dist/src/bin/tsondb.d.ts +1 -9
  2. package/dist/src/bin/tsondb.js +7 -0
  3. package/dist/src/index.d.ts +1 -1
  4. package/dist/src/node/index.d.ts +2 -2
  5. package/dist/src/node/index.js +8 -2
  6. package/dist/src/node/renderers/jsonschema/index.d.ts +1 -1
  7. package/dist/src/node/renderers/ts/index.d.ts +1 -1
  8. package/dist/src/node/{Schema.d.ts → schema/Schema.d.ts} +2 -2
  9. package/dist/src/node/{Schema.js → schema/Schema.js} +10 -7
  10. package/dist/src/node/schema/declarations/EntityDecl.d.ts +31 -30
  11. package/dist/src/node/schema/declarations/EntityDecl.js +1 -0
  12. package/dist/src/node/server/api/declarations.js +3 -4
  13. package/dist/src/node/server/api/git.js +1 -2
  14. package/dist/src/node/server/api/instances.js +2 -3
  15. package/dist/src/node/server/index.d.ts +7 -2
  16. package/dist/src/node/server/index.js +10 -10
  17. package/dist/src/node/server/init.d.ts +1 -1
  18. package/dist/src/node/server/init.js +10 -0
  19. package/dist/src/node/utils/displayName.d.ts +3 -0
  20. package/dist/src/node/utils/displayName.js +19 -0
  21. package/dist/src/shared/config.d.ts +11 -0
  22. package/dist/src/{node/renderers/Output.d.ts → shared/output.d.ts} +1 -1
  23. package/dist/src/shared/output.js +1 -0
  24. package/dist/src/shared/utils/compare.js +6 -1
  25. package/dist/src/shared/utils/displayName.d.ts +1 -1
  26. package/dist/src/shared/utils/displayName.js +1 -1
  27. package/dist/src/shared/utils/instances.d.ts +3 -2
  28. package/dist/src/shared/utils/instances.js +3 -3
  29. package/dist/src/shared/utils/markdown.js +1 -1
  30. package/dist/src/web/components/Git.js +1 -1
  31. package/dist/src/web/components/typeInputs/ArrayTypeInput.d.ts +2 -1
  32. package/dist/src/web/components/typeInputs/ArrayTypeInput.js +10 -6
  33. package/dist/src/web/components/typeInputs/BooleanTypeInput.d.ts +1 -1
  34. package/dist/src/web/components/typeInputs/BooleanTypeInput.js +4 -0
  35. package/dist/src/web/components/typeInputs/DateTypeInput.d.ts +1 -1
  36. package/dist/src/web/components/typeInputs/DateTypeInput.js +4 -0
  37. package/dist/src/web/components/typeInputs/EnumTypeInput.d.ts +1 -0
  38. package/dist/src/web/components/typeInputs/EnumTypeInput.js +2 -2
  39. package/dist/src/web/components/typeInputs/FloatTypeInput.d.ts +1 -1
  40. package/dist/src/web/components/typeInputs/FloatTypeInput.js +5 -1
  41. package/dist/src/web/components/typeInputs/IncludeIdentifierTypeInput.d.ts +1 -0
  42. package/dist/src/web/components/typeInputs/IncludeIdentifierTypeInput.js +2 -2
  43. package/dist/src/web/components/typeInputs/IntegerTypeInput.d.ts +1 -1
  44. package/dist/src/web/components/typeInputs/IntegerTypeInput.js +5 -1
  45. package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.d.ts +2 -1
  46. package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.js +21 -12
  47. package/dist/src/web/components/typeInputs/ObjectTypeInput.d.ts +2 -1
  48. package/dist/src/web/components/typeInputs/ObjectTypeInput.js +9 -4
  49. package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.d.ts +1 -1
  50. package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.js +5 -1
  51. package/dist/src/web/components/typeInputs/StringTypeInput.d.ts +1 -1
  52. package/dist/src/web/components/typeInputs/StringTypeInput.js +11 -3
  53. package/dist/src/web/components/typeInputs/TypeInput.d.ts +3 -2
  54. package/dist/src/web/components/typeInputs/TypeInput.js +16 -58
  55. package/dist/src/web/hooks/useInstanceNamesByEntity.d.ts +1 -1
  56. package/dist/src/web/hooks/useInstanceNamesByEntity.js +1 -1
  57. package/dist/src/web/hooks/useSecondaryDeclarations.d.ts +1 -1
  58. package/dist/src/web/hooks/useSecondaryDeclarations.js +3 -3
  59. package/dist/src/web/routes/CreateInstance.js +27 -13
  60. package/dist/src/web/routes/Entity.js +27 -13
  61. package/dist/src/web/routes/Home.js +15 -1
  62. package/dist/src/web/routes/Instance.js +27 -12
  63. package/dist/src/web/routes/NotFound.js +4 -0
  64. package/dist/src/web/utils/debug.d.ts +1 -0
  65. package/dist/src/web/utils/debug.js +5 -0
  66. package/package.json +6 -6
  67. package/public/css/styles.css +132 -38
  68. /package/dist/src/{node/renderers/Output.js → shared/config.js} +0 -0
@@ -1,10 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import type { Output } from "../node/renderers/Output.ts";
3
- import type { Schema } from "../node/Schema.ts";
4
- import type { ServerOptions } from "../node/server/index.ts";
5
- export type Config = {
6
- serverOptions?: ServerOptions;
7
- schema: Schema;
8
- outputs: Output[];
9
- dataRootPath: string;
10
- };
2
+ export {};
@@ -88,6 +88,13 @@ switch (passedArguments.command.name) {
88
88
  break;
89
89
  case "validate":
90
90
  debug(`running command: validate`);
91
+ if (passedArguments.command.options?.checkReferentialIntegrity !== undefined) {
92
+ debug(`check referential integrity: ${passedArguments.command.options.checkReferentialIntegrity ? "yes" : "no"}`);
93
+ }
94
+ if (passedArguments.command.options?.entities !== undefined) {
95
+ const entities = passedArguments.command.options.entities;
96
+ debug(`only check the following entities: ${entities.join(", ")}`);
97
+ }
91
98
  await validate(config.schema, config.dataRootPath, {
92
99
  checkReferentialIntegrity: passedArguments.command.options?.checkReferentialIntegrity,
93
100
  checkOnlyEntities: passedArguments.command.options?.entities,
@@ -1,2 +1,2 @@
1
- export type { Config } from "./bin/tsondb.ts";
2
1
  export * from "./node/index.ts";
2
+ export type { Config } from "./shared/config.ts";
@@ -1,5 +1,5 @@
1
- import type { Output } from "./renderers/Output.ts";
2
- import type { Schema } from "./Schema.ts";
1
+ import type { Output } from "../shared/output.ts";
2
+ import { type Schema } from "./schema/Schema.ts";
3
3
  import type { ServerOptions } from "./server/index.ts";
4
4
  export declare const generateOutputs: (schema: Schema, outputs: Output[]) => Promise<void>;
5
5
  type ValidationOptions = {
@@ -3,8 +3,9 @@ import { mkdir, writeFile } from "fs/promises";
3
3
  import { join, sep } from "path";
4
4
  import { styleText } from "util";
5
5
  import { parallelizeErrors } from "../shared/utils/validation.js";
6
- import { getEntities } from "./Schema.js";
7
- import { createValidators, validateEntityDecl } from "./schema/index.js";
6
+ import { validateEntityDecl } from "./schema/declarations/EntityDecl.js";
7
+ import { createValidators } from "./schema/Node.js";
8
+ import { getEntities } from "./schema/Schema.js";
8
9
  import { createServer } from "./server/index.js";
9
10
  import { countErrors, getErrorMessageForDisplay, wrapErrorsIfAny } from "./utils/error.js";
10
11
  import { formatInstance, getInstancesByEntityName } from "./utils/instances.js";
@@ -23,6 +24,11 @@ export const generateOutputs = async (schema, outputs) => {
23
24
  };
24
25
  const _validate = (dataRootPath, entities, instancesByEntityName, options = {}) => {
25
26
  const { checkReferentialIntegrity = false, checkOnlyEntities = [] } = options;
27
+ for (const onlyEntity of checkOnlyEntities) {
28
+ if (!entities.find(entity => entity.name === onlyEntity)) {
29
+ throw new Error(`Entity "${onlyEntity}" not found in schema`);
30
+ }
31
+ }
26
32
  const errors = (checkOnlyEntities.length > 0
27
33
  ? entities.filter(entity => checkOnlyEntities.includes(entity.name))
28
34
  : entities)
@@ -1,4 +1,4 @@
1
- import type { Output } from "../Output.ts";
1
+ import type { Output } from "../../../shared/output.ts";
2
2
  import type { JsonSchemaRendererOptions } from "./render.ts";
3
3
  export declare const JsonSchemaOutput: (options: {
4
4
  targetPath: string;
@@ -1,4 +1,4 @@
1
- import type { Output } from "../Output.ts";
1
+ import type { Output } from "../../../shared/output.ts";
2
2
  import type { TypeScriptRendererOptions } from "./render.ts";
3
3
  export declare const TypeScriptOutput: (options: {
4
4
  targetPath: string;
@@ -1,5 +1,5 @@
1
- import type { Decl } from "./schema/declarations/Declaration.ts";
2
- import type { EntityDecl } from "./schema/declarations/EntityDecl.ts";
1
+ import type { Decl } from "./declarations/Declaration.ts";
2
+ import type { EntityDecl } from "./declarations/EntityDecl.ts";
3
3
  export interface Schema {
4
4
  declarations: readonly Decl[];
5
5
  localeEntity?: EntityDecl;
@@ -1,9 +1,9 @@
1
- import { getNestedDeclarations, getParameterNames, walkNodeTree, } from "./schema/declarations/Declaration.js";
2
- import { isEntityDecl } from "./schema/declarations/EntityDecl.js";
3
- import { isStringType } from "./schema/types/primitives/StringType.js";
4
- import { isIncludeIdentifierType } from "./schema/types/references/IncludeIdentifierType.js";
5
- import { isNestedEntityMapType } from "./schema/types/references/NestedEntityMapType.js";
6
- import { findTypeAtPath } from "./schema/types/Type.js";
1
+ import { getNestedDeclarations, getParameterNames, walkNodeTree, } from "./declarations/Declaration.js";
2
+ import { isEntityDecl } from "./declarations/EntityDecl.js";
3
+ import { isStringType } from "./types/primitives/StringType.js";
4
+ import { isIncludeIdentifierType } from "./types/references/IncludeIdentifierType.js";
5
+ import { isNestedEntityMapType } from "./types/references/NestedEntityMapType.js";
6
+ import { findTypeAtPath } from "./types/Type.js";
7
7
  const checkDuplicateIdentifier = (existingDecls, decl) => {
8
8
  const existingDeclWithSameName = existingDecls
9
9
  .values()
@@ -27,7 +27,10 @@ const checkEntityDisplayNamePaths = (decls, localeEntity) => {
27
27
  for (const decl of decls) {
28
28
  if (isEntityDecl(decl) && decl.displayName !== null) {
29
29
  const displayName = decl.displayName ?? "name";
30
- if (typeof displayName === "object") {
30
+ if (typeof displayName === "function") {
31
+ continue;
32
+ }
33
+ else if (typeof displayName === "object") {
31
34
  const pathToLocaleMap = displayName.pathToLocaleMap ?? "translations";
32
35
  const pathInLocaleMap = displayName.pathInLocaleMap ?? "name";
33
36
  if (localeEntity === undefined) {
@@ -8,6 +8,32 @@ import type { AsType, SerializedAsType } from "../types/Type.ts";
8
8
  import type { ValidatorHelpers } from "../validation/type.ts";
9
9
  import type { BaseDecl, GetNestedDeclarations, SerializedBaseDecl } from "./Declaration.ts";
10
10
  import { TypeAliasDecl } from "./TypeAliasDecl.ts";
11
+ export type GenericDisplayNameFn = (instance: unknown, instanceDisplayName: string, getInstanceById: (id: string) => unknown, getDisplayNameForInstanceId: (id: string) => string | undefined, locales: string[] | undefined) => string;
12
+ export type GenericEntityDisplayName = string | {
13
+ pathToLocaleMap?: string;
14
+ pathInLocaleMap?: string;
15
+ } | null;
16
+ export type DisplayNameFn<T extends ObjectType = ObjectType> = (instance: AsType<T>, instanceDisplayName: string, getInstanceById: (id: string) => unknown, getDisplayNameForInstanceId: (id: string) => string | undefined, locales: string[] | undefined) => string;
17
+ export type EntityDisplayName<T extends ObjectType> = Leaves<AsType<T>> | {
18
+ /**
19
+ * @default "translations"
20
+ */
21
+ pathToLocaleMap?: Leaves<AsType<T>>;
22
+ /**
23
+ * @default "name"
24
+ */
25
+ pathInLocaleMap?: string;
26
+ } | null;
27
+ export type SerializedEntityDisplayName<T extends SerializedObjectType> = Leaves<SerializedAsType<T>> | {
28
+ /**
29
+ * @default "translations"
30
+ */
31
+ pathToLocaleMap?: Leaves<SerializedAsType<T>>;
32
+ /**
33
+ * @default "name"
34
+ */
35
+ pathInLocaleMap?: string;
36
+ } | null;
11
37
  export interface EntityDecl<Name extends string = string, T extends ObjectType = ObjectType> extends BaseDecl<Name, []> {
12
38
  kind: NodeKind["EntityDecl"];
13
39
  namePlural: string;
@@ -15,16 +41,8 @@ export interface EntityDecl<Name extends string = string, T extends ObjectType =
15
41
  /**
16
42
  * @default "name"
17
43
  */
18
- displayName?: Leaves<AsType<T>> | {
19
- /**
20
- * @default "translations"
21
- */
22
- pathToLocaleMap?: Leaves<AsType<T>>;
23
- /**
24
- * @default "name"
25
- */
26
- pathInLocaleMap?: string;
27
- } | null;
44
+ displayName?: EntityDisplayName<T>;
45
+ displayNameCustomizer?: DisplayNameFn<T>;
28
46
  isDeprecated?: boolean;
29
47
  }
30
48
  export interface SerializedEntityDecl<Name extends string = string, T extends SerializedObjectType = SerializedObjectType> extends SerializedBaseDecl<Name, []> {
@@ -34,16 +52,7 @@ export interface SerializedEntityDecl<Name extends string = string, T extends Se
34
52
  /**
35
53
  * @default "name"
36
54
  */
37
- displayName?: Leaves<SerializedAsType<T>> | {
38
- /**
39
- * @default "translations"
40
- */
41
- pathToLocaleMap?: Leaves<SerializedAsType<T>>;
42
- /**
43
- * @default "name"
44
- */
45
- pathInLocaleMap?: string;
46
- } | null;
55
+ displayName?: SerializedEntityDisplayName<T>;
47
56
  isDeprecated?: boolean;
48
57
  }
49
58
  export declare const EntityDecl: <Name extends string, T extends ObjectType>(sourceUrl: string, options: {
@@ -54,16 +63,8 @@ export declare const EntityDecl: <Name extends string, T extends ObjectType>(sou
54
63
  /**
55
64
  * @default "name"
56
65
  */
57
- displayName?: Leaves<AsType<T>> | {
58
- /**
59
- * @default "translations"
60
- */
61
- pathToLocaleMap?: Leaves<AsType<T>>;
62
- /**
63
- * @default "name"
64
- */
65
- pathInLocaleMap?: string;
66
- } | null;
66
+ displayName?: EntityDisplayName<T>;
67
+ displayNameCustomizer?: DisplayNameFn<T>;
67
68
  isDeprecated?: boolean;
68
69
  }) => EntityDecl<Name, T>;
69
70
  export { EntityDecl as Entity };
@@ -52,5 +52,6 @@ export const createEntityIdentifierTypeAsDecl = (decl) => TypeAliasDecl(decl.sou
52
52
  export const serializeEntityDecl = type => ({
53
53
  ...type,
54
54
  type: serializeObjectType(type.type.value),
55
+ displayName: typeof type.displayName === "function" ? null : type.displayName,
55
56
  });
56
57
  export const getReferencesForEntityDecl = (decl, value) => getReferencesForObjectType(decl.type.value, value);
@@ -3,7 +3,7 @@ import express from "express";
3
3
  import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
4
4
  import { isOk } from "../../../shared/utils/result.js";
5
5
  import { serializeDecl } from "../../schema/declarations/Declaration.js";
6
- import { isEntityDecl, serializeEntityDecl } from "../../schema/declarations/EntityDecl.js";
6
+ import { isEntityDecl } from "../../schema/declarations/EntityDecl.js";
7
7
  import { isEnumDecl } from "../../schema/declarations/EnumDecl.js";
8
8
  import { isTypeAliasDecl } from "../../schema/declarations/TypeAliasDecl.js";
9
9
  import { createInstance, deleteInstance, updateInstance } from "./instanceOperations.js";
@@ -60,11 +60,10 @@ declarationsApi.get("/:name/instances", (req, res) => {
60
60
  res.status(400).send(`Declaration "${decl.name}" is not an entity`);
61
61
  return;
62
62
  }
63
- const serializedEntityDecl = serializeEntityDecl(decl);
64
63
  const body = {
65
64
  instances: req.instancesByEntityName[req.params.name]
66
- ?.map(instanceContainer => getInstanceContainerOverview(serializedEntityDecl, instanceContainer, req.locales))
67
- .toSorted((a, b) => a.displayName.localeCompare(b.displayName)) ?? [],
65
+ ?.map(instanceContainer => getInstanceContainerOverview(decl, instanceContainer, req.getInstanceById, req.locales))
66
+ .toSorted((a, b) => a.displayName.localeCompare(b.displayName, undefined, { numeric: true })) ?? [],
68
67
  isLocaleEntity: decl === req.localeEntity,
69
68
  };
70
69
  res.json(body);
@@ -3,7 +3,6 @@ import express, {} from "express";
3
3
  import { join } from "node:path";
4
4
  import { hasFileChanges } from "../../../shared/utils/git.js";
5
5
  import { getInstanceContainerOverview } from "../../../shared/utils/instances.js";
6
- import { serializeEntityDecl } from "../../schema/index.js";
7
6
  import { attachGitStatusToInstancesByEntityName } from "../../utils/instances.js";
8
7
  import { reinit } from "../init.js";
9
8
  const debug = Debug("tsondb:server:api:git");
@@ -30,7 +29,7 @@ gitApi.get("/status", async (req, res) => {
30
29
  .filter(instance => hasFileChanges(instance.gitStatus))
31
30
  .map(instance => getInstanceContainerOverview(
32
31
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
33
- serializeEntityDecl(req.entitiesByName[entityName]), instance, req.locales)),
32
+ req.entitiesByName[entityName], instance, req.getInstanceById, req.locales)),
34
33
  ])),
35
34
  };
36
35
  res.json(body);
@@ -1,7 +1,6 @@
1
1
  import Debug from "debug";
2
2
  import express from "express";
3
- import { getDisplayNameFromEntityInstance } from "../../../shared/utils/displayName.js";
4
- import { serializeEntityDecl } from "../../schema/declarations/EntityDecl.js";
3
+ import { getDisplayNameFromEntityInstance } from "../../utils/displayName.js";
5
4
  const debug = Debug("tsondb:server:api:instances");
6
5
  export const instancesApi = express.Router();
7
6
  instancesApi.use((req, _res, next) => {
@@ -19,7 +18,7 @@ instancesApi.get("/", (req, res) => {
19
18
  id: instance.id,
20
19
  name: getDisplayNameFromEntityInstance(
21
20
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
22
- serializeEntityDecl(req.entitiesByName[entityName]), instance.content, instance.id, locales),
21
+ req.entitiesByName[entityName], instance.content, req.getInstanceById, locales),
23
22
  })),
24
23
  ])),
25
24
  };
@@ -1,8 +1,8 @@
1
1
  import type { SimpleGit } from "simple-git";
2
- import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
3
- import type { Schema } from "../Schema.ts";
2
+ import type { InstanceContainer, InstancesByEntityName } from "../../shared/utils/instances.ts";
4
3
  import type { Decl } from "../schema/declarations/Declaration.ts";
5
4
  import type { EntityDecl } from "../schema/declarations/EntityDecl.ts";
5
+ import type { Schema } from "../schema/Schema.ts";
6
6
  import type { ReferencesToInstances } from "../utils/references.ts";
7
7
  export type ServerOptions = {
8
8
  port: number;
@@ -18,7 +18,12 @@ export interface TSONDBRequestLocals {
18
18
  localeEntity?: EntityDecl;
19
19
  referencesToInstances: ReferencesToInstances;
20
20
  locales: string[];
21
+ getInstanceById: GetInstanceById;
21
22
  }
23
+ export type GetInstanceById = (id: string) => {
24
+ entity: EntityDecl;
25
+ instance: InstanceContainer;
26
+ } | undefined;
22
27
  declare global {
23
28
  namespace Express {
24
29
  interface Request extends TSONDBRequestLocals {
@@ -31,6 +31,15 @@ export const createServer = async (schema, dataRootPath, instancesByEntityName,
31
31
  next();
32
32
  });
33
33
  app.use("/api", api);
34
+ const importMap = JSON.stringify({
35
+ imports: {
36
+ preact: "/js/node_modules/preact/dist/preact.module.js",
37
+ "preact/hooks": "/js/node_modules/preact/hooks/dist/hooks.module.js",
38
+ "preact/compat": "/js/node_modules/preact/compat/dist/compat.module.js",
39
+ "preact/jsx-runtime": "/js/node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js",
40
+ "preact-iso": "/js/node_modules/preact-iso/src/index.js",
41
+ },
42
+ }, null, 2);
34
43
  app.get(/^\/.*/, (_req, res) => {
35
44
  res.send(`<!DOCTYPE html>
36
45
  <html lang="en">
@@ -39,16 +48,7 @@ export const createServer = async (schema, dataRootPath, instancesByEntityName,
39
48
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
40
49
  <title>TSONDB</title>
41
50
  <link rel="stylesheet" href="/css/styles.css">
42
- <script type="importmap">
43
- {
44
- "imports": {
45
- "preact": "/js/node_modules/preact/dist/preact.module.js",
46
- "preact/hooks": "/js/node_modules/preact/hooks/dist/hooks.module.js",
47
- "preact/jsx-runtime": "/js/node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js",
48
- "preact-iso": "/js/node_modules/preact-iso/src/index.js"
49
- }
50
- }
51
- </script>
51
+ <script type="importmap">${importMap}</script>
52
52
  </head>
53
53
  <body>
54
54
  <div id="app"></div>
@@ -1,5 +1,5 @@
1
1
  import type { InstancesByEntityName } from "../../shared/utils/instances.ts";
2
- import type { Schema } from "../Schema.ts";
2
+ import type { Schema } from "../schema/Schema.ts";
3
3
  import type { TSONDBRequestLocals } from "./index.ts";
4
4
  export declare const init: (schema: Schema, dataRootPath: string, instancesByEntityName: InstancesByEntityName) => Promise<TSONDBRequestLocals>;
5
5
  export declare const reinit: (locals: TSONDBRequestLocals) => Promise<void>;
@@ -29,6 +29,15 @@ export const init = async (schema, dataRootPath, instancesByEntityName) => {
29
29
  if (gitStatus) {
30
30
  attachGitStatusToInstancesByEntityName(instancesByEntityName, dataRootPath, gitRoot, gitStatus);
31
31
  }
32
+ const getInstanceById = id => {
33
+ for (const entityName in instancesByEntityNameInMemory) {
34
+ const instance = instancesByEntityNameInMemory[entityName]?.find(i => i.id === id);
35
+ if (instance && entitiesByName[entityName]) {
36
+ return { entity: entitiesByName[entityName], instance };
37
+ }
38
+ }
39
+ return undefined;
40
+ };
32
41
  const requestLocals = {
33
42
  git: git,
34
43
  gitRoot: gitRoot,
@@ -38,6 +47,7 @@ export const init = async (schema, dataRootPath, instancesByEntityName) => {
38
47
  instancesByEntityName: instancesByEntityNameInMemory,
39
48
  entitiesByName: entitiesByName,
40
49
  localeEntity: schema.localeEntity,
50
+ getInstanceById,
41
51
  referencesToInstances,
42
52
  locales: ["de-DE", "en-US"], // TODO: Make this configurable
43
53
  };
@@ -0,0 +1,3 @@
1
+ import { type EntityDecl } from "../../node/schema/index.ts";
2
+ import type { GetInstanceById } from "../../node/server/index.ts";
3
+ export declare const getDisplayNameFromEntityInstance: (entity: EntityDecl, instance: unknown, getInstanceById: GetInstanceById, locales?: string[], defaultName?: string, useCustomizer?: boolean) => string;
@@ -0,0 +1,19 @@
1
+ import { serializeEntityDecl } from "../../node/schema/index.js";
2
+ import { getSerializedDisplayNameFromEntityInstance } from "../../shared/utils/displayName.js";
3
+ export const getDisplayNameFromEntityInstance = (entity, instance, getInstanceById, locales = [], defaultName = "", useCustomizer = true) => {
4
+ if (useCustomizer && entity.displayNameCustomizer) {
5
+ return entity.displayNameCustomizer(instance, getDisplayNameFromEntityInstance(entity, instance, getInstanceById, locales, defaultName, false), id => getInstanceById(id)?.instance.content, id => {
6
+ const result = getInstanceById(id);
7
+ if (result) {
8
+ const { entity, instance } = result;
9
+ return getDisplayNameFromEntityInstance(entity, instance.content, getInstanceById, locales, id);
10
+ }
11
+ else {
12
+ return undefined;
13
+ }
14
+ }, locales);
15
+ }
16
+ else {
17
+ return getSerializedDisplayNameFromEntityInstance(serializeEntityDecl(entity), instance, defaultName, locales);
18
+ }
19
+ };
@@ -0,0 +1,11 @@
1
+ import type { Schema } from "../node/schema/Schema.ts";
2
+ import type { Output } from "./output.ts";
3
+ export type Config = {
4
+ serverOptions?: ServerOptions;
5
+ schema: Schema;
6
+ outputs: Output[];
7
+ dataRootPath: string;
8
+ };
9
+ export type ServerOptions = {
10
+ port: number;
11
+ };
@@ -1,4 +1,4 @@
1
- import type { Schema } from "../Schema.ts";
1
+ import type { Schema } from "../node/schema/Schema.ts";
2
2
  export interface Output {
3
3
  run: (schema: Schema) => Promise<void>;
4
4
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -18,7 +18,12 @@ export const deepEqual = (a, b) => {
18
18
  if (keys.length !== Object.keys(b).length) {
19
19
  return false;
20
20
  }
21
- return keys.every(key => key in b && deepEqual(a[key], b[key]));
21
+ for (const key of keys) {
22
+ if (!(key in b) || !deepEqual(a[key], b[key])) {
23
+ return false;
24
+ }
25
+ }
26
+ return true;
22
27
  }
23
28
  return false;
24
29
  };
@@ -1,2 +1,2 @@
1
1
  import type { SerializedEntityDecl } from "../../node/schema/index.ts";
2
- export declare const getDisplayNameFromEntityInstance: (entity: SerializedEntityDecl, instance: unknown, defaultName: string, locales?: string[]) => string;
2
+ export declare const getSerializedDisplayNameFromEntityInstance: (entity: SerializedEntityDecl, instance: unknown, defaultName: string, locales?: string[]) => string;
@@ -11,7 +11,7 @@ const getValueAtPath = (value, path) => {
11
11
  }
12
12
  return current;
13
13
  };
14
- export const getDisplayNameFromEntityInstance = (entity, instance, defaultName, locales = []) => {
14
+ export const getSerializedDisplayNameFromEntityInstance = (entity, instance, defaultName, locales = []) => {
15
15
  if (entity.displayName === null) {
16
16
  return defaultName;
17
17
  }
@@ -1,4 +1,5 @@
1
- import type { SerializedEntityDecl } from "../../node/schema/index.ts";
1
+ import type { EntityDecl } from "../../node/schema/index.ts";
2
+ import type { GetInstanceById } from "../../node/server/index.ts";
2
3
  import type { GitFileStatus } from "./git.ts";
3
4
  export interface InstanceContainer {
4
5
  fileName: string;
@@ -12,5 +13,5 @@ export interface InstanceContainerOverview {
12
13
  gitStatus?: GitFileStatus;
13
14
  displayName: string;
14
15
  }
15
- export declare const getInstanceContainerOverview: (entity: SerializedEntityDecl, instanceContainer: InstanceContainer, locales?: string[]) => InstanceContainerOverview;
16
+ export declare const getInstanceContainerOverview: (entity: EntityDecl, instanceContainer: InstanceContainer, getInstanceById: GetInstanceById, locales?: string[]) => InstanceContainerOverview;
16
17
  export type InstancesByEntityName = Record<string, InstanceContainer[]>;
@@ -1,8 +1,8 @@
1
- import { getDisplayNameFromEntityInstance } from "./displayName.js";
2
- export const getInstanceContainerOverview = (entity, instanceContainer, locales) => {
1
+ import { getDisplayNameFromEntityInstance } from "../../node/utils/displayName.js";
2
+ export const getInstanceContainerOverview = (entity, instanceContainer, getInstanceById, locales) => {
3
3
  const { content: _, ...rest } = instanceContainer;
4
4
  return {
5
5
  ...rest,
6
- displayName: getDisplayNameFromEntityInstance(entity, instanceContainer.content, instanceContainer.id, locales),
6
+ displayName: getDisplayNameFromEntityInstance(entity, instanceContainer.content, getInstanceById, locales),
7
7
  };
8
8
  };
@@ -61,7 +61,7 @@ const listRule = {
61
61
  }),
62
62
  };
63
63
  const paragraphRule = {
64
- pattern: /^((?:[^\n]+?)(?:\n[^\n]+?)*)(?:\n{2,}|$)/,
64
+ pattern: /^((?:[^\n]+?)(?:\n[^\n]+?)*)(?:\n{2,}|\s*$)/,
65
65
  map: result => ({ kind: "paragraph", content: parseInlineMarkdown(result[1] ?? "") }),
66
66
  };
67
67
  const blockRules = [listRule, paragraphRule];
@@ -8,7 +8,7 @@ const filterFilesForDisplay = (predicate, entities, data) => Object.entries(data
8
8
  entities.find(entity => entity.name === entityName)?.namePlural ?? entityName,
9
9
  instances
10
10
  .filter(instance => instance.gitStatus !== undefined && predicate(instance.gitStatus))
11
- .sort((a, b) => a.displayName.localeCompare(b.displayName)),
11
+ .sort((a, b) => a.displayName.localeCompare(b.displayName, undefined, { numeric: true })),
12
12
  ])
13
13
  .filter(([_1, _2, instances]) => instances.length > 0)
14
14
  .sort((a, b) => a[1].localeCompare(b[1]));
@@ -4,7 +4,8 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
4
4
  import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
5
5
  type Props = {
6
6
  type: SerializedArrayType;
7
- value: unknown[];
7
+ path: string | undefined;
8
+ value: unknown;
8
9
  instanceNamesByEntity: InstanceNamesByEntity;
9
10
  getDeclFromDeclName: GetDeclFromDeclName;
10
11
  onChange: (value: unknown[]) => void;
@@ -1,17 +1,21 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "preact/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { removeAt } from "../../../shared/utils/array.js";
3
3
  import { validateArrayConstraints } from "../../../shared/validation/array.js";
4
4
  import { createTypeSkeleton } from "../../utils/typeSkeleton.js";
5
5
  import { TypeInput } from "./TypeInput.js";
6
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
6
7
  import { ValidationErrors } from "./utils/ValidationErrors.js";
7
- export const ArrayTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
8
+ export const ArrayTypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
9
+ if (!Array.isArray(value)) {
10
+ return _jsx(MismatchingTypeError, { expected: "array", actual: value });
11
+ }
8
12
  const errors = validateArrayConstraints(type, value);
9
13
  const isTuple = typeof type.minItems === "number" && type.minItems === type.maxItems;
10
- return (_jsxs("div", { class: "field field--container field--array", children: [value.length > 0 && (_jsx("ol", { children: value.map((item, i) => (_jsxs("li", { class: "container-item array-item", children: [isTuple ? null : (_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [i + 1, "."] }), _jsx("button", { class: "destructive", onClick: () => {
14
+ return (_jsxs("div", { class: "field field--container field--array", children: [value.length > 0 && (_jsx("ol", { children: value.map((item, i) => (_jsxs("li", { class: "container-item array-item", children: [isTuple ? null : (_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [i + 1, "."] }), _jsxs("button", { class: "destructive", onClick: () => {
11
15
  onChange(removeAt(value, i));
12
- }, disabled: type.minItems !== undefined && value.length <= type.minItems, children: "Delete Item" })] })), _jsx(TypeInput, { type: type.items, value: item, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
16
+ }, disabled: type.minItems !== undefined && value.length <= type.minItems, children: ["Delete Item #", i + 1] })] })), _jsx(TypeInput, { type: type.items, path: path === undefined ? `[${i.toString()}]` : `${path}[${i.toString()}]`, value: item, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
13
17
  onChange(value.with(i, newItem));
14
- } })] }, i))) })), isTuple ? null : (_jsx("div", { class: "add-item-container", children: _jsx("button", { onClick: () => {
18
+ } })] }, i))) })), isTuple ? null : (_jsx("div", { class: "add-item-container", children: _jsxs("button", { onClick: () => {
15
19
  onChange([...value, createTypeSkeleton(getDeclFromDeclName, type.items)]);
16
- }, disabled: type.maxItems !== undefined && value.length >= type.maxItems, children: "Add Item" }) })), _jsx(ValidationErrors, { errors: errors })] }));
20
+ }, disabled: type.maxItems !== undefined && value.length >= type.maxItems, children: ["Add Item #", value.length + 1] }) })), _jsx(ValidationErrors, { errors: errors })] }));
17
21
  };
@@ -2,7 +2,7 @@ import type { FunctionComponent } from "preact";
2
2
  import type { SerializedBooleanType } from "../../../node/schema/types/primitives/BooleanType.ts";
3
3
  type Props = {
4
4
  type: SerializedBooleanType;
5
- value: boolean;
5
+ value: unknown;
6
6
  onChange: (value: boolean) => void;
7
7
  };
8
8
  export declare const BooleanTypeInput: FunctionComponent<Props>;
@@ -1,5 +1,9 @@
1
1
  import { jsx as _jsx } from "preact/jsx-runtime";
2
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
2
3
  export const BooleanTypeInput = ({ value, onChange }) => {
4
+ if (typeof value !== "boolean") {
5
+ return _jsx(MismatchingTypeError, { expected: "boolean", actual: value });
6
+ }
3
7
  return (_jsx("div", { class: "field", children: _jsx("input", { type: "checkbox", checked: value, onInput: event => {
4
8
  onChange(event.currentTarget.checked);
5
9
  } }) }));
@@ -2,7 +2,7 @@ import type { FunctionComponent } from "preact";
2
2
  import type { SerializedDateType } from "../../../node/schema/types/primitives/DateType.ts";
3
3
  type Props = {
4
4
  type: SerializedDateType;
5
- value: string;
5
+ value: unknown;
6
6
  onChange: (value: string) => void;
7
7
  };
8
8
  export declare const DateTypeInput: FunctionComponent<Props>;
@@ -1,7 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { validateDateConstraints } from "../../../shared/validation/date.js";
3
+ import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
3
4
  import { ValidationErrors } from "./utils/ValidationErrors.js";
4
5
  export const DateTypeInput = ({ type, value, onChange }) => {
6
+ if (typeof value !== "string") {
7
+ return _jsx(MismatchingTypeError, { expected: "date string", actual: value });
8
+ }
5
9
  const errors = validateDateConstraints(type, value);
6
10
  return (_jsxs("div", { class: "field", children: [_jsx("input", { type: "date", value: value, onInput: event => {
7
11
  onChange(event.currentTarget.value);
@@ -4,6 +4,7 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
4
4
  import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
5
5
  type Props = {
6
6
  type: SerializedEnumType;
7
+ path: string | undefined;
7
8
  value: unknown;
8
9
  instanceNamesByEntity: InstanceNamesByEntity;
9
10
  getDeclFromDeclName: GetDeclFromDeclName;
@@ -5,7 +5,7 @@ import { createTypeSkeleton } from "../../utils/typeSkeleton.js";
5
5
  import { Select } from "../Select.js";
6
6
  import { TypeInput } from "./TypeInput.js";
7
7
  import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
8
- export const EnumTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
8
+ export const EnumTypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
9
9
  if (typeof value !== "object" ||
10
10
  value === null ||
11
11
  Array.isArray(value) ||
@@ -29,7 +29,7 @@ export const EnumTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromD
29
29
  [event.currentTarget.value]: createTypeSkeleton(getDeclFromDeclName, caseMember.type),
30
30
  });
31
31
  }
32
- }, children: enumValues.map(enumValue => (_jsx("option", { value: enumValue, selected: enumValue === activeEnumCase, children: toTitleCase(enumValue) }, enumValue))) }), caseMember?.type == null ? null : (_jsx("div", { className: "associated-type", children: _jsx(TypeInput, { type: caseMember.type, value: value[activeEnumCase], instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newValue => {
32
+ }, children: enumValues.map(enumValue => (_jsx("option", { value: enumValue, selected: enumValue === activeEnumCase, children: toTitleCase(enumValue) }, enumValue))) }), caseMember?.type == null ? null : (_jsx("div", { className: "associated-type", children: _jsx(TypeInput, { type: caseMember.type, path: path === undefined ? `{${activeEnumCase}}` : `${path}.{${activeEnumCase}}`, value: value[activeEnumCase], instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newValue => {
33
33
  onChange({
34
34
  [discriminatorKey]: activeEnumCase,
35
35
  [activeEnumCase]: newValue,
@@ -2,7 +2,7 @@ import type { FunctionComponent } from "preact";
2
2
  import type { SerializedFloatType } from "../../../node/schema/types/primitives/FloatType.ts";
3
3
  type Props = {
4
4
  type: SerializedFloatType;
5
- value: number;
5
+ value: unknown;
6
6
  onChange: (value: number) => void;
7
7
  };
8
8
  export declare const FloatTypeInput: FunctionComponent<Props>;