tsondb 0.12.3 → 0.12.6

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/index.js +17 -20
  2. package/dist/src/node/schema/Node.d.ts +2 -2
  3. package/dist/src/node/schema/Node.js +3 -5
  4. package/dist/src/node/schema/declarations/EntityDecl.d.ts +4 -4
  5. package/dist/src/node/schema/types/primitives/FloatType.d.ts +5 -2
  6. package/dist/src/node/schema/types/primitives/FloatType.js +10 -4
  7. package/dist/src/node/schema/types/references/IncludeIdentifierType.js +0 -3
  8. package/dist/src/node/server/api/declarations.js +27 -10
  9. package/dist/src/node/server/api/git.js +5 -4
  10. package/dist/src/node/server/api/index.js +2 -0
  11. package/dist/src/node/server/api/instances.js +2 -3
  12. package/dist/src/node/server/api/search.d.ts +1 -0
  13. package/dist/src/node/server/api/search.js +50 -0
  14. package/dist/src/node/server/index.d.ts +4 -3
  15. package/dist/src/node/server/index.js +2 -2
  16. package/dist/src/node/server/init.d.ts +2 -2
  17. package/dist/src/node/server/init.js +14 -11
  18. package/dist/src/node/server/utils/childInstances.d.ts +0 -3
  19. package/dist/src/node/server/utils/childInstances.js +2 -43
  20. package/dist/src/node/server/utils/instanceOperations.d.ts +3 -4
  21. package/dist/src/node/server/utils/instanceOperations.js +44 -84
  22. package/dist/src/node/server/utils/query.d.ts +2 -0
  23. package/dist/src/node/server/utils/query.js +7 -0
  24. package/dist/src/node/utils/childInstances.d.ts +11 -10
  25. package/dist/src/node/utils/childInstances.js +66 -130
  26. package/dist/src/node/utils/databaseInMemory.d.ts +18 -0
  27. package/dist/src/node/utils/databaseInMemory.js +86 -0
  28. package/dist/src/node/utils/databaseOnDisk.d.ts +2 -0
  29. package/dist/src/node/utils/databaseOnDisk.js +62 -0
  30. package/dist/src/node/utils/databaseTransactions.d.ts +46 -0
  31. package/dist/src/node/utils/databaseTransactions.js +105 -0
  32. package/dist/src/node/utils/displayName.d.ts +3 -3
  33. package/dist/src/node/utils/error.d.ts +4 -0
  34. package/dist/src/node/utils/error.js +8 -0
  35. package/dist/src/node/utils/files.d.ts +4 -2
  36. package/dist/src/node/utils/files.js +2 -1
  37. package/dist/src/node/utils/git.d.ts +3 -1
  38. package/dist/src/node/utils/git.js +8 -2
  39. package/dist/src/node/utils/instanceTransactionSteps.d.ts +9 -0
  40. package/dist/src/node/utils/instanceTransactionSteps.js +40 -0
  41. package/dist/src/node/utils/references.d.ts +4 -3
  42. package/dist/src/node/utils/references.js +3 -2
  43. package/dist/src/shared/api.d.ts +4 -0
  44. package/dist/src/shared/schema/declarations/EntityDecl.d.ts +2 -2
  45. package/dist/src/shared/schema/declarations/EnumDecl.d.ts +8 -0
  46. package/dist/src/shared/schema/declarations/EnumDecl.js +5 -0
  47. package/dist/src/shared/schema/types/FloatType.d.ts +6 -5
  48. package/dist/src/shared/schema/types/FloatType.js +1 -0
  49. package/dist/src/shared/utils/dictionary.d.ts +17 -0
  50. package/dist/src/shared/utils/dictionary.js +76 -0
  51. package/dist/src/shared/utils/instances.d.ts +2 -2
  52. package/dist/src/shared/utils/result.d.ts +4 -0
  53. package/dist/src/shared/utils/result.js +4 -0
  54. package/dist/src/web/api/search.d.ts +2 -0
  55. package/dist/src/web/api/search.js +7 -0
  56. package/dist/src/web/components/InstanceRouteSkeleton.d.ts +9 -8
  57. package/dist/src/web/components/Layout.js +1 -1
  58. package/dist/src/web/components/git/GitStatusIndicator.js +1 -1
  59. package/dist/src/web/components/typeInputs/ChildEntitiesTypeInput.js +1 -1
  60. package/dist/src/web/components/typeInputs/FloatTypeInput.d.ts +1 -1
  61. package/dist/src/web/components/typeInputs/FloatTypeInput.js +2 -1
  62. package/dist/src/web/hooks/useGitClient.js +0 -1
  63. package/dist/src/web/hooks/useMappedAPIResource.js +2 -4
  64. package/dist/src/web/index.js +2 -1
  65. package/dist/src/web/routes/Search.d.ts +2 -0
  66. package/dist/src/web/routes/Search.js +56 -0
  67. package/dist/src/web/utils/typeSkeleton.d.ts +2 -2
  68. package/dist/src/web/utils/typeSkeleton.js +1 -1
  69. package/package.json +3 -3
  70. package/public/css/styles.css +10 -0
  71. package/dist/src/node/utils/instanceOperations.d.ts +0 -14
  72. package/dist/src/node/utils/instanceOperations.js +0 -88
  73. package/dist/src/node/utils/instances.d.ts +0 -6
  74. package/dist/src/node/utils/instances.js +0 -29
