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,24 +1,35 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { useLocation, useRoute } from "preact-iso";
3
- import { useEffect, useMemo, useState } from "preact/hooks";
4
- import { deepEqual } from "../../shared/utils/compare.js";
5
- import { getDisplayNameFromEntityInstance } from "../../shared/utils/displayName.js";
3
+ import { useCallback, useEffect, useState } from "preact/hooks";
4
+ import { getSerializedDisplayNameFromEntityInstance } from "../../shared/utils/displayName.js";
5
+ import { toTitleCase } from "../../shared/utils/string.js";
6
6
  import { deleteInstanceByEntityNameAndId, getInstanceByEntityNameAndId, updateInstanceByEntityNameAndId, } from "../api.js";
7
7
  import { Layout } from "../components/Layout.js";
8
8
  import { TypeInput } from "../components/typeInputs/TypeInput.js";
9
9
  import { useEntityFromRoute } from "../hooks/useEntityFromRoute.js";
10
10
  import { useInstanceNamesByEntity } from "../hooks/useInstanceNamesByEntity.js";
11
11
  import { useGetDeclFromDeclName } from "../hooks/useSecondaryDeclarations.js";
12
+ import { printJson } from "../utils/debug.js";
12
13
  import { NotFound } from "./NotFound.js";
13
14
  export const Instance = () => {
14
15
  const { params: { name, id }, } = useRoute();
15
- const getDeclFromDeclName = useGetDeclFromDeclName();
16
+ const [getDeclFromDeclName, declsLoaded] = useGetDeclFromDeclName();
16
17
  const entityFromRoute = useEntityFromRoute();
17
18
  const [instanceNamesByEntity] = useInstanceNamesByEntity();
18
19
  const [instance, setInstance] = useState();
19
20
  const [originalInstance, setOriginalInstance] = useState();
20
21
  const { route } = useLocation();
21
- const hasChanges = useMemo(() => !deepEqual(instance?.content, originalInstance?.content), [instance?.content, originalInstance?.content]);
22
+ useEffect(() => {
23
+ if (entityFromRoute?.entity && instance?.content && id) {
24
+ const defaultName = id;
25
+ const instanceName = getSerializedDisplayNameFromEntityInstance(entityFromRoute.entity, instance.content, defaultName);
26
+ const entityName = entityFromRoute.entity.name;
27
+ document.title = instanceName + " — " + toTitleCase(entityName) + " — TSONDB";
28
+ }
29
+ else {
30
+ document.title = "Not found — TSONDB";
31
+ }
32
+ }, [entityFromRoute?.entity, id, instance?.content]);
22
33
  useEffect(() => {
23
34
  if (name && id) {
24
35
  getInstanceByEntityNameAndId(name, id)
@@ -33,7 +44,7 @@ export const Instance = () => {
33
44
  }, [id, name]);
34
45
  const handleSubmit = (event) => {
35
46
  event.preventDefault();
36
- if (name && id && instance) {
47
+ if (event.submitter?.getAttribute("name") === "save" && name && id && instance) {
37
48
  updateInstanceByEntityNameAndId(name, id, instance.content)
38
49
  .then(updatedInstance => {
39
50
  setInstance(updatedInstance.instance);
@@ -46,17 +57,24 @@ export const Instance = () => {
46
57
  });
47
58
  }
48
59
  };
60
+ const handleOnChange = useCallback((value) => {
61
+ setInstance(container => container && { ...container, content: value });
62
+ }, []);
49
63
  if (!name || !id) {
50
64
  return _jsx(NotFound, {});
51
65
  }
52
- if (!entityFromRoute || !instance || !originalInstance) {
66
+ if (!entityFromRoute ||
67
+ !instance ||
68
+ !originalInstance ||
69
+ !instanceNamesByEntity ||
70
+ !declsLoaded) {
53
71
  return (_jsxs(Layout, { breadcrumbs: [
54
72
  { url: "/", label: "Home" },
55
73
  { url: `/entities/${name}`, label: name },
56
74
  ], children: [_jsx("h1", { children: id }), _jsx("p", { className: "loading", children: "Loading \u2026" })] }));
57
75
  }
58
76
  const defaultName = id;
59
- const instanceName = getDisplayNameFromEntityInstance(entityFromRoute.entity, instance.content, defaultName);
77
+ const instanceName = getSerializedDisplayNameFromEntityInstance(entityFromRoute.entity, instance.content, defaultName);
60
78
  return (_jsxs(Layout, { breadcrumbs: [
61
79
  { url: "/", label: "Home" },
62
80
  { url: `/entities/${name}`, label: entityFromRoute.entity.name },
@@ -72,8 +90,5 @@ export const Instance = () => {
72
90
  }
73
91
  });
74
92
  }
75
- }, children: "Delete" })] }), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entityFromRoute.entity.type, value: instance.content, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: value => {
76
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
77
- setInstance(container => ({ ...container, content: value }));
78
- } }), _jsx("button", { type: "submit", disabled: !hasChanges, class: "primary", children: "Save" })] })] }));
93
+ }, children: "Delete" })] }), _jsxs("div", { class: "debug-compare", style: { display: "flex", gap: "1rem", width: "100%" }, children: [_jsxs("div", { style: { flex: "1 1 0", position: "relative" }, children: [_jsx("p", { children: "Original" }), _jsx("pre", { style: { overflowX: "scroll", width: "40rem", maxWidth: "100%" }, children: _jsx("code", { dangerouslySetInnerHTML: { __html: printJson(originalInstance.content) } }) })] }), _jsxs("div", { style: { flex: "1 1 0", position: "relative" }, children: [_jsx("p", { children: "Current" }), _jsx("pre", { style: { overflowX: "scroll", width: "40rem", maxWidth: "100%" }, children: _jsx("code", { dangerouslySetInnerHTML: { __html: printJson(instance.content) } }) })] })] }), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entityFromRoute.entity.type, value: instance.content, path: undefined, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: handleOnChange }), _jsx("div", { class: "form-footer btns", children: _jsx("button", { type: "submit", name: "save", class: "primary", children: "Save" }) })] })] }));
79
94
  };
