@myko/ui-svelte 4.4.1 → 4.4.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.
- package/{src/lib → dist}/components/ConnectionStats.svelte +8 -22
- package/dist/components/ConnectionStats.svelte.d.ts +10 -0
- package/dist/components/EntityDiffBadge.svelte +10 -0
- package/dist/components/EntityDiffBadge.svelte.d.ts +7 -0
- package/{src/lib → dist}/components/EntityDiffFields.svelte +27 -49
- package/dist/components/EntityDiffFields.svelte.d.ts +11 -0
- package/{src/lib → dist}/components/EntityDiffNode.svelte +25 -43
- package/dist/components/EntityDiffNode.svelte.d.ts +14 -0
- package/{src/lib → dist}/components/EntityDiffTree.svelte +150 -200
- package/dist/components/EntityDiffTree.svelte.d.ts +21 -0
- package/dist/components/Logs.svelte +30 -0
- package/dist/components/Logs.svelte.d.ts +7 -0
- package/dist/components/Query.svelte +21 -0
- package/dist/components/Query.svelte.d.ts +35 -0
- package/dist/components/Report.svelte +13 -0
- package/dist/components/Report.svelte.d.ts +32 -0
- package/dist/components/Search.svelte +46 -0
- package/dist/components/Search.svelte.d.ts +56 -0
- package/dist/components/ServerView.svelte +76 -0
- package/dist/components/ServerView.svelte.d.ts +8 -0
- package/dist/components/state/resolutions.d.ts +11 -0
- package/dist/components/state/resolutions.js +129 -0
- package/dist/components/state/viewstate.svelte.d.ts +60 -0
- package/dist/components/state/viewstate.svelte.js +259 -0
- package/dist/components/state/windback.svelte.d.ts +16 -0
- package/dist/components/state/windback.svelte.js +65 -0
- package/dist/components/transactions/EntityHistory.svelte +135 -0
- package/dist/components/transactions/EntityHistory.svelte.d.ts +10 -0
- package/{src/lib → dist}/components/transactions/TimeStrip.svelte +18 -25
- package/dist/components/transactions/TimeStrip.svelte.d.ts +3 -0
- package/dist/components/transactions/TransactionDetails.svelte +24 -0
- package/dist/components/transactions/TransactionDetails.svelte.d.ts +7 -0
- package/{src/lib → dist}/components/transactions/TransactionEvent.svelte +20 -32
- package/dist/components/transactions/TransactionEvent.svelte.d.ts +7 -0
- package/{src/lib → dist}/components/transactions/TransactionEventGroup.svelte +8 -13
- package/dist/components/transactions/TransactionEventGroup.svelte.d.ts +8 -0
- package/dist/components/transactions/Transactions.svelte +94 -0
- package/dist/components/transactions/Transactions.svelte.d.ts +8 -0
- package/{src/lib → dist}/components/transactions/TransactonView.svelte +3 -7
- package/dist/components/transactions/TransactonView.svelte.d.ts +7 -0
- package/{src/lib → dist}/components/windback/WindbackFrame.svelte +3 -6
- package/dist/components/windback/WindbackFrame.svelte.d.ts +7 -0
- package/dist/components/windback/index.js +1 -0
- package/dist/index.d.ts +18 -0
- package/{src/lib/index.ts → dist/index.js} +2 -15
- package/dist/services/svelte-client.svelte.d.ts +278 -0
- package/dist/services/svelte-client.svelte.js +678 -0
- package/dist/utils/entity-apply.d.ts +17 -0
- package/dist/utils/entity-apply.js +35 -0
- package/dist/utils/entity-diff.d.ts +40 -0
- package/dist/utils/entity-diff.js +57 -0
- package/dist/utils/entity-tree.d.ts +24 -0
- package/dist/utils/entity-tree.js +111 -0
- package/package.json +18 -3
- package/.prettierignore +0 -4
- package/.prettierrc +0 -15
- package/src/app.d.ts +0 -13
- package/src/app.html +0 -12
- package/src/lib/components/EntityDiffBadge.svelte +0 -18
- package/src/lib/components/Logs.svelte +0 -37
- package/src/lib/components/Query.svelte +0 -34
- package/src/lib/components/Report.svelte +0 -25
- package/src/lib/components/Search.svelte +0 -85
- package/src/lib/components/ServerView.svelte +0 -95
- package/src/lib/components/state/resolutions.ts +0 -137
- package/src/lib/components/state/viewstate.svelte.ts +0 -375
- package/src/lib/components/state/windback.svelte.ts +0 -88
- package/src/lib/components/transactions/EntityHistory.svelte +0 -173
- package/src/lib/components/transactions/TransactionDetails.svelte +0 -26
- package/src/lib/components/transactions/Transactions.svelte +0 -111
- package/src/lib/services/svelte-client.svelte.ts +0 -863
- package/src/lib/utils/entity-apply.ts +0 -47
- package/src/lib/utils/entity-diff.ts +0 -105
- package/src/lib/utils/entity-tree.ts +0 -130
- package/src/routes/+page.svelte +0 -3
- package/static/favicon.png +0 -0
- package/svelte.config.js +0 -18
- package/tsconfig.json +0 -13
- package/vite.config.ts +0 -6
- /package/{src/lib/components/windback/index.ts → dist/components/windback/index.d.ts} +0 -0
|
@@ -1,25 +1,11 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
let { client, class: className = '' }: Props = $props();
|
|
13
|
-
|
|
14
|
-
const resolvedClient = client ?? getMykoClient();
|
|
15
|
-
|
|
16
|
-
const stats = $derived(resolvedClient.stats);
|
|
17
|
-
const isConnected = $derived(resolvedClient.isConnected);
|
|
18
|
-
|
|
19
|
-
// Query for connected server
|
|
20
|
-
const serverQuery = resolvedClient.liveQuery(() => new GetConnectedServer({}));
|
|
21
|
-
|
|
22
|
-
const server = $derived([...serverQuery.items.values()][0]);
|
|
1
|
+
<script lang="ts">import { GetConnectedServer } from "@myko/core";
|
|
2
|
+
import { getMykoClient } from "../services/svelte-client.svelte.js";
|
|
3
|
+
let { client, class: className = "" } = $props();
|
|
4
|
+
const resolvedClient = client ?? getMykoClient();
|
|
5
|
+
const stats = $derived(resolvedClient.stats);
|
|
6
|
+
const isConnected = $derived(resolvedClient.isConnected);
|
|
7
|
+
const serverQuery = resolvedClient.liveQuery(() => new GetConnectedServer({}));
|
|
8
|
+
const server = $derived([...serverQuery.items.values()][0]);
|
|
23
9
|
</script>
|
|
24
10
|
|
|
25
11
|
{#if isConnected && stats}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type SvelteMykoClient } from '../services/svelte-client.svelte.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Optional client instance (defaults to global singleton) */
|
|
4
|
+
client?: SvelteMykoClient;
|
|
5
|
+
/** CSS class for the container */
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
declare const ConnectionStats: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type ConnectionStats = ReturnType<typeof ConnectionStats>;
|
|
10
|
+
export default ConnectionStats;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script lang="ts">let { status } = $props();
|
|
2
|
+
const config = {
|
|
3
|
+
added: { label: "Added", class: "badge-success" },
|
|
4
|
+
removed: { label: "Removed", class: "badge-error" },
|
|
5
|
+
modified: { label: "Modified", class: "badge-warning" },
|
|
6
|
+
unchanged: { label: "Unchanged", class: "badge-ghost" }
|
|
7
|
+
};
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<span class="badge badge-sm {config[status].class}">{config[status].label}</span>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DiffStatus } from '../utils/entity-diff.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
status: DiffStatus;
|
|
4
|
+
}
|
|
5
|
+
declare const EntityDiffBadge: import("svelte").Component<Props, {}, "">;
|
|
6
|
+
type EntityDiffBadge = ReturnType<typeof EntityDiffBadge>;
|
|
7
|
+
export default EntityDiffBadge;
|
|
@@ -1,54 +1,32 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
interface DiffLine {
|
|
27
|
-
key: string;
|
|
28
|
-
status: 'added' | 'removed' | 'changed' | 'unchanged';
|
|
29
|
-
oldVal?: string;
|
|
30
|
-
newVal?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function computeJsonDiff(oldVal: unknown, newVal: unknown): DiffLine[] | null {
|
|
34
|
-
if (!isObject(oldVal) || !isObject(newVal)) return null;
|
|
35
|
-
const allKeys = new Set([...Object.keys(oldVal), ...Object.keys(newVal)]);
|
|
36
|
-
const lines: DiffLine[] = [];
|
|
37
|
-
for (const key of allKeys) {
|
|
38
|
-
const o = oldVal[key];
|
|
39
|
-
const n = newVal[key];
|
|
40
|
-
if (!(key in newVal)) {
|
|
41
|
-
lines.push({ key, status: 'removed', oldVal: JSON.stringify(o) });
|
|
42
|
-
} else if (!(key in oldVal)) {
|
|
43
|
-
lines.push({ key, status: 'added', newVal: JSON.stringify(n) });
|
|
44
|
-
} else if (JSON.stringify(o) !== JSON.stringify(n)) {
|
|
45
|
-
lines.push({ key, status: 'changed', oldVal: JSON.stringify(o), newVal: JSON.stringify(n) });
|
|
46
|
-
} else {
|
|
47
|
-
lines.push({ key, status: 'unchanged', oldVal: JSON.stringify(o), newVal: JSON.stringify(n) });
|
|
48
|
-
}
|
|
1
|
+
<script lang="ts">let { fields, currentData, incomingData } = $props();
|
|
2
|
+
const hasSideBySide = $derived(!!currentData && !!incomingData);
|
|
3
|
+
function formatValue(v) {
|
|
4
|
+
if (v === void 0 || v === null) return "";
|
|
5
|
+
if (typeof v === "object") return JSON.stringify(v, null, 2);
|
|
6
|
+
return String(v);
|
|
7
|
+
}
|
|
8
|
+
function isObject(v) {
|
|
9
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
10
|
+
}
|
|
11
|
+
function computeJsonDiff(oldVal, newVal) {
|
|
12
|
+
if (!isObject(oldVal) || !isObject(newVal)) return null;
|
|
13
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(oldVal), ...Object.keys(newVal)]);
|
|
14
|
+
const lines = [];
|
|
15
|
+
for (const key of allKeys) {
|
|
16
|
+
const o = oldVal[key];
|
|
17
|
+
const n = newVal[key];
|
|
18
|
+
if (!(key in newVal)) {
|
|
19
|
+
lines.push({ key, status: "removed", oldVal: JSON.stringify(o) });
|
|
20
|
+
} else if (!(key in oldVal)) {
|
|
21
|
+
lines.push({ key, status: "added", newVal: JSON.stringify(n) });
|
|
22
|
+
} else if (JSON.stringify(o) !== JSON.stringify(n)) {
|
|
23
|
+
lines.push({ key, status: "changed", oldVal: JSON.stringify(o), newVal: JSON.stringify(n) });
|
|
24
|
+
} else {
|
|
25
|
+
lines.push({ key, status: "unchanged", oldVal: JSON.stringify(o), newVal: JSON.stringify(n) });
|
|
49
26
|
}
|
|
50
|
-
return lines;
|
|
51
27
|
}
|
|
28
|
+
return lines;
|
|
29
|
+
}
|
|
52
30
|
</script>
|
|
53
31
|
|
|
54
32
|
{#if hasSideBySide}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FieldDiff } from '../utils/entity-diff.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
fields: FieldDiff[];
|
|
4
|
+
/** Full current entity data (for side-by-side view). */
|
|
5
|
+
currentData?: Record<string, unknown>;
|
|
6
|
+
/** Full incoming entity data (for side-by-side view). */
|
|
7
|
+
incomingData?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
declare const EntityDiffFields: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type EntityDiffFields = ReturnType<typeof EntityDiffFields>;
|
|
11
|
+
export default EntityDiffFields;
|
|
@@ -1,47 +1,29 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
let expanded = $state(node.status !== 'unchanged' || isRoot);
|
|
19
|
-
|
|
20
|
-
const key = $derived(`${node.type}:${node.id}`);
|
|
21
|
-
const isExcluded = $derived(excluded?.has(key) ?? false);
|
|
22
|
-
const canExclude = $derived(
|
|
23
|
-
!isRoot && (node.status === 'modified' || node.status === 'added' || node.status === 'removed'),
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
const toggleExclude = (e: Event) => {
|
|
27
|
-
e.stopPropagation();
|
|
28
|
-
onToggleExclude?.(key);
|
|
1
|
+
<script lang="ts">import EntityDiffBadge from "./EntityDiffBadge.svelte";
|
|
2
|
+
import EntityDiffFields from "./EntityDiffFields.svelte";
|
|
3
|
+
let { node, showUnchanged = false, excluded, onToggleExclude, isRoot = false } = $props();
|
|
4
|
+
let expanded = $state(node.status !== "unchanged" || isRoot);
|
|
5
|
+
const key = $derived(`${node.type}:${node.id}`);
|
|
6
|
+
const isExcluded = $derived(excluded?.has(key) ?? false);
|
|
7
|
+
const canExclude = $derived(
|
|
8
|
+
!isRoot && (node.status === "modified" || node.status === "added" || node.status === "removed")
|
|
9
|
+
);
|
|
10
|
+
const toggleExclude = (e) => {
|
|
11
|
+
e.stopPropagation();
|
|
12
|
+
onToggleExclude?.(key);
|
|
13
|
+
};
|
|
14
|
+
const hasChangedDescendant = $derived.by(() => {
|
|
15
|
+
const check = (n) => {
|
|
16
|
+
if (n.status !== "unchanged") return true;
|
|
17
|
+
return n.children.some(check);
|
|
29
18
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const visible = $derived(
|
|
40
|
-
isRoot || showUnchanged || node.status !== 'unchanged' || hasChangedDescendant,
|
|
41
|
-
);
|
|
42
|
-
const hasDetails = $derived(
|
|
43
|
-
(node.fields && node.fields.length > 0) || node.children.length > 0,
|
|
44
|
-
);
|
|
19
|
+
return node.children.some(check);
|
|
20
|
+
});
|
|
21
|
+
const visible = $derived(
|
|
22
|
+
isRoot || showUnchanged || node.status !== "unchanged" || hasChangedDescendant
|
|
23
|
+
);
|
|
24
|
+
const hasDetails = $derived(
|
|
25
|
+
node.fields && node.fields.length > 0 || node.children.length > 0
|
|
26
|
+
);
|
|
45
27
|
</script>
|
|
46
28
|
|
|
47
29
|
{#if visible}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EntityDiff } from '../utils/entity-diff.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
node: EntityDiff;
|
|
4
|
+
showUnchanged?: boolean;
|
|
5
|
+
/** Set of "Type:id" keys excluded from import. */
|
|
6
|
+
excluded?: Set<string>;
|
|
7
|
+
/** Callback to toggle an entity's excluded state. */
|
|
8
|
+
onToggleExclude?: (key: string) => void;
|
|
9
|
+
/** Whether this is the root node (always visible). */
|
|
10
|
+
isRoot?: boolean;
|
|
11
|
+
}
|
|
12
|
+
declare const EntityDiffNode: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type EntityDiffNode = ReturnType<typeof EntityDiffNode>;
|
|
14
|
+
export default EntityDiffNode;
|
|
@@ -1,211 +1,161 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
} else {
|
|
35
|
-
next.add(key);
|
|
1
|
+
<script lang="ts">import { diffEntityLists } from "../utils/entity-diff.js";
|
|
2
|
+
import { buildDiffTree } from "../utils/entity-tree.js";
|
|
3
|
+
import EntityDiffBadge from "./EntityDiffBadge.svelte";
|
|
4
|
+
let { rootType, rootId, current, incoming, parentFkFields, excluded = $bindable(/* @__PURE__ */ new Set()), showUnchanged = false } = $props();
|
|
5
|
+
const diffs = $derived(diffEntityLists(current, incoming));
|
|
6
|
+
const tree = $derived(buildDiffTree(rootType, rootId, diffs, parentFkFields));
|
|
7
|
+
const toggleExclude = (key) => {
|
|
8
|
+
const next = new Set(excluded);
|
|
9
|
+
if (next.has(key)) {
|
|
10
|
+
next.delete(key);
|
|
11
|
+
} else {
|
|
12
|
+
next.add(key);
|
|
13
|
+
}
|
|
14
|
+
excluded = next;
|
|
15
|
+
};
|
|
16
|
+
function actionLabels(status) {
|
|
17
|
+
switch (status) {
|
|
18
|
+
case "removed":
|
|
19
|
+
return ["delete", "keep"];
|
|
20
|
+
case "added":
|
|
21
|
+
return ["add", "skip"];
|
|
22
|
+
case "modified":
|
|
23
|
+
return ["update", "keep"];
|
|
24
|
+
default:
|
|
25
|
+
return ["apply", "skip"];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const stats = $derived.by(() => {
|
|
29
|
+
let toDelete = 0, toUpdate = 0, toAdd = 0, toSkip = 0, unchanged = 0;
|
|
30
|
+
for (const [key, d] of diffs) {
|
|
31
|
+
if (d.status === "unchanged") {
|
|
32
|
+
unchanged++;
|
|
33
|
+
continue;
|
|
36
34
|
}
|
|
37
|
-
excluded
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
/** Action labels per status. First = apply action, second = skip action. */
|
|
41
|
-
function actionLabels(status: DiffStatus): [string, string] {
|
|
42
|
-
switch (status) {
|
|
43
|
-
case 'removed': return ['delete', 'keep'];
|
|
44
|
-
case 'added': return ['add', 'skip'];
|
|
45
|
-
case 'modified': return ['update', 'keep'];
|
|
46
|
-
default: return ['apply', 'skip'];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const stats = $derived.by(() => {
|
|
52
|
-
let toDelete = 0, toUpdate = 0, toAdd = 0, toSkip = 0, unchanged = 0;
|
|
53
|
-
for (const [key, d] of diffs) {
|
|
54
|
-
if (d.status === 'unchanged') { unchanged++; continue; }
|
|
55
|
-
if (excluded.has(key)) { toSkip++; continue; }
|
|
56
|
-
if (d.status === 'removed') toDelete++;
|
|
57
|
-
else if (d.status === 'modified') toUpdate++;
|
|
58
|
-
else if (d.status === 'added') toAdd++;
|
|
35
|
+
if (excluded.has(key)) {
|
|
36
|
+
toSkip++;
|
|
37
|
+
continue;
|
|
59
38
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
for (const f of node.fields) {
|
|
160
|
-
const oldStr = formatVal(f.oldValue);
|
|
161
|
-
const newStr = formatVal(f.newValue);
|
|
162
|
-
const status: 'added' | 'removed' | 'changed' =
|
|
163
|
-
f.oldValue === undefined ? 'added' :
|
|
164
|
-
f.newValue === undefined ? 'removed' : 'changed';
|
|
39
|
+
if (d.status === "removed") toDelete++;
|
|
40
|
+
else if (d.status === "modified") toUpdate++;
|
|
41
|
+
else if (d.status === "added") toAdd++;
|
|
42
|
+
}
|
|
43
|
+
return { toDelete, toUpdate, toAdd, toSkip, unchanged };
|
|
44
|
+
});
|
|
45
|
+
const keysByStatus = $derived.by(() => {
|
|
46
|
+
const groups = { added: [], removed: [], modified: [] };
|
|
47
|
+
for (const [key, d] of diffs) {
|
|
48
|
+
if (d.status !== "unchanged") groups[d.status]?.push(key);
|
|
49
|
+
}
|
|
50
|
+
return groups;
|
|
51
|
+
});
|
|
52
|
+
const keysByEntityType = $derived.by(() => {
|
|
53
|
+
const groups = {};
|
|
54
|
+
for (const [key, d] of diffs) {
|
|
55
|
+
if (d.status === "unchanged") continue;
|
|
56
|
+
const [type] = key.split(":");
|
|
57
|
+
(groups[type] ??= []).push(key);
|
|
58
|
+
}
|
|
59
|
+
return groups;
|
|
60
|
+
});
|
|
61
|
+
function batchApply(keys) {
|
|
62
|
+
const next = new Set(excluded);
|
|
63
|
+
for (const k of keys) next.delete(k);
|
|
64
|
+
excluded = next;
|
|
65
|
+
saveExpanded(expandedSet);
|
|
66
|
+
}
|
|
67
|
+
function batchSkip(keys) {
|
|
68
|
+
const next = new Set(excluded);
|
|
69
|
+
for (const k of keys) next.add(k);
|
|
70
|
+
excluded = next;
|
|
71
|
+
saveExpanded(expandedSet);
|
|
72
|
+
}
|
|
73
|
+
const STORAGE_KEY = `rship:diff-expanded:${rootType}:${rootId}`;
|
|
74
|
+
let expandedSet = $state(loadExpanded());
|
|
75
|
+
function loadExpanded() {
|
|
76
|
+
try {
|
|
77
|
+
const stored = sessionStorage.getItem(STORAGE_KEY);
|
|
78
|
+
if (stored) return new Set(JSON.parse(stored));
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
return /* @__PURE__ */ new Set([`${rootType}:${rootId}`]);
|
|
82
|
+
}
|
|
83
|
+
function saveExpanded(set) {
|
|
84
|
+
try {
|
|
85
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify([...set]));
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function hasChanges(node) {
|
|
90
|
+
if (node.status !== "unchanged") return true;
|
|
91
|
+
return node.children.some(hasChanges);
|
|
92
|
+
}
|
|
93
|
+
function toggleExpand(key) {
|
|
94
|
+
const next = new Set(expandedSet);
|
|
95
|
+
if (next.has(key)) next.delete(key);
|
|
96
|
+
else next.add(key);
|
|
97
|
+
expandedSet = next;
|
|
98
|
+
saveExpanded(next);
|
|
99
|
+
}
|
|
100
|
+
function formatVal(v) {
|
|
101
|
+
if (v === void 0 || v === null) return "";
|
|
102
|
+
if (typeof v === "object") return JSON.stringify(v, null, 2);
|
|
103
|
+
return String(v);
|
|
104
|
+
}
|
|
105
|
+
const rows = $derived.by(() => {
|
|
106
|
+
const result = [];
|
|
107
|
+
const walk = (node, depth, isRoot) => {
|
|
108
|
+
const key = `${node.type}:${node.id}`;
|
|
109
|
+
const visible = isRoot || showUnchanged || node.status !== "unchanged" || hasChanges(node);
|
|
110
|
+
if (!visible) return;
|
|
111
|
+
result.push({ kind: "entity", node, depth, isRoot });
|
|
112
|
+
const isExpanded = expandedSet.has(key);
|
|
113
|
+
const isExcluded = excluded.has(key);
|
|
114
|
+
if (isExpanded && !isExcluded) {
|
|
115
|
+
if (node.fields && node.fields.length > 0) {
|
|
116
|
+
for (const f of node.fields) {
|
|
117
|
+
const oldStr = formatVal(f.oldValue);
|
|
118
|
+
const newStr = formatVal(f.newValue);
|
|
119
|
+
const status = f.oldValue === void 0 ? "added" : f.newValue === void 0 ? "removed" : "changed";
|
|
120
|
+
result.push({
|
|
121
|
+
kind: "field",
|
|
122
|
+
fieldName: f.field,
|
|
123
|
+
existing: oldStr,
|
|
124
|
+
pending: newStr,
|
|
125
|
+
result: status === "removed" ? "" : newStr,
|
|
126
|
+
status,
|
|
127
|
+
depth: depth + 1,
|
|
128
|
+
parentKey: key
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (!node.fields || node.fields.length === 0) {
|
|
133
|
+
const data = node.status === "removed" ? node.currentData : node.status === "added" ? node.incomingData : null;
|
|
134
|
+
if (data) {
|
|
135
|
+
for (const [k, v] of Object.entries(data)) {
|
|
136
|
+
if (k === "hash") continue;
|
|
137
|
+
const valStr = formatVal(v);
|
|
165
138
|
result.push({
|
|
166
|
-
kind:
|
|
167
|
-
fieldName:
|
|
168
|
-
existing:
|
|
169
|
-
pending:
|
|
170
|
-
result: status ===
|
|
171
|
-
status,
|
|
139
|
+
kind: "field",
|
|
140
|
+
fieldName: k,
|
|
141
|
+
existing: node.status === "removed" ? valStr : "",
|
|
142
|
+
pending: node.status === "added" ? valStr : "",
|
|
143
|
+
result: node.status === "added" ? valStr : "",
|
|
144
|
+
status: node.status === "removed" ? "removed" : "added",
|
|
172
145
|
depth: depth + 1,
|
|
173
|
-
parentKey: key
|
|
146
|
+
parentKey: key
|
|
174
147
|
});
|
|
175
148
|
}
|
|
176
149
|
}
|
|
177
|
-
|
|
178
|
-
// Emit fields for added/removed entities (all fields as a block)
|
|
179
|
-
if (!node.fields || node.fields.length === 0) {
|
|
180
|
-
const data = node.status === 'removed' ? node.currentData : node.status === 'added' ? node.incomingData : null;
|
|
181
|
-
if (data) {
|
|
182
|
-
for (const [k, v] of Object.entries(data)) {
|
|
183
|
-
if (k === 'hash') continue;
|
|
184
|
-
const valStr = formatVal(v);
|
|
185
|
-
result.push({
|
|
186
|
-
kind: 'field',
|
|
187
|
-
fieldName: k,
|
|
188
|
-
existing: node.status === 'removed' ? valStr : '',
|
|
189
|
-
pending: node.status === 'added' ? valStr : '',
|
|
190
|
-
result: node.status === 'added' ? valStr : '',
|
|
191
|
-
status: node.status === 'removed' ? 'removed' : 'added',
|
|
192
|
-
depth: depth + 1,
|
|
193
|
-
parentKey: key,
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Recurse into children
|
|
200
|
-
for (const child of node.children) {
|
|
201
|
-
walk(child, depth + 1, false);
|
|
202
|
-
}
|
|
203
150
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
151
|
+
for (const child of node.children) {
|
|
152
|
+
walk(child, depth + 1, false);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
walk(tree, 0, true);
|
|
157
|
+
return result;
|
|
158
|
+
});
|
|
209
159
|
</script>
|
|
210
160
|
|
|
211
161
|
<div class="diff-grid-wrapper">
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ExportedEntity } from '../utils/entity-diff.js';
|
|
2
|
+
import type { ParentFkFields } from '../utils/entity-tree.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Root entity type (e.g., "Project") */
|
|
5
|
+
rootType: string;
|
|
6
|
+
/** Root entity ID */
|
|
7
|
+
rootId: string;
|
|
8
|
+
/** Entities currently on the server */
|
|
9
|
+
current: ExportedEntity[];
|
|
10
|
+
/** Entities from the incoming source (file or snapshot) */
|
|
11
|
+
incoming: ExportedEntity[];
|
|
12
|
+
/** Maps child entity type to parent FK field names */
|
|
13
|
+
parentFkFields: ParentFkFields;
|
|
14
|
+
/** Set of "Type:id" keys the user chose to exclude from import */
|
|
15
|
+
excluded?: Set<string>;
|
|
16
|
+
/** Whether to show unchanged entities */
|
|
17
|
+
showUnchanged?: boolean;
|
|
18
|
+
}
|
|
19
|
+
declare const EntityDiffTree: import("svelte").Component<Props, {}, "excluded">;
|
|
20
|
+
type EntityDiffTree = ReturnType<typeof EntityDiffTree>;
|
|
21
|
+
export default EntityDiffTree;
|