@morscherlab/mint-sdk 1.2.2 → 1.2.4

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 (27) hide show
  1. package/dist/__tests__/composables/createFilePickerAdapter.test.d.ts +1 -0
  2. package/dist/__tests__/composables/usePickerNavigation.test.d.ts +1 -0
  3. package/dist/components/index.js +1 -1
  4. package/dist/{components-CxQVQCpP.js → components-CZzBCakZ.js} +38 -3
  5. package/dist/{components-CxQVQCpP.js.map → components-CZzBCakZ.js.map} +1 -1
  6. package/dist/composables/createFilePickerAdapter.d.ts +18 -0
  7. package/dist/composables/index.d.ts +2 -0
  8. package/dist/composables/index.js +2 -2
  9. package/dist/composables/usePlatformFilePickerAdapter.d.ts +3 -7
  10. package/dist/{composables-DAqOU8Aq.js → composables-BCbYb2lA.js} +125 -42
  11. package/dist/composables-BCbYb2lA.js.map +1 -0
  12. package/dist/index.js +3 -3
  13. package/dist/install.js +1 -1
  14. package/dist/types/filePicker.d.ts +2 -0
  15. package/package.json +1 -1
  16. package/src/__tests__/components/FilePicker.state.test.ts +19 -0
  17. package/src/__tests__/composables/createFilePickerAdapter.test.ts +142 -0
  18. package/src/__tests__/composables/useFileBrowser.test.ts +12 -0
  19. package/src/__tests__/composables/usePickerNavigation.test.ts +149 -0
  20. package/src/__tests__/composables/usePlatformFilePickerAdapter.test.ts +18 -0
  21. package/src/composables/createFilePickerAdapter.ts +145 -0
  22. package/src/composables/filePicker/usePickerNavigation.ts +42 -2
  23. package/src/composables/index.ts +2 -0
  24. package/src/composables/useFileBrowser.ts +6 -5
  25. package/src/composables/usePlatformFilePickerAdapter.ts +24 -91
  26. package/src/types/filePicker.ts +2 -0
  27. package/dist/composables-DAqOU8Aq.js.map +0 -1