@@ -1,5 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
+ import { useEffect } from "preact/hooks";
2
3
  import { Layout } from "../components/Layout.js";
3
4
  export const NotFound = () => {
5
+ useEffect(() => {
6
+ document.title = "Not found — TSONDB";
7
+ }, []);
4
8
  return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: "Home" }], children: [_jsx("h1", { children: "404 Not Found" }), _jsx("p", { children: "The page you are looking for does not exist." })] }));
5
9
  };
@@ -0,0 +1 @@
1
+ export declare const printJson: (data: unknown) => string;
@@ -0,0 +1,5 @@
1
+ export const printJson = (data) => JSON.stringify(data, undefined, 2)
2
+ .replace(/\n *([}\]],?)/g, " $1")
3
+ .replace(/((?:^|\n *)[{[])\n +/g, "$1 ")
4
+ .replace(/"(.+?)":/g, '<span style="color: darkorange">$1</span>:')
5
+ .replace(/ "(.*?)"([ ,])/g, ' <span style="color: darkgreen">"$1"</span>$2');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsondb",
3
- "version": "0.5.8",
3
+ "version": "0.5.12",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "Lukas Obermann",
@@ -17,7 +17,7 @@
17
17
  ".": "./dist/src/index.js",
18
18
  "./renderer/jsonschema": "./dist/src/node/renderers/jsonschema/index.js",
19
19
  "./renderer/ts": "./dist/src/node/renderers/ts/index.js",
20
- "./schema": "./dist/src/node/Schema.js",
20
+ "./schema": "./dist/src/node/schema/Schema.js",
21
21
  "./schema/def": "./dist/src/node/schema/index.js"
22
22
  },
23
23
  "scripts": {
@@ -31,18 +31,18 @@
31
31
  "release:sign": "commit-and-tag-version --sign --signoff"
32
32
  },
33
33
  "devDependencies": {
34
- "@eslint/js": "^9.34.0",
34
+ "@eslint/js": "^9.35.0",
35
35
  "@types/debug": "^4.1.12",
36
36
  "@types/express": "^5.0.3",
37
37
  "@types/node": "^24.3.1",
38
38
  "commit-and-tag-version": "^12.6.0",
39
- "eslint": "^9.34.0",
39
+ "eslint": "^9.35.0",
40
40
  "eslint-plugin-react": "^7.37.5",
41
41
  "eslint-plugin-react-hooks": "^5.2.0",
42
42
  "globals": "^16.3.0",
43
43
  "prettier": "^3.6.2",
44
44
  "typescript": "^5.9.2",
45
- "typescript-eslint": "^8.42.0"
45
+ "typescript-eslint": "^8.43.0"
46
46
  },
47
47
  "dependencies": {
48
48
  "debug": "^4.4.1",
@@ -51,7 +51,7 @@
51
51
  "preact-iso": "^2.10.0",
52
52
  "simple-cli-args": "^0.1.2",
53
53
  "simple-git": "^3.28.0",
54
- "uuid": "^11.1.0"
54
+ "uuid": "^13.0.0"
55
55
  },
56
56
  "repository": "github:elyukai/tsondb",
57
57
  "bugs": {
@@ -1,12 +1,16 @@
1
- * {
1
+ *,
2
+ *::after,
3
+ *::before {
2
4
  box-sizing: border-box;
3
5
  }
4
6
 
5
7
  :root {
6
- --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
7
- "Segoe UI Symbol", "Noto Color Emoji";
8
- --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
9
- "Courier New", monospace;
8
+ --font-sans:
9
+ ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
10
+ "Noto Color Emoji";
11
+ --font-mono:
12
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
13
+ monospace;
10
14
  --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
11
15
  --background: white;
12
16
  --markdown-background: #e1e3e7;
@@ -227,7 +231,8 @@ button.destructive:disabled {
227
231
 
228
232
  input,
229
233
  textarea,
230
- select {
234
+ select,
235
+ .textarea-grow-wrap::after {
231
236
  background: var(--background);
232
237
  color: var(--color);
233
238
  font-family: var(--font-sans);
@@ -247,6 +252,28 @@ select[aria-invalid="true"] {
247
252
  color: var(--error-color);
248
253
  }
249
254
 
255
+ /* https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ */
256
+
257
+ .textarea-grow-wrap {
258
+ display: grid;
259
+ }
260
+
261
+ .textarea-grow-wrap::after {
262
+ content: attr(data-value) " ";
263
+ white-space: pre-wrap;
264
+ visibility: hidden;
265
+ }
266
+
267
+ .textarea-grow-wrap > textarea {
268
+ resize: none;
269
+ overflow: hidden;
270
+ }
271
+
272
+ .textarea-grow-wrap > textarea,
273
+ .textarea-grow-wrap::after {
274
+ grid-area: 1 / 1 / 2 / 2;
275
+ }
276
+
250
277
  input[type="checkbox"] {
251
278
  height: 1rem;
252
279
  width: 1rem;
@@ -265,18 +292,13 @@ input[type="checkbox"]:checked {
265
292
  input[type="checkbox"]:checked::before {
266
293
  content: "";
267
294
  display: block;
268
- border-width: 0 0 1px 1px;
295
+ border-width: 0 0 0.125rem 0.125rem;
269
296
  border-style: solid;
270
- width: 0.6rem;
271
- height: 0.3rem;
297
+ width: 0.8rem;
298
+ height: 0.4rem;
272
299
  border-color: var(--background);
273
300
  transform: rotate(-45deg);
274
- margin: calc(-0.2rem - 1px) 0 0 calc(-0.275rem - 1px);
275
- }
276
-
277
- textarea {
278
- min-height: 5rem;
279
- resize: vertical;
301
+ margin: calc(-0.325rem) 0 0 calc(-0.4rem);
280
302
  }
281
303
 
282
304
  .select-wrapper {
@@ -327,59 +349,109 @@ select {
327
349
  margin-top: 2.3rem;
328
350
  }
329
351
 
330
- .entities,
331
- .instances {
352
+ .list-header {
353
+ display: flex;
354
+ justify-content: space-between;
355
+ align-items: center;
356
+ gap: 1rem;
357
+ }
358
+
359
+ .list-header form {
360
+ display: flex;
361
+ gap: 0.5rem;
362
+ align-items: center;
363
+ }
364
+
365
+ .entries {
332
366
  list-style-type: " ";
333
367
  padding: 0;
334
368
  margin: 1.5rem 0 0;
369
+ container-type: inline-size;
335
370
  }
336
371
 
337
- .entity-item,
338
- .instance-item {
339
- display: flex;
340
- gap: 1.5rem;
372
+ .entries-item {
373
+ display: grid;
374
+ gap: 0.125rem 1.5rem;
341
375
  padding: 0.5rem 0;
342
376
  border-top: 1px solid var(--separator-color);
377
+ grid-template-columns: 1fr auto;
378
+ grid-template-rows: auto 1fr;
379
+ grid-template-areas: "title side" "subtitle side" ". side";
380
+ align-items: center;
381
+ --text-padding: 0.5rem;
343
382
  }
344
383
 
345
- .entity-item:first-child,
346
- .instance-item:first-child {
384
+ @container (min-width: 40rem) {
385
+ .entries-item {
386
+ grid-template-columns: 1fr auto auto;
387
+ grid-template-rows: auto;
388
+ grid-template-areas: "title subtitle side";
389
+ align-items: center;
390
+ }
391
+ }
392
+
393
+ .entries-item:first-child {
347
394
  border-top: none;
348
395
  }
349
396
 
350
- .entity-item h2,
351
- .instance-item h2 {
397
+ .entries-item--created {
398
+ color: var(--highlight-color);
399
+ background: var(--highlight-background);
400
+ }
401
+
402
+ .entries-item h2 {
352
403
  font-size: 1rem;
353
404
  margin: 0;
354
405
  flex: 1 1 auto;
355
- padding: 0.65rem 0;
406
+ line-height: 1.3;
356
407
  }
357
408
 
358
- .entity-item .title {
359
- flex: 1 1 0;
409
+ .entries-item__title {
410
+ grid-area: title;
411
+ padding-top: var(--text-padding);
360
412
  }
361
413
 
362
- .entity-item .title p {
414
+ @container (min-width: 40rem) {
415
+ .entries-item__title {
416
+ padding-block: var(--text-padding);
417
+ }
418
+ }
419
+
420
+ .entries-item__title p {
363
421
  color: var(--secondary-color);
422
+ margin: 0.125rem 0 0;
423
+ }
424
+
425
+ .entries-item__title .description {
364
426
  margin: 0;
365
- padding-bottom: 0.5rem;
366
427
  }
367
428
 
368
- .entity-item .meta,
369
- .instance-item .id {
429
+ .entries-item__subtitle {
370
430
  color: var(--secondary-color);
371
431
  margin: 0;
372
- padding: 0.5rem 0;
432
+ padding: 0;
373
433
  white-space: nowrap;
434
+ grid-area: subtitle;
435
+ text-overflow: ellipsis;
436
+ overflow: hidden;
437
+ padding-bottom: var(--text-padding);
374
438
  }
375
439
 
376
- .instance-item .id {
440
+ @container (min-width: 40rem) {
441
+ .entries-item__subtitle {
442
+ padding-block: var(--text-padding);
443
+ }
444
+ }
445
+
446
+ .entries-item__subtitle--id {
377
447
  font-family: var(--font-mono);
378
448
  }
379
449
 
380
- .instance-item--created {
381
- color: var(--highlight-color);
382
- background: var(--highlight-background);
450
+ .entries-item__side {
451
+ grid-area: side;
452
+ display: flex;
453
+ align-items: center;
454
+ gap: 1rem;
383
455
  }
384
456
 
385
457
  .field.field--id {
@@ -464,8 +536,15 @@ form > .field--container {
464
536
 
465
537
  .field--string {
466
538
  display: flex;
539
+ flex-direction: column;
467
540
  gap: 1rem;
468
- align-items: start;
541
+ }
542
+
543
+ @container (min-width: 40rem) {
544
+ .field--string {
545
+ flex-direction: row;
546
+ align-items: start;
547
+ }
469
548
  }
470
549
 
471
550
  .field--string .editor {
@@ -490,6 +569,10 @@ form > .field--container {
490
569
  margin-bottom: 0;
491
570
  }
492
571
 
572
+ .container-item {
573
+ container-type: inline-size;
574
+ }
575
+
493
576
  .container-item-header {
494
577
  display: flex;
495
578
  align-items: center;
@@ -555,6 +638,17 @@ button[type="submit"] {
555
638
  margin: 0.35rem 0 0;
556
639
  }
557
640
 
641
+ main:has(.form-footer) {
642
+ margin-bottom: -1.5rem;
643
+ }
644
+
645
+ .form-footer {
646
+ position: sticky;
647
+ bottom: 0;
648
+ padding-bottom: 1.5rem;
649
+ background: var(--background);
650
+ }
651
+
558
652
  .git-status {
559
653
  width: 1.75rem;
560
654
  height: 1.75rem;