@cytario/web 2.1.4 → 2.1.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/README.md +20 -20
- package/app/.server/auth/README.md +8 -8
- package/app/.server/auth/authMiddleware.ts +9 -25
- package/app/.server/auth/exchangeAuthCode.ts +2 -6
- package/app/.server/auth/getS3Client.ts +3 -13
- package/app/.server/auth/getSessionCredentials.ts +6 -20
- package/app/.server/auth/getUserInfo.ts +2 -6
- package/app/.server/auth/keycloakAdmin/client.ts +2 -9
- package/app/.server/auth/keycloakAdmin/groups.ts +9 -26
- package/app/.server/auth/keycloakAdmin/users.ts +7 -23
- package/app/.server/auth/oauthState.ts +4 -13
- package/app/.server/auth/redirectIfAuthenticated.ts +1 -3
- package/app/.server/auth/refreshAuthTokens.ts +5 -19
- package/app/.server/auth/sessionMiddleware.ts +1 -4
- package/app/.server/auth/sessionStorage.ts +1 -4
- package/app/.server/auth/verifyIdToken.ts +1 -3
- package/app/.server/auth/wellKnownEndpoints.ts +1 -4
- package/app/.server/db/redis.ts +5 -1
- package/app/.server/logging.ts +1 -4
- package/app/.server/requestDurationMiddleware.ts +1 -4
- package/app/components/.client/ImageViewer/README.md +5 -5
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsController.tsx +7 -9
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerBrightfieldItem.tsx +1 -2
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerItem.tsx +2 -5
- package/app/components/.client/ImageViewer/components/ChannelsController/ChannelsControllerItemList.tsx +7 -15
- package/app/components/.client/ImageViewer/components/ChannelsController/ColorPicker/ColorPicker.tsx +2 -9
- package/app/components/.client/ImageViewer/components/ChannelsController/ColorPicker/ColorSwatch.tsx +1 -4
- package/app/components/.client/ImageViewer/components/ChannelsController/DomainSlider.tsx +1 -3
- package/app/components/.client/ImageViewer/components/ChannelsController/Histogram.tsx +16 -18
- package/app/components/.client/ImageViewer/components/ChannelsController/HistogramChannel.tsx +2 -8
- package/app/components/.client/ImageViewer/components/ChannelsController/MinMaxSettings.tsx +6 -15
- package/app/components/.client/ImageViewer/components/FeatureBar/FeatureBarDragHandle.tsx +1 -5
- package/app/components/.client/ImageViewer/components/FeatureBar/FeatureBarToggle.tsx +1 -5
- package/app/components/.client/ImageViewer/components/FeatureBar/FeatureItem.tsx +1 -5
- package/app/components/.client/ImageViewer/components/FeatureBar/Presets.tsx +3 -11
- package/app/components/.client/ImageViewer/components/FeatureBar/useFeatureBar.tsx +16 -25
- package/app/components/.client/ImageViewer/components/Image/Channels/useChannelsLayer.ts +7 -18
- package/app/components/.client/ImageViewer/components/Image/ImageContainer.tsx +1 -1
- package/app/components/.client/ImageViewer/components/Image/ImagePanel.tsx +6 -26
- package/app/components/.client/ImageViewer/components/Image/ImagePreview.tsx +2 -9
- package/app/components/.client/ImageViewer/components/Image/Overlays/AdditivePolygonLayer.tsx +1 -5
- package/app/components/.client/ImageViewer/components/Image/Overlays/AdditiveScatterplotLayer.tsx +1 -5
- package/app/components/.client/ImageViewer/components/Image/Overlays/OverlaysLayer.tsx +6 -24
- package/app/components/.client/ImageViewer/components/Image/Overlays/markerUniforms.ts +2 -5
- package/app/components/.client/ImageViewer/components/Image/Overlays/useOverlaysLayer.tsx +7 -21
- package/app/components/.client/ImageViewer/components/Image/useInitializeChannels.ts +1 -7
- package/app/components/.client/ImageViewer/components/Image/useResizeObserver.ts +1 -1
- package/app/components/.client/ImageViewer/components/Magnifier.tsx +5 -13
- package/app/components/.client/ImageViewer/components/Measurements/ActiveViewStatePreview.tsx +3 -8
- package/app/components/.client/ImageViewer/components/Measurements/CursorTick.tsx +2 -7
- package/app/components/.client/ImageViewer/components/Measurements/Ruler.tsx +1 -8
- package/app/components/.client/ImageViewer/components/Measurements/SlideCarrier.tsx +3 -13
- package/app/components/.client/ImageViewer/components/Measurements/Tick.tsx +1 -1
- package/app/components/.client/ImageViewer/components/Measurements/calculateViewStateToFit.ts +1 -1
- package/app/components/.client/ImageViewer/components/Measurements/useMeasurements.ts +9 -28
- package/app/components/.client/ImageViewer/components/OverlaysController/AddOverlay.tsx +4 -13
- package/app/components/.client/ImageViewer/components/OverlaysController/OverlayPicker.modal.tsx +1 -6
- package/app/components/.client/ImageViewer/components/OverlaysController/OverlaysController.Item.tsx +24 -54
- package/app/components/.client/ImageViewer/components/OverlaysController/OverlaysController.tsx +1 -3
- package/app/components/.client/ImageViewer/components/SplitViewToggle.tsx +1 -3
- package/app/components/.client/ImageViewer/components/ViewerHeader.tsx +1 -3
- package/app/components/.client/ImageViewer/state/decoders/decodeJPEG2000.d.ts +9 -11
- package/app/components/.client/ImageViewer/state/decoders/decodeJPEG2000.js +11 -11
- package/app/components/.client/ImageViewer/state/decoders/decoder.worker.js +49 -49
- package/app/components/.client/ImageViewer/state/decoders/genericDecoder.ts +76 -81
- package/app/components/.client/ImageViewer/state/decoders/jp2k-decoder.ts +9 -9
- package/app/components/.client/ImageViewer/state/decoders/lzwDecoder.ts +9 -9
- package/app/components/.client/ImageViewer/state/loaders/loadBioformatsZarrWithCredentials.ts +10 -22
- package/app/components/.client/ImageViewer/state/store/ViewerStoreContext.tsx +4 -18
- package/app/components/.client/ImageViewer/state/store/createViewerStore.ts +110 -194
- package/app/components/.client/ImageViewer/state/store/getInitialChannelsState.ts +2 -6
- package/app/components/.client/ImageViewer/state/store/selectors.ts +9 -9
- package/app/components/.client/ImageViewer/state/store/types.ts +3 -12
- package/app/components/.client/ImageViewer/state/transport/CredentialedHTTPStore.ts +1 -5
- package/app/components/.client/ImageViewer/state/transport/SigV4TiffClient.ts +2 -9
- package/app/components/.client/ImageViewer/utils/getSelectionStats.ts +1 -4
- package/app/components/.client/ImageViewer/utils/handleImageViewerHover.ts +1 -1
- package/app/components/.client/ImageViewer/utils/mapChannelConfigsToState.ts +2 -4
- package/app/components/.client/ImageViewer/utils/useTilesLoading.ts +3 -3
- package/app/components/AppHeader.tsx +1 -4
- package/app/components/Breadcrumbs/Breadcrumbs.tsx +4 -13
- package/app/components/Breadcrumbs/getCrumbs.tsx +1 -1
- package/app/components/ClientOnly.tsx +1 -1
- package/app/components/Container.tsx +3 -15
- package/app/components/DataGrid/ConvertOverlay.modal.tsx +1 -6
- package/app/components/DataGrid/DataGrid.tsx +7 -27
- package/app/components/DataGrid/WktSvg.tsx +2 -4
- package/app/components/DataGrid/getParquetSchema.ts +1 -4
- package/app/components/DescriptionList.tsx +1 -3
- package/app/components/DirectoryView/ConnectionMenu.tsx +8 -22
- package/app/components/DirectoryView/DirectoryView.tsx +10 -46
- package/app/components/DirectoryView/DirectoryViewGrid.tsx +16 -49
- package/app/components/DirectoryView/DirectoryViewTableConnection.tsx +1 -4
- package/app/components/DirectoryView/DirectoryViewTableDirectory.tsx +2 -7
- package/app/components/DirectoryView/DirectoryViewTree.tsx +5 -21
- package/app/components/DirectoryView/FilterBar.tsx +9 -48
- package/app/components/DirectoryView/buildDirectoryTree.ts +6 -25
- package/app/components/DirectoryView/filterNodes.ts +4 -11
- package/app/components/DirectoryView/modals/Cyberduck.modal.tsx +6 -15
- package/app/components/DirectoryView/modals/FileInfo.modal.tsx +1 -5
- package/app/components/DirectoryView/useLayoutStore.ts +5 -25
- package/app/components/GlobalSearch/GlobalSearch.tsx +1 -4
- package/app/components/GlobalSearch/SearchBar.tsx +1 -7
- package/app/components/GlobalSearch/Suggestions.tsx +0 -1
- package/app/components/ImageViewer/state/formatRegistry.ts +5 -18
- package/app/components/LavaLoader.tsx +4 -11
- package/app/components/Layout/Footer.tsx +1 -4
- package/app/components/Pills/ScopePill.tsx +2 -8
- package/app/components/Table/ColumnFilterInput.tsx +5 -20
- package/app/components/Table/ColumnResizeHandle.tsx +1 -5
- package/app/components/Table/ColumnSortButton.tsx +1 -5
- package/app/components/Table/SelectionFooter.tsx +3 -5
- package/app/components/Table/Table.tsx +5 -21
- package/app/components/Table/TableBodyRow.tsx +19 -31
- package/app/components/Table/TableHeaderRow.tsx +7 -28
- package/app/components/Table/TableMenu.tsx +4 -20
- package/app/components/Table/state/createTableStore.ts +3 -9
- package/app/components/Table/state/useTableStore.ts +1 -3
- package/app/components/Table/types.ts +4 -10
- package/app/components/Table/useColumnFilters.ts +1 -4
- package/app/components/Table/useColumnVisibility.ts +4 -11
- package/app/components/Table/useColumnWidths.ts +1 -3
- package/app/components/Table/useTableSorting.ts +4 -12
- package/app/components/Tooltip/Tooltip.tsx +4 -17
- package/app/components/Tooltip/TooltipSpan.tsx +5 -28
- package/app/components/Tooltip/useCopyToClipboard.ts +1 -3
- package/app/components/Tooltip/useMiddleEllipsis.ts +2 -7
- package/app/components/Tooltip/useOverflowDetection.ts +2 -5
- package/app/components/UserMenu.tsx +2 -9
- package/app/entry.server.tsx +9 -19
- package/app/hooks/useSearchParam.ts +2 -4
- package/app/lib/bootstrapPluginsCore.ts +4 -9
- package/app/root.tsx +4 -15
- package/app/routes/admin/assertAdminScope.ts +1 -3
- package/app/routes/admin/assertGroupPathsInScope.ts +3 -11
- package/app/routes/admin/assertGroupsInScope.ts +3 -11
- package/app/routes/admin/assertUsersInScope.ts +2 -8
- package/app/routes/admin/bulkInvite/bulkInvite.action.ts +2 -13
- package/app/routes/admin/bulkInvite/bulkInvite.form.tsx +18 -35
- package/app/routes/admin/createGroup/createGroup.action.ts +3 -10
- package/app/routes/admin/createGroup/createGroup.form.tsx +2 -9
- package/app/routes/admin/createGroup/createGroup.modal.tsx +1 -5
- package/app/routes/admin/inviteUser/inviteUser.action.ts +1 -4
- package/app/routes/admin/inviteUser/inviteUser.form.tsx +2 -8
- package/app/routes/admin/inviteUser/inviteUser.loader.ts +1 -4
- package/app/routes/admin/inviteUser/inviteUser.modal.tsx +3 -16
- package/app/routes/admin/updateUser/updateUser.form.tsx +4 -15
- package/app/routes/admin/updateUser/updateUser.modal.tsx +5 -23
- package/app/routes/admin/updateUser/userDetail.action.ts +2 -10
- package/app/routes/admin/users/BulkActions.tsx +15 -38
- package/app/routes/admin/users/bulkUsers.action.ts +2 -9
- package/app/routes/admin/users/bulkUsers.schema.ts +1 -6
- package/app/routes/admin/users/users.route.tsx +14 -63
- package/app/routes/api/cyberduck-profile.$name.ts +6 -2
- package/app/routes/auth/callback.route.tsx +8 -33
- package/app/routes/auth/login.route.tsx +8 -11
- package/app/routes/auth/logout.route.tsx +4 -14
- package/app/routes/config.route.tsx +1 -5
- package/app/routes/connections/connection.form.tsx +5 -14
- package/app/routes/connections/connection.schema.ts +4 -16
- package/app/routes/connections/connections.loader.ts +11 -23
- package/app/routes/connections/connections.route.tsx +1 -5
- package/app/routes/connections/connections.server.ts +1 -3
- package/app/routes/connections/createConnection.action.ts +2 -8
- package/app/routes/connections/createConnection.modal.tsx +3 -13
- package/app/routes/connections/deleteConnection.action.ts +1 -4
- package/app/routes/connections/updateConnection.action.ts +2 -8
- package/app/routes/connections/updateConnection.modal.tsx +4 -14
- package/app/routes/home/home.route.tsx +8 -33
- package/app/routes/layouts/ModalOutlet.tsx +6 -18
- package/app/routes/objects/objects.loader.ts +5 -19
- package/app/routes/objects/objects.route.tsx +11 -30
- package/app/routes/presign.route.tsx +5 -18
- package/app/routes/recent.route.tsx +1 -4
- package/app/routes/search.route.tsx +4 -17
- package/app/routes.ts +1 -4
- package/app/tailwind.css +17 -12
- package/app/types/cornerstone-codecs.d.ts +25 -29
- package/app/utils/connectionsStore/selectors.ts +2 -6
- package/app/utils/connectionsStore/useConnectionsStore.ts +2 -6
- package/app/utils/db/convertCsvToParquet.ts +1 -3
- package/app/utils/db/createDatabase.ts +1 -3
- package/app/utils/db/createSingleton.ts +1 -1
- package/app/utils/db/getBlobFromObjectNode.ts +3 -9
- package/app/utils/db/getGeomQuery.ts +1 -1
- package/app/utils/db/getMarkerInfoWasm.ts +1 -3
- package/app/utils/db/getTileBoundingBox.ts +1 -4
- package/app/utils/db/sqlQueries.ts +1 -4
- package/app/utils/fileType.ts +80 -10
- package/app/utils/filterObjects.ts +2 -5
- package/app/utils/localFilesStore/useFileStore.ts +7 -7
- package/app/utils/recentlyViewed.server.ts +1 -4
- package/app/utils/resourceId.ts +3 -11
- package/app/utils/s3Provider.ts +3 -7
- package/app/utils/signedFetch.ts +4 -13
- package/bin-src/codegen.ts +4 -1
- package/package.json +5 -1
- package/prisma/seed.ts +1 -2
- package/public/favicon/site.webmanifest +1 -1
- package/server.js +1 -4
- package/server.js.map +1 -1
- package/vite-plugins/cytario-plugins.ts +2 -8
|
@@ -6,15 +6,11 @@ import type { ConnectionConfig } from "~/.generated/client";
|
|
|
6
6
|
import { authContext } from "~/.server/auth/authMiddleware";
|
|
7
7
|
import { getS3Client } from "~/.server/auth/getS3Client";
|
|
8
8
|
import { Section } from "~/components/Container";
|
|
9
|
-
import {
|
|
10
|
-
buildDirectoryTree,
|
|
11
|
-
TreeNode,
|
|
12
|
-
} from "~/components/DirectoryView/buildDirectoryTree";
|
|
9
|
+
import { buildDirectoryTree, TreeNode } from "~/components/DirectoryView/buildDirectoryTree";
|
|
13
10
|
import { DirectoryTree } from "~/components/DirectoryView/DirectoryViewTree";
|
|
14
11
|
import { getObjects } from "~/utils/getObjects";
|
|
15
12
|
import { getPrefix } from "~/utils/pathUtils";
|
|
16
13
|
|
|
17
|
-
|
|
18
14
|
interface ConfigFiles {
|
|
19
15
|
config: ConnectionConfig;
|
|
20
16
|
files: _Object[];
|
|
@@ -37,11 +33,7 @@ export const loader = async ({
|
|
|
37
33
|
const url = new URL(request.url);
|
|
38
34
|
const searchQuery = url.searchParams.get("query") ?? "";
|
|
39
35
|
|
|
40
|
-
const {
|
|
41
|
-
user,
|
|
42
|
-
credentials: connectionsCredentials,
|
|
43
|
-
connectionConfigs,
|
|
44
|
-
} = context.get(authContext);
|
|
36
|
+
const { user, credentials: connectionsCredentials, connectionConfigs } = context.get(authContext);
|
|
45
37
|
|
|
46
38
|
const results: ConfigFiles[] = [];
|
|
47
39
|
|
|
@@ -54,8 +46,7 @@ export const loader = async ({
|
|
|
54
46
|
|
|
55
47
|
const s3Client = await getS3Client(connectionConfig, credentials, user.sub);
|
|
56
48
|
const prefix = getPrefix(connectionConfig.prefix);
|
|
57
|
-
const files =
|
|
58
|
-
(await getObjects(connectionConfig, s3Client, searchQuery, prefix)) ?? [];
|
|
49
|
+
const files = (await getObjects(connectionConfig, s3Client, searchQuery, prefix)) ?? [];
|
|
59
50
|
|
|
60
51
|
if (files.length > 0) {
|
|
61
52
|
results.push({ config: connectionConfig, files, prefix });
|
|
@@ -68,11 +59,7 @@ export const loader = async ({
|
|
|
68
59
|
name: config.name,
|
|
69
60
|
type: "bucket" as const,
|
|
70
61
|
pathName: "",
|
|
71
|
-
children: buildDirectoryTree(
|
|
72
|
-
files as _Object[],
|
|
73
|
-
config.name,
|
|
74
|
-
prefix ?? "",
|
|
75
|
-
),
|
|
62
|
+
children: buildDirectoryTree(files as _Object[], config.name, prefix ?? ""),
|
|
76
63
|
}));
|
|
77
64
|
|
|
78
65
|
return { searchQuery, nodes };
|
package/app/routes.ts
CHANGED
|
@@ -92,10 +92,7 @@ const apiRoutes = [
|
|
|
92
92
|
|
|
93
93
|
export default [
|
|
94
94
|
...authRoutes,
|
|
95
|
-
layout("routes/layouts/protected.layout.tsx", [
|
|
96
|
-
...appRoutes,
|
|
97
|
-
...adminRoutes,
|
|
98
|
-
]),
|
|
95
|
+
layout("routes/layouts/protected.layout.tsx", [...appRoutes, ...adminRoutes]),
|
|
99
96
|
...apiRoutes,
|
|
100
97
|
|
|
101
98
|
{
|
package/app/tailwind.css
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
@plugin "tailwindcss-react-aria-components";
|
|
4
4
|
|
|
5
5
|
/* Scan @cytario/design dist for class usage */
|
|
6
|
-
@source "
|
|
6
|
+
@source "@cytario/design/dist";
|
|
7
7
|
|
|
8
8
|
/* Match cytario-design's selector convention */
|
|
9
9
|
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
|
|
@@ -12,16 +12,16 @@
|
|
|
12
12
|
/* Custom colors */
|
|
13
13
|
--color-cytario-purple-500: #5c2483;
|
|
14
14
|
|
|
15
|
-
--color-cytario-turquoise-50: #
|
|
16
|
-
--color-cytario-turquoise-100: #
|
|
17
|
-
--color-cytario-turquoise-200: #
|
|
18
|
-
--color-cytario-turquoise-300: #
|
|
19
|
-
--color-cytario-turquoise-400: #
|
|
20
|
-
--color-cytario-turquoise-500: #
|
|
21
|
-
--color-cytario-turquoise-600: #
|
|
22
|
-
--color-cytario-turquoise-700: #
|
|
15
|
+
--color-cytario-turquoise-50: #f0fafa;
|
|
16
|
+
--color-cytario-turquoise-100: #cceded;
|
|
17
|
+
--color-cytario-turquoise-200: #99dbdb;
|
|
18
|
+
--color-cytario-turquoise-300: #66c9c9;
|
|
19
|
+
--color-cytario-turquoise-400: #4dc3c3;
|
|
20
|
+
--color-cytario-turquoise-500: #35b7b8;
|
|
21
|
+
--color-cytario-turquoise-600: #2a9293;
|
|
22
|
+
--color-cytario-turquoise-700: #1f7172;
|
|
23
23
|
--color-cytario-turquoise-800: #165859;
|
|
24
|
-
--color-cytario-turquoise-900: #
|
|
24
|
+
--color-cytario-turquoise-900: #0d3f40;
|
|
25
25
|
--color-cytario-turquoise-950: #072425;
|
|
26
26
|
|
|
27
27
|
/* Custom font family */
|
|
@@ -46,8 +46,13 @@
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
@keyframes pulse-once {
|
|
49
|
-
0%,
|
|
50
|
-
|
|
49
|
+
0%,
|
|
50
|
+
100% {
|
|
51
|
+
opacity: 0;
|
|
52
|
+
}
|
|
53
|
+
50% {
|
|
54
|
+
opacity: 1;
|
|
55
|
+
}
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
html,
|
|
@@ -1,36 +1,32 @@
|
|
|
1
1
|
declare module "@cornerstonejs/codec-openjpeg" {
|
|
2
|
-
|
|
3
|
-
wasmModuleURL?: string;
|
|
4
|
-
}): Promise<void>;
|
|
2
|
+
export function initialize(config?: { wasmModuleURL?: string }): Promise<void>;
|
|
5
3
|
}
|
|
6
4
|
|
|
7
5
|
declare module "@cornerstonejs/codec-openjpeg/dist/openjpegwasm_decode" {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
interface EmscriptenModule {
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
}
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
moduleOverrides?: Partial<T>
|
|
14
|
-
) => Promise<T>;
|
|
10
|
+
type EmscriptenModuleFactory<T = EmscriptenModule> = (moduleOverrides?: Partial<T>) => Promise<T>;
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
12
|
+
export class J2KDecoder {
|
|
13
|
+
decode: () => unknown;
|
|
14
|
+
getBlockDimensions: () => unknown;
|
|
15
|
+
getColorSpace: () => unknown;
|
|
16
|
+
getDecodedBuffer: () => unknown;
|
|
17
|
+
getEncodedBuffer: (length: number) => unknown;
|
|
18
|
+
getFrameInfo: () => unknown;
|
|
19
|
+
getImageOffset: () => unknown;
|
|
20
|
+
getIsReversible: () => unknown;
|
|
21
|
+
getNumDecompositions: () => unknown;
|
|
22
|
+
getNumLayers: () => unknown;
|
|
23
|
+
getProgressionOrder: () => number;
|
|
24
|
+
getTileOffset: () => unknown;
|
|
25
|
+
getTileSize: () => unknown;
|
|
26
|
+
}
|
|
27
|
+
export interface OpenJpegModule extends EmscriptenModule {
|
|
28
|
+
J2KDecoder: typeof J2KDecoder;
|
|
29
|
+
}
|
|
30
|
+
declare const Module: EmscriptenModuleFactory<OpenJpegModule>;
|
|
31
|
+
export { Module };
|
|
36
32
|
}
|
|
@@ -75,12 +75,8 @@ export const selectHttpsUrl =
|
|
|
75
75
|
* For reactive use, subscribe to `selectHttpsUrl`. For non-reactive use
|
|
76
76
|
* (async callbacks), use `resolveResourceId`.
|
|
77
77
|
*/
|
|
78
|
-
function resolveResource(
|
|
79
|
-
|
|
80
|
-
state: ConnectionsStore,
|
|
81
|
-
): ResolvedResource | null {
|
|
82
|
-
const { connectionName, pathName: connectionPathName } =
|
|
83
|
-
parseResourceId(resourceId);
|
|
78
|
+
function resolveResource(resourceId: string, state: ConnectionsStore): ResolvedResource | null {
|
|
79
|
+
const { connectionName, pathName: connectionPathName } = parseResourceId(resourceId);
|
|
84
80
|
|
|
85
81
|
const connection = state.connections[connectionName];
|
|
86
82
|
if (!connection) return null;
|
|
@@ -31,10 +31,7 @@ export interface ConnectionsStore {
|
|
|
31
31
|
* set of credentials per connection). Prunes entries for connections
|
|
32
32
|
* deleted server-side.
|
|
33
33
|
*/
|
|
34
|
-
setConnections: (
|
|
35
|
-
configs: ConnectionConfig[],
|
|
36
|
-
credentials: Record<string, Credentials>,
|
|
37
|
-
) => void;
|
|
34
|
+
setConnections: (configs: ConnectionConfig[], credentials: Record<string, Credentials>) => void;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
const name = "ConnectionsStore";
|
|
@@ -85,8 +82,7 @@ export const useConnectionsStore = create<ConnectionsStore>()(
|
|
|
85
82
|
connections: state.connections,
|
|
86
83
|
}),
|
|
87
84
|
onRehydrateStorage: () => (_state, error) => {
|
|
88
|
-
if (error)
|
|
89
|
-
console.error("[ConnectionsStore] Rehydration failed:", error);
|
|
85
|
+
if (error) console.error("[ConnectionsStore] Rehydration failed:", error);
|
|
90
86
|
},
|
|
91
87
|
},
|
|
92
88
|
),
|
|
@@ -54,9 +54,7 @@ export async function convertCsvToParquet(resourceId: string) {
|
|
|
54
54
|
// Write to Parquet with WKB geometry
|
|
55
55
|
const parquetDestination = `${s3Uri}.parquet`;
|
|
56
56
|
|
|
57
|
-
console.log(
|
|
58
|
-
`[CSV→Parquet] Writing to S3 as Parquet (ZSTD compression, 500k row groups)...`
|
|
59
|
-
);
|
|
57
|
+
console.log(`[CSV→Parquet] Writing to S3 as Parquet (ZSTD compression, 500k row groups)...`);
|
|
60
58
|
console.log(`[CSV→Parquet] → Destination: ${parquetDestination}`);
|
|
61
59
|
console.log("[CSV→Parquet] ⏳ This may take a while for large datasets...");
|
|
62
60
|
|
|
@@ -70,9 +70,7 @@ const createDatabaseInternal = async (
|
|
|
70
70
|
await connection.query(`SET s3_url_style='path'`);
|
|
71
71
|
await connection.query(`SET s3_use_ssl=${useSSL}`);
|
|
72
72
|
|
|
73
|
-
console.info(
|
|
74
|
-
`[createDatabase] DuckDB initialized (endpoint: ${hostname}, style: path)`,
|
|
75
|
-
);
|
|
73
|
+
console.info(`[createDatabase] DuckDB initialized (endpoint: ${hostname}, style: path)`);
|
|
76
74
|
|
|
77
75
|
return connection;
|
|
78
76
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @returns Memoized version that caches by first parameter (key)
|
|
7
7
|
*/
|
|
8
8
|
export function createSingleton<K, V, Args extends unknown[]>(
|
|
9
|
-
initFn: (key: K, ...args: Args) => Promise<V
|
|
9
|
+
initFn: (key: K, ...args: Args) => Promise<V>,
|
|
10
10
|
): (key: K, ...args: Args) => Promise<V> {
|
|
11
11
|
const cache = new Map<K, Promise<V>>();
|
|
12
12
|
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
useFileStore,
|
|
4
|
-
type DownloadProgress,
|
|
5
|
-
} from "../localFilesStore/useFileStore";
|
|
1
|
+
import { useFileStore, type DownloadProgress } from "../localFilesStore/useFileStore";
|
|
6
2
|
import { parseResourceId } from "../resourceId";
|
|
7
3
|
|
|
8
4
|
export type ProgressCallback = (progress: DownloadProgress) => void;
|
|
@@ -12,7 +8,7 @@ export type ProgressCallback = (progress: DownloadProgress) => void;
|
|
|
12
8
|
*/
|
|
13
9
|
async function downloadFileWithProgress(
|
|
14
10
|
url: string,
|
|
15
|
-
onProgress?: ProgressCallback
|
|
11
|
+
onProgress?: ProgressCallback,
|
|
16
12
|
): Promise<Uint8Array> {
|
|
17
13
|
const response = await fetch(url);
|
|
18
14
|
|
|
@@ -73,9 +69,7 @@ async function getPresignedUrl(resourceId: string): Promise<string> {
|
|
|
73
69
|
* Get file data for a resourceId, with caching and progress tracking
|
|
74
70
|
* @param resourceId - S3 resource identifier (provider/bucketName/pathName)
|
|
75
71
|
*/
|
|
76
|
-
export const getUint8ArrayForResourceId = async (
|
|
77
|
-
resourceId: string
|
|
78
|
-
): Promise<Uint8Array> => {
|
|
72
|
+
export const getUint8ArrayForResourceId = async (resourceId: string): Promise<Uint8Array> => {
|
|
79
73
|
const { getFile, saveFile, setFileProgress } = useFileStore.getState();
|
|
80
74
|
|
|
81
75
|
// Check cache first
|
|
@@ -17,7 +17,7 @@ function buildBitmaskExpression(markerColumns: string[]): string {
|
|
|
17
17
|
const limitedMarkers = markerColumns.slice(0, 32);
|
|
18
18
|
|
|
19
19
|
const expressions = limitedMarkers.map(
|
|
20
|
-
(col, idx) => `(CAST(CAST("${col}" AS BOOLEAN) AS INTEGER) << ${idx})
|
|
20
|
+
(col, idx) => `(CAST(CAST("${col}" AS BOOLEAN) AS INTEGER) << ${idx})`,
|
|
21
21
|
);
|
|
22
22
|
|
|
23
23
|
return `(${expressions.join(" | ")})`;
|
|
@@ -5,9 +5,7 @@ import { MarkerInfo } from "~/components/.client/ImageViewer/components/Overlays
|
|
|
5
5
|
/**
|
|
6
6
|
* Extract marker information from DuckDB-WASM database.
|
|
7
7
|
*/
|
|
8
|
-
export async function getMarkerInfoWasm(
|
|
9
|
-
resourceId: string,
|
|
10
|
-
): Promise<MarkerInfo> {
|
|
8
|
+
export async function getMarkerInfoWasm(resourceId: string): Promise<MarkerInfo> {
|
|
11
9
|
const { credentials, connectionConfig, s3Uri } = resolveResourceId(resourceId);
|
|
12
10
|
const connection = await createDatabase(resourceId, credentials, connectionConfig);
|
|
13
11
|
|
|
@@ -9,10 +9,7 @@ interface TileIndex {
|
|
|
9
9
|
/**
|
|
10
10
|
* Get tile bounding box in projected coordinates
|
|
11
11
|
*/
|
|
12
|
-
export function getTileBoundingBox(
|
|
13
|
-
{ z, x, y }: TileIndex,
|
|
14
|
-
tileSize: number = 256
|
|
15
|
-
): BBox {
|
|
12
|
+
export function getTileBoundingBox({ z, x, y }: TileIndex, tileSize: number = 256): BBox {
|
|
16
13
|
const zoom = z * -1 + 1;
|
|
17
14
|
const projectedTileSize = tileSize * 2 ** zoom;
|
|
18
15
|
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
export function buildCreateTableQuery(
|
|
2
|
-
id: string,
|
|
3
|
-
geometryColumn: string = "polygon"
|
|
4
|
-
): string {
|
|
1
|
+
export function buildCreateTableQuery(id: string, geometryColumn: string = "polygon"): string {
|
|
5
2
|
return /*sql*/ `
|
|
6
3
|
CREATE TABLE IF NOT EXISTS geometries AS
|
|
7
4
|
SELECT
|
package/app/utils/fileType.ts
CHANGED
|
@@ -42,16 +42,86 @@ interface FileTypeEntry {
|
|
|
42
42
|
// specific pattern. Built-ins stay hardcoded (not auto-derived from the
|
|
43
43
|
// registry) so labels are available during SSR before bootstrap runs.
|
|
44
44
|
const STATIC_FILE_TYPES: FileTypeEntry[] = [
|
|
45
|
-
{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
|
|
45
|
+
{
|
|
46
|
+
pattern: /\.ome\.tiff?$/i,
|
|
47
|
+
type: "OME-TIFF",
|
|
48
|
+
label: "OME-TIFF",
|
|
49
|
+
icon: "Microscope",
|
|
50
|
+
iconComponent: Microscope,
|
|
51
|
+
isImage: true,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
pattern: /\.ome\.zarr\/?$/i,
|
|
55
|
+
type: "OME-Zarr",
|
|
56
|
+
label: "OME-Zarr",
|
|
57
|
+
icon: "Microscope",
|
|
58
|
+
iconComponent: Microscope,
|
|
59
|
+
isImage: true,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
pattern: /\.zarr\/?$/i,
|
|
63
|
+
type: "OME-Zarr",
|
|
64
|
+
label: "OME-Zarr",
|
|
65
|
+
icon: "Microscope",
|
|
66
|
+
iconComponent: Microscope,
|
|
67
|
+
isImage: true,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
pattern: /\.tiff?$/i,
|
|
71
|
+
type: "TIFF",
|
|
72
|
+
label: "TIFF",
|
|
73
|
+
icon: "Image",
|
|
74
|
+
iconComponent: Image,
|
|
75
|
+
isImage: true,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
pattern: /\.parquet$/i,
|
|
79
|
+
type: "Parquet",
|
|
80
|
+
label: "Parquet",
|
|
81
|
+
icon: "Table",
|
|
82
|
+
iconComponent: Table,
|
|
83
|
+
isImage: false,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
pattern: /\.csv$/i,
|
|
87
|
+
type: "CSV",
|
|
88
|
+
label: "CSV",
|
|
89
|
+
icon: "FileSpreadsheet",
|
|
90
|
+
iconComponent: FileSpreadsheet,
|
|
91
|
+
isImage: false,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
pattern: /\.ndjson$/i,
|
|
95
|
+
type: "JSON",
|
|
96
|
+
label: "NDJSON",
|
|
97
|
+
icon: "Braces",
|
|
98
|
+
iconComponent: Braces,
|
|
99
|
+
isImage: false,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
pattern: /\.json$/i,
|
|
103
|
+
type: "JSON",
|
|
104
|
+
label: "JSON",
|
|
105
|
+
icon: "Braces",
|
|
106
|
+
iconComponent: Braces,
|
|
107
|
+
isImage: false,
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
pattern: /\.png$/i,
|
|
111
|
+
type: "PNG",
|
|
112
|
+
label: "PNG",
|
|
113
|
+
icon: "Image",
|
|
114
|
+
iconComponent: Image,
|
|
115
|
+
isImage: true,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
pattern: /\.jpe?g$/i,
|
|
119
|
+
type: "JPEG",
|
|
120
|
+
label: "JPEG",
|
|
121
|
+
icon: "Image",
|
|
122
|
+
iconComponent: Image,
|
|
123
|
+
isImage: true,
|
|
124
|
+
},
|
|
55
125
|
];
|
|
56
126
|
|
|
57
127
|
function escapeForRegExp(s: string): string {
|
|
@@ -3,14 +3,11 @@ import { _Object } from "@aws-sdk/client-s3";
|
|
|
3
3
|
import { search } from "~/components/GlobalSearch/search";
|
|
4
4
|
import { cytarioConfig } from "~/config";
|
|
5
5
|
|
|
6
|
-
export const allowedFilesPattern = new RegExp(
|
|
7
|
-
cytarioConfig.setup.allowedFiles,
|
|
8
|
-
"i"
|
|
9
|
-
);
|
|
6
|
+
export const allowedFilesPattern = new RegExp(cytarioConfig.setup.allowedFiles, "i");
|
|
10
7
|
|
|
11
8
|
export const filterObjects = (
|
|
12
9
|
objects: Readonly<_Object>[] = [],
|
|
13
|
-
{ query }: { query?: string | null }
|
|
10
|
+
{ query }: { query?: string | null },
|
|
14
11
|
): _Object[] => {
|
|
15
12
|
return objects
|
|
16
13
|
.reduce((acc, item) => {
|
|
@@ -63,7 +63,7 @@ export const useFileStore = create<FileStore>()(
|
|
|
63
63
|
},
|
|
64
64
|
}),
|
|
65
65
|
false,
|
|
66
|
-
"saveFile"
|
|
66
|
+
"saveFile",
|
|
67
67
|
);
|
|
68
68
|
},
|
|
69
69
|
|
|
@@ -79,7 +79,7 @@ export const useFileStore = create<FileStore>()(
|
|
|
79
79
|
},
|
|
80
80
|
}),
|
|
81
81
|
false,
|
|
82
|
-
"setFileProgress"
|
|
82
|
+
"setFileProgress",
|
|
83
83
|
);
|
|
84
84
|
},
|
|
85
85
|
|
|
@@ -98,7 +98,7 @@ export const useFileStore = create<FileStore>()(
|
|
|
98
98
|
return { files: rest };
|
|
99
99
|
},
|
|
100
100
|
false,
|
|
101
|
-
"deleteFile"
|
|
101
|
+
"deleteFile",
|
|
102
102
|
);
|
|
103
103
|
},
|
|
104
104
|
|
|
@@ -113,7 +113,7 @@ export const useFileStore = create<FileStore>()(
|
|
|
113
113
|
key,
|
|
114
114
|
size: data?.length ?? 0,
|
|
115
115
|
};
|
|
116
|
-
})
|
|
116
|
+
}),
|
|
117
117
|
);
|
|
118
118
|
|
|
119
119
|
set(
|
|
@@ -144,10 +144,10 @@ export const useFileStore = create<FileStore>()(
|
|
|
144
144
|
return { files };
|
|
145
145
|
},
|
|
146
146
|
false,
|
|
147
|
-
"hydrate"
|
|
147
|
+
"hydrate",
|
|
148
148
|
);
|
|
149
149
|
},
|
|
150
150
|
}),
|
|
151
|
-
{ name }
|
|
152
|
-
)
|
|
151
|
+
{ name },
|
|
152
|
+
),
|
|
153
153
|
);
|
|
@@ -28,10 +28,7 @@ export async function upsertRecentlyViewed(
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/** Get the most recently viewed items for a user, ordered newest-first. */
|
|
31
|
-
export async function getRecentlyViewed(
|
|
32
|
-
userId: string,
|
|
33
|
-
limit = 20,
|
|
34
|
-
): Promise<RecentlyViewed[]> {
|
|
31
|
+
export async function getRecentlyViewed(userId: string, limit = 20): Promise<RecentlyViewed[]> {
|
|
35
32
|
return prisma.recentlyViewed.findMany({
|
|
36
33
|
where: { userId },
|
|
37
34
|
orderBy: { viewedAt: "desc" },
|
package/app/utils/resourceId.ts
CHANGED
|
@@ -25,9 +25,7 @@ export function parseResourceId(resourceId: string): ResourceIdParts {
|
|
|
25
25
|
.replace(/^\/+/, "");
|
|
26
26
|
|
|
27
27
|
if (!connectionName) {
|
|
28
|
-
throw new Error(
|
|
29
|
-
`Invalid resourceId: "${resourceId}" — empty connectionName`,
|
|
30
|
-
);
|
|
28
|
+
throw new Error(`Invalid resourceId: "${resourceId}" — empty connectionName`);
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
return { connectionName, pathName };
|
|
@@ -40,10 +38,7 @@ export function getFileName(resourceId: string): string {
|
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
/** Builds a routable URL path from a connection name and an object path. */
|
|
43
|
-
export function buildConnectionPath(
|
|
44
|
-
connectionName: string,
|
|
45
|
-
pathName: string,
|
|
46
|
-
): string {
|
|
41
|
+
export function buildConnectionPath(connectionName: string, pathName: string): string {
|
|
47
42
|
const path = pathName
|
|
48
43
|
? `/connections/${connectionName}/${pathName}`
|
|
49
44
|
: `/connections/${connectionName}`;
|
|
@@ -70,10 +65,7 @@ export function buildConnectionPath(
|
|
|
70
65
|
* constructS3Url({ bucketName: "b", endpoint: "http://localhost:9000" }, "x.zarr")
|
|
71
66
|
* // → "http://localhost:9000/b/x.zarr"
|
|
72
67
|
*/
|
|
73
|
-
export function constructS3Url(
|
|
74
|
-
connectionConfig: ConnectionConfig,
|
|
75
|
-
s3Key: string,
|
|
76
|
-
): string {
|
|
68
|
+
export function constructS3Url(connectionConfig: ConnectionConfig, s3Key: string): string {
|
|
77
69
|
const bucket = connectionConfig.bucketName;
|
|
78
70
|
const encodedPath = s3Key.split("/").map(encodeURIComponent).join("/");
|
|
79
71
|
|
package/app/utils/s3Provider.ts
CHANGED
|
@@ -32,7 +32,7 @@ export function isAwsS3Endpoint(endpoint?: string | null): boolean {
|
|
|
32
32
|
*/
|
|
33
33
|
export function getS3ProviderConfig(
|
|
34
34
|
endpoint?: string | null,
|
|
35
|
-
region?: string | null
|
|
35
|
+
region?: string | null,
|
|
36
36
|
): S3ProviderConfig {
|
|
37
37
|
const actualRegion = region ?? DEFAULT_REGION;
|
|
38
38
|
const actualEndpoint = endpoint ?? DEFAULT_ENDPOINT;
|
|
@@ -41,12 +41,8 @@ export function getS3ProviderConfig(
|
|
|
41
41
|
return {
|
|
42
42
|
isAwsS3,
|
|
43
43
|
usePathStyle: !isAwsS3,
|
|
44
|
-
stsEndpoint: isAwsS3
|
|
45
|
-
|
|
46
|
-
: actualEndpoint,
|
|
47
|
-
s3Endpoint: isAwsS3
|
|
48
|
-
? `https://s3.${actualRegion}.amazonaws.com`
|
|
49
|
-
: actualEndpoint,
|
|
44
|
+
stsEndpoint: isAwsS3 ? `https://sts.${actualRegion}.amazonaws.com` : actualEndpoint,
|
|
45
|
+
s3Endpoint: isAwsS3 ? `https://s3.${actualRegion}.amazonaws.com` : actualEndpoint,
|
|
50
46
|
};
|
|
51
47
|
}
|
|
52
48
|
|
package/app/utils/signedFetch.ts
CHANGED
|
@@ -5,10 +5,7 @@ import { SignatureV4 } from "@smithy/signature-v4";
|
|
|
5
5
|
import type { ConnectionConfig } from "~/.generated/client";
|
|
6
6
|
import { sanitizeHeaders } from "~/utils/sanitizeHeaders";
|
|
7
7
|
|
|
8
|
-
export type SignedFetch = (
|
|
9
|
-
url: string,
|
|
10
|
-
init?: RequestInit,
|
|
11
|
-
) => Promise<Response>;
|
|
8
|
+
export type SignedFetch = (url: string, init?: RequestInit) => Promise<Response>;
|
|
12
9
|
|
|
13
10
|
// Image tile/chunk bytes are immutable per object version → 7-day cache.
|
|
14
11
|
const IMAGE_DATA_CACHE_CONTROL = "private, max-age=604800";
|
|
@@ -20,9 +17,7 @@ const OTHER_DATA_CACHE_CONTROL = "private, max-age=3600";
|
|
|
20
17
|
// TIFF/OME-TIFF reads, or OME-Zarr chunks whose last path segment is digits
|
|
21
18
|
// only (`image.zarr/0/0/0` or `image.zarr/0.0.0`).
|
|
22
19
|
function isImageDataPath(pathname: string): boolean {
|
|
23
|
-
return (
|
|
24
|
-
/\.tiff?$/i.test(pathname) || /\/\d+(?:\.\d+)*$/.test(pathname)
|
|
25
|
-
);
|
|
20
|
+
return /\.tiff?$/i.test(pathname) || /\/\d+(?:\.\d+)*$/.test(pathname);
|
|
26
21
|
}
|
|
27
22
|
|
|
28
23
|
/**
|
|
@@ -43,9 +38,7 @@ export function createSignedFetch(
|
|
|
43
38
|
const credentials = getCredentials();
|
|
44
39
|
|
|
45
40
|
if (!credentials.AccessKeyId || !credentials.SecretAccessKey) {
|
|
46
|
-
throw new Error(
|
|
47
|
-
"Invalid credentials: AccessKeyId and SecretAccessKey are required",
|
|
48
|
-
);
|
|
41
|
+
throw new Error("Invalid credentials: AccessKeyId and SecretAccessKey are required");
|
|
49
42
|
}
|
|
50
43
|
|
|
51
44
|
if (credentials.AccessKeyId !== cachedKeyId) {
|
|
@@ -76,9 +69,7 @@ export function createSignedFetch(
|
|
|
76
69
|
// avoid CORS/signature mismatches. Caller-supplied headers run through
|
|
77
70
|
// sanitizeHeaders, and the merge order below puts signed headers LAST
|
|
78
71
|
// so a bypass of sanitizeHeaders still cannot override the signature.
|
|
79
|
-
const callerHeaders = sanitizeHeaders(
|
|
80
|
-
(init?.headers as Record<string, string> | undefined),
|
|
81
|
-
);
|
|
72
|
+
const callerHeaders = sanitizeHeaders(init?.headers as Record<string, string> | undefined);
|
|
82
73
|
|
|
83
74
|
const request = {
|
|
84
75
|
method: (init?.method as string) ?? "GET",
|
package/bin-src/codegen.ts
CHANGED
|
@@ -14,7 +14,10 @@ export class InvalidPluginNameError extends Error {
|
|
|
14
14
|
|
|
15
15
|
/** Validate + parse the CYTARIO_PLUGINS env var. Invalid entries throw. */
|
|
16
16
|
export function parseCytarioPluginsEnv(env: string | undefined): CodegenInput {
|
|
17
|
-
const raw = (env ?? "")
|
|
17
|
+
const raw = (env ?? "")
|
|
18
|
+
.split(",")
|
|
19
|
+
.map((s) => s.trim())
|
|
20
|
+
.filter(Boolean);
|
|
18
21
|
for (const name of raw) {
|
|
19
22
|
if (!NPM_NAME_RE.test(name)) {
|
|
20
23
|
throw new InvalidPluginNameError(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cytario/web",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.6",
|
|
4
4
|
"description": "Cytario Web — scientific imaging data browser and viewer for OME-TIFF, OME-Zarr, Parquet and GeoTIFF on S3-compatible storage.",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -60,6 +60,8 @@
|
|
|
60
60
|
"lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
|
|
61
61
|
"prestart": "npm run build:server",
|
|
62
62
|
"start": "node bin/cytario-web.mjs start",
|
|
63
|
+
"format:check": "prettier --check .",
|
|
64
|
+
"format:write": "prettier --write .",
|
|
63
65
|
"typecheck": "react-router typegen && tsc",
|
|
64
66
|
"test": "vitest",
|
|
65
67
|
"coverage": "vitest run --coverage"
|
|
@@ -151,6 +153,7 @@
|
|
|
151
153
|
"@vitest/coverage-v8": "^3.2.3",
|
|
152
154
|
"concurrently": "^9.2.1",
|
|
153
155
|
"eslint": ">=8.38.0",
|
|
156
|
+
"eslint-config-prettier": "^10.1.8",
|
|
154
157
|
"eslint-import-resolver-typescript": ">=3.6.1",
|
|
155
158
|
"eslint-plugin-import": ">=2.28.1",
|
|
156
159
|
"eslint-plugin-jsx-a11y": ">=6.7.1",
|
|
@@ -159,6 +162,7 @@
|
|
|
159
162
|
"happy-dom": ">=20.0.2",
|
|
160
163
|
"husky": ">=9.1.7",
|
|
161
164
|
"jsdom-testing-mocks": "^1.16.0",
|
|
165
|
+
"prettier": "^3.8.3",
|
|
162
166
|
"semantic-release": "^25.0.3",
|
|
163
167
|
"tsup": "^8.3.5",
|
|
164
168
|
"tsx": "^4.21.0",
|