@@ -0,0 +1,46 @@
1
+ import type { SimpleGit } from "simple-git";
2
+ import type { InstanceContainer, InstanceContent } from "../../shared/utils/instances.ts";
3
+ import { type Result } from "../../shared/utils/result.ts";
4
+ import type { EntityDecl } from "../schema/index.ts";
5
+ import { type DatabaseInMemory } from "./databaseInMemory.ts";
6
+ import { type ReferencesToInstances } from "./references.ts";
7
+ export type TransactionStep = {
8
+ kind: "set";
9
+ entity: EntityDecl;
10
+ instanceId: string;
11
+ instance: InstanceContent;
12
+ oldInstance: InstanceContent | undefined;
13
+ } | {
14
+ kind: "delete";
15
+ entity: EntityDecl;
16
+ instanceId: string;
17
+ oldInstance: InstanceContent;
18
+ };
19
+ export type TransactionResult<A extends object = object> = Result<{
20
+ entitiesByName: Record<string, EntityDecl>;
21
+ db: DatabaseInMemory;
22
+ refs: ReferencesToInstances;
23
+ steps: TransactionStep[];
24
+ } & A, Error>;
25
+ /**
26
+ * Run a transaction on the database in memory, applying all changes to disk if successful.
27
+ */
28
+ export declare const runDatabaseTransaction: (root: string, git: SimpleGit | undefined, entitiesByName: Record<string, EntityDecl>, database: DatabaseInMemory, references: ReferencesToInstances, transactionFn: (db: TransactionResult) => TransactionResult<{
29
+ instanceContainer: InstanceContainer;
30
+ }>) => Promise<Result<{
31
+ db: DatabaseInMemory;
32
+ refs: ReferencesToInstances;
33
+ instanceContainer: InstanceContainer;
34
+ }, Error>>;
35
+ /**
36
+ * Function to be used within a transaction to set an instance in the database.
37
+ *
38
+ * Note that the transaction result is immutable, so the returned `TransactionResult` must be used for further operations.
39
+ */
40
+ export declare const setInstanceT: (res: TransactionResult, entity: EntityDecl, instance: InstanceContainer) => TransactionResult;
41
+ /**
42
+ * Function to be used within a transaction to delete an instance from the database.
43
+ *
44
+ * Note that the transaction result is immutable, so the returned `TransactionResult` must be used for further operations.
45
+ */
46
+ export declare const deleteInstanceT: (res: TransactionResult, entity: EntityDecl, instanceId: string) => TransactionResult;
@@ -0,0 +1,105 @@
1
+ import { isError, map, ok } from "../../shared/utils/result.js";
2
+ import { deleteInstanceInDatabaseInMemory, setInstanceInDatabaseInMemory, } from "./databaseInMemory.js";
3
+ import { applyStepsToDisk } from "./databaseOnDisk.js";
4
+ import { attachGitStatusToDatabaseInMemory } from "./git.js";
5
+ import { updateReferencesToInstances } from "./references.js";
6
+ /**
7
+ * Run a transaction on the database in memory, applying all changes to disk if successful.
8
+ */
9
+ export const runDatabaseTransaction = async (root, git, entitiesByName, database, references, transactionFn) => {
10
+ const result = transactionFn(ok({
11
+ db: database,
12
+ entitiesByName,
13
+ refs: references,
14
+ steps: [],
15
+ }));
16
+ if (isError(result)) {
17
+ return result;
18
+ }
19
+ const { db: newDb, refs: newRefs, steps, instanceContainer } = result.value;
20
+ const diskResult = await applyStepsToDisk(root, steps);
21
+ if (isError(diskResult)) {
22
+ return diskResult;
23
+ }
24
+ if (git !== undefined) {
25
+ const status = await git.status();
26
+ const repoRoot = await git.revparse(["--show-toplevel"]);
27
+ const newDbWithUpdatedGit = attachGitStatusToDatabaseInMemory(newDb, root, repoRoot, status);
28
+ return ok({ db: newDbWithUpdatedGit, refs: newRefs, instanceContainer });
29
+ }
30
+ return ok({ db: newDb, refs: newRefs, instanceContainer });
31
+ };
32
+ /**
33
+ * Function to be used within a transaction to set an instance in the database.
34
+ *
35
+ * Note that the transaction result is immutable, so the returned `TransactionResult` must be used for further operations.
36
+ */
37
+ export const setInstanceT = (res, entity, instance) => map(res, ({ db, steps, refs, entitiesByName }) => {
38
+ const [updatedDb, oldInstance] = setInstanceInDatabaseInMemory(db, entity.name, instance);
39
+ const updatedRefs = updateReferencesToInstances(entitiesByName, refs, entity.name, instance.id, oldInstance, instance.content);
40
+ const step = {
41
+ kind: "set",
42
+ entity,
43
+ instanceId: instance.id,
44
+ instance: instance.content,
45
+ oldInstance,
46
+ };
47
+ return {
48
+ db: updatedDb,
49
+ steps: [...steps, step],
50
+ refs: updatedRefs,
51
+ entitiesByName,
52
+ additionalInformation: undefined,
53
+ };
54
+ });
55
+ // /**
56
+ // * Function to set an instance in the database directly.
57
+ // *
58
+ // * This is the same as running a singular set transaction.
59
+ // */
60
+ // export const setInstance = (
61
+ // root: string,
62
+ // db: DatabaseInMemory,
63
+ // entity: EntityDecl,
64
+ // instance: InstanceContainer,
65
+ // ): Promise<Result<DatabaseInMemory, Error>> =>
66
+ // runTransaction(root, db, res => setInstanceT(res, entity, instance))
67
+ /**
68
+ * Function to be used within a transaction to delete an instance from the database.
69
+ *
70
+ * Note that the transaction result is immutable, so the returned `TransactionResult` must be used for further operations.
71
+ */
72
+ export const deleteInstanceT = (res, entity, instanceId) => map(res, tres => {
73
+ const { db, steps, refs, entitiesByName } = tres;
74
+ const [updatedDb, oldInstance] = deleteInstanceInDatabaseInMemory(db, entity.name, instanceId);
75
+ if (oldInstance === undefined) {
76
+ // instance did not exist, no-op
77
+ return tres;
78
+ }
79
+ const updatedRefs = updateReferencesToInstances(entitiesByName, refs, entity.name, instanceId, oldInstance, undefined);
80
+ const step = {
81
+ kind: "delete",
82
+ entity,
83
+ instanceId,
84
+ oldInstance,
85
+ };
86
+ return {
87
+ db: updatedDb,
88
+ steps: [...steps, step],
89
+ refs: updatedRefs,
90
+ entitiesByName,
91
+ additionalInformation: undefined,
92
+ };
93
+ });
94
+ // /**
95
+ // * Function to delete an instance from the database directly.
96
+ // *
97
+ // * This is the same as running a singular deletion transaction.
98
+ // */
99
+ // export const deleteInstance = (
100
+ // root: string,
101
+ // db: DatabaseInMemory,
102
+ // entityName: string,
103
+ // instanceId: string,
104
+ // ): Promise<Result<DatabaseInMemory, Error>> =>
105
+ // runTransaction(root, db, res => deleteInstanceT(res, entityName, instanceId))
@@ -1,11 +1,11 @@
1
1
  import type { GetInstanceById } from "../../node/server/index.ts";
