@eqtylab/explorer 0.0.0

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 (140) hide show
  1. package/.env.api +6 -0
  2. package/.env.iframe +2 -0
  3. package/.env.manifest +2 -0
  4. package/.envrc +1 -0
  5. package/.eslintrc.cjs +17 -0
  6. package/.github/workflows/ci.yml +46 -0
  7. package/.github/workflows/publish.yml +44 -0
  8. package/.husky/pre-push +2 -0
  9. package/.prettierignore +2 -0
  10. package/.prettierrc.json +13 -0
  11. package/LICENSE +201 -0
  12. package/README.md +84 -0
  13. package/flake.lock +61 -0
  14. package/flake.nix +23 -0
  15. package/index.html +13 -0
  16. package/package.json +93 -0
  17. package/postcss.config.js +5 -0
  18. package/public/logos/eci.svg +23 -0
  19. package/public/logos/eqty.svg +3 -0
  20. package/public/manifests/B-200-merged.json +773 -0
  21. package/public/manifests/amd-sev.json +426 -0
  22. package/public/manifests/anchors.json +609 -0
  23. package/public/manifests/association-no-metadata.json +1393 -0
  24. package/public/manifests/association.json +1402 -0
  25. package/public/manifests/eqty-docker.json +562 -0
  26. package/public/manifests/governance.json +1324 -0
  27. package/public/manifests/intel-tdx-with-nvidia.json +441 -0
  28. package/public/manifests/intel-tdx.json +404 -0
  29. package/public/manifests/iroh-collections.json +1285 -0
  30. package/public/manifests/model-signing.json +1536 -0
  31. package/public/manifests/multiple-registrations.json +129 -0
  32. package/public/manifests/no-vcs.json +114 -0
  33. package/public/manifests/storage.json +294 -0
  34. package/public/manifests/sub-graphs.json +1 -0
  35. package/src/animations/variants.ts +52 -0
  36. package/src/app/apiClient/integrityService/fetchAttributes.ts +29 -0
  37. package/src/app/apiClient/integrityService/fetchManifest.ts +28 -0
  38. package/src/app/apiClient/integrityService/types.ts +12 -0
  39. package/src/app/components/integrityService/IntegrityService.tsx +105 -0
  40. package/src/app/components/integrityService/components/AuthButton.tsx +26 -0
  41. package/src/app/components/integrityService/components/FiltersModal.tsx +214 -0
  42. package/src/app/components/integrityService/components/FiltersToggle.tsx +28 -0
  43. package/src/app/components/manifestViewer/ManifestViewer.tsx +90 -0
  44. package/src/app/components/manifestViewer/components/ManifestButtons.tsx +215 -0
  45. package/src/app/config.json +23 -0
  46. package/src/app/config.ts +28 -0
  47. package/src/app/pages/index.tsx +34 -0
  48. package/src/app/stores/manifestStore.ts +19 -0
  49. package/src/app/utils/appMode.ts +37 -0
  50. package/src/assets/fonts/TWKLausanne-300.woff +0 -0
  51. package/src/assets/fonts/TWKLausanne-300.woff2 +0 -0
  52. package/src/assets/fonts/TWKLausanne-400.woff +0 -0
  53. package/src/assets/fonts/TWKLausanne-400.woff2 +0 -0
  54. package/src/assets/fonts/TWKLausanne-500.woff +0 -0
  55. package/src/assets/fonts/TWKLausanne-500.woff2 +0 -0
  56. package/src/assets/fonts/TWKLausanne-600.woff +0 -0
  57. package/src/assets/fonts/TWKLausanne-600.woff2 +0 -0
  58. package/src/assets/vectors/eqtylab.svg +6 -0
  59. package/src/components/app-header.tsx +61 -0
  60. package/src/components/dark-mode-switch.tsx +16 -0
  61. package/src/components/ui/accordion.tsx +47 -0
  62. package/src/components/ui/certificate-chain.tsx +60 -0
  63. package/src/components/ui/certificate-code.tsx +39 -0
  64. package/src/components/ui/details.tsx +20 -0
  65. package/src/components/ui/display-code.tsx +47 -0
  66. package/src/components/ui/panel-label.tsx +12 -0
  67. package/src/components/ui/verify-state-badge.tsx +32 -0
  68. package/src/explorer-component/LineageExplorer.tsx +89 -0
  69. package/src/explorer-component/components/CustomEdge.tsx +28 -0
  70. package/src/explorer-component/components/LineageFlow.tsx +220 -0
  71. package/src/explorer-component/components/NodeIconLoader.tsx +26 -0
  72. package/src/explorer-component/components/nodes/ComputationNode.tsx +56 -0
  73. package/src/explorer-component/components/nodes/CustomNode.tsx +112 -0
  74. package/src/explorer-component/components/nodes/index.ts +4 -0
  75. package/src/explorer-component/components/nodes/nodes.tsx +144 -0
  76. package/src/explorer-component/components/nodes/types.ts +167 -0
  77. package/src/explorer-component/components/sidebar/SectionList.tsx +54 -0
  78. package/src/explorer-component/components/sidebar/SectionTitle.tsx +10 -0
  79. package/src/explorer-component/components/sidebar/Sidebar.tsx +70 -0
  80. package/src/explorer-component/components/sidebar/SidebarWrapper.tsx +76 -0
  81. package/src/explorer-component/components/sidebar/sections/CustomSection.tsx +238 -0
  82. package/src/explorer-component/components/sidebar/sections/FileViewer.tsx +229 -0
  83. package/src/explorer-component/components/sidebar/sections/GovernanceSection.tsx +52 -0
  84. package/src/explorer-component/components/sidebar/sections/RegistrationSection.tsx +254 -0
  85. package/src/explorer-component/components/sidebar/sections/SigstoreSection.tsx +37 -0
  86. package/src/explorer-component/components/sidebar/sections/StorageSection.tsx +73 -0
  87. package/src/explorer-component/components/sidebar/sections/custom/IrohCollectionsView.tsx +139 -0
  88. package/src/explorer-component/components/sidebar/sections/registration/VerificationIcon.tsx +108 -0
  89. package/src/explorer-component/components/sidebar/sections/registration/actor/ActorBadge.tsx +62 -0
  90. package/src/explorer-component/components/sidebar/sections/registration/actor/svg/AMDLogo.tsx +40 -0
  91. package/src/explorer-component/components/sidebar/sections/registration/actor/svg/AzureLogo.tsx +65 -0
  92. package/src/explorer-component/components/sidebar/sections/registration/actor/svg/IntelLogo.tsx +24 -0
  93. package/src/explorer-component/components/sidebar/sections/registration/actor/svg/NvidiaLogo.tsx +27 -0
  94. package/src/explorer-component/components/sidebar/sections/registration/verifierClient.ts +13 -0
  95. package/src/explorer-component/icons/AttributionIcon.svg +6 -0
  96. package/src/explorer-component/icons/BenchmarkIcon.svg +6 -0
  97. package/src/explorer-component/icons/BenchmarkResultIcon.svg +6 -0
  98. package/src/explorer-component/icons/CertificateIcon.svg +6 -0
  99. package/src/explorer-component/icons/CodeIcon.svg +6 -0
  100. package/src/explorer-component/icons/ComputationIcon.svg +6 -0
  101. package/src/explorer-component/icons/DataIcon.svg +6 -0
  102. package/src/explorer-component/icons/DatabaseIcon.svg +6 -0
  103. package/src/explorer-component/icons/DatasetIcon.svg +6 -0
  104. package/src/explorer-component/icons/DeltaLakeIcon.svg +7 -0
  105. package/src/explorer-component/icons/DocumentIcon.svg +6 -0
  106. package/src/explorer-component/icons/MediaIcon.svg +6 -0
  107. package/src/explorer-component/icons/ModelIcon.svg +6 -0
  108. package/src/explorer-component/icons/TeamIcon.svg +3 -0
  109. package/src/explorer-component/icons/TokenIcon.svg +6 -0
  110. package/src/explorer-component/icons/UnknownIcon.svg +6 -0
  111. package/src/explorer-component/icons/index.ts +35 -0
  112. package/src/explorer-component/index.ts +5 -0
  113. package/src/explorer-component/integrityTypes/AnchorTypes.ts +31 -0
  114. package/src/explorer-component/integrityTypes/StatementTypes.ts +333 -0
  115. package/src/explorer-component/integrityTypes/VCTypes.ts +85 -0
  116. package/src/explorer-component/integrityTypes/types.ts +3 -0
  117. package/src/explorer-component/utils/config.ts +163 -0
  118. package/src/explorer-component/utils/formatTimestamp.ts +5 -0
  119. package/src/explorer-component/utils/generateGraph.ts +271 -0
  120. package/src/explorer-component/utils/layoutGraph.ts +148 -0
  121. package/src/explorer-component/utils/resolveIrohBlobs.ts +117 -0
  122. package/src/explorer-component/utils/statementHelpers.ts +252 -0
  123. package/src/explorer-component/utils/stripPrefix.ts +4 -0
  124. package/src/hooks/use-mobile.tsx +19 -0
  125. package/src/index.ts +3 -0
  126. package/src/lib/resource-types.ts +31 -0
  127. package/src/lib/use-escape-key.tsx +16 -0
  128. package/src/lib/utils.ts +19 -0
  129. package/src/main.css +82 -0
  130. package/src/main.tsx +39 -0
  131. package/src/v-comp-viewer/components/container-did-details.tsx +53 -0
  132. package/src/v-comp-viewer/components/system-did-details.tsx +356 -0
  133. package/src/v-comp-viewer/useVComp.ts +196 -0
  134. package/src/v-comp-viewer/utils.tsx +824 -0
  135. package/src/vite-env.d.ts +1 -0
  136. package/tsconfig.json +33 -0
  137. package/tsconfig.node.json +10 -0
  138. package/vercel.json +13 -0
  139. package/vite.app.config.ts +48 -0
  140. package/vite.lib.config.ts +61 -0
