@morscherlab/mint-sdk 1.3.0-beta.9 → 1.3.1

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 (70) hide show
  1. package/dist/__tests__/components/AutoGroupModal.anatomy.test.d.ts +1 -0
  2. package/dist/__tests__/components/AutoGroupModal.wiring.test.d.ts +1 -0
  3. package/dist/__tests__/composables/autoGroup/fieldEdits.test.d.ts +1 -0
  4. package/dist/__tests__/composables/autoGroup/golden.test.d.ts +1 -0
  5. package/dist/components/AutoGroupModal.adapter.d.ts +27 -4
  6. package/dist/components/SmartGroup.types.d.ts +22 -0
  7. package/dist/components/SmartGroupFieldRecipe.groups.d.ts +10 -2
  8. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +3 -0
  9. package/dist/components/index.js +1 -1
  10. package/dist/{components-GDDvof8D.js → components-bbanLd1u.js} +510 -287
  11. package/dist/components-bbanLd1u.js.map +1 -0
  12. package/dist/composables/autoGroup/classify.d.ts +7 -1
  13. package/dist/composables/autoGroup/columns.d.ts +16 -1
  14. package/dist/composables/autoGroup/index.d.ts +5 -7
  15. package/dist/composables/autoGroup/tokenize.d.ts +36 -1
  16. package/dist/composables/index.js +2 -2
  17. package/dist/composables/useAutoGroup.d.ts +13 -2
  18. package/dist/{composables-D6uqp0UF.js → composables-BbHozlA7.js} +2 -2
  19. package/dist/{composables-D6uqp0UF.js.map → composables-BbHozlA7.js.map} +1 -1
  20. package/dist/index.js +3 -3
  21. package/dist/install.js +1 -1
  22. package/dist/permissions.js +2 -2
  23. package/dist/permissions.js.map +1 -1
  24. package/dist/styles.css +129 -96
  25. package/dist/types/auto-group.d.ts +1 -1
  26. package/dist/{validation-Dmp9Q5W0.js → validation-CukfLjy-.js} +323 -187
  27. package/dist/validation-CukfLjy-.js.map +1 -0
  28. package/package.json +2 -2
  29. package/src/__tests__/components/AppTopBarPageSelector.test.ts +4 -4
  30. package/src/__tests__/components/AutoGroupModal.anatomy.test.ts +86 -0
  31. package/src/__tests__/components/AutoGroupModal.wiring.test.ts +53 -0
  32. package/src/__tests__/components/SampleSelector.test.ts +14 -16
  33. package/src/__tests__/components/SmartGroupFieldRecipe.groups.test.ts +23 -0
  34. package/src/__tests__/components/SmartGroupFieldRecipe.test.ts +176 -0
  35. package/src/__tests__/composables/autoGroup/columns.test.ts +100 -11
  36. package/src/__tests__/composables/autoGroup/fieldEdits.test.ts +74 -0
  37. package/src/__tests__/composables/autoGroup/fingerprint.test.ts +9 -0
  38. package/src/__tests__/composables/autoGroup/golden.test.ts +187 -0
  39. package/src/__tests__/composables/autoGroup/groupByOrder.test.ts +7 -7
  40. package/src/__tests__/composables/autoGroup/tokenLength.test.ts +7 -0
  41. package/src/__tests__/composables/autoGroup/tokenize.test.ts +78 -1
  42. package/src/__tests__/composables/useAutoGroup.test.ts +2 -1
  43. package/src/__tests__/fixtures/auto-group/share-exp021.txt +103 -0
  44. package/src/__tests__/fixtures/auto-group/share-exp031.txt +39 -0
  45. package/src/__tests__/fixtures/auto-group/share-exp114.txt +123 -0
  46. package/src/__tests__/fixtures/auto-group/share-exp214.txt +63 -0
  47. package/src/__tests__/utils/permissions.test.ts +15 -0
  48. package/src/components/AutoGroupModal.adapter.ts +80 -9
  49. package/src/components/AutoGroupModal.vue +41 -8
  50. package/src/components/SmartGroup.types.ts +23 -0
  51. package/src/components/SmartGroupFieldRecipe.groups.ts +14 -2
  52. package/src/components/SmartGroupFieldRecipe.vue +289 -169
  53. package/src/components/internal/AppTopBarPageSelectorInternal.vue +3 -6
  54. package/src/composables/autoGroup/classify.ts +26 -7
  55. package/src/composables/autoGroup/columns.ts +144 -36
  56. package/src/composables/autoGroup/compose.ts +8 -10
  57. package/src/composables/autoGroup/fingerprint.ts +1 -1
  58. package/src/composables/autoGroup/index.ts +5 -12
  59. package/src/composables/autoGroup/tokenLength.ts +8 -0
  60. package/src/composables/autoGroup/tokenize.ts +148 -1
  61. package/src/composables/autoGroup/vocab.json +5 -11
  62. package/src/composables/useAutoGroup.ts +92 -79
  63. package/src/permissions.ts +3 -2
  64. package/src/styles/components/app-page-selector.css +58 -14
  65. package/src/styles/components/smart-group.css +71 -82
  66. package/src/types/auto-group.ts +1 -0
  67. package/dist/components-GDDvof8D.js.map +0 -1
  68. package/dist/composables/autoGroup/replicatePreGroup.d.ts +0 -36
  69. package/dist/validation-Dmp9Q5W0.js.map +0 -1
  70. package/src/composables/autoGroup/replicatePreGroup.ts +0 -124
@@ -1,4 +1,4 @@
1
- import { SampleClass } from '../../types/auto-group';
1
+ import { SampleClass, SampleClassKind } from '../../types/auto-group';
2
2
  export interface ClassVocab {
3
3
  blankPattern: RegExp;
4
4
  iqcPattern: RegExp;
@@ -23,6 +23,12 @@ export interface DetectClassOptions {
23
23
  vocab?: Partial<ClassVocab>;
24
24
  /** Per-sample explicit `sample_type` hint (e.g. from experiment design data). */
25
25
  sampleTypeHints?: (string | undefined)[];
26
+ /** Number of leading tokens that all samples share (`TokenizedBatch.sharedPrefix`). */
27
+ sharedPrefix?: number;
26
28
  }
29
+ /** A token that names a QC-type class (blank, QC, standard, spike-in, stock). */
30
+ export declare function isClassMarker(token: string, vocab?: ClassVocab): boolean;
31
+ /** Kinds that form experimental groups; all other kinds are QC-type overlay classes. */
32
+ export declare function isExperimentalKind(kind: SampleClassKind): boolean;
27
33
  /** Classify each tokenized sample into a SampleClass. */
28
34
  export declare function detectClass(tokenizedSamples: string[][], options: DetectClassOptions): SampleClass[];
@@ -1,10 +1,25 @@
1
1
  import { ColumnRole, ClassSchema, NumericParse, SampleClassKind } from '../../types/auto-group';
2
+ /** Roles the Field Recipe offers as grouping fields. `replicate` and `id` are off by default. */
3
+ export declare const GROUPING_FIELD_ROLES: ReadonlySet<ColumnRole | undefined>;
2
4
  export declare function parseNumeric(token: string): NumericParse | null;
3
5
  export interface InferRoleInput {
4
6
  values: string[];
5
7
  position: number;
6
8
  totalPositions: number;
7
9
  classSize: number;
10
+ /** The column ends a name or a split token, where replicate counters sit. Defaults to the last position. */
11
+ tail?: boolean;
8
12
  }
9
13
  export declare function inferRole(input: InferRoleInput): ColumnRole;
10
- export declare function buildClassSchema(tokenizedMembers: string[][], classKind: SampleClassKind, subKind: string | undefined, classTagPositions: number[]): ClassSchema;
14
+ export interface ColumnSplit {
15
+ rows: string[][];
16
+ /** `origin[newIndex]` = index of the source column before the split. */
17
+ origin: number[];
18
+ /** New indices that end a split token. */
19
+ tail: number[];
20
+ }
21
+ /** Split the columns of one class (rows of equal length) that hold two fields in one token. */
22
+ export declare function splitColumns(rows: string[][]): ColumnSplit;
23
+ export declare function buildClassSchema(tokenizedMembers: string[][], classKind: SampleClassKind, subKind: string | undefined, classTagPositions: number[],
24
+ /** Positions that end a split token (from `splitColumns`). */
25
+ tailPositions?: number[]): ClassSchema;
@@ -1,9 +1,9 @@
1
- export { splitMulti, pickPrimaryDelimiter } from './tokenize';
2
- export type { Delimiter } from './tokenize';
3
- export { detectClass, classVocab } from './classify';
1
+ export { splitMulti, pickPrimaryDelimiter, tokenizeBatch } from './tokenize';
2
+ export type { Delimiter, ExperimentHeader, TokenizedBatch } from './tokenize';
3
+ export { detectClass, classVocab, isExperimentalKind } from './classify';
4
4
  export type { ClassVocab, DetectClassOptions } from './classify';
5
- export { inferRole, parseNumeric, buildClassSchema } from './columns';
6
- export type { InferRoleInput } from './columns';
5
+ export { inferRole, parseNumeric, buildClassSchema, splitColumns, GROUPING_FIELD_ROLES } from './columns';
6
+ export type { InferRoleInput, ColumnSplit } from './columns';
7
7
  export { findMerges } from './cooccurrence';
8
8
  export { composeGroups } from './compose';
9
9
  export type { ComposeInput } from './compose';
@@ -12,6 +12,4 @@ export { composeTemplate } from './template';
12
12
  export type { TemplateOptions } from './template';
13
13
  export { DEFAULT_COLORS, QC_OVERLAY_COLOR } from './colors';
14
14
  export { classKey } from './classKey';
15
- export { stripReplicateTokens, preGroupReplicates, expandGroupsWithReplicates, expandTreeWithReplicates, } from './replicatePreGroup';
16
- export type { ReplicatePreGrouping } from './replicatePreGroup';
17
15
  export { splitByTokenLength } from './tokenLength';
@@ -1,8 +1,43 @@
1
- declare const DELIMITER_CANDIDATES: readonly ["_", "-", "."];
1
+ declare const DELIMITER_CANDIDATES: readonly ["_", "-", " ", "."];
2
2
  export type Delimiter = (typeof DELIMITER_CANDIDATES)[number];
3
3
  /** Pick the delimiter whose split produces the most consistent field count across `lines`. */
4
4
  export declare function pickPrimaryDelimiter(lines: string[]): Delimiter;
5
5
  /** Split a single sample name on `delimiter`. Empty input returns `['']` so callers
6
6
  * always receive at least one token. */
7
7
  export declare function splitMulti(name: string, delimiter: Delimiter): string[];
8
+ /** The `exp<NNN>_<date>_<operator>` fields at the start of a sample name. */
9
+ export interface ExperimentHeader {
10
+ code?: string;
11
+ /** ISO date. Names carry it as YYMMDD, YYYYMMDD or DDMMYYYY. */
12
+ date?: string;
13
+ operator?: string;
14
+ }
15
+ export interface TokenizedBatch {
16
+ delimiter: Delimiter;
17
+ /** One row of tokens for each input name, in input order. */
18
+ rows: string[][];
19
+ /** Header fields with one value in the batch. They are not in `rows`. */
20
+ experiment: ExperimentHeader;
21
+ /**
22
+ * Number of leading tokens that all rows share (normally the experiment
23
+ * title). They cannot tell a QC sample from a study sample.
24
+ */
25
+ sharedPrefix: number;
26
+ }
27
+ /** Read a 6- or 8-digit date token; year-first is tried before day-first. */
28
+ export declare function parseDateToken(token: string): string | undefined;
29
+ /**
30
+ * Sample names → token rows.
31
+ *
32
+ * 1. Trim; drop a file extension that all names share.
33
+ * 2. Split on runs of the primary delimiter.
34
+ * 3. Drop a trailing 14-digit acquisition stamp.
35
+ * 4. Read the experiment header. A field moves to `experiment` when the batch
36
+ * has one value for it. Any other field stays in the rows as written, so
37
+ * `QC` or `WT` after the date in some names is not taken for operator
38
+ * initials.
39
+ * 5. Count the tokens that all names share after the header (the experiment
40
+ * title), so a title word such as `std` cannot classify the batch as QC.
41
+ */
42
+ export declare function tokenizeBatch(names: string[]): TokenizedBatch;
8
43
  export {};
@@ -1,7 +1,7 @@
1
1
  import { a as SORT_OPTIONS, c as formatExperimentDate, d as resolveExperimentCode, i as EXPERIMENT_STATUS_VARIANT_MAP, l as formatExperimentStatus, m as useEventListener, n as EXPERIMENT_STATUS_LABELS, o as datePresetToISO, p as useFocusTrap, r as EXPERIMENT_STATUS_OPTIONS, s as experimentIdFromRoute, t as DATE_PRESET_OPTIONS, u as getExperimentStatusVariant } from "../experiment-utils-DTANyVKV.js";
2
2
  import { $r as snapToSlot, An as getControlDefaults, B as toBioTemplateComponentProps, Bn as getDefaultControlView, Br as addMinutes, Fn as controlsToSettingsSchema, Gn as getFieldRegistryEntry, Gr as formatDuration, H as toBioTemplateComponentPropsById, Hr as durationMinutes, In as controlsToSidebarPanels, Jr as fromMinutes, Kn as getTypeDefault, Kr as formatTime, L as toBioTemplateComponentBindings, Ln as controlsToTopBarSettingsConfig, Mn as controlsToFormSchema, Nn as controlsToSectionFormSchema, On as defineControlComponentBindings, P as getBioTemplateComponentBindings, Pn as controlsToSectionFormSchemas, Qn as useSequenceUtils, Qr as rangesOverlap, R as toBioTemplateComponentBindingsById, Rn as controlsToViewIds, Ur as findAvailableSlots, Vr as compareTime, Wr as findNearestTimeSlotIndex, Xr as isTimeInRange, Yn as useConcentrationUnits, Yr as generateTimeSlots, Zr as parseTime, ai as useListSelection, cr as resolveCurrentPluginId, dr as usePluginJobs, ei as toMinutes, er as ATOMIC_WEIGHTS, jn as useControlSchema, kn as defineControls, l as requireBioTemplateControlSchema, lr as createGeneratedAnalysisTransport, o as getBioTemplateControlSchema, oi as useSelectionLimit, qr as formatTimeSlot, ti as useTimeUtils, tr as useChemicalFormula, ur as useGeneratedAnalysis, xr as useMenuKeyboard, zn as controlsToViewItems } from "../templates-BU1pet28.js";
3
- import { $ as pluginFormDataFromPayload, A as parseDelimitedText, At as getPresenceClientId, B as useRackEditor, Bt as useRovingFocus, C as useExperimentSamples, Ct as controlValuesToComponentBindingsById, Dt as useAppExperiment, E as useSampleGroups, Et as APP_EXPERIMENT_KEY, F as useAutoGroup, Ft as candidateMatchesSearch, G as useDoseCalculator, H as reorderList, I as extractSampleNamesFromDesignData, It as normalizeSearchQuery, J as usePluginJobCenter, K as combinePluginJobSources, L as extractSampleOptionsFromDesignData, Lt as useTextSearch, M as useFileImport, Mt as usePlatformContext, N as validateImportFile, Nt as useTheme, Ot as getInjectedFrontendRevision, P as extractSamplesFromDesignData, Pt as useToast, Q as getPluginPageSelectorItems, R as unwrapExperimentDesignData, Rt as compareSortValues, S as useScheduleDrag, St as controlValuesToComponentBindings, T as useExpansionSet, U as useListReorder, V as REORDER_ANNOUNCER_STYLE, W as useReorderAnnouncer, X as downloadBlob, Y as createPluginClient, Z as downloadPluginEndpoint, _ as toBioTemplateComponentPropsByComponent, _t as defineWellPlateDoseComponentBindings, a as useExperimentSelector, at as buildPluginEndpointUrl, b as useTemplateCollection, bt as defineControlModel, c as DEFAULT_PRESETS, ct as usePluginWorkspace, d as useReagentSeries, dt as useFormBuilder, et as uploadPluginEndpoint, f as useGroupAssignment, ft as evaluateCondition, g as getBioTemplateComponentProps, gt as defineWellPlateControlProps, h as useBioTemplateWorkspace, ht as defineDoseDesignControlModel, i as useAnalysisArtifacts, it as usePluginSettings, j as readFileAsText, jt as usePresenceHeartbeat, k as detectDelimiter, kt as useRuntimeAlignment, l as DEFAULT_UNITS, lt as DEFAULT_MOBILE_VIEWPORT_QUERY, m as useBioTemplatePackWorkspace, mt as defineDoseCalculatorControlProps, nt as usePluginClient, o as useDebouncedWatch, ot as resolvePluginBaseUrl, p as useBioTemplatePresetWorkspace, pt as useForm, q as useJobsStatusTray, rt as usePluginEventStream, s as useProtocolTemplates, st as useRequestSyncState, tt as useCurrentExperiment, u as generateDilutionSeries, ut as useMobileSupportGate, v as useBioTemplateComponents, vt as defineWellPlateDoseControlProps, w as useExperimentData, wt as controlValuesToComponentProps, x as useExperimentSave, y as useBioTemplateControls, yt as useControlWorkspace, z as DEFAULT_COLORS, zt as useSortedItems } from "../validation-Dmp9Q5W0.js";
3
+ import { $ as downloadBlob, A as parseDelimitedText, At as useAppExperiment, Bt as useTextSearch, C as useExperimentSamples, Ct as defineControlModel, Dt as controlValuesToComponentProps, E as useSampleGroups, Et as controlValuesToComponentBindingsById, F as useAutoGroup, Ft as usePlatformContext, G as reorderList, Ht as useSortedItems, I as extractSampleNamesFromDesignData, It as useTheme, J as useDoseCalculator, K as useListReorder, L as extractSampleOptionsFromDesignData, Lt as useToast, M as useFileImport, Mt as useRuntimeAlignment, N as validateImportFile, Nt as getPresenceClientId, P as extractSamplesFromDesignData, Pt as usePresenceHeartbeat, Q as createPluginClient, R as unwrapExperimentDesignData, Rt as candidateMatchesSearch, S as useScheduleDrag, St as useControlWorkspace, T as useExpansionSet, Tt as controlValuesToComponentBindings, U as useRackEditor, Ut as useRovingFocus, Vt as compareSortValues, W as REORDER_ANNOUNCER_STYLE, X as useJobsStatusTray, Y as combinePluginJobSources, Z as usePluginJobCenter, _ as toBioTemplateComponentPropsByComponent, _t as defineDoseCalculatorControlProps, a as useExperimentSelector, at as usePluginClient, b as useTemplateCollection, bt as defineWellPlateDoseComponentBindings, c as DEFAULT_PRESETS, ct as buildPluginEndpointUrl, d as useReagentSeries, dt as usePluginWorkspace, et as downloadPluginEndpoint, f as useGroupAssignment, ft as DEFAULT_MOBILE_VIEWPORT_QUERY, g as getBioTemplateComponentProps, gt as useForm, h as useBioTemplateWorkspace, ht as evaluateCondition, i as useAnalysisArtifacts, it as useCurrentExperiment, j as readFileAsText, jt as getInjectedFrontendRevision, k as detectDelimiter, kt as APP_EXPERIMENT_KEY, l as DEFAULT_UNITS, lt as resolvePluginBaseUrl, m as useBioTemplatePackWorkspace, mt as useFormBuilder, nt as pluginFormDataFromPayload, o as useDebouncedWatch, ot as usePluginEventStream, p as useBioTemplatePresetWorkspace, pt as useMobileSupportGate, q as useReorderAnnouncer, rt as uploadPluginEndpoint, s as useProtocolTemplates, st as usePluginSettings, tt as getPluginPageSelectorItems, u as generateDilutionSeries, ut as useRequestSyncState, v as useBioTemplateComponents, vt as defineDoseDesignControlModel, w as useExperimentData, x as useExperimentSave, xt as defineWellPlateDoseControlProps, y as useBioTemplateControls, yt as defineWellPlateControlProps, z as DEFAULT_COLORS, zt as normalizeSearchQuery } from "../validation-CukfLjy-.js";
4
4
  import { a as useApi, r as AuthenticationRequiredError } from "../experiment-DgllIc3I.js";
5
5
  import { t as ANALYSIS_FILE_ARTIFACT_SCHEMA } from "../analysisArtifactTypes-Cy3D0Ipg.js";
6
- import { a as buildAnalysisArtifactTargetQuery, c as resizedLeadingPanelWidth, d as useManualLayoutResize, f as resolvePluginFrontendBase, i as encodePlatformPickerPath, l as resizedTrailingPanelWidth, n as createFilePickerAdapter, o as parseAnalysisArtifactTarget, r as decodePlatformPickerPath, s as useFileBrowser, t as usePlatformFilePickerAdapter, u as resizedVerticalSplitPercent } from "../composables-D6uqp0UF.js";
6
+ import { a as buildAnalysisArtifactTargetQuery, c as resizedLeadingPanelWidth, d as useManualLayoutResize, f as resolvePluginFrontendBase, i as encodePlatformPickerPath, l as resizedTrailingPanelWidth, n as createFilePickerAdapter, o as parseAnalysisArtifactTarget, r as decodePlatformPickerPath, s as useFileBrowser, t as usePlatformFilePickerAdapter, u as resizedVerticalSplitPercent } from "../composables-BbHozlA7.js";
7
7
  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, createFilePickerAdapter, createGeneratedAnalysisTransport, createPluginClient, 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, snapToSlot, toBioTemplateComponentBindings, toBioTemplateComponentBindingsById, toBioTemplateComponentProps, toBioTemplateComponentPropsByComponent, toBioTemplateComponentPropsById, toMinutes, unwrapExperimentDesignData, uploadPluginEndpoint, useAnalysisArtifacts, useApi, useAppExperiment, useAutoGroup, useBioTemplateComponents, useBioTemplateControls, useBioTemplatePackWorkspace, useBioTemplatePresetWorkspace, useBioTemplateWorkspace, useChemicalFormula, 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, usePlatformContext, usePlatformFilePickerAdapter, usePluginClient, usePluginEventStream, usePluginJobCenter, usePluginJobs, usePluginSettings, usePluginWorkspace, usePresenceHeartbeat, useProtocolTemplates, useRackEditor, useReagentSeries, useReorderAnnouncer, useRequestSyncState, useRovingFocus, useRuntimeAlignment, useSampleGroups, useScheduleDrag, useSelectionLimit, useSequenceUtils, useSortedItems, useTemplateCollection, useTextSearch, useTheme, useTimeUtils, useToast, validateImportFile };