@@ -0,0 +1,18 @@
1
+ import { PickerAdapter, PickerRequest } from '../types/filePicker';
2
+ import { FileBrowserLocation, FileDirectoryListing, ServerMount } from '../types/fileBrowserTypes';
3
+ export interface FilePickerTransport {
4
+ listMounts(request?: PickerRequest): Promise<ServerMount[]>;
5
+ browse(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryListing>;
6
+ }
7
+ export interface FilePickerAdapterOptions {
8
+ /** Show this directory's contents directly and prevent navigation outside it. */
9
+ rootLocation?: FileBrowserLocation;
10
+ }
11
+ /** Opaque picker identity; decode before passing a selection to a mount-scoped API. */
12
+ export declare function encodePlatformPickerPath(mountId: string, path?: string): string;
13
+ export declare function decodePlatformPickerPath(path: string): {
14
+ mountId: string;
15
+ path: string;
16
+ };
17
+ /** Mount browsing and search shared by platform and generated plugin clients. */
18
+ export declare function createFilePickerAdapter(transport: FilePickerTransport, options?: FilePickerAdapterOptions): PickerAdapter;
@@ -70,3 +70,5 @@ export { useRovingFocus, type UseRovingFocusOptions, type UseRovingFocusReturn,
70
70
  export { reorderList, useListReorder, useReorderAnnouncer, REORDER_ANNOUNCER_STYLE, type ListReorderMove, type UseListReorderOptions, type UseListReorderReturn, type UseReorderAnnouncerReturn, } from './useListReorder';
71
71
  export { useMenuKeyboard, type UseMenuKeyboardOptions, type UseMenuKeyboardReturn, } from './useMenuKeyboard';
72
72
  export { usePlatformFilePickerAdapter, encodePlatformPickerPath, decodePlatformPickerPath } from './usePlatformFilePickerAdapter';
73
+ export { createFilePickerAdapter } from './createFilePickerAdapter';
74
+ export type { FilePickerTransport, FilePickerAdapterOptions } from './createFilePickerAdapter';
@@ -7,5 +7,5 @@ import { i as usePlatformContext, n as evaluateCondition, r as useForm, t as use
7
7
  import { t as ANALYSIS_FILE_ARTIFACT_SCHEMA } from "../analysisArtifactTypes-DQ0MjwHZ.js";
8
8
  import { B as toBioTemplateComponentProps, H as toBioTemplateComponentPropsById, L as toBioTemplateComponentBindings, P as getBioTemplateComponentBindings, R as toBioTemplateComponentBindingsById, l as requireBioTemplateControlSchema, o as getBioTemplateControlSchema } from "../templates-NuaNHohp.js";
9
9
  import { n as useDebouncedWatch, t as useExperimentSelector } from "../useExperimentSelector-G_CAOFTX.js";
10
- import { A as buildAnalysisArtifactTargetQuery, B as resolvePluginFrontendBase, C as useWellPainting, D as useResourceCrud, E as createPluginResourceClient, F as resizedTrailingPanelWidth, H as useAuth, I as resizedVerticalSplitPercent, L as useManualLayoutResize, M as useFileBrowser, N as usePluginConfig, O as useOptimisticMutation, P as resizedLeadingPanelWidth, R as useAsync, S as coordinateToWellId, T as wellIdsInRectangle, V as usePasskey, _ as useWellPlateValidation, a as downloadBlob, b as createWellPlateWells, c as pluginFormDataFromPayload, d as usePluginClient, f as usePluginEventStream, g as runWellPlateValidation, h as resolvePluginBaseUrl, i as createPluginClient, j as parseAnalysisArtifactTarget, k as useCommandHistory, l as uploadPluginEndpoint, m as buildPluginEndpointUrl, n as encodePlatformPickerPath, o as downloadPluginEndpoint, p as usePluginSettings, r as usePlatformFilePickerAdapter, s as getPluginPageSelectorItems, t as decodePlatformPickerPath, u as useCurrentExperiment, v as createColumnConditions, w as wellIdToCoordinate, x as useWellPlateAdapter, y as createRowConditions, z as useAsyncBatch } from "../composables-DAqOU8Aq.js";
11
- export { ANALYSIS_FILE_ARTIFACT_SCHEMA, APP_EXPERIMENT_KEY, ATOMIC_WEIGHTS, AuthenticationRequiredError, DATE_PRESET_OPTIONS, DEFAULT_COLORS, DEFAULT_MOBILE_VIEWPORT_QUERY, DEFAULT_PRESETS, DEFAULT_UNITS, EXPERIMENT_STATUS_LABELS, EXPERIMENT_STATUS_OPTIONS, EXPERIMENT_STATUS_VARIANT_MAP, REORDER_ANNOUNCER_STYLE, SORT_OPTIONS, addMinutes, buildAnalysisArtifactTargetQuery, buildPluginEndpointUrl, candidateMatchesSearch, combinePluginJobSources, compareSortValues, compareTime, controlValuesToComponentBindings, controlValuesToComponentBindingsById, controlValuesToComponentProps, controlsToFormSchema, controlsToSectionFormSchema, controlsToSectionFormSchemas, controlsToSettingsSchema, controlsToSidebarPanels, controlsToTopBarSettingsConfig, controlsToViewIds, controlsToViewItems, coordinateToWellId, createColumnConditions, createGeneratedAnalysisTransport, createPluginClient, createPluginResourceClient, createRowConditions, createWellPlateWells, datePresetToISO, decodePlatformPickerPath, defineControlComponentBindings, defineControlModel, defineControls, defineDoseCalculatorControlProps, defineDoseDesignControlModel, defineWellPlateControlProps, defineWellPlateDoseComponentBindings, defineWellPlateDoseControlProps, detectDelimiter, downloadBlob, downloadPluginEndpoint, durationMinutes, encodePlatformPickerPath, evaluateCondition, experimentIdFromRoute, extractSampleNamesFromDesignData, extractSampleOptionsFromDesignData, extractSamplesFromDesignData, findAvailableSlots, findNearestTimeSlotIndex, formatDuration, formatExperimentDate, formatExperimentStatus, formatTime, formatTimeSlot, fromMinutes, generateDilutionSeries, generateTimeSlots, getBioTemplateComponentBindings, getBioTemplateComponentProps, getBioTemplateControlSchema, getControlDefaults, getDefaultControlView, getExperimentStatusVariant, getFieldRegistryEntry, getInjectedFrontendRevision, getPluginPageSelectorItems, getPresenceClientId, getTypeDefault, isTimeInRange, normalizeSearchQuery, parseAnalysisArtifactTarget, parseDelimitedText, parseTime, pluginFormDataFromPayload, rangesOverlap, readFileAsText, reorderList, requireBioTemplateControlSchema, resizedLeadingPanelWidth, resizedTrailingPanelWidth, resizedVerticalSplitPercent, resolveCurrentPluginId, resolveExperimentCode, resolvePluginBaseUrl, resolvePluginFrontendBase, runWellPlateValidation, snapToSlot, toBioTemplateComponentBindings, toBioTemplateComponentBindingsById, toBioTemplateComponentProps, toBioTemplateComponentPropsByComponent, toBioTemplateComponentPropsById, toMinutes, unwrapExperimentDesignData, uploadPluginEndpoint, useAnalysisArtifacts, useApi, useAppExperiment, useAsync, useAsyncBatch, useAuth, useAutoGroup, useBioTemplateComponents, useBioTemplateControls, useBioTemplatePackWorkspace, useBioTemplatePresetWorkspace, useBioTemplateWorkspace, useChemicalFormula, useCommandHistory, useConcentrationUnits, useControlSchema, useControlWorkspace, useCurrentExperiment, useDebouncedWatch, useDoseCalculator, useEventListener, useExpansionSet, useExperimentData, useExperimentSamples, useExperimentSave, useExperimentSelector, useFileBrowser, useFileImport, useFocusTrap, useForm, useFormBuilder, useGeneratedAnalysis, useGroupAssignment, useJobsStatusTray, useListReorder, useListSelection, useManualLayoutResize, useMenuKeyboard, useMobileSupportGate, useOptimisticMutation, usePasskey, usePlatformContext, usePlatformFilePickerAdapter, usePluginClient, usePluginConfig, usePluginEventStream, usePluginJobCenter, usePluginJobs, usePluginSettings, usePluginWorkspace, usePresenceHeartbeat, useProtocolTemplates, useRackEditor, useReagentSeries, useReorderAnnouncer, useRequestSyncState, useResourceCrud, useRovingFocus, useRuntimeAlignment, useSampleGroups, useScheduleDrag, useSelectionLimit, useSequenceUtils, useSortedItems, useTemplateCollection, useTextSearch, useTheme, useTimeUtils, useToast, useWellPainting, useWellPlateAdapter, useWellPlateEditor, useWellPlateValidation, validateImportFile, wellIdToCoordinate, wellIdsInRectangle };
10
+ import { A as useCommandHistory, B as useAsyncBatch, C as coordinateToWellId, D as createPluginResourceClient, E as wellIdsInRectangle, F as resizedLeadingPanelWidth, H as usePasskey, I as resizedTrailingPanelWidth, L as resizedVerticalSplitPercent, M as parseAnalysisArtifactTarget, N as useFileBrowser, O as useResourceCrud, P as usePluginConfig, R as useManualLayoutResize, S as useWellPlateAdapter, T as wellIdToCoordinate, U as useAuth, V as resolvePluginFrontendBase, _ as runWellPlateValidation, a as createPluginClient, b as createRowConditions, c as getPluginPageSelectorItems, d as useCurrentExperiment, f as usePluginClient, g as resolvePluginBaseUrl, h as buildPluginEndpointUrl, i as encodePlatformPickerPath, j as buildAnalysisArtifactTargetQuery, k as useOptimisticMutation, l as pluginFormDataFromPayload, m as usePluginSettings, n as createFilePickerAdapter, o as downloadBlob, p as usePluginEventStream, r as decodePlatformPickerPath, s as downloadPluginEndpoint, t as usePlatformFilePickerAdapter, u as uploadPluginEndpoint, v as useWellPlateValidation, w as useWellPainting, x as createWellPlateWells, y as createColumnConditions, z as useAsync } from "../composables-BCbYb2lA.js";
11
+ export { ANALYSIS_FILE_ARTIFACT_SCHEMA, APP_EXPERIMENT_KEY, ATOMIC_WEIGHTS, AuthenticationRequiredError, DATE_PRESET_OPTIONS, DEFAULT_COLORS, DEFAULT_MOBILE_VIEWPORT_QUERY, DEFAULT_PRESETS, DEFAULT_UNITS, EXPERIMENT_STATUS_LABELS, EXPERIMENT_STATUS_OPTIONS, EXPERIMENT_STATUS_VARIANT_MAP, REORDER_ANNOUNCER_STYLE, SORT_OPTIONS, addMinutes, buildAnalysisArtifactTargetQuery, buildPluginEndpointUrl, candidateMatchesSearch, combinePluginJobSources, compareSortValues, compareTime, controlValuesToComponentBindings, controlValuesToComponentBindingsById, controlValuesToComponentProps, controlsToFormSchema, controlsToSectionFormSchema, controlsToSectionFormSchemas, controlsToSettingsSchema, controlsToSidebarPanels, controlsToTopBarSettingsConfig, controlsToViewIds, controlsToViewItems, coordinateToWellId, createColumnConditions, createFilePickerAdapter, createGeneratedAnalysisTransport, createPluginClient, createPluginResourceClient, createRowConditions, createWellPlateWells, datePresetToISO, decodePlatformPickerPath, defineControlComponentBindings, defineControlModel, defineControls, defineDoseCalculatorControlProps, defineDoseDesignControlModel, defineWellPlateControlProps, defineWellPlateDoseComponentBindings, defineWellPlateDoseControlProps, detectDelimiter, downloadBlob, downloadPluginEndpoint, durationMinutes, encodePlatformPickerPath, evaluateCondition, experimentIdFromRoute, extractSampleNamesFromDesignData, extractSampleOptionsFromDesignData, extractSamplesFromDesignData, findAvailableSlots, findNearestTimeSlotIndex, formatDuration, formatExperimentDate, formatExperimentStatus, formatTime, formatTimeSlot, fromMinutes, generateDilutionSeries, generateTimeSlots, getBioTemplateComponentBindings, getBioTemplateComponentProps, getBioTemplateControlSchema, getControlDefaults, getDefaultControlView, getExperimentStatusVariant, getFieldRegistryEntry, getInjectedFrontendRevision, getPluginPageSelectorItems, getPresenceClientId, getTypeDefault, isTimeInRange, normalizeSearchQuery, parseAnalysisArtifactTarget, parseDelimitedText, parseTime, pluginFormDataFromPayload, rangesOverlap, readFileAsText, reorderList, requireBioTemplateControlSchema, resizedLeadingPanelWidth, resizedTrailingPanelWidth, resizedVerticalSplitPercent, resolveCurrentPluginId, resolveExperimentCode, resolvePluginBaseUrl, resolvePluginFrontendBase, runWellPlateValidation, snapToSlot, toBioTemplateComponentBindings, toBioTemplateComponentBindingsById, toBioTemplateComponentProps, toBioTemplateComponentPropsByComponent, toBioTemplateComponentPropsById, toMinutes, unwrapExperimentDesignData, uploadPluginEndpoint, useAnalysisArtifacts, useApi, useAppExperiment, useAsync, useAsyncBatch, useAuth, useAutoGroup, useBioTemplateComponents, useBioTemplateControls, useBioTemplatePackWorkspace, useBioTemplatePresetWorkspace, useBioTemplateWorkspace, useChemicalFormula, useCommandHistory, useConcentrationUnits, useControlSchema, useControlWorkspace, useCurrentExperiment, useDebouncedWatch, useDoseCalculator, useEventListener, useExpansionSet, useExperimentData, useExperimentSamples, useExperimentSave, useExperimentSelector, useFileBrowser, useFileImport, useFocusTrap, useForm, useFormBuilder, useGeneratedAnalysis, useGroupAssignment, useJobsStatusTray, useListReorder, useListSelection, useManualLayoutResize, useMenuKeyboard, useMobileSupportGate, useOptimisticMutation, usePasskey, usePlatformContext, usePlatformFilePickerAdapter, usePluginClient, usePluginConfig, usePluginEventStream, usePluginJobCenter, usePluginJobs, usePluginSettings, usePluginWorkspace, usePresenceHeartbeat, useProtocolTemplates, useRackEditor, useReagentSeries, useReorderAnnouncer, useRequestSyncState, useResourceCrud, useRovingFocus, useRuntimeAlignment, useSampleGroups, useScheduleDrag, useSelectionLimit, useSequenceUtils, useSortedItems, useTemplateCollection, useTextSearch, useTheme, useTimeUtils, useToast, useWellPainting, useWellPlateAdapter, useWellPlateEditor, useWellPlateValidation, validateImportFile, wellIdToCoordinate, wellIdsInRectangle };
@@ -1,11 +1,7 @@
1
1
  import { PickerAdapter } from '../types/filePicker';
2
- /** Opaque picker identity; decode before passing a selection to a mount-scoped API. */
3
- export declare function encodePlatformPickerPath(mountId: string, path?: string): string;
4
- export declare function decodePlatformPickerPath(path: string): {
5
- mountId: string;
6
- path: string;
7
- };
2
+ import { FilePickerAdapterOptions } from './createFilePickerAdapter';
3
+ export { encodePlatformPickerPath, decodePlatformPickerPath } from './createFilePickerAdapter';
8
4
  /** Adapt the authenticated MINT mount API without exposing absolute host paths. */
9
- export declare function usePlatformFilePickerAdapter(options?: {
5
+ export declare function usePlatformFilePickerAdapter(options?: FilePickerAdapterOptions & {
10
6
  apiBaseUrl?: string;
11
7
  }): PickerAdapter;
@@ -924,7 +924,7 @@ function useFileBrowser(options = {}) {
924
924
  if (token === mountsToken) mountsLoading.value = false;
925
925
  }
926
926
  }
927
- async function navigate(location) {
927
+ async function navigate(location, refresh = false) {
928
928
  if (disposed || !location.mount_id) return;
929
929
  clearTimeout(searchTimer);
930
930
  const token = ++listToken;
@@ -936,7 +936,7 @@ function useFileBrowser(options = {}) {
936
936
  resetListing();
937
937
  sync.clearError();
938
938
  try {
939
- const query = `mount_id=${encodeURIComponent(location.mount_id)}&path=${encodeURIComponent(location.path)}&sort=${sort.value}` + (search.value ? `&search=${encodeURIComponent(search.value)}` : "") + typeRuleParams();
939
+ const query = `mount_id=${encodeURIComponent(location.mount_id)}&path=${encodeURIComponent(location.path)}&sort=${sort.value}` + (refresh ? "&refresh=true" : "") + (search.value ? `&search=${encodeURIComponent(search.value)}` : "") + typeRuleParams();
940
940
  const listing = await api.get(`/filesystem/browse?${query}`, { signal: listingController.signal });
941
941
  if (disposed || token !== listToken) return;
942
942
  mountId.value = listing.mount_id;
@@ -981,7 +981,7 @@ function useFileBrowser(options = {}) {
981
981
  if (mountId.value) await navigate({
982
982
  mount_id: mountId.value,
983
983
  path: path.value
984
- });
984
+ }, true);
985
985
  else await init();
986
986
  }
987
987
  function selectCurrentFolder() {
@@ -1004,14 +1004,23 @@ function useFileBrowser(options = {}) {
1004
1004
  if (!mountId.value) return;
1005
1005
  clearTimeout(searchTimer);
1006
1006
  searchTimer = setTimeout(() => {
1007
- refresh();
1007
+ navigate({
1008
+ mount_id: mountId.value,
1009
+ path: path.value
1010
+ });
1008
1011
  }, 250);
1009
1012
  });
1010
1013
  watch(sort, () => {
1011
- if (mountId.value) refresh();
1014
+ if (mountId.value) navigate({
1015
+ mount_id: mountId.value,
1016
+ path: path.value
1017
+ });
1012
1018
  });
1013
1019
  watch(() => toValue(options.typeRules), () => {
1014
- if (mountId.value) refresh();
1020
+ if (mountId.value) navigate({
1021
+ mount_id: mountId.value,
1022
+ path: path.value
1023
+ });
1015
1024
  }, { deep: true });
1016
1025
  if (getCurrentScope()) onScopeDispose(() => {
1017
1026
  disposed = true;
@@ -2189,7 +2198,7 @@ function useCurrentExperiment(options = {}) {
2189
2198
  };
2190
2199
  }
2191
2200
  //#endregion
2192
- //#region src/composables/usePlatformFilePickerAdapter.ts
2201
+ //#region src/composables/createFilePickerAdapter.ts
2193
2202
  /** Opaque picker identity; decode before passing a selection to a mount-scoped API. */
2194
2203
  function encodePlatformPickerPath(mountId, path = "") {
2195
2204
  return JSON.stringify([mountId, path]);
@@ -2202,14 +2211,49 @@ function decodePlatformPickerPath(path) {
2202
2211
  path: decoded[1]
2203
2212
  };
2204
2213
  }
2205
- /** Adapt the authenticated MINT mount API without exposing absolute host paths. */
2206
- function usePlatformFilePickerAdapter(options = {}) {
2207
- const api = useApi({ baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl });
2214
+ function isRelativePath(path) {
2215
+ return path === "" || !/^[a-z]:/i.test(path) && !/[\\\x00-\x1f\x7f]/.test(path) && path.split("/").every((part) => part !== "" && part !== "." && part !== "..");
2216
+ }
2217
+ /** Mount browsing and search shared by platform and generated plugin clients. */
2218
+ function createFilePickerAdapter(transport, options = {}) {
2219
+ const root = options.rootLocation && { ...options.rootLocation };
2208
2220
  const mountLabels = /* @__PURE__ */ new Map();
2209
- async function listRoot(request) {
2210
- try {
2211
- const { mounts } = await api.get("/filesystem/mounts", { signal: request?.signal });
2212
- return mounts.map((mount) => {
2221
+ const listings = /* @__PURE__ */ new Map();
2222
+ let generation = 0;
2223
+ function invalidate() {
2224
+ generation++;
2225
+ listings.clear();
2226
+ mountLabels.clear();
2227
+ }
2228
+ function validateLocation(location) {
2229
+ if (!location.mount_id || !isRelativePath(location.path) || root && (location.mount_id !== root.mount_id || root.path !== "" && location.path !== root.path && !location.path.startsWith(`${root.path}/`))) throw new Error("Invalid file picker location.");
2230
+ }
2231
+ if (root) validateLocation(root);
2232
+ function begin(request) {
2233
+ request?.signal?.throwIfAborted();
2234
+ if (request?.refresh) invalidate();
2235
+ return generation;
2236
+ }
2237
+ function check(request, token) {
2238
+ request?.signal?.throwIfAborted();
2239
+ if (token !== generation) throw new DOMException("Cancelled", "AbortError");
2240
+ }
2241
+ async function read(path, request, token) {
2242
+ check(request, token);
2243
+ const decoded = path ? decodePlatformPickerPath(path) : void 0;
2244
+ const location = decoded ? {
2245
+ mount_id: decoded.mountId,
2246
+ path: decoded.path
2247
+ } : root;
2248
+ if (location) validateLocation(location);
2249
+ const key = location ? encodePlatformPickerPath(location.mount_id, location.path) : "";
2250
+ const cached = listings.get(key);
2251
+ if (cached) return cached;
2252
+ let nodes;
2253
+ if (!location) {
2254
+ const mounts = await transport.listMounts(request);
2255
+ check(request, token);
2256
+ nodes = mounts.map((mount) => {
2213
2257
  mountLabels.set(mount.id, mount.label);
2214
2258
  return {
2215
2259
  kind: "folder",
@@ -2220,45 +2264,58 @@ function usePlatformFilePickerAdapter(options = {}) {
2220
2264
  unavailableReason: mount.available === false ? "This mount is offline." : void 0
2221
2265
  };
2222
2266
  });
2223
- } catch (cause) {
2224
- if (cause.response?.status === 404) return [];
2225
- throw cause;
2267
+ } else {
2268
+ const listing = await transport.browse(location, request);
2269
+ check(request, token);
2270
+ if (listing.truncated) throw new Error("This directory exceeds the server listing limit. Choose a smaller data directory.");
2271
+ const atRoot = root && location.mount_id === root.mount_id && location.path === root.path;
2272
+ nodes = (listing.entries ?? []).map((entry) => {
2273
+ if (!isRelativePath(entry.path) || !entry.name || entry.path !== `${location.path ? `${location.path}/` : ""}${entry.name}` || entry.name.includes("/")) throw new Error("Invalid file picker directory entry.");
2274
+ const common = {
2275
+ path: encodePlatformPickerPath(location.mount_id, entry.path),
2276
+ parentPath: atRoot ? null : key,
2277
+ parentName: `${mountLabels.get(location.mount_id) ?? location.mount_id}${location.path ? ` / ${location.path}` : ""}`,
2278
+ name: entry.name,
2279
+ bytes: entry.size_bytes
2280
+ };
2281
+ return entry.kind === "directory" ? {
2282
+ ...common,
2283
+ kind: "folder"
2284
+ } : {
2285
+ ...common,
2286
+ kind: "file",
2287
+ format: pickerFormat(entry.name)
2288
+ };
2289
+ });
2226
2290
  }
2227
- }
2228
- async function listChildren(path, request) {
2229
- const location = decodePlatformPickerPath(path);
2230
- const query = new URLSearchParams({
2231
- mount_id: location.mountId,
2232
- path: location.path
2233
- });
2234
- const listing = await api.get(`/filesystem/browse?${query}`, { signal: request?.signal });
2235
- if (listing.truncated) throw new Error("This directory exceeds the server listing limit. Choose a smaller data directory.");
2236
- return (listing.entries ?? []).map((entry) => ({
2237
- kind: entry.kind === "directory" ? "folder" : "file",
2238
- path: encodePlatformPickerPath(location.mountId, entry.path),
2239
- parentPath: path,
2240
- parentName: `${mountLabels.get(location.mountId) ?? location.mountId}${location.path ? ` / ${location.path}` : ""}`,
2241
- name: entry.name,
2242
- bytes: entry.size_bytes,
2243
- ...entry.kind === "directory" ? { hasChildren: entry.child_count !== 0 } : { format: pickerFormat(entry.name) }
2244
- }));
2291
+ listings.set(key, nodes);
2292
+ return nodes;
2245
2293
  }
2246
2294
  return {
2247
- listRoot,
2248
- listChildren,
2295
+ invalidate,
2296
+ listRoot: async (request) => read("", request, begin(request)),
2297
+ listChildren: async (path, request) => read(path, request, begin(request)),
2249
2298
  async search(query, scope, request) {
2299
+ const token = begin(request);
2250
2300
  const matches = [];
2251
2301
  const queue = [scope.path];
2252
2302
  const visited = /* @__PURE__ */ new Set();
2303
+ const needle = query.toLowerCase();
2304
+ let currentRequest = request;
2253
2305
  while (queue.length) {
2254
- request?.signal?.throwIfAborted();
2306
+ check(request, token);
2255
2307
  const path = queue.shift();
2256
2308
  if (visited.has(path)) continue;
2257
2309
  visited.add(path);
2258
2310
  if (visited.size > 500) throw new Error("Search reached its folder limit. Search a smaller location or turn off Subfolders.");
2259
- const nodes = path ? await listChildren(path, request) : await listRoot(request);
2311
+ const nodes = await read(path, currentRequest, token);
2312
+ check(request, token);
2313
+ if (currentRequest?.refresh) {
2314
+ currentRequest = { ...currentRequest };
2315
+ delete currentRequest.refresh;
2316
+ }
2260
2317
  for (const node of nodes) {
2261
- if (node.name.toLowerCase().includes(query.toLowerCase())) matches.push(node);
2318
+ if (node.name.toLowerCase().includes(needle)) matches.push(node);
2262
2319
  if (scope.recursive && node.kind === "folder" && !node.unavailableReason) queue.push(node.path);
2263
2320
  }
2264
2321
  if (matches.length > 1e4) throw new Error("Too many search results. Use a more specific search.");
@@ -2268,6 +2325,32 @@ function usePlatformFilePickerAdapter(options = {}) {
2268
2325
  };
2269
2326
  }
2270
2327
  //#endregion
2271
- export { buildAnalysisArtifactTargetQuery as A, resolvePluginFrontendBase as B, useWellPainting as C, useResourceCrud as D, createPluginResourceClient as E, resizedTrailingPanelWidth as F, useAuth as H, resizedVerticalSplitPercent as I, useManualLayoutResize as L, useFileBrowser as M, usePluginConfig as N, useOptimisticMutation as O, resizedLeadingPanelWidth as P, useAsync as R, coordinateToWellId as S, wellIdsInRectangle as T, usePasskey as V, useWellPlateValidation as _, downloadBlob as a, createWellPlateWells as b, pluginFormDataFromPayload as c, usePluginClient as d, usePluginEventStream as f, runWellPlateValidation as g, resolvePluginBaseUrl as h, createPluginClient as i, parseAnalysisArtifactTarget as j, useCommandHistory as k, uploadPluginEndpoint as l, buildPluginEndpointUrl as m, encodePlatformPickerPath as n, downloadPluginEndpoint as o, usePluginSettings as p, usePlatformFilePickerAdapter as r, getPluginPageSelectorItems as s, decodePlatformPickerPath as t, useCurrentExperiment as u, createColumnConditions as v, wellIdToCoordinate as w, useWellPlateAdapter as x, createRowConditions as y, useAsyncBatch as z };
2328
+ //#region src/composables/usePlatformFilePickerAdapter.ts
2329
+ /** Adapt the authenticated MINT mount API without exposing absolute host paths. */
2330
+ function usePlatformFilePickerAdapter(options = {}) {
2331
+ const api = useApi({ baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl });
2332
+ return createFilePickerAdapter({
2333
+ async listMounts(request) {
2334
+ try {
2335
+ const url = `/filesystem/mounts${request?.refresh ? "?refresh=true" : ""}`;
2336
+ const { mounts } = await api.get(url, { signal: request?.signal });
2337
+ return mounts;
2338
+ } catch (cause) {
2339
+ if (cause.response?.status === 404) return [];
2340
+ throw cause;
2341
+ }
2342
+ },
2343
+ browse(location, request) {
2344
+ const query = new URLSearchParams({
2345
+ mount_id: location.mount_id,
2346
+ path: location.path
2347
+ });
2348
+ if (request?.refresh) query.set("refresh", "true");
2349
+ return api.get(`/filesystem/browse?${query}`, { signal: request?.signal });
2350
+ }
2351
+ }, options);
2352
+ }
2353
+ //#endregion
2354
+ export { useCommandHistory as A, useAsyncBatch as B, coordinateToWellId as C, createPluginResourceClient as D, wellIdsInRectangle as E, resizedLeadingPanelWidth as F, usePasskey as H, resizedTrailingPanelWidth as I, resizedVerticalSplitPercent as L, parseAnalysisArtifactTarget as M, useFileBrowser as N, useResourceCrud as O, usePluginConfig as P, useManualLayoutResize as R, useWellPlateAdapter as S, wellIdToCoordinate as T, useAuth as U, resolvePluginFrontendBase as V, runWellPlateValidation as _, createPluginClient as a, createRowConditions as b, getPluginPageSelectorItems as c, useCurrentExperiment as d, usePluginClient as f, resolvePluginBaseUrl as g, buildPluginEndpointUrl as h, encodePlatformPickerPath as i, buildAnalysisArtifactTargetQuery as j, useOptimisticMutation as k, pluginFormDataFromPayload as l, usePluginSettings as m, createFilePickerAdapter as n, downloadBlob as o, usePluginEventStream as p, decodePlatformPickerPath as r, downloadPluginEndpoint as s, usePlatformFilePickerAdapter as t, uploadPluginEndpoint as u, useWellPlateValidation as v, useWellPainting as w, createWellPlateWells as x, createColumnConditions as y, useAsync as z };
2272
2355
 
2273
- //# sourceMappingURL=composables-DAqOU8Aq.js.map
2356
+ //# sourceMappingURL=composables-BCbYb2lA.js.map