diff --git a/src/api/generated/analysis/analysis.ts b/src/api/generated/analysis/analysis.ts
index 1cff535..23c6bb1 100644
--- a/src/api/generated/analysis/analysis.ts
+++ b/src/api/generated/analysis/analysis.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -33,6 +33,21 @@ import { customInstance } from "../../api";
type SecondParameter unknown> = Parameters[1];
+const withQueryKey = (query: T, queryKey: K): T & { queryKey: K } => {
+ const result = { queryKey } as T & { queryKey: K };
+ for (const key of Object.keys(query)) {
+ // The explicit queryKey always wins, matching the previous
+ // `{ ...query, queryKey }` spread where it was set last.
+ if (key === "queryKey") continue;
+ Object.defineProperty(result, key, {
+ enumerable: true,
+ configurable: true,
+ get: () => (query as Record)[key],
+ });
+ }
+ return result;
+};
+
/**
* Retrieve all static analyses performed on a binary.
* @summary List static analyses for a binary
@@ -140,7 +155,7 @@ export function useListAnalyses>
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -250,7 +265,7 @@ export function useGetAnalysis>,
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -376,7 +391,7 @@ export function useListStructures<
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -486,7 +501,7 @@ export function useListStrings>,
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -596,7 +611,7 @@ export function useListSegments>
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -706,7 +721,7 @@ export function useListImports>,
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -832,7 +847,7 @@ export function useListGlobalVars<
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -965,7 +980,7 @@ export function useListFunctions<
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -1075,5 +1090,5 @@ export function useListEnums>, TErr
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
diff --git a/src/api/generated/api.schemas.ts b/src/api/generated/api.schemas.ts
index 9654e0d..a08aeaf 100644
--- a/src/api/generated/api.schemas.ts
+++ b/src/api/generated/api.schemas.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -84,6 +84,23 @@ export interface LibraryResponse {
createdAt?: string;
}
+export interface SymbolEntry {
+ moduleName?: string;
+ className?: string;
+ symbolName?: string;
+ ordinal?: number;
+ symbolType?: string;
+ parameters?: string;
+ category?: string;
+}
+
+export interface Library {
+ name?: string;
+ description?: string;
+ aliases?: string[];
+ symbols?: SymbolEntry[];
+}
+
export type JobResponseType = (typeof JobResponseType)[keyof typeof JobResponseType];
export const JobResponseType = {
@@ -160,8 +177,8 @@ export interface PageableObject {
paged?: boolean;
pageNumber?: number;
pageSize?: number;
- unpaged?: boolean;
sort?: SortObject;
+ unpaged?: boolean;
}
export interface PageLibrarySymbolResponse {
@@ -171,10 +188,10 @@ export interface PageLibrarySymbolResponse {
content?: LibrarySymbolResponse[];
number?: number;
pageable?: PageableObject;
- numberOfElements?: number;
- sort?: SortObject;
first?: boolean;
last?: boolean;
+ sort?: SortObject;
+ numberOfElements?: number;
empty?: boolean;
}
@@ -357,10 +374,10 @@ export interface PageFunctionSummaryResponse {
content?: FunctionSummaryResponse[];
number?: number;
pageable?: PageableObject;
- numberOfElements?: number;
- sort?: SortObject;
first?: boolean;
last?: boolean;
+ sort?: SortObject;
+ numberOfElements?: number;
empty?: boolean;
}
diff --git a/src/api/generated/binary/binary.ts b/src/api/generated/binary/binary.ts
index 73805c8..b902fbd 100644
--- a/src/api/generated/binary/binary.ts
+++ b/src/api/generated/binary/binary.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -35,6 +35,21 @@ import { customInstance } from "../../api";
type SecondParameter unknown> = Parameters[1];
+const withQueryKey = (query: T, queryKey: K): T & { queryKey: K } => {
+ const result = { queryKey } as T & { queryKey: K };
+ for (const key of Object.keys(query)) {
+ // The explicit queryKey always wins, matching the previous
+ // `{ ...query, queryKey }` spread where it was set last.
+ if (key === "queryKey") continue;
+ Object.defineProperty(result, key, {
+ enumerable: true,
+ configurable: true,
+ get: () => (query as Record)[key],
+ });
+ }
+ return result;
+};
+
/**
* Retrieve all binaries belonging to a project, optionally filtered by filename.
* @summary List binaries of a project
@@ -153,7 +168,7 @@ export function useGetBinaries>,
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -370,7 +385,7 @@ export function useGetDependencies<
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -651,7 +666,7 @@ export function useGetBinary>, TErr
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -852,7 +867,7 @@ export function useDownloadBinary<
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
diff --git a/src/api/generated/chat/chat.ts b/src/api/generated/chat/chat.ts
index 07b536b..da9c7de 100644
--- a/src/api/generated/chat/chat.ts
+++ b/src/api/generated/chat/chat.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -26,6 +26,21 @@ import { customInstance } from "../../api";
type SecondParameter unknown> = Parameters[1];
+const withQueryKey = (query: T, queryKey: K): T & { queryKey: K } => {
+ const result = { queryKey } as T & { queryKey: K };
+ for (const key of Object.keys(query)) {
+ // The explicit queryKey always wins, matching the previous
+ // `{ ...query, queryKey }` spread where it was set last.
+ if (key === "queryKey") continue;
+ Object.defineProperty(result, key, {
+ enumerable: true,
+ configurable: true,
+ get: () => (query as Record)[key],
+ });
+ }
+ return result;
+};
+
/**
* @summary List chat sessions for a binary
*/
@@ -136,7 +151,7 @@ export function useListSessions>
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -324,7 +339,7 @@ export function useGetSession>, TE
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -510,5 +525,5 @@ export function useGetMessages>,
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
diff --git a/src/api/generated/engine/engine.ts b/src/api/generated/engine/engine.ts
index 6b76587..76ce891 100644
--- a/src/api/generated/engine/engine.ts
+++ b/src/api/generated/engine/engine.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -23,6 +23,21 @@ import { customInstance } from "../../api";
type SecondParameter unknown> = Parameters[1];
+const withQueryKey = (query: T, queryKey: K): T & { queryKey: K } => {
+ const result = { queryKey } as T & { queryKey: K };
+ for (const key of Object.keys(query)) {
+ // The explicit queryKey always wins, matching the previous
+ // `{ ...query, queryKey }` spread where it was set last.
+ if (key === "queryKey") continue;
+ Object.defineProperty(result, key, {
+ enumerable: true,
+ configurable: true,
+ get: () => (query as Record)[key],
+ });
+ }
+ return result;
+};
+
/**
* Retrieve all registered static analysis engines with their availability status.
* @summary List available analysis engines
@@ -116,5 +131,5 @@ export function useListEngines>,
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
diff --git a/src/api/generated/function/function.ts b/src/api/generated/function/function.ts
index aaf359f..b28739e 100644
--- a/src/api/generated/function/function.ts
+++ b/src/api/generated/function/function.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -23,6 +23,21 @@ import { customInstance } from "../../api";
type SecondParameter unknown> = Parameters[1];
+const withQueryKey = (query: T, queryKey: K): T & { queryKey: K } => {
+ const result = { queryKey } as T & { queryKey: K };
+ for (const key of Object.keys(query)) {
+ // The explicit queryKey always wins, matching the previous
+ // `{ ...query, queryKey }` spread where it was set last.
+ if (key === "queryKey") continue;
+ Object.defineProperty(result, key, {
+ enumerable: true,
+ configurable: true,
+ get: () => (query as Record)[key],
+ });
+ }
+ return result;
+};
+
/**
* Retrieve full details of a function including assembly, decompiled code, and xref counts.
* @summary Get function details
@@ -130,7 +145,7 @@ export function useGetFunction>,
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -240,7 +255,7 @@ export function useGetCallers>, TE
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -350,5 +365,5 @@ export function useGetCallees>, TE
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
diff --git a/src/api/generated/job/job.ts b/src/api/generated/job/job.ts
index 411c73f..708ff83 100644
--- a/src/api/generated/job/job.ts
+++ b/src/api/generated/job/job.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -26,6 +26,21 @@ import { customInstance } from "../../api";
type SecondParameter unknown> = Parameters[1];
+const withQueryKey = (query: T, queryKey: K): T & { queryKey: K } => {
+ const result = { queryKey } as T & { queryKey: K };
+ for (const key of Object.keys(query)) {
+ // The explicit queryKey always wins, matching the previous
+ // `{ ...query, queryKey }` spread where it was set last.
+ if (key === "queryKey") continue;
+ Object.defineProperty(result, key, {
+ enumerable: true,
+ configurable: true,
+ get: () => (query as Record)[key],
+ });
+ }
+ return result;
+};
+
/**
* Cancel a job that is enqueued, started, or in progress. Returns 409 if the job is already in a final state.
* @summary Cancel a job
@@ -200,7 +215,7 @@ export function useGetJobs>, TError =
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -307,5 +322,5 @@ export function useGetJob>, TError = u
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
diff --git a/src/api/generated/library-symbols/library-symbols.ts b/src/api/generated/library-symbols/library-symbols.ts
index d647724..27bbcae 100644
--- a/src/api/generated/library-symbols/library-symbols.ts
+++ b/src/api/generated/library-symbols/library-symbols.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -21,6 +21,7 @@ import type {
} from "@tanstack/react-query";
import type {
+ Library,
LibraryResponse,
ListLibrarySymbolsParams,
PageLibrarySymbolResponse,
@@ -34,6 +35,21 @@ import { customInstance } from "../../api";
type SecondParameter unknown> = Parameters[1];
+const withQueryKey = (query: T, queryKey: K): T & { queryKey: K } => {
+ const result = { queryKey } as T & { queryKey: K };
+ for (const key of Object.keys(query)) {
+ // The explicit queryKey always wins, matching the previous
+ // `{ ...query, queryKey }` spread where it was set last.
+ if (key === "queryKey") continue;
+ Object.defineProperty(result, key, {
+ enumerable: true,
+ configurable: true,
+ get: () => (query as Record)[key],
+ });
+ }
+ return result;
+};
+
/**
* Parse and store symbols from a library dump file (e.g., TurboDump output).
* @summary Upload a library symbol dump
@@ -184,6 +200,86 @@ export const useResolveAllSymbols = (
): UseMutationResult>, TError, void, TContext> => {
return useMutation(getResolveAllSymbolsMutationOptions(options), queryClient);
};
+/**
+ * Create a new library from a previously exported JSON backup. The new library gets its own IDs — existing libraries are not affected.
+ * @summary Import a library from JSON export
+ */
+export const importLibrary = (
+ library: Library,
+ options?: SecondParameter,
+ signal?: AbortSignal
+) => {
+ return customInstance(
+ {
+ url: `/libraries/import`,
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ data: library,
+ signal,
+ },
+ options
+ );
+};
+
+export const getImportLibraryMutationOptions = (options?: {
+ mutation?: UseMutationOptions<
+ Awaited>,
+ TError,
+ { data: Library },
+ TContext
+ >;
+ request?: SecondParameter;
+}): UseMutationOptions<
+ Awaited>,
+ TError,
+ { data: Library },
+ TContext
+> => {
+ const mutationKey = ["importLibrary"];
+ const { mutation: mutationOptions, request: requestOptions } = options
+ ? options.mutation && "mutationKey" in options.mutation && options.mutation.mutationKey
+ ? options
+ : { ...options, mutation: { ...options.mutation, mutationKey } }
+ : { mutation: { mutationKey }, request: undefined };
+
+ const mutationFn: MutationFunction<
+ Awaited>,
+ { data: Library }
+ > = (props) => {
+ const { data } = props ?? {};
+
+ return importLibrary(data, requestOptions);
+ };
+
+ return { mutationFn, ...mutationOptions };
+};
+
+export type ImportLibraryMutationResult = NonNullable>>;
+export type ImportLibraryMutationBody = Library;
+export type ImportLibraryMutationError = unknown;
+
+/**
+ * @summary Import a library from JSON export
+ */
+export const useImportLibrary = (
+ options?: {
+ mutation?: UseMutationOptions<
+ Awaited>,
+ TError,
+ { data: Library },
+ TContext
+ >;
+ request?: SecondParameter;
+ },
+ queryClient?: QueryClient
+): UseMutationResult<
+ Awaited>,
+ TError,
+ { data: Library },
+ TContext
+> => {
+ return useMutation(getImportLibraryMutationOptions(options), queryClient);
+};
/**
* Cross-reference imports from a specific analysis against all loaded library symbol databases.
* @summary Resolve symbols for an analysis
@@ -367,7 +463,7 @@ export function useListLibraries<
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -477,7 +573,7 @@ export function useGetLibrary>, TE
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -691,7 +787,129 @@ export function useListLibrarySymbols<
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
+}
+
+/**
+ * Download the complete library (name, description, aliases, and all symbols) as a JSON file for backup or transfer.
+ * @summary Export a library as JSON
+ */
+export const exportLibrary = (
+ libraryId: string,
+ options?: SecondParameter,
+ signal?: AbortSignal
+) => {
+ return customInstance(
+ { url: `/libraries/${encodeURIComponent(String(libraryId))}/export`, method: "GET", signal },
+ options
+ );
+};
+
+export const getExportLibraryQueryKey = (libraryId: string) => {
+ return [`/libraries/${libraryId}/export`] as const;
+};
+
+export const getExportLibraryQueryOptions = <
+ TData = Awaited>,
+ TError = unknown,
+>(
+ libraryId: string,
+ options?: {
+ query?: Partial>, TError, TData>>;
+ request?: SecondParameter;
+ }
+) => {
+ const { query: queryOptions, request: requestOptions } = options ?? {};
+
+ const queryKey = queryOptions?.queryKey ?? getExportLibraryQueryKey(libraryId);
+
+ const queryFn: QueryFunction>> = ({ signal }) =>
+ exportLibrary(libraryId, requestOptions, signal);
+
+ return {
+ queryKey,
+ queryFn,
+ enabled: libraryId !== null && libraryId !== undefined,
+ ...queryOptions,
+ } as UseQueryOptions>, TError, TData> & {
+ queryKey: DataTag;
+ };
+};
+
+export type ExportLibraryQueryResult = NonNullable>>;
+export type ExportLibraryQueryError = unknown;
+
+export function useExportLibrary<
+ TData = Awaited>,
+ TError = unknown,
+>(
+ libraryId: string,
+ options: {
+ query: Partial>, TError, TData>> &
+ Pick<
+ DefinedInitialDataOptions<
+ Awaited>,
+ TError,
+ Awaited>
+ >,
+ "initialData"
+ >;
+ request?: SecondParameter;
+ },
+ queryClient?: QueryClient
+): DefinedUseQueryResult & { queryKey: DataTag };
+export function useExportLibrary<
+ TData = Awaited>,
+ TError = unknown,
+>(
+ libraryId: string,
+ options?: {
+ query?: Partial>, TError, TData>> &
+ Pick<
+ UndefinedInitialDataOptions<
+ Awaited>,
+ TError,
+ Awaited>
+ >,
+ "initialData"
+ >;
+ request?: SecondParameter;
+ },
+ queryClient?: QueryClient
+): UseQueryResult & { queryKey: DataTag };
+export function useExportLibrary<
+ TData = Awaited>,
+ TError = unknown,
+>(
+ libraryId: string,
+ options?: {
+ query?: Partial>, TError, TData>>;
+ request?: SecondParameter;
+ },
+ queryClient?: QueryClient
+): UseQueryResult & { queryKey: DataTag };
+/**
+ * @summary Export a library as JSON
+ */
+
+export function useExportLibrary<
+ TData = Awaited>,
+ TError = unknown,
+>(
+ libraryId: string,
+ options?: {
+ query?: Partial>, TError, TData>>;
+ request?: SecondParameter;
+ },
+ queryClient?: QueryClient
+): UseQueryResult & { queryKey: DataTag } {
+ const queryOptions = getExportLibraryQueryOptions(libraryId, options);
+
+ const query = useQuery(queryOptions, queryClient) as UseQueryResult & {
+ queryKey: DataTag;
+ };
+
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -787,5 +1005,5 @@ export function useListParsers>,
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
diff --git a/src/api/generated/mutation/mutation.ts b/src/api/generated/mutation/mutation.ts
index 2682421..40f5f11 100644
--- a/src/api/generated/mutation/mutation.ts
+++ b/src/api/generated/mutation/mutation.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -26,6 +26,21 @@ import { customInstance } from "../../api";
type SecondParameter unknown> = Parameters[1];
+const withQueryKey = (query: T, queryKey: K): T & { queryKey: K } => {
+ const result = { queryKey } as T & { queryKey: K };
+ for (const key of Object.keys(query)) {
+ // The explicit queryKey always wins, matching the previous
+ // `{ ...query, queryKey }` spread where it was set last.
+ if (key === "queryKey") continue;
+ Object.defineProperty(result, key, {
+ enumerable: true,
+ configurable: true,
+ get: () => (query as Record)[key],
+ });
+ }
+ return result;
+};
+
/**
* Reject the suggested mutation without applying it.
* @summary Reject a mutation suggestion
@@ -306,5 +321,5 @@ export function useListMutations<
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
diff --git a/src/api/generated/project/project.ts b/src/api/generated/project/project.ts
index cd71149..3f70ed5 100644
--- a/src/api/generated/project/project.ts
+++ b/src/api/generated/project/project.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -26,6 +26,21 @@ import { customInstance } from "../../api";
type SecondParameter unknown> = Parameters[1];
+const withQueryKey = (query: T, queryKey: K): T & { queryKey: K } => {
+ const result = { queryKey } as T & { queryKey: K };
+ for (const key of Object.keys(query)) {
+ // The explicit queryKey always wins, matching the previous
+ // `{ ...query, queryKey }` spread where it was set last.
+ if (key === "queryKey") continue;
+ Object.defineProperty(result, key, {
+ enumerable: true,
+ configurable: true,
+ get: () => (query as Record)[key],
+ });
+ }
+ return result;
+};
+
/**
* Retrieve a single project by ID.
* @summary Get a project
@@ -133,7 +148,7 @@ export function useGetProject>, TE
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -409,7 +424,7 @@ export function useGetProjects>,
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
diff --git a/src/api/generated/workspace/workspace.ts b/src/api/generated/workspace/workspace.ts
index d5825f5..9b4278b 100644
--- a/src/api/generated/workspace/workspace.ts
+++ b/src/api/generated/workspace/workspace.ts
@@ -1,5 +1,5 @@
/**
- * Generated by orval v8.15.0 🍺
+ * Generated by orval v8.20.0 🍺
* Do not edit manually.
* OpenAPI definition
* OpenAPI spec version: v0
@@ -26,6 +26,21 @@ import { customInstance } from "../../api";
type SecondParameter unknown> = Parameters[1];
+const withQueryKey = (query: T, queryKey: K): T & { queryKey: K } => {
+ const result = { queryKey } as T & { queryKey: K };
+ for (const key of Object.keys(query)) {
+ // The explicit queryKey always wins, matching the previous
+ // `{ ...query, queryKey }` spread where it was set last.
+ if (key === "queryKey") continue;
+ Object.defineProperty(result, key, {
+ enumerable: true,
+ configurable: true,
+ get: () => (query as Record)[key],
+ });
+ }
+ return result;
+};
+
/**
* Retrieve a single workspace by its unique identifier.
* @summary Get a workspace by ID
@@ -133,7 +148,7 @@ export function useGetWorkspace>
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
@@ -411,7 +426,7 @@ export function useGetWorkspaces<
queryKey: DataTag;
};
- return { ...query, queryKey: queryOptions.queryKey };
+ return withQueryKey(query, queryOptions.queryKey);
}
/**
diff --git a/src/features/libraries/ImportLibraryDialog.tsx b/src/features/libraries/ImportLibraryDialog.tsx
new file mode 100644
index 0000000..7d74929
--- /dev/null
+++ b/src/features/libraries/ImportLibraryDialog.tsx
@@ -0,0 +1,166 @@
+import { useState, useRef } from "react";
+import { Upload, Loader2, BookOpen, Hash, Tag } from "lucide-react";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog.tsx";
+import { Button } from "@/components/ui/button.tsx";
+import { Badge } from "@/components/ui/badge.tsx";
+import type { Library } from "@/api/generated/api.schemas.ts";
+
+const MAX_FILE_SIZE = 50 * 1024 * 1024;
+
+interface ImportLibraryDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ isImporting: boolean;
+ onImport: (data: Library) => void;
+}
+
+export const ImportLibraryDialog = ({
+ open,
+ onOpenChange,
+ isImporting,
+ onImport,
+}: ImportLibraryDialogProps) => {
+ const fileInputRef = useRef(null);
+ const [parsedData, setParsedData] = useState(null);
+ const [parseError, setParseError] = useState(null);
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0] ?? null;
+ setParseError(null);
+ setParsedData(null);
+
+ if (!file) return;
+
+ if (file.size > MAX_FILE_SIZE) {
+ setParseError("File must be under 50MB");
+ return;
+ }
+
+ if (!file.name.endsWith(".json")) {
+ setParseError("Only .json files are accepted");
+ return;
+ }
+
+ const reader = new FileReader();
+ reader.onload = () => {
+ try {
+ const json = JSON.parse(reader.result as string);
+ if (!json.name || typeof json.name !== "string" || json.name.trim() === "") {
+ setParseError('Invalid format: "name" field is required');
+ return;
+ }
+ setParsedData(json);
+ } catch {
+ setParseError("Invalid JSON file");
+ }
+ };
+ reader.onerror = () => {
+ setParseError("Failed to read file");
+ };
+ reader.readAsText(file);
+ };
+
+ const handleImport = () => {
+ if (!parsedData) return;
+ onImport(parsedData);
+ };
+
+ const handleClose = () => {
+ if (!isImporting) {
+ setParsedData(null);
+ setParseError(null);
+ if (fileInputRef.current) {
+ fileInputRef.current.value = "";
+ }
+ }
+ onOpenChange(false);
+ };
+
+ const symbolCount = parsedData?.symbols?.length ?? 0;
+ const aliasCount = parsedData?.aliases?.length ?? 0;
+
+ return (
+
+ );
+};
diff --git a/src/features/libraries/LibraryCard.tsx b/src/features/libraries/LibraryCard.tsx
index d8c5785..766c782 100644
--- a/src/features/libraries/LibraryCard.tsx
+++ b/src/features/libraries/LibraryCard.tsx
@@ -1,4 +1,4 @@
-import { Eye, Trash2, BookOpen } from "lucide-react";
+import { Eye, Trash2, BookOpen, Download } from "lucide-react";
import { Card, CardContent, CardFooter } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@@ -9,9 +9,10 @@ interface LibraryCardProps {
library: LibraryResponse;
onDelete: (library: LibraryResponse) => void;
onViewSymbols: (library: LibraryResponse) => void;
+ onExport: (library: LibraryResponse) => void;
}
-export const LibraryCard = ({ library, onDelete, onViewSymbols }: LibraryCardProps) => {
+export const LibraryCard = ({ library, onDelete, onViewSymbols, onExport }: LibraryCardProps) => {
return (
@@ -45,6 +46,9 @@ export const LibraryCard = ({ library, onDelete, onViewSymbols }: LibraryCardPro
+
+
{libraries.length > 0 && (
@@ -119,6 +172,13 @@ const Libraries = () => {
onUpload={handleUpload}
/>
+
+
{