@@ -0,0 +1,254 @@
1
+ import { IconButton } from "@eqtylab/equality";
2
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@eqtylab/equality";
3
+
4
+ import { DataNodeRegistration, LineageNode } from "../../nodes/types";
5
+ import { StatementVerficationIcon, VCVerficationIcon } from "./registration/VerificationIcon";
6
+ import { ActorBadge } from "./registration/actor/ActorBadge";
7
+ import {
8
+ Actor,
9
+ Anchor,
10
+ Statement,
11
+ ComputationStatement,
12
+ VcStatement,
13
+ RegistrationTypes,
14
+ } from "@/explorer-component/integrityTypes/types";
15
+ import SectionTitle from "../SectionTitle";
16
+ import { SectionList } from "../SectionList";
17
+ import ContainerDidDetails from "@/v-comp-viewer/components/container-did-details";
18
+ import { ParsedManifest, UploadVerification } from "@/v-comp-viewer/utils";
19
+ import SystemDidDetails from "@/v-comp-viewer/components/system-did-details";
20
+ import { useEffect, useState } from "react";
21
+ import { formatTimestamp } from "@/explorer-component/utils/formatTimestamp";
22
+ import DisplayCode from "@/components/ui/display-code";
23
+
24
+ type Props = {
25
+ node: LineageNode;
26
+ vCompData: ParsedManifest | null;
27
+ vCompVerifyData: UploadVerification | null;
28
+ };
29
+
30
+ const RegistrationSection = ({ node, vCompData, vCompVerifyData }: Props) => {
31
+ const sidebarData = node?.data.sidebarData;
32
+
33
+ if (sidebarData && "registrations" in sidebarData) {
34
+ // Display Registration(s) for Data Node
35
+ const registrations = sidebarData.registrations;
36
+
37
+ if (!registrations) return null;
38
+
39
+ return (
40
+ <div>
41
+ <SectionTitle title="Registration" />
42
+ <MultipleRegistrationsDisplay
43
+ node={node}
44
+ registrations={registrations}
45
+ vCompData={vCompData}
46
+ vCompVerifyData={vCompVerifyData}
47
+ />
48
+ </div>
49
+ );
50
+ } else if (sidebarData && "statement" in sidebarData) {
51
+ // Display Registration for Compute Node
52
+ return (
53
+ <div>
54
+ <SectionTitle title="Registration" />
55
+ <RegistrationDisplay
56
+ node={node}
57
+ statement={sidebarData.statement}
58
+ registeredByActor={sidebarData.registeredByActor}
59
+ executedOnActor={sidebarData.executedOnActor}
60
+ operatedByActor={sidebarData.operatedByActor}
61
+ anchor={sidebarData.anchor}
62
+ vc={sidebarData.vcStatement}
63
+ vCompData={vCompData}
64
+ vCompVerifyData={vCompVerifyData}
65
+ />
66
+ </div>
67
+ );
68
+ }
69
+ };
70
+
71
+ const MultipleRegistrationsDisplay = ({
72
+ node,
73
+ registrations,
74
+ vCompData,
75
+ vCompVerifyData,
76
+ }: {
77
+ node: LineageNode;
78
+ registrations: DataNodeRegistration[];
79
+ storedOn?: string;
80
+ vCompData: ParsedManifest | null;
81
+ vCompVerifyData: UploadVerification | null;
82
+ }) => {
83
+ const [statementIndex, setStatementIndex] = useState(0);
84
+
85
+ const handleStatementIndexChange = (index: number) => {
86
+ setStatementIndex(index);
87
+ };
88
+
89
+ useEffect(() => {
90
+ setStatementIndex(0);
91
+ }, [node]);
92
+
93
+ const { statement, registeredByActor, anchors, vcs } = registrations[statementIndex] || registrations[0];
94
+
95
+ return (
96
+ <RegistrationDisplay
97
+ node={node}
98
+ handleStatementIndexChange={handleStatementIndexChange}
99
+ statement={statement}
100
+ registrations={registrations}
101
+ registeredByActor={registeredByActor}
102
+ anchor={anchors}
103
+ vc={vcs}
104
+ vCompData={vCompData}
105
+ vCompVerifyData={vCompVerifyData}
106
+ />
107
+ );
108
+ };
109
+
110
+ const RegistrationDisplay = ({
111
+ node,
112
+ handleStatementIndexChange,
113
+ statement,
114
+ registrations,
115
+ registeredByActor,
116
+ executedOnActor,
117
+ operatedByActor,
118
+ anchor,
119
+ vc,
120
+ vCompData,
121
+ vCompVerifyData,
122
+ }: {
123
+ node: LineageNode;
124
+ handleStatementIndexChange?: (index: number) => void;
125
+ statement: Statement;
126
+ registrations?: DataNodeRegistration[];
127
+ registeredByActor?: Actor;
128
+ executedOnActor?: Actor;
129
+ operatedByActor?: Actor;
130
+ anchor?: Anchor;
131
+ vc?: VcStatement[];
132
+ vCompData: ParsedManifest | null;
133
+ vCompVerifyData: UploadVerification | null;
134
+ }) => {
135
+ const isComputeNode = node.type === "ComputationNode";
136
+
137
+ const registeredByContainerInfo = (vCompData && vCompData.containers[statement.registeredBy]) || null;
138
+
139
+ const executedOnSystemInfo =
140
+ (vCompData && vCompData.systems[(statement as ComputationStatement).executedOn || ""]) || null;
141
+
142
+ const data = [
143
+ {
144
+ label: "Node ID",
145
+ value: <DisplayCode value={node.id} />,
146
+ offsetTopLabel: true,
147
+ },
148
+ // {
149
+ // label: "SubGraph",
150
+ // value: <p> {node.data.sub_graph?.name} </p>,
151
+ // },
152
+ {
153
+ label: isComputeNode ? "Timestamp" : "Instance",
154
+ value:
155
+ registrations && registrations.length > 1 && handleStatementIndexChange ? (
156
+ <Select onValueChange={(value) => handleStatementIndexChange(parseInt(value))} defaultValue="0">
157
+ <SelectTrigger>
158
+ <SelectValue placeholder="Select a statement" />
159
+ </SelectTrigger>
160
+ <SelectContent>
161
+ {registrations.map((registration, index) => {
162
+ const id = registration.statement.id;
163
+ const timestamp = formatTimestamp(registration.statement.timestamp);
164
+ return (
165
+ <SelectItem value={String(index)} key={id}>
166
+ {`${id.split("cid:")[1].slice(0, 5)}...${id.slice(-4)} (${timestamp})`}
167
+ </SelectItem>
168
+ );
169
+ })}
170
+ </SelectContent>
171
+ </Select>
172
+ ) : statement ? (
173
+ <p>{formatTimestamp(statement.timestamp)}</p>
174
+ ) : null,
175
+ offsetTopLabel: registrations && registrations.length > 1 ? true : false,
176
+ },
177
+ {
178
+ label: "Registered By",
179
+ value: statement && <ActorBadge didString={statement.registeredBy} actor={registeredByActor} />,
180
+ offsetTopLabel: true,
181
+ },
182
+ {
183
+ value: isComputeNode && registeredByContainerInfo && (
184
+ <ContainerDidDetails containerInfo={registeredByContainerInfo} />
185
+ ),
186
+ },
187
+ {
188
+ label: "Executed On",
189
+ value: statement.type == RegistrationTypes.Computation &&
190
+ (statement as ComputationStatement).executedOn && (
191
+ <ActorBadge
192
+ didString={(statement as ComputationStatement).executedOn || ""}
193
+ actor={executedOnActor}
194
+ />
195
+ ),
196
+ offsetTopLabel: true,
197
+ },
198
+ {
199
+ value: isComputeNode && executedOnSystemInfo && (
200
+ <SystemDidDetails systemInfo={executedOnSystemInfo} vCompVerifyData={vCompVerifyData} />
201
+ ),
202
+ },
203
+ {
204
+ label: "Operated By",
205
+ value: statement.type == RegistrationTypes.Computation &&
206
+ (statement as ComputationStatement).operatedBy && (
207
+ <ActorBadge
208
+ didString={(statement as ComputationStatement).operatedBy || ""}
209
+ actor={operatedByActor}
210
+ />
211
+ ),
212
+ offsetTopLabel: true,
213
+ },
214
+ {
215
+ label: "Anchors",
216
+ value: anchor && (
217
+ <div className="space-y-2">
218
+ {anchor.locations.map((location) => {
219
+ return (
220
+ <div className="space-y-2" key={`${location.type}:${location.txHash}`}>
221
+ <DisplayCode
222
+ value={`${location.type}:${location.txHash}`}
223
+ actions={
224
+ <IconButton
225
+ name="ExternalLink"
226
+ href={location.urls?.[0]}
227
+ label="View"
228
+ onClick={() => console.log("Open clicked")}
229
+ />
230
+ }
231
+ />
232
+ </div>
233
+ );
234
+ })}
235
+ </div>
236
+ ),
237
+ offsetTopLabel: true,
238
+ },
239
+ {
240
+ label: "Statement",
241
+ value: statement && <StatementVerficationIcon statement={statement} displayText={true} />,
242
+ },
243
+ {
244
+ label: "Credential",
245
+ value: statement && (
246
+ <VCVerficationIcon vc={vc?.[0].credential} statementId={statement.id} displayText={true} />
247
+ ),
248
+ },
249
+ ];
250
+
251
+ return <SectionList data={data} />;
252
+ };
253
+
254
+ export default RegistrationSection;
@@ -0,0 +1,37 @@
1
+ import { CodeBlock } from "@eqtylab/equality";
2
+ import { LineageNode } from "../../nodes/types";
3
+ import SectionTitle from "../SectionTitle";
4
+ import { Fragment } from "react/jsx-runtime";
5
+
6
+ const SigstoreSection = ({ node }: { node: LineageNode }) => {
7
+ const sidebarData = node?.data.sidebarData;
8
+
9
+ if (!sidebarData || !("sigstoreBundles" in sidebarData)) return null;
10
+
11
+ const bundles = sidebarData.sigstoreBundles;
12
+ if (!bundles || bundles.length === 0) return null;
13
+
14
+ return (
15
+ <div className="overflow-auto w-full">
16
+ <SectionTitle title="Sigstore Bundles" />
17
+ <div className="space-y-4 mt-6">
18
+ {bundles.map((bundle, idx) => (
19
+ <Fragment key={idx}>
20
+ <CodeBlock
21
+ title={`Sigstore Bundle (${idx + 1})`}
22
+ code={JSON.stringify(bundle, null, 2)}
23
+ language="json"
24
+ />
25
+ <CodeBlock
26
+ title={`DSSE Payload (${idx + 1})`}
27
+ code={JSON.stringify(JSON.parse(atob(bundle.dsseEnvelope.payload)), null, 2)}
28
+ language="json"
29
+ />
30
+ </Fragment>
31
+ ))}
32
+ </div>
33
+ </div>
34
+ );
35
+ };
36
+
37
+ export default SigstoreSection;
@@ -0,0 +1,73 @@
1
+ import { Button } from "@eqtylab/equality";
2
+
3
+ import SectionTitle from "@/explorer-component/components/sidebar/SectionTitle";
4
+ import { DataNodeSidebarData, LineageNode } from "@/explorer-component/components/nodes/types";
5
+ import { SectionList } from "@/explorer-component/components/sidebar/SectionList";
6
+ import { formatTimestamp } from "@/explorer-component/utils/formatTimestamp";
7
+ import { FileViewer } from "./FileViewer";
8
+ import DisplayCode from "@/components/ui/display-code";
9
+
10
+ const StorageSection = ({ node }: { node: LineageNode }) => {
11
+ const storage = (node.data.sidebarData as DataNodeSidebarData)?.storage;
12
+
13
+ if (!storage) return null;
14
+
15
+ const data = [
16
+ {
17
+ label: "Context",
18
+ value: <DisplayCode value={storage.statement.context} />,
19
+ offsetTopLabel: true,
20
+ },
21
+ {
22
+ label: "Data",
23
+ value: <DisplayCode value={storage.statement.data} />,
24
+ offsetTopLabel: true,
25
+ },
26
+ {
27
+ label: "Registration ID",
28
+ value: <DisplayCode value={storage.statement.id} />,
29
+ offsetTopLabel: true,
30
+ },
31
+ {
32
+ label: "Operated By",
33
+ value: <DisplayCode value={storage.statement.operatedBy} />,
34
+ offsetTopLabel: true,
35
+ },
36
+ {
37
+ label: "Registered By",
38
+ value: <DisplayCode value={storage.statement.registeredBy} />,
39
+ offsetTopLabel: true,
40
+ },
41
+ {
42
+ label: "Stored On",
43
+ value: storage.statement.storedOn && (
44
+ <div className="space-y-2">
45
+ <DisplayCode value={storage.statement.storedOn} />
46
+ <FileViewer
47
+ filename="Stored On"
48
+ language="json"
49
+ content={JSON.stringify(storage.metadata, null, 2)}
50
+ >
51
+ <Button variant="tertiary" size="sm">
52
+ View json
53
+ </Button>
54
+ </FileViewer>
55
+ </div>
56
+ ),
57
+ offsetTopLabel: true,
58
+ },
59
+ {
60
+ label: "Timestamp",
61
+ value: <p>{formatTimestamp(storage.statement.timestamp)}</p>,
62
+ },
63
+ ];
64
+
65
+ return (
66
+ <div>
67
+ <SectionTitle title="Storage" />
68
+ <SectionList data={data} />
69
+ </div>
70
+ );
71
+ };
72
+
73
+ export default StorageSection;
@@ -0,0 +1,139 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Download } from "lucide-react";
3
+ import { Button, TableContainer, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@eqtylab/equality";
4
+
5
+ import { FileViewer } from "../FileViewer";
6
+ import { IrohData } from "../../../nodes/types";
7
+ import DisplayCode from "@/components/ui/display-code";
8
+
9
+ const TEXT_FILE_EXTS = [
10
+ "txt",
11
+ "md",
12
+ "json",
13
+ "csv",
14
+ "xml",
15
+ "yaml",
16
+ "yml",
17
+ "html",
18
+ "htm",
19
+ "js",
20
+ "jsx",
21
+ "ts",
22
+ "tsx",
23
+ "css",
24
+ "scss",
25
+ "sass",
26
+ "less",
27
+ "py",
28
+ "java",
29
+ "c",
30
+ "cpp",
31
+ "h",
32
+ "hpp",
33
+ "cs",
34
+ "php",
35
+ "sh",
36
+ "bat",
37
+ "ps1",
38
+ "r",
39
+ "rb",
40
+ "rs",
41
+ "go",
42
+ "pl",
43
+ "perl",
44
+ "sas",
45
+ "sql",
46
+ "log",
47
+ "ini",
48
+ "conf",
49
+ "config",
50
+ ];
51
+
52
+ const filenameIsForText = (filename: string) => {
53
+ const extension = filename.split(".").pop();
54
+ return TEXT_FILE_EXTS.includes(extension || "");
55
+ };
56
+
57
+ export interface IrohCollectionViewProps {
58
+ data: IrohData[] | null | undefined | string;
59
+ }
60
+
61
+ const IrohCollectionView = ({ data }: IrohCollectionViewProps) => {
62
+ const [dataUrls, setDataUrls] = useState<Record<string, string | undefined>>({});
63
+
64
+ useEffect(() => {
65
+ if (!data || typeof data === "string") return;
66
+
67
+ const precalculatedDataUrls: Record<string, string | undefined> = {};
68
+
69
+ data.forEach((item) => {
70
+ const key = item.path;
71
+ const data = item.data && filenameIsForText(item.path) ? item.data.toString() : null;
72
+ const blob = data ? new Blob([data], { type: "text/plain" }) : undefined;
73
+ const dataUrl = blob ? URL.createObjectURL(blob) : undefined;
74
+ precalculatedDataUrls[key] = dataUrl;
75
+ });
76
+
77
+ setDataUrls(precalculatedDataUrls);
78
+
79
+ // This needs to be done in a useEffect so we can revoke the URLs when the component unmounts
80
+ return () => {
81
+ Object.values(precalculatedDataUrls).forEach((url) => {
82
+ if (url) URL.revokeObjectURL(url);
83
+ });
84
+ };
85
+ }, [data]);
86
+
87
+ if (!data || typeof data === "string") return <p> No data</p>;
88
+
89
+ const hasActions =
90
+ data.filter((item) => {
91
+ const data = item.data && filenameIsForText(item.path) ? item.data.toString() : null;
92
+ return data !== null;
93
+ }).length > 0;
94
+
95
+ return (
96
+ <div className="mt-4 overflow-hidden rounded-md border border-border max-w-full">
97
+ <TableContainer style={{ tableLayout: "fixed" }}>
98
+ <TableHeader>
99
+ <TableRow>
100
+ <TableHead className={hasActions ? "" : "w-[65%]"}>Path</TableHead>
101
+ <TableHead className={hasActions ? "" : "w-[35%]"}>CID</TableHead>
102
+ {hasActions && <TableHead className="w-52">Actions</TableHead>}
103
+ </TableRow>
104
+ </TableHeader>
105
+ <TableBody>
106
+ {data.map((item) => {
107
+ const data = item.data && filenameIsForText(item.path) ? item.data.toString() : null;
108
+ return (
109
+ <TableRow key={item.path}>
110
+ <TableCell className="font-medium whitespace-pre-wrap break-all">{item.path}</TableCell>
111
+ <TableCell>
112
+ <DisplayCode value={item.cid.toString()} truncate={true} />
113
+ </TableCell>
114
+ {data && dataUrls[item.path] && (
115
+ <TableCell>
116
+ <div className="flex items-center gap-2">
117
+ <FileViewer filename={item.path} content={data} buttonText="View" />
118
+ <Button
119
+ variant="tertiary"
120
+ size="sm"
121
+ prefix={<Download />}
122
+ href={dataUrls[item.path]}
123
+ download={item.path}
124
+ >
125
+ Download
126
+ </Button>
127
+ </div>
128
+ </TableCell>
129
+ )}
130
+ </TableRow>
131
+ );
132
+ })}
133
+ </TableBody>
134
+ </TableContainer>
135
+ </div>
136
+ );
137
+ };
138
+
139
+ export default IrohCollectionView;
@@ -0,0 +1,108 @@
1
+ import { Button } from "@eqtylab/equality";
2
+
3
+ import { useState, useEffect } from "react";
4
+ import { FileViewer } from "../FileViewer";
5
+ import { VerifiableCredential, Statement, StatementToJSON } from "@/explorer-component/integrityTypes/types";
6
+ import { initVerifier, getVerifier } from "./verifierClient";
7
+ import VerifyStateBadge from "@/components/ui/verify-state-badge";
8
+ import { useManifestStore } from "@/app/stores/manifestStore";
9
+
10
+ // type VerificationType = "ZK" | "TEE" | null;
11
+ // type VerificationResult = {
12
+ // verificationStatus: NodeVerification;
13
+ // verificationType: VerificationType;
14
+ // };
15
+
16
+ type VerificationState = "verifying" | "unverified" | "verified";
17
+
18
+ type Props = {
19
+ displayText?: boolean;
20
+ onClick?: () => {};
21
+ };
22
+ type VCVerifyProps = Props & {
23
+ vc?: VerifiableCredential;
24
+ statementId?: string;
25
+ };
26
+
27
+ type StatementVerifyProps = Props & {
28
+ statement: Statement;
29
+ };
30
+
31
+ export const StatementVerficationIcon = ({ statement, displayText, onClick }: StatementVerifyProps) => {
32
+ const [verifyState, setVerifyState] = useState<VerificationState>("verifying");
33
+ const manifest = useManifestStore((state) => state.manifest);
34
+
35
+ const statementJson = StatementToJSON(statement);
36
+ useEffect(() => {
37
+ const getVerification = async () => {
38
+ try {
39
+ await initVerifier(); // safe to call multiple times
40
+ const verifier = getVerifier();
41
+ const contexts = manifest?.contexts ?? {};
42
+ if (!(await verifier.verify_statement(JSON.stringify(statementJson), contexts))) {
43
+ setVerifyState("unverified");
44
+ } else {
45
+ setVerifyState("verified");
46
+ }
47
+ } catch {
48
+ setVerifyState("unverified");
49
+ }
50
+ };
51
+ if (statement) getVerification();
52
+ }, [statement, manifest]);
53
+
54
+ if (!statement) {
55
+ return (
56
+ <FileViewer content={" "} filename="Statement.json" buttonText="View">
57
+ <VerifyStateBadge state="unverified" displayText={displayText} />
58
+ </FileViewer>
59
+ );
60
+ }
61
+
62
+ return (
63
+ <div className="flex items-center gap-2">
64
+ <VerifyStateBadge state={verifyState} displayText={displayText} />
65
+ <FileViewer statement={statement} filename="Statement.json" buttonText="View">
66
+ <Button variant="tertiary" size="sm" onClick={onClick}>
67
+ View json
68
+ </Button>
69
+ </FileViewer>
70
+ </div>
71
+ );
72
+ };
73
+
74
+ export const VCVerficationIcon = ({ vc, statementId, displayText, onClick }: VCVerifyProps) => {
75
+ const [verifyState, setVerifyState] = useState<VerificationState>("verifying");
76
+
77
+ useEffect(() => {
78
+ const getVerification = async () => {
79
+ try {
80
+ await initVerifier(); // safe to call multiple times
81
+ const verifier = getVerifier();
82
+ if (!(await verifier.verify_vc(JSON.stringify(vc), statementId || ""))) {
83
+ setVerifyState("unverified");
84
+ } else {
85
+ setVerifyState("verified");
86
+ }
87
+ } catch {
88
+ setVerifyState("unverified");
89
+ }
90
+ };
91
+ if (vc && statementId) getVerification();
92
+ }, [vc, statementId]);
93
+
94
+ if (!vc || !statementId) {
95
+ return <VerifyStateBadge state="unverified" displayText={displayText} />;
96
+ }
97
+
98
+ return (
99
+ <div className="flex items-center gap-2">
100
+ <VerifyStateBadge state={verifyState} displayText={displayText} />
101
+ <FileViewer content={JSON.stringify(vc, null, 2)} filename="VC.json" buttonText="View">
102
+ <Button variant="tertiary" size="sm" onClick={onClick}>
103
+ View json
104
+ </Button>
105
+ </FileViewer>
106
+ </div>
107
+ );
108
+ };
@@ -0,0 +1,62 @@
1
+ import NvidiaLogo from "./svg/NvidiaLogo";
2
+ import AMDLogo from "./svg/AMDLogo";
3
+ import IntelLogo from "./svg/IntelLogo";
4
+ import AzureLogo from "./svg/AzureLogo";
5
+ import { Actor, TeeEnvType } from "@/explorer-component/integrityTypes/VCTypes.ts";
6
+ import DisplayCode from "@/components/ui/display-code";
7
+ import { TwoColumnListItem } from "@/explorer-component/components/sidebar/SectionList";
8
+
9
+ export interface ActorBadgeProps {
10
+ didString: string;
11
+ actor?: Actor;
12
+ }
13
+
14
+ export function ActorBadge({ didString, actor }: ActorBadgeProps) {
15
+ if (!actor) return <DisplayCode value={didString} />;
16
+
17
+ const { did, teeEnv, usesNvidia, metadata } = actor;
18
+
19
+ const bottomRow = teeEnv || usesNvidia;
20
+
21
+ return (
22
+ <DisplayCode
23
+ value={did}
24
+ slot={
25
+ <div className="space-y-2">
26
+ {metadata && Object.entries(metadata).length > 0 ? (
27
+ <div className="grid grid-cols-[2fr_5fr] gap-3 max-w-lg">
28
+ {Object.entries(metadata).map(([key, value]) => (
29
+ <TwoColumnListItem label={key} value={value} key={key} />
30
+ ))}
31
+ </div>
32
+ ) : (
33
+ <p className="text-xs">No additional data available</p>
34
+ )}
35
+ {bottomRow && (
36
+ <div className="flex flex-wrap items-center gap-2">
37
+ {renderTeeEnvLogo(teeEnv)}
38
+ {usesNvidia && <NvidiaLogo />}
39
+ </div>
40
+ )}
41
+ </div>
42
+ }
43
+ />
44
+ );
45
+ }
46
+
47
+ function renderTeeEnvLogo(teeEnv: TeeEnvType) {
48
+ if (!teeEnv) return null;
49
+
50
+ switch (teeEnv) {
51
+ case "AMD SEV":
52
+ return <AMDLogo />;
53
+ case "Intel TDX":
54
+ return <IntelLogo />;
55
+ case "Azure":
56
+ return <AzureLogo />;
57
+ case "EQTY Docker":
58
+ return <div>VComp Container</div>;
59
+ default:
60
+ return null;
61
+ }
62
+ }