2
2
  import { type DisplayNameResult } from "../../shared/utils/displayName.ts";
3
- import type { InstanceContainer } from "../../shared/utils/instances.ts";
3
+ import type { InstanceContainer, InstanceContent } from "../../shared/utils/instances.ts";
4
4
  import { type EntityDecl } from "../schema/declarations/EntityDecl.ts";
5
5
  import type { AsDeepType, Type } from "../schema/types/Type.ts";
6
6
  export type GetChildInstancesForInstanceId = (parentEntityName: string, parentId: string, childEntityName: string) => {
7
7
  id: string;
8
- content: unknown;
8
+ content: InstanceContent;
9
9
  }[];
10
10
  export type DisplayNameCustomizer<T extends Type> = (params: {
11
11
  instance: AsDeepType<T>;
@@ -13,7 +13,7 @@ export type DisplayNameCustomizer<T extends Type> = (params: {
13
13
  instanceDisplayName: string;
14
14
  instanceDisplayNameLocaleId: string | undefined;
15
15
  locales: string[];
16
- getInstanceById: (id: string) => unknown;
16
+ getInstanceById: (id: string) => InstanceContent | undefined;
17
17
  getDisplayNameForInstanceId: (id: string) => DisplayNameResult | undefined;
18
18
  getChildInstancesForInstanceId: GetChildInstancesForInstanceId;
19
19
  }) => DisplayNameResult;
@@ -2,3 +2,7 @@ export declare const getErrorMessageForDisplay: (error: Error) => string;
2
2
  export declare const countError: (error: Error) => number;
3
3
  export declare const countErrors: (errors: Error[]) => number;
4
4
  export declare const wrapErrorsIfAny: (message: string, errors: Error[]) => AggregateError | undefined;
5
+ export declare class HTTPError extends Error {
6
+ code: number;
7
+ constructor(code: number, message: string);
8
+ }
@@ -25,3 +25,11 @@ export const wrapErrorsIfAny = (message, errors) => {
25
25
  }
26
26
  return new AggregateError(errors, message);
27
27
  };
28
+ export class HTTPError extends Error {
29
+ code;
30
+ constructor(code, message) {
31
+ super(message);
32
+ this.code = code;
33
+ this.name = "HTTPError";
34
+ }
35
+ }
@@ -1,5 +1,7 @@
1
- import type { EntityDecl } from "../schema/index.ts";
1
+ import type { InstanceContent } from "../../shared/utils/instances.ts";
2
+ import { type EntityDecl } from "../schema/index.ts";
2
3
  export declare const getFileNameForId: (id: string) => string;
3
4
  export declare const getPathToInstance: (dataRoot: string, entityName: string, id: string) => string;
4
- export declare const writeInstance: (dataRoot: string, entity: EntityDecl, id: string, instance: unknown) => Promise<void>;
5
+ export declare const writeInstance: (dataRoot: string, entity: EntityDecl, id: string, instance: InstanceContent) => Promise<void>;
5
6
  export declare const deleteInstance: (dataRoot: string, entityName: string, id: string) => Promise<void>;
7
+ export declare const formatInstance: (entity: EntityDecl, instanceContent: InstanceContent) => string;
@@ -1,9 +1,10 @@
1
1
  import { rm, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
- import { formatInstance } from "./instances.js";
3
+ import { formatValue } from "../schema/index.js";
4
4
  export const getFileNameForId = (id) => `${id}.json`;
5
5
  export const getPathToInstance = (dataRoot, entityName, id) => join(dataRoot, entityName, getFileNameForId(id));
6
6
  export const writeInstance = (dataRoot, entity, id, instance) => writeFile(getPathToInstance(dataRoot, entity.name, id), formatInstance(entity, instance), {
7
7
  encoding: "utf-8",
8
8
  });
9
9
  export const deleteInstance = (dataRoot, entityName, id) => rm(getPathToInstance(dataRoot, entityName, id));
10
+ export const formatInstance = (entity, instanceContent) => JSON.stringify(formatValue(entity.type.value, instanceContent), undefined, 2) + "\n";
@@ -1,3 +1,5 @@
1
1
  import type { StatusResult } from "simple-git";
2
2
  import type { GitFileStatus } from "../../shared/utils/git.ts";
3
- export declare const getGitFileStatusFromStatusResult: (statusResult: StatusResult, repoRoot: string, dataRoot: string, entityName: string, fileName: string) => GitFileStatus | undefined;
3
+ import type { DatabaseInMemory } from "./databaseInMemory.ts";
4
+ export declare const getGitFileStatusFromStatusResult: (gitStatus: StatusResult, gitRoot: string, dataRoot: string, entityName: string, fileName: string) => GitFileStatus | undefined;
5
+ export declare const attachGitStatusToDatabaseInMemory: (databaseInMemory: DatabaseInMemory, dataRoot: string, gitRoot: string, gitStatus: StatusResult) => DatabaseInMemory;
@@ -1,7 +1,9 @@
1
1
  import { join } from "path";
2
- export const getGitFileStatusFromStatusResult = (statusResult, repoRoot, dataRoot, entityName, fileName) => {
2
+ import { mapD } from "../../shared/utils/dictionary.js";
3
+ import { getFileNameForId } from "./files.js";
4
+ export const getGitFileStatusFromStatusResult = (gitStatus, gitRoot, dataRoot, entityName, fileName) => {
3
5
  const filePath = join(dataRoot, entityName, fileName);
4
- const gitFile = statusResult.files.find(file => join(repoRoot, file.path) === filePath);
6
+ const gitFile = gitStatus.files.find(file => join(gitRoot, file.path) === filePath);
5
7
  if (gitFile === undefined) {
6
8
  return;
7
9
  }
@@ -10,3 +12,7 @@ export const getGitFileStatusFromStatusResult = (statusResult, repoRoot, dataRoo
10
12
  workingDir: gitFile.working_dir,
11
13
  };
12
14
  };
15
+ export const attachGitStatusToDatabaseInMemory = (databaseInMemory, dataRoot, gitRoot, gitStatus) => mapD(databaseInMemory, (instances, entityName) => mapD(instances, instanceContainer => ({
16
+ ...instanceContainer,
17
+ gitStatus: getGitFileStatusFromStatusResult(gitStatus, gitRoot, dataRoot, entityName, getFileNameForId(instanceContainer.id)),
18
+ })));
@@ -0,0 +1,9 @@
1
+ import type { InstanceContent } from "../../shared/utils/instances.ts";
2
+ import type { EntityDecl } from "../schema/declarations/EntityDecl.ts";
3
+ import { type TransactionResult } from "./databaseTransactions.ts";
4
+ export declare const createNewId: () => `${string}-${string}-${string}-${string}-${string}`;
5
+ export declare const createInstance: (res: TransactionResult, localeEntity: EntityDecl | undefined, entity: EntityDecl, instanceContent: InstanceContent, customId: unknown) => TransactionResult<{
6
+ instanceId: string;
7
+ }>;
8
+ export declare const updateInstance: (res: TransactionResult, entity: EntityDecl, instanceId: string, instanceContent: InstanceContent) => TransactionResult;
9
+ export declare const deleteInstance: (res: TransactionResult, entity: EntityDecl, instanceId: string) => TransactionResult;
@@ -0,0 +1,40 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { error, map, ok, then } from "../../shared/utils/result.js";
3
+ import { createValidators, validateEntityDecl } from "../schema/index.js";
4
+ import { getInstanceOfEntityFromDatabaseInMemory, } from "./databaseInMemory.js";
5
+ import { deleteInstanceT, setInstanceT } from "./databaseTransactions.js";
6
+ import { getErrorMessageForDisplay, HTTPError } from "./error.js";
7
+ import { isReferencedByOtherInstances } from "./references.js";
8
+ export const createNewId = () => randomUUID();
9
+ const checkCreateInstancePossible = (localeEntity, databaseInMemory, entity, instanceContent, customId) => {
10
+ const newInstanceId = entity === localeEntity ? customId : createNewId();
11
+ if (typeof newInstanceId !== "string") {
12
+ return error(new HTTPError(400, `New identifier "${String(newInstanceId)}" is not a string`));
13
+ }
14
+ if (localeEntity === entity &&
15
+ getInstanceOfEntityFromDatabaseInMemory(databaseInMemory, entity.name, newInstanceId) !==
16
+ undefined) {
17
+ return error(new HTTPError(400, `Duplicate id "${newInstanceId}" for locale entity`));
18
+ }
19
+ return map(checkUpdateInstancePossible(databaseInMemory, entity, instanceContent), () => newInstanceId);
20
+ };
21
+ export const createInstance = (res, localeEntity, entity, instanceContent, customId) => then(res, data => then(checkCreateInstancePossible(localeEntity, data.db, entity, instanceContent, customId), newInstanceId => map(setInstanceT(ok(data), entity, { id: newInstanceId, content: instanceContent }), data => ({ ...data, instanceId: newInstanceId }))));
22
+ const checkUpdateInstancePossible = (databaseInMemory, entity, instanceContent) => {
23
+ const validationErrors = validateEntityDecl(createValidators(databaseInMemory, false), [], entity, instanceContent);
24
+ if (validationErrors.length > 0) {
25
+ return error(new HTTPError(400, validationErrors.map(getErrorMessageForDisplay).join("\n\n")));
26
+ }
27
+ else {
28
+ return ok();
29
+ }
30
+ };
31
+ export const updateInstance = (res, entity, instanceId, instanceContent) => then(res, data => then(checkUpdateInstancePossible(data.db, entity, instanceContent), () => setInstanceT(ok(data), entity, { id: instanceId, content: instanceContent })));
32
+ const checkDeleteInstancePossible = (referencesToInstances, instanceId) => {
33
+ if (isReferencedByOtherInstances(referencesToInstances, instanceId)) {
34
+ return error(new HTTPError(400, "Cannot delete instance that is referenced by other instances"));
35
+ }
36
+ else {
37
+ return ok();
38
+ }
39
+ };
40
+ export const deleteInstance = (res, entity, instanceId) => then(res, data => then(checkDeleteInstancePossible(data.refs, instanceId), () => deleteInstanceT(ok(data), entity, instanceId)));
@@ -1,6 +1,7 @@
1
1
  import type { SerializedDecl } from "../../shared/schema/declarations/Declaration.ts";
2
- import type { InstanceContainer } from "../../shared/utils/instances.ts";
2
+ import type { InstanceContent } from "../../shared/utils/instances.ts";
3
3
  import type { EntityDecl } from "../schema/declarations/EntityDecl.ts";
4
+ import { type DatabaseInMemory } from "./databaseInMemory.ts";
4
5
  /**
5
6
  * A mapping from instance IDs to the list of instance IDs that reference them.
6
7
  */
@@ -8,5 +9,5 @@ export type ReferencesToInstances = {
8
9
  [instanceId: string]: string[];
9
10
  };
10
11
  export declare const isReferencedByOtherInstances: (referencesToInstances: ReferencesToInstances, instanceId: string, otherInstancesToDelete?: string[]) => boolean;
11
- export declare const getReferencesToInstances: (instancesByEntityName: Record<string, InstanceContainer[]>, serializedDeclarationsByName: Record<string, SerializedDecl>) => Promise<ReferencesToInstances>;
12
- export declare const updateReferencesToInstances: (entitiesByName: Record<string, EntityDecl>, referencesToInstances: ReferencesToInstances, entityName: string, instanceId: string, oldInstance: unknown, newInstance: unknown) => ReferencesToInstances;
12
+ export declare const getReferencesToInstances: (databaseInMemory: DatabaseInMemory, serializedDeclarationsByName: Record<string, SerializedDecl>) => Promise<ReferencesToInstances>;
13
+ export declare const updateReferencesToInstances: (entitiesByName: Record<string, EntityDecl>, referencesToInstances: ReferencesToInstances, entityName: string, instanceId: string, oldInstance: InstanceContent | undefined, newInstance: InstanceContent | undefined) => ReferencesToInstances;
@@ -3,6 +3,7 @@ import { resolve } from "node:path";
3
3
  import { difference, removeAt } from "../../shared/utils/array.js";
4
4
  import { isOk } from "../../shared/utils/result.js";
5
5
  import { getReferencesForEntityDecl } from "../schema/declarations/EntityDecl.js";
6
+ import { getGroupedInstancesFromDatabaseInMemory, } from "./databaseInMemory.js";
6
7
  import { WorkerPool } from "./workers.js";
7
8
  const debug = Debug("tsondb:utils:references");
8
9
  export const isReferencedByOtherInstances = (referencesToInstances, instanceId, otherInstancesToDelete) => {
@@ -34,11 +35,11 @@ const removeReference = (acc, reference, instanceId) => {
34
35
  return acc;
35
36
  };
36
37
  const removeReferences = (acc, references, instanceId) => references.reduce((acc1, reference) => removeReference(acc1, reference, instanceId), acc);
37
- export const getReferencesToInstances = async (instancesByEntityName, serializedDeclarationsByName) => {
38
+ export const getReferencesToInstances = async (databaseInMemory, serializedDeclarationsByName) => {
38
39
  debug("creating reference worker pool ...");
39
40
  const pool = new WorkerPool(6, resolve(import.meta.dirname, "./referencesWorker.js"), serializedDeclarationsByName);
40
41
  debug("collecting references ...");
41
- const separateResults = await Promise.all(Object.entries(instancesByEntityName).map(([entityName, instances]) => new Promise((resolve, reject) => {
42
+ const separateResults = await Promise.all(getGroupedInstancesFromDatabaseInMemory(databaseInMemory).map(([entityName, instances]) => new Promise((resolve, reject) => {
42
43
  pool.runTask({ entityName, instances }, result => {
43
44
  if (isOk(result)) {
44
45
  debug("collected references for entity %s in %d instances", entityName, instances.length);
@@ -87,3 +87,7 @@ export interface CreateCommitRequestBody {
87
87
  export interface CreateBranchRequestBody {
88
88
  branchName: string;
89
89
  }
90
+ export interface SearchResponseBody {
91
+ query: string;
92
+ results: [entityName: string, instane: InstanceContainerOverview][];
93
+ }
@@ -14,7 +14,7 @@ export type SerializedEntityDisplayName<T extends SerializedObjectType> = Leaves
14
14
  pathInLocaleMap?: string;
15
15
  } | null;
16
16
  type TConstraint = Record<string, SerializedMemberDecl>;
17
- export interface SerializedEntityDecl<Name extends string = string, T extends TConstraint = TConstraint, FK extends (keyof T & string) | undefined = (keyof T & string) | undefined> extends SerializedBaseDecl<Name, []> {
17
+ export interface SerializedEntityDecl<Name extends string = string, T extends TConstraint = TConstraint, FK extends Extract<keyof T, string> | undefined = Extract<keyof T, string> | undefined> extends SerializedBaseDecl<Name, []> {
18
18
  kind: NodeKind["EntityDecl"];
19
19
  namePlural: string;
20
20
  type: SerializedObjectType<T>;
@@ -27,7 +27,7 @@ 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>>;
30
+ export declare const isSerializedEntityDeclWithParentReference: <Name extends string, T extends TConstraint, FK extends Extract<keyof T, string> | undefined>(decl: SerializedEntityDecl<Name, T, FK>) => decl is SerializedEntityDecl<Name, T, NonNullable<FK>>;
31
31
  export declare const isSerializedEntityDeclWithoutParentReference: <Name extends string, T extends TConstraint>(decl: SerializedEntityDecl<Name, T>) => decl is SerializedEntityDecl<Name, T, undefined>;
32
32
  export declare const resolveTypeArgumentsInSerializedEntityDecl: SerializedTypeArgumentsResolver<SerializedEntityDecl>;
33
33
  export declare const getReferencesForSerializedEntityDecl: GetReferencesSerialized<SerializedEntityDecl>;
@@ -10,3 +10,11 @@ export interface SerializedEnumDecl<Name extends string = string, T extends Reco
10
10
  export declare const isSerializedEnumDecl: (node: SerializedNode) => node is SerializedEnumDecl;
11
11
  export declare const resolveTypeArgumentsInSerializedEnumDecl: SerializedTypeArgumentsResolver<SerializedEnumDecl>;
12
12
  export declare const getReferencesForSerializedEnumDecl: GetReferencesSerialized<SerializedEnumDecl>;
13
+ export declare const ENUM_DISCRIMINATOR_KEY = "kind";
14
+ export type ENUM_DISCRIMINATOR_KEY = typeof ENUM_DISCRIMINATOR_KEY;
15
+ export type EnumValue<K extends string, V> = {
16
+ [ENUM_DISCRIMINATOR_KEY]: K;
17
+ } & {
18
+ [Key2 in K]: V;
19
+ };
20
+ export declare const createEnumCaseValue: <K extends string, V>(caseName: K, caseValue: V) => EnumValue<K, V>;
@@ -9,3 +9,8 @@ export const resolveTypeArgumentsInSerializedEnumDecl = (decls, args, decl) => {
9
9
  };
10
10
  };
11
11
  export const getReferencesForSerializedEnumDecl = (decls, decl, value) => getReferencesForSerializedEnumType(decls, decl.type, value);
12
+ export const ENUM_DISCRIMINATOR_KEY = "kind";
13
+ export const createEnumCaseValue = (caseName, caseValue) => ({
14
+ [ENUM_DISCRIMINATOR_KEY]: caseName,
15
+ [caseName]: caseValue,
16
+ });
@@ -1,11 +1,12 @@
1
- import type { RangeBound } from "../../validation/number.ts";
1
+ import type { NumberConstraints } from "../../validation/number.ts";
2
2
  import type { GetReferencesSerialized, NodeKind, SerializedTypeArgumentsResolver } from "../Node.ts";
3
3
  import type { SerializedBaseType } from "./Type.ts";
4
- export interface SerializedFloatType extends SerializedBaseType {
4
+ export interface FloatConstraints extends NumberConstraints {
5
+ fractionDigits?: number;
6
+ }
7
+ export declare const DEFAULT_FRACTION_DIGITS = 2;
8
+ export interface SerializedFloatType extends SerializedBaseType, FloatConstraints {
5
9
  kind: NodeKind["FloatType"];
6
- minimum?: RangeBound;
7
- maximum?: RangeBound;
8
- multipleOf?: number;
9
10
  }
10
11
  export declare const resolveTypeArgumentsInSerializedFloatType: SerializedTypeArgumentsResolver<SerializedFloatType>;
11
12
  export declare const getReferencesForSerializedFloatType: GetReferencesSerialized<SerializedFloatType>;
@@ -1,2 +1,3 @@
1
+ export const DEFAULT_FRACTION_DIGITS = 2;
1
2
  export const resolveTypeArgumentsInSerializedFloatType = (_decls, _args, type) => type;
2
3
  export const getReferencesForSerializedFloatType = () => [];
@@ -0,0 +1,17 @@
1
+ export type Dictionary<T> = readonly [Readonly<Record<string, T>>, readonly string[]];
2
+ export declare const emptyD: Dictionary<never>;
3
+ export declare const getD: <T>(dict: Dictionary<T>, key: string) => T | undefined;
4
+ export declare const getMapD: <T, U>(dict: Dictionary<T>, key: string, mapFn: (value: T) => U) => U | undefined;
5
+ export declare const hasD: <T>(dict: Dictionary<T>, key: string) => boolean;
6
+ export declare const setD: <T>(dict: Dictionary<T>, key: string, value: T) => Dictionary<T>;
7
+ export declare const removeD: <T>(dict: Dictionary<T>, key: string) => Dictionary<T>;
8
+ export declare const sizeD: (dict: Dictionary<unknown>) => number;
9
+ export declare const toEntriesD: <T>(dict: Dictionary<T>) => [string, T][];
10
+ export declare const fromEntriesD: <T>(entries: [string, T][]) => Dictionary<T>;
11
+ export declare const toValuesD: <T>(dict: Dictionary<T>) => T[];
12
+ export declare const forEachD: <T>(dict: Dictionary<T>, fn: (value: T, key: string) => void) => void;
13
+ export declare const forEachAsyncD: <T>(dict: Dictionary<T>, fn: (value: T, key: string) => Promise<void>) => Promise<void>;
14
+ export declare const modifyD: <T>(dict: Dictionary<T>, key: string, modifyFn: (currentValue: T | undefined) => T | undefined) => Dictionary<T>;
15
+ export declare const findD: <T>(dict: Dictionary<T>, predicate: (value: T, key: string) => boolean) => T | undefined;
16
+ export declare const mapFirstD: <T, U>(dict: Dictionary<T>, mapFn: (value: T, key: string) => U | undefined) => U | undefined;
17
+ export declare const mapD: <T, U>(dict: Dictionary<T>, mapFn: (value: T, key: string) => U) => Dictionary<U>;
@@ -0,0 +1,76 @@
1
+ export const emptyD = [{}, []];
2
+ export const getD = (dict, key) => dict[0][key];
3
+ export const getMapD = (dict, key, mapFn) => {
4
+ const value = getD(dict, key);
5
+ return value === undefined ? undefined : mapFn(value);
6
+ };
7
+ export const hasD = (dict, key) => Object.prototype.hasOwnProperty.call(dict[0], key);
8
+ export const setD = (dict, key, value) => {
9
+ if (hasD(dict, key)) {
10
+ return [{ ...dict[0], [key]: value }, dict[1]];
11
+ }
12
+ else {
13
+ return [{ ...dict[0], [key]: value }, [...dict[1], key]];
14
+ }
15
+ };
16
+ export const removeD = (dict, key) => {
17
+ if (hasD(dict, key)) {
18
+ const { [key]: _, ...newRecord } = dict[0];
19
+ return [newRecord, dict[1].filter(k => k !== key)];
20
+ }
21
+ else {
22
+ return dict;
23
+ }
24
+ };
25
+ export const sizeD = (dict) => dict[1].length;
26
+ export const toEntriesD = (dict) => Object.entries(dict[0]);
27
+ export const fromEntriesD = (entries) => {
28
+ const record = Object.fromEntries(entries);
29
+ const keys = Object.keys(record);
30
+ return [record, keys];
31
+ };
32
+ export const toValuesD = (dict) => Object.values(dict[0]);
33
+ export const forEachD = (dict, fn) => {
34
+ for (const [key, value] of Object.entries(dict[0])) {
35
+ fn(value, key);
36
+ }
37
+ };
38
+ export const forEachAsyncD = async (dict, fn) => {
39
+ for (const [key, value] of Object.entries(dict[0])) {
40
+ await fn(value, key);
41
+ }
42
+ };
43
+ export const modifyD = (dict, key, modifyFn) => {
44
+ const currentValue = getD(dict, key);
45
+ const newValue = modifyFn(currentValue);
46
+ if (newValue === undefined) {
47
+ return removeD(dict, key);
48
+ }
49
+ else {
50
+ return setD(dict, key, newValue);
51
+ }
52
+ };
53
+ export const findD = (dict, predicate) => {
54
+ for (const [key, value] of Object.entries(dict[0])) {
55
+ if (predicate(value, key)) {
56
+ return value;
57
+ }
58
+ }
59
+ return undefined;
60
+ };
61
+ export const mapFirstD = (dict, mapFn) => {
62
+ for (const [key, value] of Object.entries(dict[0])) {
63
+ const mapped = mapFn(value, key);
64
+ if (mapped !== undefined) {
65
+ return mapped;
66
+ }
67
+ }
68
+ return undefined;
69
+ };
70
+ export const mapD = (dict, mapFn) => {
71
+ const newRecord = {};
72
+ for (const [key, value] of Object.entries(dict[0])) {
73
+ newRecord[key] = mapFn(value, key);
74
+ }
75
+ return [newRecord, dict[1]];
76
+ };
@@ -2,9 +2,10 @@ import type { EntityDecl } from "../../node/schema/index.ts";
2
2
  import type { GetInstanceById } from "../../node/server/index.ts";
3
3
  import { type GetChildInstancesForInstanceId } from "../../node/utils/displayName.ts";
4
4
  import type { GitFileStatus } from "./git.ts";
5
+ export type InstanceContent = object;
5
6
  export interface InstanceContainer {
6
7
  id: string;
7
- content: unknown;
8
+ content: InstanceContent;
8
9
  gitStatus?: GitFileStatus;
9
10
  }
10
11
  export interface InstanceContainerOverview {
@@ -14,4 +15,3 @@ export interface InstanceContainerOverview {
14
15
  displayNameLocaleId?: string;
15
16
  }
16
17
  export declare const getInstanceContainerOverview: (entity: EntityDecl, instanceContainer: InstanceContainer, getInstanceById: GetInstanceById, getChildInstancesForInstanceId: GetChildInstancesForInstanceId, locales: string[]) => InstanceContainerOverview;
17
- export type InstancesByEntityName = Record<string, InstanceContainer[]>;
@@ -43,6 +43,10 @@ export declare const reduce: <T, E, R>(result: Result<T, E>, fok: (value: T) =>
43
43
  * Maps the value of a result to a new value.
44
44
  */
45
45
  export declare const map: <T, U, E>(result: Result<T, E>, f: (value: T) => U) => Result<U, E>;
46
+ /**
47
+ * Chains a result to a new result.
48
+ */
49
+ export declare const then: <T, U, E>(result: Result<T, E>, f: (value: T) => Result<U, E>) => Result<U, E>;
46
50
  /**
47
51
  * Maps an error to a new error.
48
52
  */
@@ -25,6 +25,10 @@ export const reduce = (result, fok, ferror) => (isOk(result) ? fok(result.value)
25
25
  * Maps the value of a result to a new value.
26
26
  */
27
27
  export const map = (result, f) => isOk(result) ? ok(f(result.value)) : result;
28
+ /**
29
+ * Chains a result to a new result.
30
+ */
31
+ export const then = (result, f) => isOk(result) ? f(result.value) : result;
28
32
  /**
29
33
  * Maps an error to a new error.
30
34
  */
@@ -0,0 +1,2 @@
1
+ import type { SearchResponseBody } from "../../shared/api.ts";
2
+ export declare const searchInstances: (locales: string[], query: string) => Promise<SearchResponseBody>;
@@ -0,0 +1,7 @@
1
+ import { getResource } from "../utils/api.js";
2
+ export const searchInstances = async (locales, query) => getResource("/api/search", {
3
+ locales,
4
+ modifyUrl: url => {
5
+ url.searchParams.set("q", query);
6
+ },
7
+ });
@@ -4,25 +4,26 @@ import type { SetStateAction } from "preact/compat";
4
4
  import { type Dispatch } from "preact/hooks";
5
5
  import type { UnsafeEntityTaggedInstanceContainerWithChildInstances } from "../../node/utils/childInstances.ts";
6
6
  import type { SerializedEntityDecl } from "../../shared/schema/declarations/EntityDecl.ts";
7
+ import type { InstanceContent } from "../../shared/utils/instances.ts";
7
8
  import { type GetDeclFromDeclName } from "../hooks/useSecondaryDeclarations.ts";
8
9
  export type InstanceRouteSkeletonInitializer = (values: {
9
10
  locales: string[];
10
11
  entity: SerializedEntityDecl;
11
12
  instanceId: string | undefined;
12
- setInstanceContent: Dispatch<SetStateAction<unknown>>;
13
+ setInstanceContent: Dispatch<SetStateAction<InstanceContent>>;
13
14
  getDeclFromDeclName: GetDeclFromDeclName;
14
15
  }) => Promise<void>;
15
16
  export type InstanceRouteSkeletonSubmitHandler<A extends string = string> = (values: {
16
17
  locales: string[];
17
18
  entity: SerializedEntityDecl;
18
19
  instanceId: string | undefined;
19
- instanceContent: unknown;
20
+ instanceContent: InstanceContent;
20
21
  action: A;
21
22
  customId: string;
22
23
  isLocaleEntity: boolean | undefined;
23
24
  childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[];
24
25
  route: LocationHook["route"];
25
- setInstanceContent: Dispatch<SetStateAction<unknown>>;
26
+ setInstanceContent: Dispatch<SetStateAction<InstanceContent>>;
26
27
  setCustomId: Dispatch<SetStateAction<string>>;
27
28
  getDeclFromDeclName: GetDeclFromDeclName;
28
29
  updateLocalGitState?: () => Promise<void>;
@@ -31,13 +32,13 @@ export type InstanceRouteSkeletonOnSubmitHandler = (values: {
31
32
  locales: string[];
32
33
  entity: SerializedEntityDecl;
33
34
  instanceId: string | undefined;
34
- instanceContent: unknown;
35
+ instanceContent: InstanceContent;
35
36
  buttonName: string | undefined;
36
37
  customId: string;
37
38
  isLocaleEntity: boolean | undefined;
38
39
  childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[];
39
40
  route: LocationHook["route"];
40
- setInstanceContent: Dispatch<SetStateAction<unknown>>;
41
+ setInstanceContent: Dispatch<SetStateAction<InstanceContent>>;
41
42
  setCustomId: Dispatch<SetStateAction<string>>;
42
43
  getDeclFromDeclName: GetDeclFromDeclName;
43
44
  updateLocalGitState?: () => Promise<void>;
@@ -46,12 +47,12 @@ export type InstanceRouteSkeletonOnSaveHandler = (values: {
46
47
  locales: string[];
47
48
  entity: SerializedEntityDecl;
48
49
  instanceId: string | undefined;
49
- instanceContent: unknown;
50
+ instanceContent: InstanceContent;
50
51
  customId: string;
51
52
  isLocaleEntity: boolean | undefined;
52
53
  childInstances: UnsafeEntityTaggedInstanceContainerWithChildInstances[];
53
54
  route: LocationHook["route"];
54
- setInstanceContent: Dispatch<SetStateAction<unknown>>;
55
+ setInstanceContent: Dispatch<SetStateAction<InstanceContent>>;
55
56
  setCustomId: Dispatch<SetStateAction<string>>;
56
57
  getDeclFromDeclName: GetDeclFromDeclName;
57
58
  updateLocalGitState?: () => Promise<void>;
@@ -60,7 +61,7 @@ export type InstanceRouteSkeletonTitleBuilder = (values: {
60
61
  locales: string[];
61
62
  entity: SerializedEntityDecl;
62
63
  instanceId: string | undefined;
63
- instanceContent: unknown;
64
+ instanceContent: InstanceContent | undefined;
64
65
  }) => string | undefined;
65
66
  type Props = {
66
67
  mode: "create" | "edit";