@@ -1,5 +1,5 @@
1
1
  import { AutoGroupResult, ClassDisposition, ClassSchema, ColumnInfo, ColumnRole, InputMode, MergeSuggestion, NumericBinning, OutlierAction, OutlierInfo, ParsedCsvData, SampleClass, SchemaFingerprint, ValueOps } from '../types/auto-group';
2
- import { TemplateOptions } from './autoGroup';
2
+ import { ExperimentHeader, TemplateOptions } from './autoGroup';
3
3
  export { DEFAULT_COLORS } from './autoGroup';
4
4
  type ParsedExperimentCsvData = ParsedCsvData & {
5
5
  sampleTypeHints: (string | undefined)[];
@@ -47,7 +47,16 @@ export declare function useAutoGroup(): {
47
47
  }[]>;
48
48
  schemas: import('vue').Ref<Record<string, ClassSchema>, Record<string, ClassSchema>>;
49
49
  activeClassKey: import('vue').Ref<string, string>;
50
- delimiter: import('vue').Ref<"-" | "." | "_", "-" | "." | "_">;
50
+ delimiter: import('vue').Ref<" " | "-" | "." | "_", " " | "-" | "." | "_">;
51
+ experiment: import('vue').Ref<{
52
+ code?: string | undefined;
53
+ date?: string | undefined;
54
+ operator?: string | undefined;
55
+ }, ExperimentHeader | {
56
+ code?: string | undefined;
57
+ date?: string | undefined;
58
+ operator?: string | undefined;
59
+ }>;
51
60
  dominantFieldCount: import('vue').ComputedRef<number>;
52
61
  minFieldCount: import('vue').ComputedRef<number>;
53
62
  samples: import('vue').ComputedRef<string[]>;
@@ -85,6 +94,8 @@ export declare function useAutoGroup(): {
85
94
  moveGroupBy: (key: string, idx: number, direction: "up" | "down") => void;
86
95
  toggleGroupByField: (fieldKey: string) => void;
87
96
  moveGroupByField: (fieldKey: string, direction: "up" | "down") => void;
97
+ renameGroupByField: (fieldKey: string, name: string) => void;
98
+ groupCountWithout: (fieldKey: string) => number;
88
99
  setRole: (key: string, idx: number, role: ColumnRole) => void;
89
100
  setColumnName: (key: string, idx: number, name: string) => void;
90
101
  setValueOps: (key: string, idx: number, ops: ValueOps) => void;
@@ -1,5 +1,5 @@
1
1
  import { ar as getInjectedPlatformContext } from "./templates-BU1pet28.js";
2
- import { n as pickerFormat, st as useRequestSyncState } from "./validation-Dmp9Q5W0.js";
2
+ import { n as pickerFormat, ut as useRequestSyncState } from "./validation-CukfLjy-.js";
3
3
  import { a as useApi } from "./experiment-DgllIc3I.js";
4
4
  import { computed, getCurrentScope, onBeforeUnmount, onScopeDispose, ref, shallowRef, toValue, watch } from "vue";
5
5
  //#region src/composables/pluginFrontendBase.ts
@@ -588,4 +588,4 @@ function usePlatformFilePickerAdapter(options = {}) {
588
588
  //#endregion
589
589
  export { buildAnalysisArtifactTargetQuery as a, resizedLeadingPanelWidth as c, useManualLayoutResize as d, resolvePluginFrontendBase as f, encodePlatformPickerPath as i, resizedTrailingPanelWidth as l, createFilePickerAdapter as n, parseAnalysisArtifactTarget as o, decodePlatformPickerPath as r, useFileBrowser as s, usePlatformFilePickerAdapter as t, resizedVerticalSplitPercent as u };
590
590
 
591
- //# sourceMappingURL=composables-D6uqp0UF.js.map
591
+ //# sourceMappingURL=composables-BbHozlA7.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"composables-D6uqp0UF.js","names":[],"sources":["../src/composables/pluginFrontendBase.ts","../src/composables/useManualLayoutResize.ts","../src/composables/useFileBrowser.ts","../src/composables/analysisArtifactTarget.ts","../src/composables/createFilePickerAdapter.ts","../src/composables/usePlatformFilePickerAdapter.ts"],"sourcesContent":["import { getInjectedPlatformContext } from './platformContextHelpers'\n\nfunction normalizePluginRoutePrefix(prefix: string | undefined): string | undefined {\n const candidate = prefix?.trim()\n if (!candidate) return undefined\n\n const path = candidate.split('/').filter(Boolean).join('/')\n return path ? `/${path}/` : '/'\n}\n\n/**\n * Resolve the Vue Router base for a plugin frontend.\n *\n * Integrated plugins use the platform-injected mount prefix. Standalone and\n * development builds retain the caller's Vite base unchanged.\n */\nexport function resolvePluginFrontendBase(fallbackBase: string = '/'): string {\n const context = getInjectedPlatformContext()\n if (!context?.isIntegrated) return fallbackBase\n\n return normalizePluginRoutePrefix(context.plugin?.route_prefix) ?? fallbackBase\n}\n","import { computed, onBeforeUnmount, shallowRef, toValue, watch, type MaybeRefOrGetter } from 'vue'\n\nexport interface ResizePanelWidthConstraints {\n minWidth: number\n maxWidth: number\n mainMinWidth?: number\n layoutGap?: number\n}\n\nexport interface ResizePercentConstraints {\n minPercent: number\n maxPercent: number\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\nfunction availablePanelWidth(\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.min(\n constraints.maxWidth,\n containerWidth - (constraints.mainMinWidth ?? 0) - (constraints.layoutGap ?? 0),\n )\n}\n\nexport function resizedLeadingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.round(clamp(\n startWidth + deltaX,\n constraints.minWidth,\n Math.max(constraints.minWidth, availablePanelWidth(containerWidth, constraints)),\n ))\n}\n\nexport function resizedTrailingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return resizedLeadingPanelWidth(startWidth, -deltaX, containerWidth, constraints)\n}\n\nexport function resizedVerticalSplitPercent(\n startPercent: number,\n deltaY: number,\n containerHeight: number,\n constraints: ResizePercentConstraints,\n): number {\n if (containerHeight <= 0) return startPercent\n return Math.round(clamp(\n startPercent + (deltaY / containerHeight) * 100,\n constraints.minPercent,\n constraints.maxPercent,\n ))\n}\n\nexport interface ManualLayoutResizeContext<TKind extends string> {\n kind: TKind\n startValue: number\n deltaX: number\n deltaY: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n}\n\nexport interface ManualLayoutResizeTarget<TKind extends string> {\n cursor?: string\n getContainerElement: () => HTMLElement | null\n getSecondaryElement?: () => HTMLElement | null\n getStartValue: () => number\n onResize: (context: ManualLayoutResizeContext<TKind>) => void\n}\n\n/** Coordinates pointer-driven resizing for one or more layout splitters. */\nexport function useManualLayoutResize<TKind extends string>(options: {\n enabled: MaybeRefOrGetter<boolean>\n resolveTarget: (kind: TKind) => ManualLayoutResizeTarget<TKind> | null\n}) {\n const activeResize = shallowRef<{\n kind: TKind\n startClientX: number\n startClientY: number\n startValue: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n cursor: string\n } | null>(null)\n const isResizing = computed(() => activeResize.value !== null)\n let previousBodyCursor = ''\n let previousBodyUserSelect = ''\n\n function stopResize(): void {\n if (!activeResize.value) return\n activeResize.value = null\n window.removeEventListener('pointermove', handlePointerMove)\n window.removeEventListener('pointerup', stopResize)\n window.removeEventListener('pointercancel', stopResize)\n if (typeof document !== 'undefined') {\n document.body.style.cursor = previousBodyCursor\n document.body.style.userSelect = previousBodyUserSelect\n }\n }\n\n function handlePointerMove(event: PointerEvent): void {\n const state = activeResize.value\n if (!state) return\n options.resolveTarget(state.kind)?.onResize({\n kind: state.kind,\n startValue: state.startValue,\n deltaX: event.clientX - state.startClientX,\n deltaY: event.clientY - state.startClientY,\n containerRect: state.containerRect,\n secondaryRect: state.secondaryRect,\n })\n }\n\n function startResize(kind: TKind, event: PointerEvent): void {\n if (!toValue(options.enabled) || event.button !== 0) return\n const target = options.resolveTarget(kind)\n const containerRect = target?.getContainerElement()?.getBoundingClientRect()\n if (!target || !containerRect) return\n\n event.preventDefault()\n if (event.currentTarget instanceof HTMLElement) {\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n const secondary = target.getSecondaryElement?.() ?? null\n activeResize.value = {\n kind,\n startClientX: event.clientX,\n startClientY: event.clientY,\n startValue: target.getStartValue(),\n containerRect,\n secondaryRect: secondary?.getBoundingClientRect() ?? null,\n cursor: target.cursor ?? 'col-resize',\n }\n\n if (typeof document !== 'undefined') {\n previousBodyCursor = document.body.style.cursor\n previousBodyUserSelect = document.body.style.userSelect\n document.body.style.cursor = activeResize.value.cursor\n document.body.style.userSelect = 'none'\n }\n window.addEventListener('pointermove', handlePointerMove)\n window.addEventListener('pointerup', stopResize)\n window.addEventListener('pointercancel', stopResize)\n }\n\n function isResizingKind(kind: TKind): boolean {\n return activeResize.value?.kind === kind\n }\n\n watch(() => toValue(options.enabled), enabled => { if (!enabled) stopResize() })\n onBeforeUnmount(stopResize)\n\n return { activeResize, isResizing, isResizingKind, startResize, stopResize }\n}\n","import {\n computed,\n getCurrentScope,\n onScopeDispose,\n ref,\n shallowRef,\n toValue,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\n\nimport type {\n FileBrowserSortKey,\n FileDirectoryListing,\n FileEntry,\n FilePathCrumb,\n FileSelection,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport { useApi } from './useApi'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { useRequestSyncState } from './useRequestSyncState'\n\nexport interface UseFileBrowserOptions {\n /** Extensions the caller cares about; entries are flagged, never removed. */\n typeRules?: MaybeRefOrGetter<string[] | undefined>\n /** Mount to open first; defaults to the first available one. */\n initialMountId?: MaybeRefOrGetter<string | undefined>\n /** Override the platform API base URL (standalone plugin dev). */\n apiBaseUrl?: string\n}\n\nexport interface UseFileBrowserReturn {\n /** Every configured mount, offline ones included. */\n mounts: Ref<ServerMount[]>\n /** Mount the current listing belongs to. */\n mountId: Ref<string>\n /** Mount-relative path of the current listing. */\n path: Ref<string>\n /** Parent path, or null at a mount root. */\n parent: Ref<string | null>\n entries: Ref<FileEntry[]>\n breadcrumbs: Ref<FilePathCrumb[]>\n /** Directory size before any search filter. */\n totalCount: Ref<number | null>\n /** How many entries satisfy the type rules. */\n matchCount: Ref<number | null>\n /** How many rows this listing carries, after search and any cap. */\n shownCount: Ref<number | null>\n /** True when the server capped the listing. */\n truncated: Ref<boolean>\n search: Ref<string>\n sort: Ref<FileBrowserSortKey>\n /** Paths the user has picked, keyed by mount and path. */\n selected: Ref<FileSelection[]>\n isLoading: ComputedRef<boolean>\n error: Ref<string | null>\n /** Load the mount list and open the first available mount. */\n init: () => Promise<void>\n /** Open a directory, or a mount root when path is empty. */\n navigate: (location: { mount_id: string; path: string }) => Promise<void>\n /** Re-read the current directory. */\n refresh: () => Promise<void>\n /** Take the directory currently being viewed as the input. */\n selectCurrentFolder: () => void\n clear: () => void\n}\n\n/**\n * Browses the platform's `/api/filesystem` routes for a custom file UI. For the\n * ready-made dialog, use `FilePicker` with `usePlatformFilePickerAdapter()`.\n *\n * Requires the `filesystem.browse` permission and a deployment that declares\n * mounts; with none configured the routes are absent and `mounts` stays empty.\n */\nexport function useFileBrowser(options: UseFileBrowserOptions = {}): UseFileBrowserReturn {\n const injectedContext = getInjectedPlatformContext()\n const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl })\n const sync = useRequestSyncState('Could not read the server mounts.')\n\n const mounts = ref<ServerMount[]>([])\n const mountId = shallowRef('')\n const path = shallowRef('')\n const parent = ref<string | null>(null)\n const entries = ref<FileEntry[]>([])\n const breadcrumbs = ref<FilePathCrumb[]>([])\n const totalCount = ref<number | null>(null)\n const matchCount = ref<number | null>(null)\n const shownCount = ref<number | null>(null)\n const truncated = ref(false)\n const search = shallowRef('')\n const sort = shallowRef<FileBrowserSortKey>('name')\n const selected = ref<FileSelection[]>([])\n\n const mountsLoading = shallowRef(false)\n const listingLoading = shallowRef(false)\n const listingReady = shallowRef(false)\n const isLoading = computed(() => mountsLoading.value || listingLoading.value)\n let listToken = 0\n let mountsToken = 0\n let disposed = false\n let listingController: AbortController | undefined\n let mountsController: AbortController | undefined\n let searchTimer: ReturnType<typeof setTimeout> | undefined\n\n function resetListing(): void {\n listingReady.value = false\n entries.value = []\n breadcrumbs.value = []\n parent.value = null\n totalCount.value = null\n matchCount.value = null\n shownCount.value = null\n truncated.value = false\n }\n\n function typeRuleParams(): string {\n const rules = toValue(options.typeRules) ?? []\n return rules.map((rule) => `&type_rules=${encodeURIComponent(rule)}`).join('')\n }\n\n /** Prefer the API's own `detail` over axios's \"Request failed with status N\". */\n function describe(cause: unknown, fallback: string): string {\n const detail = (cause as { response?: { data?: { detail?: unknown } } })?.response?.data?.detail\n return typeof detail === 'string' && detail ? detail : sync.readErrorMessage(cause, fallback)\n }\n\n function fail(cause: unknown, fallback: string): void {\n sync.setError(new Error(describe(cause, fallback)), fallback)\n }\n\n function statusOf(cause: unknown): number | undefined {\n return (cause as { response?: { status?: number } })?.response?.status\n }\n\n async function loadMounts(): Promise<boolean> {\n const token = ++mountsToken\n mountsController?.abort()\n mountsController = new AbortController()\n mountsLoading.value = true\n try {\n const data = await api.get<{ mounts: ServerMount[] }>('/filesystem/mounts', {\n signal: mountsController.signal,\n })\n if (disposed || token !== mountsToken) return false\n mounts.value = data.mounts ?? []\n sync.clearError()\n sync.markLoaded()\n return true\n } catch (cause) {\n if (disposed || token !== mountsToken) return false\n // The platform only registers these routes once mounts are configured, so\n // a 404 means \"none declared\" — an empty sidebar, not a failure.\n if (statusOf(cause) === 404) {\n mounts.value = []\n sync.clearError()\n sync.markLoaded()\n return true\n }\n mounts.value = []\n fail(cause, 'Could not read the server mounts.')\n return false\n } finally {\n if (token === mountsToken) mountsLoading.value = false\n }\n }\n\n async function navigate(location: { mount_id: string; path: string }, refresh = false): Promise<void> {\n if (disposed || !location.mount_id) return\n clearTimeout(searchTimer)\n const token = ++listToken\n listingController?.abort()\n listingController = new AbortController()\n listingLoading.value = true\n mountId.value = location.mount_id\n path.value = location.path\n resetListing()\n sync.clearError()\n try {\n const query =\n `mount_id=${encodeURIComponent(location.mount_id)}` +\n `&path=${encodeURIComponent(location.path)}` +\n `&sort=${sort.value}` +\n (refresh ? '&refresh=true' : '') +\n (search.value ? `&search=${encodeURIComponent(search.value)}` : '') +\n typeRuleParams()\n const listing = await api.get<FileDirectoryListing>(`/filesystem/browse?${query}`, {\n signal: listingController.signal,\n })\n if (disposed || token !== listToken) return\n mountId.value = listing.mount_id\n path.value = listing.path\n parent.value = listing.parent ?? null\n entries.value = listing.entries ?? []\n breadcrumbs.value = listing.breadcrumbs ?? []\n totalCount.value = listing.total_count ?? null\n matchCount.value = listing.match_count ?? null\n shownCount.value = listing.shown_count ?? listing.entries?.length ?? null\n truncated.value = listing.truncated ?? false\n listingReady.value = true\n sync.clearError()\n sync.markLoaded()\n } catch (cause) {\n if (disposed || token !== listToken) return\n entries.value = []\n fail(cause, 'Could not read that folder.')\n } finally {\n if (token === listToken) listingLoading.value = false\n }\n }\n\n async function init(): Promise<void> {\n if (disposed) return\n const navigationToken = listToken\n if (!(await loadMounts()) || disposed || navigationToken !== listToken) return\n const preferred = toValue(options.initialMountId)\n // An offline mount is not navigable, so a remembered preference for one\n // falls through to the first reachable mount instead of opening on an error.\n const reachable = (mount: ServerMount): boolean => mount.available !== false\n const target =\n mounts.value.find((mount) => mount.id === preferred && reachable(mount)) ??\n mounts.value.find(reachable)\n if (target) await navigate({ mount_id: target.id, path: '' })\n else {\n mountId.value = ''\n path.value = ''\n resetListing()\n }\n }\n\n async function refresh(): Promise<void> {\n if (disposed) return\n if (mountId.value) await navigate({ mount_id: mountId.value, path: path.value }, true)\n else await init()\n }\n\n function selectCurrentFolder(): void {\n if (!mountId.value || !listingReady.value || isLoading.value || sync.error.value) return\n const crumb = breadcrumbs.value[breadcrumbs.value.length - 1]\n const key = JSON.stringify([mountId.value, path.value])\n if (selected.value.some((item) => JSON.stringify([item.mount_id, item.path]) === key)) return\n selected.value = [\n ...selected.value,\n {\n mount_id: mountId.value,\n path: path.value,\n name: crumb?.label ?? path.value,\n kind: 'directory',\n child_count: totalCount.value,\n },\n ]\n }\n\n function clear(): void {\n selected.value = []\n }\n\n // Both controls are server-side, so they only take effect on a re-fetch.\n // Without these watchers the search box and sort button look live but are\n // inert until the user happens to navigate.\n watch(search, () => {\n if (!mountId.value) return\n clearTimeout(searchTimer)\n // Debounced: search changes on every keystroke.\n searchTimer = setTimeout(() => {\n void navigate({ mount_id: mountId.value, path: path.value })\n }, 250)\n })\n\n watch(sort, () => {\n if (mountId.value) void navigate({ mount_id: mountId.value, path: path.value })\n })\n\n watch(\n () => toValue(options.typeRules),\n () => {\n if (mountId.value) void navigate({ mount_id: mountId.value, path: path.value })\n },\n { deep: true },\n )\n\n if (getCurrentScope())\n onScopeDispose(() => {\n disposed = true\n ++listToken\n ++mountsToken\n clearTimeout(searchTimer)\n listingController?.abort()\n mountsController?.abort()\n })\n\n return {\n mounts,\n mountId,\n path,\n parent,\n entries,\n breadcrumbs,\n totalCount,\n matchCount,\n shownCount,\n truncated,\n search,\n sort,\n selected,\n isLoading,\n error: sync.error,\n init,\n navigate,\n refresh,\n selectCurrentFolder,\n clear,\n }\n}\n","import type { AnalysisArtifactTarget } from '../types/analysisArtifactTypes'\n\nconst EXPERIMENT_ID = 'experiment_id'\nconst ARTIFACT_PLUGIN_ID = 'artifact_plugin_id'\nconst ARTIFACT_KEY = 'artifact_key'\nconst ARTIFACT_ID = 'artifact_id'\n\nfunction parsePositiveInteger(value: string | null): number | undefined {\n if (value === null || !/^\\d+$/.test(value)) return undefined\n const parsed = Number(value)\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined\n}\n\n/** Build the canonical query used to open one analysis artifact in a plugin. */\nexport function buildAnalysisArtifactTargetQuery(\n target: AnalysisArtifactTarget,\n): URLSearchParams {\n const query = new URLSearchParams()\n query.set(EXPERIMENT_ID, String(target.experimentId))\n query.set(ARTIFACT_PLUGIN_ID, target.artifactPluginId)\n query.set(ARTIFACT_KEY, target.artifactKey)\n if (target.artifactId !== undefined) {\n query.set(ARTIFACT_ID, String(target.artifactId))\n }\n return query\n}\n\n/** Parse a complete canonical target. Incomplete or invalid input is not guessed. */\nexport function parseAnalysisArtifactTarget(\n query: URLSearchParams,\n): AnalysisArtifactTarget | null {\n const experimentId = parsePositiveInteger(query.get(EXPERIMENT_ID))\n const artifactPluginId = query.get(ARTIFACT_PLUGIN_ID)?.trim()\n const artifactKey = query.get(ARTIFACT_KEY)?.trim()\n const rawArtifactId = query.get(ARTIFACT_ID)\n const artifactId = parsePositiveInteger(rawArtifactId)\n\n if (\n experimentId === undefined\n || !artifactPluginId\n || !artifactKey\n || (rawArtifactId !== null && artifactId === undefined)\n ) {\n return null\n }\n\n return {\n experimentId,\n artifactPluginId,\n artifactKey,\n ...(artifactId === undefined ? {} : { artifactId }),\n }\n}\n","import type { PickerAdapter, PickerNode, PickerRequest, PickerSearchResult } from '../types/filePicker'\nimport type {\n FileBrowserLocation,\n FileDirectoryListing,\n FileDirectoryTree,\n FileEntry,\n FileSearchResult,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport { pickerFormat } from './filePicker/validation'\n\nexport interface FilePickerTransport {\n listMounts(request?: PickerRequest): Promise<ServerMount[]>\n browse(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryListing>\n /** `GET …/filesystem/tree`: one request fills the cache for the subfolders too. */\n tree?(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryTree>\n /** `GET …/filesystem/search`: replaces the folder-by-folder search walk. */\n search?(\n location: FileBrowserLocation,\n query: string,\n recursive: boolean,\n request?: PickerRequest,\n ): Promise<FileSearchResult>\n}\nexport interface FilePickerAdapterOptions {\n /** Show this directory's contents directly and prevent navigation outside it. */\n rootLocation?: FileBrowserLocation\n}\n\n/** Opaque picker identity; decode before passing a selection to a mount-scoped API. */\nexport function encodePlatformPickerPath(mountId: string, path = ''): string {\n return JSON.stringify([mountId, path])\n}\nexport function decodePlatformPickerPath(path: string): { mountId: string; path: string } {\n const decoded: unknown = JSON.parse(path)\n if (\n !Array.isArray(decoded) || decoded.length !== 2 ||\n !decoded.every((item) => typeof item === 'string') || !decoded[0]\n ) throw new Error('Invalid platform picker path.')\n return { mountId: decoded[0], path: decoded[1] }\n}\n\nfunction isRelativePath(path: string): boolean {\n return path === '' || (!/^[a-z]:/i.test(path) && !/[\\\\\\x00-\\x1f\\x7f]/.test(path) &&\n path.split('/').every((part) => part !== '' && part !== '.' && part !== '..'))\n}\n\n/** Mount browsing and search shared by platform and generated plugin clients. */\nexport function createFilePickerAdapter(\n transport: FilePickerTransport,\n options: FilePickerAdapterOptions = {},\n): PickerAdapter {\n const root = options.rootLocation && { ...options.rootLocation }\n const mountLabels = new Map<string, string>()\n // Per picker instance: never share permission-sensitive listings across clients.\n const listings = new Map<string, PickerNode[]>()\n let generation = 0\n\n function invalidate(): void {\n generation++\n listings.clear()\n mountLabels.clear()\n }\n\n function validateLocation(location: FileBrowserLocation) {\n if (!location.mount_id || !isRelativePath(location.path) || (root && (\n location.mount_id !== root.mount_id ||\n (root.path !== '' && location.path !== root.path && !location.path.startsWith(`${root.path}/`))\n ))) throw new Error('Invalid file picker location.')\n }\n if (root) validateLocation(root)\n function begin(request?: PickerRequest): number {\n request?.signal?.throwIfAborted()\n if (request?.refresh) invalidate()\n return generation\n }\n function check(request: PickerRequest | undefined, token: number) {\n request?.signal?.throwIfAborted()\n if (token !== generation) throw new DOMException('Cancelled', 'AbortError')\n }\n async function read(path: string, request: PickerRequest | undefined, token: number): Promise<PickerNode[]> {\n check(request, token)\n const decoded = path ? decodePlatformPickerPath(path) : undefined\n const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root\n if (location) validateLocation(location)\n const key = location ? encodePlatformPickerPath(location.mount_id, location.path) : ''\n const cached = listings.get(key)\n if (cached) return cached\n let nodes: PickerNode[]\n if (!location) {\n const mounts = await transport.listMounts(request)\n check(request, token)\n nodes = mounts.map((mount) => {\n mountLabels.set(mount.id, mount.label)\n return {\n kind: 'folder', path: encodePlatformPickerPath(mount.id), name: mount.label,\n parentPath: null, parentName: 'Server mounts',\n unavailableReason: mount.available === false ? 'This mount is offline.' : undefined,\n }\n })\n } else if (transport.tree) {\n const tree = await transport.tree(location, request)\n check(request, token)\n for (const listing of tree.listings ?? []) {\n const at = { mount_id: listing.mount_id, path: listing.path }\n validateLocation(at)\n if (at.mount_id !== location.mount_id) throw new Error('Invalid file picker directory entry.')\n // A capped subfolder is read on its own when opened, and reports the cap then.\n if (!listing.truncated) listings.set(encodePlatformPickerPath(at.mount_id, at.path), toNodes(at, listing))\n }\n const cached = listings.get(key)\n if (!cached)\n throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')\n return cached\n } else {\n const listing = await transport.browse(location, request)\n check(request, token)\n if (listing.truncated)\n throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')\n nodes = toNodes(location, listing)\n }\n listings.set(key, nodes)\n return nodes\n }\n function toNodes(location: FileBrowserLocation, listing: FileDirectoryListing): PickerNode[] {\n return (listing.entries ?? []).map((entry) => {\n if (entry.path !== `${location.path ? `${location.path}/` : ''}${entry.name}`)\n throw new Error('Invalid file picker directory entry.')\n return toNode(location.mount_id, entry)\n })\n }\n /** One entry, parented on the folder its mount-relative path names. */\n function toNode(mountId: string, entry: FileEntry): PickerNode {\n if (!isRelativePath(entry.path) || !entry.name || entry.name.includes('/') ||\n (entry.path !== entry.name && !entry.path.endsWith(`/${entry.name}`)))\n throw new Error('Invalid file picker directory entry.')\n const parent = entry.path.slice(0, Math.max(0, entry.path.length - entry.name.length - 1))\n const atRoot = root && mountId === root.mount_id && parent === root.path\n const common = {\n path: encodePlatformPickerPath(mountId, entry.path),\n parentPath: atRoot ? null : encodePlatformPickerPath(mountId, parent),\n parentName: `${mountLabels.get(mountId) ?? mountId}${parent ? ` / ${parent}` : ''}`,\n name: entry.name, bytes: entry.size_bytes, modifiedAt: entry.modified_at,\n }\n return entry.kind === 'directory'\n // Counts may include only matching formats, not nested directories.\n ? { ...common, kind: 'folder' }\n : { ...common, kind: 'file', format: pickerFormat(entry.name) }\n }\n async function serverSearch(\n query: string,\n scope: { path: string; recursive: boolean },\n request: PickerRequest | undefined,\n ): Promise<PickerNode[] | PickerSearchResult> {\n const token = begin(request)\n const decoded = scope.path ? decodePlatformPickerPath(scope.path) : undefined\n const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root!\n validateLocation(location)\n const result = await transport.search!(location, query, scope.recursive, request)\n check(request, token)\n const prefix = location.path ? `${location.path}/` : ''\n const nodes = (result.entries ?? []).map((entry) => {\n // \"This folder\" searches may return direct children only.\n if (!entry.path.startsWith(prefix) || (!scope.recursive && entry.path.slice(prefix.length).includes('/')))\n throw new Error('Invalid file picker directory entry.')\n return toNode(location.mount_id, entry)\n })\n return result.truncated ? { nodes, truncated: true } : nodes\n }\n return {\n mounts: !root,\n mountOf(path) {\n try {\n return encodePlatformPickerPath(decodePlatformPickerPath(path).mountId)\n } catch {\n return undefined // initialPath is caller input; the listing reports a bad one.\n }\n },\n invalidate,\n listRoot: async (request) => read('', request, begin(request)),\n listChildren: async (path, request) => read(path, request, begin(request)),\n async search(query, scope, request) {\n // The mount list itself has no server to search, so it keeps the walk.\n if (transport.search && (scope.path || root)) return serverSearch(query, scope, request)\n const token = begin(request)\n const matches: PickerNode[] = []\n const queue = [scope.path]\n const visited = new Set<string>()\n const needle = query.toLowerCase()\n let currentRequest = request\n while (queue.length) {\n check(request, token)\n const path = queue.shift()!\n if (visited.has(path)) continue\n visited.add(path)\n if (visited.size > 500) return { nodes: matches, truncated: true }\n const nodes = await read(path, currentRequest, token)\n check(request, token)\n if (currentRequest?.refresh) {\n currentRequest = { ...currentRequest }\n delete currentRequest.refresh\n }\n for (const node of nodes) {\n if (node.name.toLowerCase().includes(needle)) matches.push(node)\n if (scope.recursive && node.kind === 'folder' && !node.unavailableReason)\n queue.push(node.path)\n }\n if (matches.length > 10000) return { nodes: matches.slice(0, 10000), truncated: true }\n }\n return matches\n },\n }\n}\n","import type { PickerAdapter } from '../types/filePicker'\nimport type {\n FileBrowserLocation,\n FileDirectoryListing,\n FileDirectoryTree,\n FileSearchResult,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport type { PickerRequest } from '../types/filePicker'\nimport { useApi } from './useApi'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { createFilePickerAdapter, type FilePickerAdapterOptions } from './createFilePickerAdapter'\n\nexport { encodePlatformPickerPath, decodePlatformPickerPath } from './createFilePickerAdapter'\n\nfunction query(location: FileBrowserLocation, request?: PickerRequest): URLSearchParams {\n const params = new URLSearchParams({ mount_id: location.mount_id, path: location.path })\n if (request?.refresh) params.set('refresh', 'true')\n return params\n}\n\n/** Adapt the authenticated MINT mount API without exposing absolute host paths. */\nexport function usePlatformFilePickerAdapter(\n options: FilePickerAdapterOptions & { apiBaseUrl?: string } = {},\n): PickerAdapter {\n const api = useApi({\n baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl,\n })\n return createFilePickerAdapter({\n async listMounts(request) {\n try {\n const url = `/filesystem/mounts${request?.refresh ? '?refresh=true' : ''}`\n const { mounts } = await api.get<{ mounts: ServerMount[] }>(url, {\n signal: request?.signal,\n })\n return mounts\n } catch (cause) {\n if ((cause as { response?: { status?: number } }).response?.status === 404) return []\n throw cause\n }\n },\n browse(location, request) {\n return api.get<FileDirectoryListing>(`/filesystem/browse?${query(location, request)}`, {\n signal: request?.signal,\n })\n },\n tree(location, request) {\n return api.get<FileDirectoryTree>(`/filesystem/tree?${query(location, request)}`, {\n signal: request?.signal,\n })\n },\n search(location, text, recursive, request) {\n const params = query(location, request)\n params.set('q', text)\n params.set('recursive', String(recursive))\n return api.get<FileSearchResult>(`/filesystem/search?${params}`, { signal: request?.signal })\n },\n }, options)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"composables-BbHozlA7.js","names":[],"sources":["../src/composables/pluginFrontendBase.ts","../src/composables/useManualLayoutResize.ts","../src/composables/useFileBrowser.ts","../src/composables/analysisArtifactTarget.ts","../src/composables/createFilePickerAdapter.ts","../src/composables/usePlatformFilePickerAdapter.ts"],"sourcesContent":["import { getInjectedPlatformContext } from './platformContextHelpers'\n\nfunction normalizePluginRoutePrefix(prefix: string | undefined): string | undefined {\n const candidate = prefix?.trim()\n if (!candidate) return undefined\n\n const path = candidate.split('/').filter(Boolean).join('/')\n return path ? `/${path}/` : '/'\n}\n\n/**\n * Resolve the Vue Router base for a plugin frontend.\n *\n * Integrated plugins use the platform-injected mount prefix. Standalone and\n * development builds retain the caller's Vite base unchanged.\n */\nexport function resolvePluginFrontendBase(fallbackBase: string = '/'): string {\n const context = getInjectedPlatformContext()\n if (!context?.isIntegrated) return fallbackBase\n\n return normalizePluginRoutePrefix(context.plugin?.route_prefix) ?? fallbackBase\n}\n","import { computed, onBeforeUnmount, shallowRef, toValue, watch, type MaybeRefOrGetter } from 'vue'\n\nexport interface ResizePanelWidthConstraints {\n minWidth: number\n maxWidth: number\n mainMinWidth?: number\n layoutGap?: number\n}\n\nexport interface ResizePercentConstraints {\n minPercent: number\n maxPercent: number\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\nfunction availablePanelWidth(\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.min(\n constraints.maxWidth,\n containerWidth - (constraints.mainMinWidth ?? 0) - (constraints.layoutGap ?? 0),\n )\n}\n\nexport function resizedLeadingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return Math.round(clamp(\n startWidth + deltaX,\n constraints.minWidth,\n Math.max(constraints.minWidth, availablePanelWidth(containerWidth, constraints)),\n ))\n}\n\nexport function resizedTrailingPanelWidth(\n startWidth: number,\n deltaX: number,\n containerWidth: number,\n constraints: ResizePanelWidthConstraints,\n): number {\n return resizedLeadingPanelWidth(startWidth, -deltaX, containerWidth, constraints)\n}\n\nexport function resizedVerticalSplitPercent(\n startPercent: number,\n deltaY: number,\n containerHeight: number,\n constraints: ResizePercentConstraints,\n): number {\n if (containerHeight <= 0) return startPercent\n return Math.round(clamp(\n startPercent + (deltaY / containerHeight) * 100,\n constraints.minPercent,\n constraints.maxPercent,\n ))\n}\n\nexport interface ManualLayoutResizeContext<TKind extends string> {\n kind: TKind\n startValue: number\n deltaX: number\n deltaY: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n}\n\nexport interface ManualLayoutResizeTarget<TKind extends string> {\n cursor?: string\n getContainerElement: () => HTMLElement | null\n getSecondaryElement?: () => HTMLElement | null\n getStartValue: () => number\n onResize: (context: ManualLayoutResizeContext<TKind>) => void\n}\n\n/** Coordinates pointer-driven resizing for one or more layout splitters. */\nexport function useManualLayoutResize<TKind extends string>(options: {\n enabled: MaybeRefOrGetter<boolean>\n resolveTarget: (kind: TKind) => ManualLayoutResizeTarget<TKind> | null\n}) {\n const activeResize = shallowRef<{\n kind: TKind\n startClientX: number\n startClientY: number\n startValue: number\n containerRect: DOMRectReadOnly\n secondaryRect: DOMRectReadOnly | null\n cursor: string\n } | null>(null)\n const isResizing = computed(() => activeResize.value !== null)\n let previousBodyCursor = ''\n let previousBodyUserSelect = ''\n\n function stopResize(): void {\n if (!activeResize.value) return\n activeResize.value = null\n window.removeEventListener('pointermove', handlePointerMove)\n window.removeEventListener('pointerup', stopResize)\n window.removeEventListener('pointercancel', stopResize)\n if (typeof document !== 'undefined') {\n document.body.style.cursor = previousBodyCursor\n document.body.style.userSelect = previousBodyUserSelect\n }\n }\n\n function handlePointerMove(event: PointerEvent): void {\n const state = activeResize.value\n if (!state) return\n options.resolveTarget(state.kind)?.onResize({\n kind: state.kind,\n startValue: state.startValue,\n deltaX: event.clientX - state.startClientX,\n deltaY: event.clientY - state.startClientY,\n containerRect: state.containerRect,\n secondaryRect: state.secondaryRect,\n })\n }\n\n function startResize(kind: TKind, event: PointerEvent): void {\n if (!toValue(options.enabled) || event.button !== 0) return\n const target = options.resolveTarget(kind)\n const containerRect = target?.getContainerElement()?.getBoundingClientRect()\n if (!target || !containerRect) return\n\n event.preventDefault()\n if (event.currentTarget instanceof HTMLElement) {\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n const secondary = target.getSecondaryElement?.() ?? null\n activeResize.value = {\n kind,\n startClientX: event.clientX,\n startClientY: event.clientY,\n startValue: target.getStartValue(),\n containerRect,\n secondaryRect: secondary?.getBoundingClientRect() ?? null,\n cursor: target.cursor ?? 'col-resize',\n }\n\n if (typeof document !== 'undefined') {\n previousBodyCursor = document.body.style.cursor\n previousBodyUserSelect = document.body.style.userSelect\n document.body.style.cursor = activeResize.value.cursor\n document.body.style.userSelect = 'none'\n }\n window.addEventListener('pointermove', handlePointerMove)\n window.addEventListener('pointerup', stopResize)\n window.addEventListener('pointercancel', stopResize)\n }\n\n function isResizingKind(kind: TKind): boolean {\n return activeResize.value?.kind === kind\n }\n\n watch(() => toValue(options.enabled), enabled => { if (!enabled) stopResize() })\n onBeforeUnmount(stopResize)\n\n return { activeResize, isResizing, isResizingKind, startResize, stopResize }\n}\n","import {\n computed,\n getCurrentScope,\n onScopeDispose,\n ref,\n shallowRef,\n toValue,\n watch,\n type ComputedRef,\n type MaybeRefOrGetter,\n type Ref,\n} from 'vue'\n\nimport type {\n FileBrowserSortKey,\n FileDirectoryListing,\n FileEntry,\n FilePathCrumb,\n FileSelection,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport { useApi } from './useApi'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { useRequestSyncState } from './useRequestSyncState'\n\nexport interface UseFileBrowserOptions {\n /** Extensions the caller cares about; entries are flagged, never removed. */\n typeRules?: MaybeRefOrGetter<string[] | undefined>\n /** Mount to open first; defaults to the first available one. */\n initialMountId?: MaybeRefOrGetter<string | undefined>\n /** Override the platform API base URL (standalone plugin dev). */\n apiBaseUrl?: string\n}\n\nexport interface UseFileBrowserReturn {\n /** Every configured mount, offline ones included. */\n mounts: Ref<ServerMount[]>\n /** Mount the current listing belongs to. */\n mountId: Ref<string>\n /** Mount-relative path of the current listing. */\n path: Ref<string>\n /** Parent path, or null at a mount root. */\n parent: Ref<string | null>\n entries: Ref<FileEntry[]>\n breadcrumbs: Ref<FilePathCrumb[]>\n /** Directory size before any search filter. */\n totalCount: Ref<number | null>\n /** How many entries satisfy the type rules. */\n matchCount: Ref<number | null>\n /** How many rows this listing carries, after search and any cap. */\n shownCount: Ref<number | null>\n /** True when the server capped the listing. */\n truncated: Ref<boolean>\n search: Ref<string>\n sort: Ref<FileBrowserSortKey>\n /** Paths the user has picked, keyed by mount and path. */\n selected: Ref<FileSelection[]>\n isLoading: ComputedRef<boolean>\n error: Ref<string | null>\n /** Load the mount list and open the first available mount. */\n init: () => Promise<void>\n /** Open a directory, or a mount root when path is empty. */\n navigate: (location: { mount_id: string; path: string }) => Promise<void>\n /** Re-read the current directory. */\n refresh: () => Promise<void>\n /** Take the directory currently being viewed as the input. */\n selectCurrentFolder: () => void\n clear: () => void\n}\n\n/**\n * Browses the platform's `/api/filesystem` routes for a custom file UI. For the\n * ready-made dialog, use `FilePicker` with `usePlatformFilePickerAdapter()`.\n *\n * Requires the `filesystem.browse` permission and a deployment that declares\n * mounts; with none configured the routes are absent and `mounts` stays empty.\n */\nexport function useFileBrowser(options: UseFileBrowserOptions = {}): UseFileBrowserReturn {\n const injectedContext = getInjectedPlatformContext()\n const api = useApi({ baseUrl: options.apiBaseUrl ?? injectedContext?.platformApiUrl })\n const sync = useRequestSyncState('Could not read the server mounts.')\n\n const mounts = ref<ServerMount[]>([])\n const mountId = shallowRef('')\n const path = shallowRef('')\n const parent = ref<string | null>(null)\n const entries = ref<FileEntry[]>([])\n const breadcrumbs = ref<FilePathCrumb[]>([])\n const totalCount = ref<number | null>(null)\n const matchCount = ref<number | null>(null)\n const shownCount = ref<number | null>(null)\n const truncated = ref(false)\n const search = shallowRef('')\n const sort = shallowRef<FileBrowserSortKey>('name')\n const selected = ref<FileSelection[]>([])\n\n const mountsLoading = shallowRef(false)\n const listingLoading = shallowRef(false)\n const listingReady = shallowRef(false)\n const isLoading = computed(() => mountsLoading.value || listingLoading.value)\n let listToken = 0\n let mountsToken = 0\n let disposed = false\n let listingController: AbortController | undefined\n let mountsController: AbortController | undefined\n let searchTimer: ReturnType<typeof setTimeout> | undefined\n\n function resetListing(): void {\n listingReady.value = false\n entries.value = []\n breadcrumbs.value = []\n parent.value = null\n totalCount.value = null\n matchCount.value = null\n shownCount.value = null\n truncated.value = false\n }\n\n function typeRuleParams(): string {\n const rules = toValue(options.typeRules) ?? []\n return rules.map((rule) => `&type_rules=${encodeURIComponent(rule)}`).join('')\n }\n\n /** Prefer the API's own `detail` over axios's \"Request failed with status N\". */\n function describe(cause: unknown, fallback: string): string {\n const detail = (cause as { response?: { data?: { detail?: unknown } } })?.response?.data?.detail\n return typeof detail === 'string' && detail ? detail : sync.readErrorMessage(cause, fallback)\n }\n\n function fail(cause: unknown, fallback: string): void {\n sync.setError(new Error(describe(cause, fallback)), fallback)\n }\n\n function statusOf(cause: unknown): number | undefined {\n return (cause as { response?: { status?: number } })?.response?.status\n }\n\n async function loadMounts(): Promise<boolean> {\n const token = ++mountsToken\n mountsController?.abort()\n mountsController = new AbortController()\n mountsLoading.value = true\n try {\n const data = await api.get<{ mounts: ServerMount[] }>('/filesystem/mounts', {\n signal: mountsController.signal,\n })\n if (disposed || token !== mountsToken) return false\n mounts.value = data.mounts ?? []\n sync.clearError()\n sync.markLoaded()\n return true\n } catch (cause) {\n if (disposed || token !== mountsToken) return false\n // The platform only registers these routes once mounts are configured, so\n // a 404 means \"none declared\" — an empty sidebar, not a failure.\n if (statusOf(cause) === 404) {\n mounts.value = []\n sync.clearError()\n sync.markLoaded()\n return true\n }\n mounts.value = []\n fail(cause, 'Could not read the server mounts.')\n return false\n } finally {\n if (token === mountsToken) mountsLoading.value = false\n }\n }\n\n async function navigate(location: { mount_id: string; path: string }, refresh = false): Promise<void> {\n if (disposed || !location.mount_id) return\n clearTimeout(searchTimer)\n const token = ++listToken\n listingController?.abort()\n listingController = new AbortController()\n listingLoading.value = true\n mountId.value = location.mount_id\n path.value = location.path\n resetListing()\n sync.clearError()\n try {\n const query =\n `mount_id=${encodeURIComponent(location.mount_id)}` +\n `&path=${encodeURIComponent(location.path)}` +\n `&sort=${sort.value}` +\n (refresh ? '&refresh=true' : '') +\n (search.value ? `&search=${encodeURIComponent(search.value)}` : '') +\n typeRuleParams()\n const listing = await api.get<FileDirectoryListing>(`/filesystem/browse?${query}`, {\n signal: listingController.signal,\n })\n if (disposed || token !== listToken) return\n mountId.value = listing.mount_id\n path.value = listing.path\n parent.value = listing.parent ?? null\n entries.value = listing.entries ?? []\n breadcrumbs.value = listing.breadcrumbs ?? []\n totalCount.value = listing.total_count ?? null\n matchCount.value = listing.match_count ?? null\n shownCount.value = listing.shown_count ?? listing.entries?.length ?? null\n truncated.value = listing.truncated ?? false\n listingReady.value = true\n sync.clearError()\n sync.markLoaded()\n } catch (cause) {\n if (disposed || token !== listToken) return\n entries.value = []\n fail(cause, 'Could not read that folder.')\n } finally {\n if (token === listToken) listingLoading.value = false\n }\n }\n\n async function init(): Promise<void> {\n if (disposed) return\n const navigationToken = listToken\n if (!(await loadMounts()) || disposed || navigationToken !== listToken) return\n const preferred = toValue(options.initialMountId)\n // An offline mount is not navigable, so a remembered preference for one\n // falls through to the first reachable mount instead of opening on an error.\n const reachable = (mount: ServerMount): boolean => mount.available !== false\n const target =\n mounts.value.find((mount) => mount.id === preferred && reachable(mount)) ??\n mounts.value.find(reachable)\n if (target) await navigate({ mount_id: target.id, path: '' })\n else {\n mountId.value = ''\n path.value = ''\n resetListing()\n }\n }\n\n async function refresh(): Promise<void> {\n if (disposed) return\n if (mountId.value) await navigate({ mount_id: mountId.value, path: path.value }, true)\n else await init()\n }\n\n function selectCurrentFolder(): void {\n if (!mountId.value || !listingReady.value || isLoading.value || sync.error.value) return\n const crumb = breadcrumbs.value[breadcrumbs.value.length - 1]\n const key = JSON.stringify([mountId.value, path.value])\n if (selected.value.some((item) => JSON.stringify([item.mount_id, item.path]) === key)) return\n selected.value = [\n ...selected.value,\n {\n mount_id: mountId.value,\n path: path.value,\n name: crumb?.label ?? path.value,\n kind: 'directory',\n child_count: totalCount.value,\n },\n ]\n }\n\n function clear(): void {\n selected.value = []\n }\n\n // Both controls are server-side, so they only take effect on a re-fetch.\n // Without these watchers the search box and sort button look live but are\n // inert until the user happens to navigate.\n watch(search, () => {\n if (!mountId.value) return\n clearTimeout(searchTimer)\n // Debounced: search changes on every keystroke.\n searchTimer = setTimeout(() => {\n void navigate({ mount_id: mountId.value, path: path.value })\n }, 250)\n })\n\n watch(sort, () => {\n if (mountId.value) void navigate({ mount_id: mountId.value, path: path.value })\n })\n\n watch(\n () => toValue(options.typeRules),\n () => {\n if (mountId.value) void navigate({ mount_id: mountId.value, path: path.value })\n },\n { deep: true },\n )\n\n if (getCurrentScope())\n onScopeDispose(() => {\n disposed = true\n ++listToken\n ++mountsToken\n clearTimeout(searchTimer)\n listingController?.abort()\n mountsController?.abort()\n })\n\n return {\n mounts,\n mountId,\n path,\n parent,\n entries,\n breadcrumbs,\n totalCount,\n matchCount,\n shownCount,\n truncated,\n search,\n sort,\n selected,\n isLoading,\n error: sync.error,\n init,\n navigate,\n refresh,\n selectCurrentFolder,\n clear,\n }\n}\n","import type { AnalysisArtifactTarget } from '../types/analysisArtifactTypes'\n\nconst EXPERIMENT_ID = 'experiment_id'\nconst ARTIFACT_PLUGIN_ID = 'artifact_plugin_id'\nconst ARTIFACT_KEY = 'artifact_key'\nconst ARTIFACT_ID = 'artifact_id'\n\nfunction parsePositiveInteger(value: string | null): number | undefined {\n if (value === null || !/^\\d+$/.test(value)) return undefined\n const parsed = Number(value)\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : undefined\n}\n\n/** Build the canonical query used to open one analysis artifact in a plugin. */\nexport function buildAnalysisArtifactTargetQuery(\n target: AnalysisArtifactTarget,\n): URLSearchParams {\n const query = new URLSearchParams()\n query.set(EXPERIMENT_ID, String(target.experimentId))\n query.set(ARTIFACT_PLUGIN_ID, target.artifactPluginId)\n query.set(ARTIFACT_KEY, target.artifactKey)\n if (target.artifactId !== undefined) {\n query.set(ARTIFACT_ID, String(target.artifactId))\n }\n return query\n}\n\n/** Parse a complete canonical target. Incomplete or invalid input is not guessed. */\nexport function parseAnalysisArtifactTarget(\n query: URLSearchParams,\n): AnalysisArtifactTarget | null {\n const experimentId = parsePositiveInteger(query.get(EXPERIMENT_ID))\n const artifactPluginId = query.get(ARTIFACT_PLUGIN_ID)?.trim()\n const artifactKey = query.get(ARTIFACT_KEY)?.trim()\n const rawArtifactId = query.get(ARTIFACT_ID)\n const artifactId = parsePositiveInteger(rawArtifactId)\n\n if (\n experimentId === undefined\n || !artifactPluginId\n || !artifactKey\n || (rawArtifactId !== null && artifactId === undefined)\n ) {\n return null\n }\n\n return {\n experimentId,\n artifactPluginId,\n artifactKey,\n ...(artifactId === undefined ? {} : { artifactId }),\n }\n}\n","import type { PickerAdapter, PickerNode, PickerRequest, PickerSearchResult } from '../types/filePicker'\nimport type {\n FileBrowserLocation,\n FileDirectoryListing,\n FileDirectoryTree,\n FileEntry,\n FileSearchResult,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport { pickerFormat } from './filePicker/validation'\n\nexport interface FilePickerTransport {\n listMounts(request?: PickerRequest): Promise<ServerMount[]>\n browse(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryListing>\n /** `GET …/filesystem/tree`: one request fills the cache for the subfolders too. */\n tree?(location: FileBrowserLocation, request?: PickerRequest): Promise<FileDirectoryTree>\n /** `GET …/filesystem/search`: replaces the folder-by-folder search walk. */\n search?(\n location: FileBrowserLocation,\n query: string,\n recursive: boolean,\n request?: PickerRequest,\n ): Promise<FileSearchResult>\n}\nexport interface FilePickerAdapterOptions {\n /** Show this directory's contents directly and prevent navigation outside it. */\n rootLocation?: FileBrowserLocation\n}\n\n/** Opaque picker identity; decode before passing a selection to a mount-scoped API. */\nexport function encodePlatformPickerPath(mountId: string, path = ''): string {\n return JSON.stringify([mountId, path])\n}\nexport function decodePlatformPickerPath(path: string): { mountId: string; path: string } {\n const decoded: unknown = JSON.parse(path)\n if (\n !Array.isArray(decoded) || decoded.length !== 2 ||\n !decoded.every((item) => typeof item === 'string') || !decoded[0]\n ) throw new Error('Invalid platform picker path.')\n return { mountId: decoded[0], path: decoded[1] }\n}\n\nfunction isRelativePath(path: string): boolean {\n return path === '' || (!/^[a-z]:/i.test(path) && !/[\\\\\\x00-\\x1f\\x7f]/.test(path) &&\n path.split('/').every((part) => part !== '' && part !== '.' && part !== '..'))\n}\n\n/** Mount browsing and search shared by platform and generated plugin clients. */\nexport function createFilePickerAdapter(\n transport: FilePickerTransport,\n options: FilePickerAdapterOptions = {},\n): PickerAdapter {\n const root = options.rootLocation && { ...options.rootLocation }\n const mountLabels = new Map<string, string>()\n // Per picker instance: never share permission-sensitive listings across clients.\n const listings = new Map<string, PickerNode[]>()\n let generation = 0\n\n function invalidate(): void {\n generation++\n listings.clear()\n mountLabels.clear()\n }\n\n function validateLocation(location: FileBrowserLocation) {\n if (!location.mount_id || !isRelativePath(location.path) || (root && (\n location.mount_id !== root.mount_id ||\n (root.path !== '' && location.path !== root.path && !location.path.startsWith(`${root.path}/`))\n ))) throw new Error('Invalid file picker location.')\n }\n if (root) validateLocation(root)\n function begin(request?: PickerRequest): number {\n request?.signal?.throwIfAborted()\n if (request?.refresh) invalidate()\n return generation\n }\n function check(request: PickerRequest | undefined, token: number) {\n request?.signal?.throwIfAborted()\n if (token !== generation) throw new DOMException('Cancelled', 'AbortError')\n }\n async function read(path: string, request: PickerRequest | undefined, token: number): Promise<PickerNode[]> {\n check(request, token)\n const decoded = path ? decodePlatformPickerPath(path) : undefined\n const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root\n if (location) validateLocation(location)\n const key = location ? encodePlatformPickerPath(location.mount_id, location.path) : ''\n const cached = listings.get(key)\n if (cached) return cached\n let nodes: PickerNode[]\n if (!location) {\n const mounts = await transport.listMounts(request)\n check(request, token)\n nodes = mounts.map((mount) => {\n mountLabels.set(mount.id, mount.label)\n return {\n kind: 'folder', path: encodePlatformPickerPath(mount.id), name: mount.label,\n parentPath: null, parentName: 'Server mounts',\n unavailableReason: mount.available === false ? 'This mount is offline.' : undefined,\n }\n })\n } else if (transport.tree) {\n const tree = await transport.tree(location, request)\n check(request, token)\n for (const listing of tree.listings ?? []) {\n const at = { mount_id: listing.mount_id, path: listing.path }\n validateLocation(at)\n if (at.mount_id !== location.mount_id) throw new Error('Invalid file picker directory entry.')\n // A capped subfolder is read on its own when opened, and reports the cap then.\n if (!listing.truncated) listings.set(encodePlatformPickerPath(at.mount_id, at.path), toNodes(at, listing))\n }\n const cached = listings.get(key)\n if (!cached)\n throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')\n return cached\n } else {\n const listing = await transport.browse(location, request)\n check(request, token)\n if (listing.truncated)\n throw new Error('This directory exceeds the server listing limit. Choose a smaller data directory.')\n nodes = toNodes(location, listing)\n }\n listings.set(key, nodes)\n return nodes\n }\n function toNodes(location: FileBrowserLocation, listing: FileDirectoryListing): PickerNode[] {\n return (listing.entries ?? []).map((entry) => {\n if (entry.path !== `${location.path ? `${location.path}/` : ''}${entry.name}`)\n throw new Error('Invalid file picker directory entry.')\n return toNode(location.mount_id, entry)\n })\n }\n /** One entry, parented on the folder its mount-relative path names. */\n function toNode(mountId: string, entry: FileEntry): PickerNode {\n if (!isRelativePath(entry.path) || !entry.name || entry.name.includes('/') ||\n (entry.path !== entry.name && !entry.path.endsWith(`/${entry.name}`)))\n throw new Error('Invalid file picker directory entry.')\n const parent = entry.path.slice(0, Math.max(0, entry.path.length - entry.name.length - 1))\n const atRoot = root && mountId === root.mount_id && parent === root.path\n const common = {\n path: encodePlatformPickerPath(mountId, entry.path),\n parentPath: atRoot ? null : encodePlatformPickerPath(mountId, parent),\n parentName: `${mountLabels.get(mountId) ?? mountId}${parent ? ` / ${parent}` : ''}`,\n name: entry.name, bytes: entry.size_bytes, modifiedAt: entry.modified_at,\n }\n return entry.kind === 'directory'\n // Counts may include only matching formats, not nested directories.\n ? { ...common, kind: 'folder' }\n : { ...common, kind: 'file', format: pickerFormat(entry.name) }\n }\n async function serverSearch(\n query: string,\n scope: { path: string; recursive: boolean },\n request: PickerRequest | undefined,\n ): Promise<PickerNode[] | PickerSearchResult> {\n const token = begin(request)\n const decoded = scope.path ? decodePlatformPickerPath(scope.path) : undefined\n const location = decoded ? { mount_id: decoded.mountId, path: decoded.path } : root!\n validateLocation(location)\n const result = await transport.search!(location, query, scope.recursive, request)\n check(request, token)\n const prefix = location.path ? `${location.path}/` : ''\n const nodes = (result.entries ?? []).map((entry) => {\n // \"This folder\" searches may return direct children only.\n if (!entry.path.startsWith(prefix) || (!scope.recursive && entry.path.slice(prefix.length).includes('/')))\n throw new Error('Invalid file picker directory entry.')\n return toNode(location.mount_id, entry)\n })\n return result.truncated ? { nodes, truncated: true } : nodes\n }\n return {\n mounts: !root,\n mountOf(path) {\n try {\n return encodePlatformPickerPath(decodePlatformPickerPath(path).mountId)\n } catch {\n return undefined // initialPath is caller input; the listing reports a bad one.\n }\n },\n invalidate,\n listRoot: async (request) => read('', request, begin(request)),\n listChildren: async (path, request) => read(path, request, begin(request)),\n async search(query, scope, request) {\n // The mount list itself has no server to search, so it keeps the walk.\n if (transport.search && (scope.path || root)) return serverSearch(query, scope, request)\n const token = begin(request)\n const matches: PickerNode[] = []\n const queue = [scope.path]\n const visited = new Set<string>()\n const needle = query.toLowerCase()\n let currentRequest = request\n while (queue.length) {\n check(request, token)\n const path = queue.shift()!\n if (visited.has(path)) continue\n visited.add(path)\n if (visited.size > 500) return { nodes: matches, truncated: true }\n const nodes = await read(path, currentRequest, token)\n check(request, token)\n if (currentRequest?.refresh) {\n currentRequest = { ...currentRequest }\n delete currentRequest.refresh\n }\n for (const node of nodes) {\n if (node.name.toLowerCase().includes(needle)) matches.push(node)\n if (scope.recursive && node.kind === 'folder' && !node.unavailableReason)\n queue.push(node.path)\n }\n if (matches.length > 10000) return { nodes: matches.slice(0, 10000), truncated: true }\n }\n return matches\n },\n }\n}\n","import type { PickerAdapter } from '../types/filePicker'\nimport type {\n FileBrowserLocation,\n FileDirectoryListing,\n FileDirectoryTree,\n FileSearchResult,\n ServerMount,\n} from '../types/fileBrowserTypes'\nimport type { PickerRequest } from '../types/filePicker'\nimport { useApi } from './useApi'\nimport { getInjectedPlatformContext } from './platformContextHelpers'\nimport { createFilePickerAdapter, type FilePickerAdapterOptions } from './createFilePickerAdapter'\n\nexport { encodePlatformPickerPath, decodePlatformPickerPath } from './createFilePickerAdapter'\n\nfunction query(location: FileBrowserLocation, request?: PickerRequest): URLSearchParams {\n const params = new URLSearchParams({ mount_id: location.mount_id, path: location.path })\n if (request?.refresh) params.set('refresh', 'true')\n return params\n}\n\n/** Adapt the authenticated MINT mount API without exposing absolute host paths. */\nexport function usePlatformFilePickerAdapter(\n options: FilePickerAdapterOptions & { apiBaseUrl?: string } = {},\n): PickerAdapter {\n const api = useApi({\n baseUrl: options.apiBaseUrl ?? getInjectedPlatformContext()?.platformApiUrl,\n })\n return createFilePickerAdapter({\n async listMounts(request) {\n try {\n const url = `/filesystem/mounts${request?.refresh ? '?refresh=true' : ''}`\n const { mounts } = await api.get<{ mounts: ServerMount[] }>(url, {\n signal: request?.signal,\n })\n return mounts\n } catch (cause) {\n if ((cause as { response?: { status?: number } }).response?.status === 404) return []\n throw cause\n }\n },\n browse(location, request) {\n return api.get<FileDirectoryListing>(`/filesystem/browse?${query(location, request)}`, {\n signal: request?.signal,\n })\n },\n tree(location, request) {\n return api.get<FileDirectoryTree>(`/filesystem/tree?${query(location, request)}`, {\n signal: request?.signal,\n })\n },\n search(location, text, recursive, request) {\n const params = query(location, request)\n params.set('q', text)\n params.set('recursive', String(recursive))\n return api.get<FileSearchResult>(`/filesystem/search?${params}`, { signal: request?.signal })\n },\n }, options)\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
1
- import { $ as PlateEditor_default, A as SampleLegend_default, At as AppToastContainer_default, B as StatusIndicator_default, C as ChemicalFormula_default, Ct as AppAvatarMenu_default, D as ReagentEditor_default, Dt as ActionMenu_default, E as SampleHierarchyTree_default, Et as ThemeToggle_default, F as SettingsModal_default, Ft as Calendar_default, G as ControlWorkspaceView_default, H as LayoutResizeHandle_default, I as AdductText_default, It as BasePill_default, J as SampleSelector_default, K as ComponentBindingRenderer_default, L as Breadcrumb_default, Lt as SegmentedControl_default, M as BioTemplatePackWorkspaceView_default, Mt as FormField_default, N as BioTemplateExperimentWorkspaceView_default, Nt as DataFrame_default, O as SmartGroupModal_default, Ot as IconButton_default, P as BioTemplateRenderer_default, Pt as LoadingSpinner_default, Q as ReagentList_default, R as Avatar_default, Rt as BaseTabs_default, S as sequenceSamplesRemaining, St as AppTopBar_default, T as ProtocolStepEditor_default, Tt as CollapsibleCard_default, U as AppContainer_default, V as Divider_default, W as DoseDesignWorkspaceView_default, X as SmartGroupManual_default, Y as AutoGroupModal_default, Z as SmartGroupFieldRecipe_default, _ as estimateSequenceFinishDate, _t as AppLayout_default, a as ArtifactSaveDialog_default, at as GeneratedPluginView_default, b as formatSequenceRemaining, bt as FormActions_default, c as SectionCard_default, ct as GeneratedPlotlyResult_default, d as ExperimentCodeBadge_default, dt as ChartContainer_default, et as ConfirmDialog_default, f as ExperimentDataViewer_default, ft as generatedJobDefaults, g as SequenceProgressBar_default, gt as MobileSupportGate_default, h as InstrumentStateBadge_default, ht as PluginWorkspaceView_default, i as ExperimentPopover_default, it as DoseCalculator_default, j as BioTemplatePresetWorkspaceView_default, jt as AlertBox_default, k as GroupAssigner_default, kt as RuntimeUpdateNotice_default, l as ResourceCard_default, lt as PlotlyChart_default, m as AuditTrail_default, mt as normalizeGeneratedJobInput, n as FilePicker_default, nt as sampleTypeMarker, o as ArtifactSelectorModal_default, ot as JobsStatusTray_default, p as BatchProgressList_default, pt as generatedJobFormSchema, q as ScheduleCalendar_default, r as FitPanel_default, rt as ExperimentTimeline_default, s as ExperimentSelectorModal_default, st as GeneratedResultRenderer_default, tt as WellPlate_default, u as TimeRangeInput_default, ut as mintPlotlyTemplate, v as estimateSequenceRemainingSeconds, vt as AppSidebar_default, w as ScientificNumber_default, wt as PluginIcon_default, x as sequenceProgressPercent, xt as StepWizard_default, y as formatSequenceEta, yt as FormBuilder_default, z as ProgressBar_default, zt as SearchableSelect_default } from "./components-GDDvof8D.js";
1
+ import { $ as PlateEditor_default, A as SampleLegend_default, At as AppToastContainer_default, B as StatusIndicator_default, C as ChemicalFormula_default, Ct as AppAvatarMenu_default, D as ReagentEditor_default, Dt as ActionMenu_default, E as SampleHierarchyTree_default, Et as ThemeToggle_default, F as SettingsModal_default, Ft as Calendar_default, G as ControlWorkspaceView_default, H as LayoutResizeHandle_default, I as AdductText_default, It as BasePill_default, J as SampleSelector_default, K as ComponentBindingRenderer_default, L as Breadcrumb_default, Lt as SegmentedControl_default, M as BioTemplatePackWorkspaceView_default, Mt as FormField_default, N as BioTemplateExperimentWorkspaceView_default, Nt as DataFrame_default, O as SmartGroupModal_default, Ot as IconButton_default, P as BioTemplateRenderer_default, Pt as LoadingSpinner_default, Q as ReagentList_default, R as Avatar_default, Rt as BaseTabs_default, S as sequenceSamplesRemaining, St as AppTopBar_default, T as ProtocolStepEditor_default, Tt as CollapsibleCard_default, U as AppContainer_default, V as Divider_default, W as DoseDesignWorkspaceView_default, X as SmartGroupManual_default, Y as AutoGroupModal_default, Z as SmartGroupFieldRecipe_default, _ as estimateSequenceFinishDate, _t as AppLayout_default, a as ArtifactSaveDialog_default, at as GeneratedPluginView_default, b as formatSequenceRemaining, bt as FormActions_default, c as SectionCard_default, ct as GeneratedPlotlyResult_default, d as ExperimentCodeBadge_default, dt as ChartContainer_default, et as ConfirmDialog_default, f as ExperimentDataViewer_default, ft as generatedJobDefaults, g as SequenceProgressBar_default, gt as MobileSupportGate_default, h as InstrumentStateBadge_default, ht as PluginWorkspaceView_default, i as ExperimentPopover_default, it as DoseCalculator_default, j as BioTemplatePresetWorkspaceView_default, jt as AlertBox_default, k as GroupAssigner_default, kt as RuntimeUpdateNotice_default, l as ResourceCard_default, lt as PlotlyChart_default, m as AuditTrail_default, mt as normalizeGeneratedJobInput, n as FilePicker_default, nt as sampleTypeMarker, o as ArtifactSelectorModal_default, ot as JobsStatusTray_default, p as BatchProgressList_default, pt as generatedJobFormSchema, q as ScheduleCalendar_default, r as FitPanel_default, rt as ExperimentTimeline_default, s as ExperimentSelectorModal_default, st as GeneratedResultRenderer_default, tt as WellPlate_default, u as TimeRangeInput_default, ut as mintPlotlyTemplate, v as estimateSequenceRemainingSeconds, vt as AppSidebar_default, w as ScientificNumber_default, wt as PluginIcon_default, x as sequenceProgressPercent, xt as StepWizard_default, y as formatSequenceEta, yt as FormBuilder_default, z as ProgressBar_default, zt as SearchableSelect_default } from "./components-bbanLd1u.js";
2
2
  import { a as tryUseSettingsStore, c as mintApiErrorFromResponse, i as tableDensityToSize, n as colorPalettes, o as useSettingsStore, r as paletteCssVariables, s as MintApiError, t as PALETTE_CSS_VARIABLES } from "./settings-BLr8lzw6.js";
3
3
  import { a as SORT_OPTIONS, c as formatExperimentDate, d as resolveExperimentCode, f as BaseModal_default, h as BaseButton_default, i as EXPERIMENT_STATUS_VARIANT_MAP, l as formatExperimentStatus, m as useEventListener, n as EXPERIMENT_STATUS_LABELS, o as datePresetToISO, p as useFocusTrap, r as EXPERIMENT_STATUS_OPTIONS, s as experimentIdFromRoute, t as DATE_PRESET_OPTIONS, u as getExperimentStatusVariant } from "./experiment-utils-DTANyVKV.js";
4
4
  import { i as BaseSelect_default, n as Skeleton_default, o as BaseInput_default, r as EmptyState_default } from "./ResourcePickerListInternal-CFAw4vTZ.js";
5
5
  import { $ as toTimeCourseDataFrame, $n as FormulaInput_default, $r as snapToSlot, $t as getBioTemplatePackInfo, A as createSamplePrepTemplate, An as getControlDefaults, Ar as isPluginSecretLocked, At as toCalibrationCurveDataFrame, B as toBioTemplateComponentProps, Bn as getDefaultControlView, Br as addMinutes, Bt as validateInstrumentRunData, C as createFlowCytometryPanelTemplate, Cr as FileUploader_default, Ct as toInstrumentRunRows, D as createTimeCourseTemplate, Dn as hslToHex, Dr as PLUGIN_SECRET_FORMAT_REF, Dt as toFlowPanelDataFrame, E as createProtocolStepsTemplate, En as hexToHsl, Er as PLUGIN_SECRET_FORMAT_KEY, Et as toFlowPanelColumns, Fn as controlsToSettingsSchema, Fr as pluginSecretState, Ft as toAssayMatrixSampleOptions, G as toTemplateDataFrame, Gn as getFieldRegistryEntry, Gr as formatDuration, Gt as validateSamplePrepData, H as toBioTemplateComponentPropsById, Hr as durationMinutes, Ht as validateProtocolStepsData, I as listBioTemplateComponentBindings, In as controlsToSidebarPanels, Ir as setPluginSecret, It as validateAssayMatrixData, J as toDoseConditions, Jn as UnitInput_default, Jr as fromMinutes, Jt as bioTemplatePresets, K as toWellMapArray, Kn as getTypeDefault, Kr as formatTime, Kt as validateSampleSheetData, L as toBioTemplateComponentBindings, Ln as controlsToTopBarSettingsConfig, Lr as usesPluginSecretReferences, Lt as validateCalibrationCurveData, M as createPlateMapTemplate, Mn as controlsToFormSchema, Mr as keepPluginSecret, Mt as toAssayMatrixColumns, N as bioTemplatePresetControlValuesToOptions, Nn as controlsToSectionFormSchema, Nr as pluginSecretId, Nt as toAssayMatrixDataFrame, O as createCalibrationCurveTemplate, On as defineControlComponentBindings, Or as PLUGIN_SECRET_REF_KEY, Ot as toFlowPanelRows, P as getBioTemplateComponentBindings, Pn as controlsToSectionFormSchemas, Pr as pluginSecretLabel, Pt as toAssayMatrixRows, Q as toTimeCourseColumns, Qn as useSequenceUtils, Qr as rangesOverlap, Qt as bioTemplatePacks, R as toBioTemplateComponentBindingsById, Rn as controlsToViewIds, Rr as TagsInput_default, Rt as validateDoseResponseData, S as createInstrumentRunTemplate, St as toInstrumentRunDataFrame, T as createAssayMatrixTemplate, Tn as deriveShade, Tr as SecretInput_default, Tt as toInstrumentRunSteps, U as toBioTemplateComponentSnippets, Ur as findAvailableSlots, Ut as validateQpcrPlateData, Vr as compareTime, Vt as validatePlateMapData, W as toBioTemplateComponentUsage, Wr as findNearestTimeSlotIndex, Wt as validateReagentListData, X as toPlateMapEditorState, Xn as MoleculeInput_default, Xr as isTimeInRange, Xt as listBioTemplatePresets, Y as toDoseLayoutState, Yn as useConcentrationUnits, Yr as generateTimeSlots, Yt as getBioTemplatePresetInfo, Z as toWellPlateWells, Zn as SequenceInput_default, Zr as parseTime, Zt as searchBioTemplatePresets, _ as createElisaAssayCollection, _r as resolveJobCapabilities, _t as toProtocolColumns, a as getBioTemplateControlDefaults, ai as useListSelection, an as searchBioTemplateCatalog, at as toSampleRows, b as createQpcrExpressionCollection, bt as toProtocolSteps, c as listBioTemplatePresetControlSchemas, ci as BaseRadioGroup_default, cn as createTemplateCollection, cr as resolveCurrentPluginId, ct as toSamplePrepRows, d as createBioTemplatePresetCollectionFromControls, di as Tooltip_default, dn as ensureTemplateFromCollection, dr as usePluginJobs, dt as toReagentListItems, ei as toMinutes, en as listBioTemplatePacks, er as ATOMIC_WEIGHTS, et as toTimeCourseRows, f as createWellPlateScreenCollection, fi as BaseTextarea_default, fn as extractTemplateCollection, fr as isActiveJobStatus, ft as toReagentRows, g as createFlowCytometryAssayCollection, gr as normalizeJobState, gt as toQpcrWellPlateWells, h as createWesternBlotAssayCollection, hr as normalizeJobPercent, ht as toQpcrRows, i as createBioTemplateControlToolkit, ii as MultiSelect_default, in as listBioTemplateCatalog, it as toSampleOptions, j as createSampleSheetTemplate, jn as useControlSchema, jr as isPluginSecretRef, jt as toCalibrationCurveRows, k as createDoseResponseTemplate, kn as defineControls, kr as clearPluginSecret, kt as toCalibrationCurveColumns, l as requireBioTemplateControlSchema, li as BaseToggle_default, ln as createTemplateEnvelope, lr as createGeneratedAnalysisTransport, lt as toReagentColumns, m as createDefaultBioTemplate, mr as jobStatusLabel, mt as toQpcrDataFrame, n as bioTemplateControlsToSectionFormSchemas, ni as DatePicker_default, nn as bioTemplateCatalog, nr as GeneratedPathInput_default, nt as toSampleColumns, o as getBioTemplateControlSchema, oi as useSelectionLimit, on as TEMPLATE_COLLECTION_KEY, ot as toSamplePrepColumns, p as createBioTemplatePackCollection, pn as getTemplateData, pr as isTerminalJobStatus, pt as toQpcrColumns, q as racksFromPlateState, qn as ConcentrationInput_default, qr as formatTimeSlot, qt as validateTimeCourseData, r as bioTemplateControlsToSidebarPanels, rn as getBioTemplateInfo, rt as toSampleDataFrame, s as listBioTemplateControlSchemas, si as BaseSlider_default, sn as assertTemplateEnvelope, st as toSamplePrepDataFrame, t as bioTemplateControlsToFormSchema, ti as useTimeUtils, tn as searchBioTemplatePacks, tr as useChemicalFormula, tt as toTimeCourseSteps, u as createBioTemplatePresetCollection, ui as BaseCheckbox_default, un as ensureTemplateEnvelope, ur as useGeneratedAnalysis, ut as toReagentDataFrame, v as createTargetedMetabolomicsCollection, vt as toProtocolDataFrame, w as createReagentListTemplate, wr as NumberInput_default, wt as toInstrumentRunScheduleEvents, x as createQpcrPlateTemplate, xr as useMenuKeyboard, xt as toInstrumentRunColumns, y as createLcmsBatchCollection, yr as DateTimePicker_default, yt as toProtocolRows, z as toBioTemplateComponentImports, zn as controlsToViewItems, zr as TimePicker_default, zt as validateFlowCytometryPanelData } from "./templates-BU1pet28.js";
6
- import { $ as pluginFormDataFromPayload, A as parseDelimitedText, At as getPresenceClientId, B as useRackEditor, Bt as useRovingFocus, C as useExperimentSamples, Ct as controlValuesToComponentBindingsById, Dt as useAppExperiment, E as useSampleGroups, Et as APP_EXPERIMENT_KEY, F as useAutoGroup, Ft as candidateMatchesSearch, G as useDoseCalculator, H as reorderList, I as extractSampleNamesFromDesignData, It as normalizeSearchQuery, J as usePluginJobCenter, K as combinePluginJobSources, L as extractSampleOptionsFromDesignData, Lt as useTextSearch, M as useFileImport, Mt as usePlatformContext, N as validateImportFile, Nt as useTheme, Ot as getInjectedFrontendRevision, P as extractSamplesFromDesignData, Pt as useToast, Q as getPluginPageSelectorItems, R as unwrapExperimentDesignData, Rt as compareSortValues, S as useScheduleDrag, St as controlValuesToComponentBindings, T as useExpansionSet, U as useListReorder, V as REORDER_ANNOUNCER_STYLE, W as useReorderAnnouncer, X as downloadBlob, Y as createPluginClient, Z as downloadPluginEndpoint, _ as toBioTemplateComponentPropsByComponent, _t as defineWellPlateDoseComponentBindings, a as useExperimentSelector, at as buildPluginEndpointUrl, b as useTemplateCollection, bt as defineControlModel, c as DEFAULT_PRESETS, ct as usePluginWorkspace, d as useReagentSeries, dt as useFormBuilder, et as uploadPluginEndpoint, f as useGroupAssignment, ft as evaluateCondition, g as getBioTemplateComponentProps, gt as defineWellPlateControlProps, h as useBioTemplateWorkspace, ht as defineDoseDesignControlModel, i as useAnalysisArtifacts, it as usePluginSettings, j as readFileAsText, jt as usePresenceHeartbeat, k as detectDelimiter, kt as useRuntimeAlignment, l as DEFAULT_UNITS, lt as DEFAULT_MOBILE_VIEWPORT_QUERY, m as useBioTemplatePackWorkspace, mt as defineDoseCalculatorControlProps, nt as usePluginClient, o as useDebouncedWatch, ot as resolvePluginBaseUrl, p as useBioTemplatePresetWorkspace, pt as useForm, q as useJobsStatusTray, rt as usePluginEventStream, s as useProtocolTemplates, st as useRequestSyncState, tt as useCurrentExperiment, u as generateDilutionSeries, ut as useMobileSupportGate, v as useBioTemplateComponents, vt as defineWellPlateDoseControlProps, w as useExperimentData, wt as controlValuesToComponentProps, x as useExperimentSave, y as useBioTemplateControls, yt as useControlWorkspace, z as DEFAULT_COLORS, zt as useSortedItems } from "./validation-Dmp9Q5W0.js";
6
+ import { $ as downloadBlob, A as parseDelimitedText, At as useAppExperiment, Bt as useTextSearch, C as useExperimentSamples, Ct as defineControlModel, Dt as controlValuesToComponentProps, E as useSampleGroups, Et as controlValuesToComponentBindingsById, F as useAutoGroup, Ft as usePlatformContext, G as reorderList, Ht as useSortedItems, I as extractSampleNamesFromDesignData, It as useTheme, J as useDoseCalculator, K as useListReorder, L as extractSampleOptionsFromDesignData, Lt as useToast, M as useFileImport, Mt as useRuntimeAlignment, N as validateImportFile, Nt as getPresenceClientId, P as extractSamplesFromDesignData, Pt as usePresenceHeartbeat, Q as createPluginClient, R as unwrapExperimentDesignData, Rt as candidateMatchesSearch, S as useScheduleDrag, St as useControlWorkspace, T as useExpansionSet, Tt as controlValuesToComponentBindings, U as useRackEditor, Ut as useRovingFocus, Vt as compareSortValues, W as REORDER_ANNOUNCER_STYLE, X as useJobsStatusTray, Y as combinePluginJobSources, Z as usePluginJobCenter, _ as toBioTemplateComponentPropsByComponent, _t as defineDoseCalculatorControlProps, a as useExperimentSelector, at as usePluginClient, b as useTemplateCollection, bt as defineWellPlateDoseComponentBindings, c as DEFAULT_PRESETS, ct as buildPluginEndpointUrl, d as useReagentSeries, dt as usePluginWorkspace, et as downloadPluginEndpoint, f as useGroupAssignment, ft as DEFAULT_MOBILE_VIEWPORT_QUERY, g as getBioTemplateComponentProps, gt as useForm, h as useBioTemplateWorkspace, ht as evaluateCondition, i as useAnalysisArtifacts, it as useCurrentExperiment, j as readFileAsText, jt as getInjectedFrontendRevision, k as detectDelimiter, kt as APP_EXPERIMENT_KEY, l as DEFAULT_UNITS, lt as resolvePluginBaseUrl, m as useBioTemplatePackWorkspace, mt as useFormBuilder, nt as pluginFormDataFromPayload, o as useDebouncedWatch, ot as usePluginEventStream, p as useBioTemplatePresetWorkspace, pt as useMobileSupportGate, q as useReorderAnnouncer, rt as uploadPluginEndpoint, s as useProtocolTemplates, st as usePluginSettings, tt as getPluginPageSelectorItems, u as generateDilutionSeries, ut as useRequestSyncState, v as useBioTemplateComponents, vt as defineDoseDesignControlModel, w as useExperimentData, x as useExperimentSave, xt as defineWellPlateDoseControlProps, y as useBioTemplateControls, yt as defineWellPlateControlProps, z as DEFAULT_COLORS, zt as normalizeSearchQuery } from "./validation-CukfLjy-.js";
7
7
  import { ADMIN_PANEL_PERMISSIONS, ADMIN_ROLE, canAccessAdmin, canAccessByPolicy, canAccessPlugin, getAccessAudience, getRoleInfo, getUserPermissions, hasAllPermissions, hasAnyPermission, isAdminRole, isAdminUser } from "./permissions.js";
8
8
  import { a as useApi, o as useAuthStore, r as AuthenticationRequiredError, t as useExperimentStore } from "./experiment-DgllIc3I.js";
9
9
  import { t as ANALYSIS_FILE_ARTIFACT_SCHEMA } from "./analysisArtifactTypes-Cy3D0Ipg.js";
10
10
  import { MINTSdk } from "./install.js";
11
- import { a as buildAnalysisArtifactTargetQuery, c as resizedLeadingPanelWidth, d as useManualLayoutResize, f as resolvePluginFrontendBase, i as encodePlatformPickerPath, l as resizedTrailingPanelWidth, n as createFilePickerAdapter, o as parseAnalysisArtifactTarget, r as decodePlatformPickerPath, s as useFileBrowser, t as usePlatformFilePickerAdapter, u as resizedVerticalSplitPercent } from "./composables-D6uqp0UF.js";
11
+ import { a as buildAnalysisArtifactTargetQuery, c as resizedLeadingPanelWidth, d as useManualLayoutResize, f as resolvePluginFrontendBase, i as encodePlatformPickerPath, l as resizedTrailingPanelWidth, n as createFilePickerAdapter, o as parseAnalysisArtifactTarget, r as decodePlatformPickerPath, s as useFileBrowser, t as usePlatformFilePickerAdapter, u as resizedVerticalSplitPercent } from "./composables-BbHozlA7.js";
12
12
  import "./stores/index.js";
13
13
  //#region src/lcms.ts
14
14
  /** @deprecated Scheduled for removal in MINT 1.4; no SDK replacement. */
package/dist/install.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as components_exports } from "./components-GDDvof8D.js";
1
+ import { t as components_exports } from "./components-bbanLd1u.js";
2
2
  //#region src/install.ts
3
3
  /**
4
4
  * Vue plugin that registers all MINT SDK components globally.
@@ -42,9 +42,9 @@ function canAccessAdmin(user) {
42
42
  return isAdminUser(user) || hasAnyPermission(user, ADMIN_PANEL_PERMISSIONS);
43
43
  }
44
44
  function canAccessPlugin(user, pluginName) {
45
- if (!pluginName) return true;
45
+ if (!pluginName || isAdminUser(user)) return true;
46
46
  const access = getRoleInfo(user)?.plugin_access;
47
- if (!access || access === "all") return true;
47
+ if (access == null || access === "all") return true;
48
48
  return Array.isArray(access) && access.includes(pluginName);
49
49
  }
50
50
  function canAccessByPolicy(user, policy = {}, isAuthenticated = user !== null && user !== void 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.js","names":[],"sources":["../src/permissions.ts"],"sourcesContent":["export const ADMIN_ROLE = 'admin'\n\nexport const ADMIN_PANEL_PERMISSIONS = [\n 'users.view',\n 'users.manage',\n 'platform.configure',\n 'platform.view_logs',\n 'plugins.configure',\n 'plugins.install',\n 'notices.publish',\n] as const\n\nexport type AccessAudience = 'all' | 'admin' | 'user'\nexport type AccessAudienceInput = AccessAudience | readonly AccessAudience[]\n\nexport interface RoleInfo {\n id?: number\n slug: string\n name?: string\n color?: string\n permissions?: readonly string[] | null\n project_scope?: 'all' | 'assigned' | string\n plugin_access?: readonly string[] | 'all' | null\n}\n\nexport interface PermissionUser {\n role?: string | null\n role_obj?: RoleInfo | null\n roleObj?: RoleInfo | null\n permissions?: readonly string[] | null\n}\n\nexport interface AccessPolicy {\n audience?: AccessAudienceInput\n visibleFor?: AccessAudienceInput\n requiresAuth?: boolean\n requiresAdmin?: boolean\n permissions?: readonly string[]\n anyPermissions?: readonly string[]\n plugin?: string\n}\n\n/** An object that carries an access rule in its nested `access` policy. */\nexport interface AccessControlled {\n access?: AccessPolicy\n}\n\nexport function getRoleInfo(user: PermissionUser | null | undefined): RoleInfo | null {\n return user?.roleObj ?? user?.role_obj ?? null\n}\n\nexport function isAdminRole(role: string | null | undefined): boolean {\n return role === ADMIN_ROLE\n}\n\nexport function isAdminUser(user: PermissionUser | null | undefined): boolean {\n return isAdminRole(user?.role) || getRoleInfo(user)?.slug === ADMIN_ROLE\n}\n\nexport function getAccessAudience(user: PermissionUser | null | undefined): Exclude<AccessAudience, 'all'> {\n return isAdminUser(user) ? 'admin' : 'user'\n}\n\nexport function getUserPermissions(user: PermissionUser | null | undefined): string[] {\n const rolePermissions = getRoleInfo(user)?.permissions\n if (rolePermissions?.length) return [...rolePermissions]\n return [...(user?.permissions ?? [])]\n}\n\nexport function hasAllPermissions(\n user: PermissionUser | null | undefined,\n permissions: readonly string[] = [],\n): boolean {\n if (permissions.length === 0) return true\n if (isAdminUser(user)) return true\n const granted = new Set(getUserPermissions(user))\n return permissions.every(permission => granted.has(permission))\n}\n\nexport function hasAnyPermission(\n user: PermissionUser | null | undefined,\n permissions: readonly string[] = [],\n): boolean {\n if (permissions.length === 0) return true\n if (isAdminUser(user)) return true\n const granted = new Set(getUserPermissions(user))\n return permissions.some(permission => granted.has(permission))\n}\n\nexport function canAccessAdmin(user: PermissionUser | null | undefined): boolean {\n return isAdminUser(user) || hasAnyPermission(user, ADMIN_PANEL_PERMISSIONS)\n}\n\nexport function canAccessPlugin(\n user: PermissionUser | null | undefined,\n pluginName: string | null | undefined,\n): boolean {\n if (!pluginName) return true\n const access = getRoleInfo(user)?.plugin_access\n if (!access || access === 'all') return true\n return Array.isArray(access) && access.includes(pluginName)\n}\n\nexport function canAccessByPolicy(\n user: PermissionUser | null | undefined,\n policy: AccessPolicy = {},\n isAuthenticated = user !== null && user !== undefined,\n): boolean {\n if (policy.requiresAuth && !isAuthenticated) return false\n if (policy.requiresAdmin && !isAdminUser(user)) return false\n if (!audienceAllowsUser(policy.visibleFor ?? policy.audience, user)) return false\n if (!hasAllPermissions(user, policy.permissions)) return false\n if (!hasAnyPermission(user, policy.anyPermissions)) return false\n if (policy.plugin && !canAccessPlugin(user, policy.plugin)) return false\n return true\n}\n\nfunction audienceAllowsUser(\n audience: AccessAudienceInput | undefined,\n user: PermissionUser | null | undefined,\n): boolean {\n if (audience === undefined) return true\n const allowed = Array.isArray(audience) ? audience : [audience]\n if (allowed.includes('all')) return true\n return allowed.includes(getAccessAudience(user))\n}\n"],"mappings":";AAAA,IAAa,aAAa;AAE1B,IAAa,0BAA0B;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAqCA,SAAgB,YAAY,MAA0D;CACpF,OAAO,MAAM,WAAW,MAAM,YAAY;AAC5C;AAEA,SAAgB,YAAY,MAA0C;CACpE,OAAO,SAAS;AAClB;AAEA,SAAgB,YAAY,MAAkD;CAC5E,OAAO,YAAY,MAAM,IAAI,KAAK,YAAY,IAAI,CAAC,EAAE,SAAA;AACvD;AAEA,SAAgB,kBAAkB,MAAyE;CACzG,OAAO,YAAY,IAAI,IAAI,UAAU;AACvC;AAEA,SAAgB,mBAAmB,MAAmD;CACpF,MAAM,kBAAkB,YAAY,IAAI,CAAC,EAAE;CAC3C,IAAI,iBAAiB,QAAQ,OAAO,CAAC,GAAG,eAAe;CACvD,OAAO,CAAC,GAAI,MAAM,eAAe,CAAC,CAAE;AACtC;AAEA,SAAgB,kBACd,MACA,cAAiC,CAAC,GACzB;CACT,IAAI,YAAY,WAAW,GAAG,OAAO;CACrC,IAAI,YAAY,IAAI,GAAG,OAAO;CAC9B,MAAM,UAAU,IAAI,IAAI,mBAAmB,IAAI,CAAC;CAChD,OAAO,YAAY,OAAM,eAAc,QAAQ,IAAI,UAAU,CAAC;AAChE;AAEA,SAAgB,iBACd,MACA,cAAiC,CAAC,GACzB;CACT,IAAI,YAAY,WAAW,GAAG,OAAO;CACrC,IAAI,YAAY,IAAI,GAAG,OAAO;CAC9B,MAAM,UAAU,IAAI,IAAI,mBAAmB,IAAI,CAAC;CAChD,OAAO,YAAY,MAAK,eAAc,QAAQ,IAAI,UAAU,CAAC;AAC/D;AAEA,SAAgB,eAAe,MAAkD;CAC/E,OAAO,YAAY,IAAI,KAAK,iBAAiB,MAAM,uBAAuB;AAC5E;AAEA,SAAgB,gBACd,MACA,YACS;CACT,IAAI,CAAC,YAAY,OAAO;CACxB,MAAM,SAAS,YAAY,IAAI,CAAC,EAAE;CAClC,IAAI,CAAC,UAAU,WAAW,OAAO,OAAO;CACxC,OAAO,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,UAAU;AAC5D;AAEA,SAAgB,kBACd,MACA,SAAuB,CAAC,GACxB,kBAAkB,SAAS,QAAQ,SAAS,KAAA,GACnC;CACT,IAAI,OAAO,gBAAgB,CAAC,iBAAiB,OAAO;CACpD,IAAI,OAAO,iBAAiB,CAAC,YAAY,IAAI,GAAG,OAAO;CACvD,IAAI,CAAC,mBAAmB,OAAO,cAAc,OAAO,UAAU,IAAI,GAAG,OAAO;CAC5E,IAAI,CAAC,kBAAkB,MAAM,OAAO,WAAW,GAAG,OAAO;CACzD,IAAI,CAAC,iBAAiB,MAAM,OAAO,cAAc,GAAG,OAAO;CAC3D,IAAI,OAAO,UAAU,CAAC,gBAAgB,MAAM,OAAO,MAAM,GAAG,OAAO;CACnE,OAAO;AACT;AAEA,SAAS,mBACP,UACA,MACS;CACT,IAAI,aAAa,KAAA,GAAW,OAAO;CACnC,MAAM,UAAU,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;CAC9D,IAAI,QAAQ,SAAS,KAAK,GAAG,OAAO;CACpC,OAAO,QAAQ,SAAS,kBAAkB,IAAI,CAAC;AACjD"}
1
+ {"version":3,"file":"permissions.js","names":[],"sources":["../src/permissions.ts"],"sourcesContent":["export const ADMIN_ROLE = 'admin'\n\nexport const ADMIN_PANEL_PERMISSIONS = [\n 'users.view',\n 'users.manage',\n 'platform.configure',\n 'platform.view_logs',\n 'plugins.configure',\n 'plugins.install',\n 'notices.publish',\n] as const\n\nexport type AccessAudience = 'all' | 'admin' | 'user'\nexport type AccessAudienceInput = AccessAudience | readonly AccessAudience[]\n\nexport interface RoleInfo {\n id?: number\n slug: string\n name?: string\n color?: string\n permissions?: readonly string[] | null\n project_scope?: 'all' | 'assigned' | string\n plugin_access?: readonly string[] | 'all' | null\n}\n\nexport interface PermissionUser {\n role?: string | null\n role_obj?: RoleInfo | null\n roleObj?: RoleInfo | null\n permissions?: readonly string[] | null\n}\n\nexport interface AccessPolicy {\n audience?: AccessAudienceInput\n visibleFor?: AccessAudienceInput\n requiresAuth?: boolean\n requiresAdmin?: boolean\n permissions?: readonly string[]\n anyPermissions?: readonly string[]\n plugin?: string\n}\n\n/** An object that carries an access rule in its nested `access` policy. */\nexport interface AccessControlled {\n access?: AccessPolicy\n}\n\nexport function getRoleInfo(user: PermissionUser | null | undefined): RoleInfo | null {\n return user?.roleObj ?? user?.role_obj ?? null\n}\n\nexport function isAdminRole(role: string | null | undefined): boolean {\n return role === ADMIN_ROLE\n}\n\nexport function isAdminUser(user: PermissionUser | null | undefined): boolean {\n return isAdminRole(user?.role) || getRoleInfo(user)?.slug === ADMIN_ROLE\n}\n\nexport function getAccessAudience(user: PermissionUser | null | undefined): Exclude<AccessAudience, 'all'> {\n return isAdminUser(user) ? 'admin' : 'user'\n}\n\nexport function getUserPermissions(user: PermissionUser | null | undefined): string[] {\n const rolePermissions = getRoleInfo(user)?.permissions\n if (rolePermissions?.length) return [...rolePermissions]\n return [...(user?.permissions ?? [])]\n}\n\nexport function hasAllPermissions(\n user: PermissionUser | null | undefined,\n permissions: readonly string[] = [],\n): boolean {\n if (permissions.length === 0) return true\n if (isAdminUser(user)) return true\n const granted = new Set(getUserPermissions(user))\n return permissions.every(permission => granted.has(permission))\n}\n\nexport function hasAnyPermission(\n user: PermissionUser | null | undefined,\n permissions: readonly string[] = [],\n): boolean {\n if (permissions.length === 0) return true\n if (isAdminUser(user)) return true\n const granted = new Set(getUserPermissions(user))\n return permissions.some(permission => granted.has(permission))\n}\n\nexport function canAccessAdmin(user: PermissionUser | null | undefined): boolean {\n return isAdminUser(user) || hasAnyPermission(user, ADMIN_PANEL_PERMISSIONS)\n}\n\nexport function canAccessPlugin(\n user: PermissionUser | null | undefined,\n pluginName: string | null | undefined,\n): boolean {\n if (!pluginName || isAdminUser(user)) return true\n const access = getRoleInfo(user)?.plugin_access\n // As on the platform: a value that is not null, 'all' or a list allows no plugin.\n if (access == null || access === 'all') return true\n return Array.isArray(access) && access.includes(pluginName)\n}\n\nexport function canAccessByPolicy(\n user: PermissionUser | null | undefined,\n policy: AccessPolicy = {},\n isAuthenticated = user !== null && user !== undefined,\n): boolean {\n if (policy.requiresAuth && !isAuthenticated) return false\n if (policy.requiresAdmin && !isAdminUser(user)) return false\n if (!audienceAllowsUser(policy.visibleFor ?? policy.audience, user)) return false\n if (!hasAllPermissions(user, policy.permissions)) return false\n if (!hasAnyPermission(user, policy.anyPermissions)) return false\n if (policy.plugin && !canAccessPlugin(user, policy.plugin)) return false\n return true\n}\n\nfunction audienceAllowsUser(\n audience: AccessAudienceInput | undefined,\n user: PermissionUser | null | undefined,\n): boolean {\n if (audience === undefined) return true\n const allowed = Array.isArray(audience) ? audience : [audience]\n if (allowed.includes('all')) return true\n return allowed.includes(getAccessAudience(user))\n}\n"],"mappings":";AAAA,IAAa,aAAa;AAE1B,IAAa,0BAA0B;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAqCA,SAAgB,YAAY,MAA0D;CACpF,OAAO,MAAM,WAAW,MAAM,YAAY;AAC5C;AAEA,SAAgB,YAAY,MAA0C;CACpE,OAAO,SAAS;AAClB;AAEA,SAAgB,YAAY,MAAkD;CAC5E,OAAO,YAAY,MAAM,IAAI,KAAK,YAAY,IAAI,CAAC,EAAE,SAAA;AACvD;AAEA,SAAgB,kBAAkB,MAAyE;CACzG,OAAO,YAAY,IAAI,IAAI,UAAU;AACvC;AAEA,SAAgB,mBAAmB,MAAmD;CACpF,MAAM,kBAAkB,YAAY,IAAI,CAAC,EAAE;CAC3C,IAAI,iBAAiB,QAAQ,OAAO,CAAC,GAAG,eAAe;CACvD,OAAO,CAAC,GAAI,MAAM,eAAe,CAAC,CAAE;AACtC;AAEA,SAAgB,kBACd,MACA,cAAiC,CAAC,GACzB;CACT,IAAI,YAAY,WAAW,GAAG,OAAO;CACrC,IAAI,YAAY,IAAI,GAAG,OAAO;CAC9B,MAAM,UAAU,IAAI,IAAI,mBAAmB,IAAI,CAAC;CAChD,OAAO,YAAY,OAAM,eAAc,QAAQ,IAAI,UAAU,CAAC;AAChE;AAEA,SAAgB,iBACd,MACA,cAAiC,CAAC,GACzB;CACT,IAAI,YAAY,WAAW,GAAG,OAAO;CACrC,IAAI,YAAY,IAAI,GAAG,OAAO;CAC9B,MAAM,UAAU,IAAI,IAAI,mBAAmB,IAAI,CAAC;CAChD,OAAO,YAAY,MAAK,eAAc,QAAQ,IAAI,UAAU,CAAC;AAC/D;AAEA,SAAgB,eAAe,MAAkD;CAC/E,OAAO,YAAY,IAAI,KAAK,iBAAiB,MAAM,uBAAuB;AAC5E;AAEA,SAAgB,gBACd,MACA,YACS;CACT,IAAI,CAAC,cAAc,YAAY,IAAI,GAAG,OAAO;CAC7C,MAAM,SAAS,YAAY,IAAI,CAAC,EAAE;CAElC,IAAI,UAAU,QAAQ,WAAW,OAAO,OAAO;CAC/C,OAAO,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,UAAU;AAC5D;AAEA,SAAgB,kBACd,MACA,SAAuB,CAAC,GACxB,kBAAkB,SAAS,QAAQ,SAAS,KAAA,GACnC;CACT,IAAI,OAAO,gBAAgB,CAAC,iBAAiB,OAAO;CACpD,IAAI,OAAO,iBAAiB,CAAC,YAAY,IAAI,GAAG,OAAO;CACvD,IAAI,CAAC,mBAAmB,OAAO,cAAc,OAAO,UAAU,IAAI,GAAG,OAAO;CAC5E,IAAI,CAAC,kBAAkB,MAAM,OAAO,WAAW,GAAG,OAAO;CACzD,IAAI,CAAC,iBAAiB,MAAM,OAAO,cAAc,GAAG,OAAO;CAC3D,IAAI,OAAO,UAAU,CAAC,gBAAgB,MAAM,OAAO,MAAM,GAAG,OAAO;CACnE,OAAO;AACT;AAEA,SAAS,mBACP,UACA,MACS;CACT,IAAI,aAAa,KAAA,GAAW,OAAO;CACnC,MAAM,UAAU,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;CAC9D,IAAI,QAAQ,SAAS,KAAK,GAAG,OAAO;CACpC,OAAO,QAAQ,SAAS,kBAAkB,IAAI,CAAC;AACjD"}