@morscherlab/mint-sdk 1.1.25 → 1.2.0-beta.2

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 (72) hide show
  1. package/README.md +9 -7
  2. package/dist/{ExperimentSelectorModal-B6P2msT4.js → ExperimentSelectorModal-DyPAUCLw.js} +3 -3
  3. package/dist/{ExperimentSelectorModal-B6P2msT4.js.map → ExperimentSelectorModal-DyPAUCLw.js.map} +1 -1
  4. package/dist/{SettingsModal-BW3Ar-uy.js → SettingsModal-BgKY93zh.js} +4 -4
  5. package/dist/{SettingsModal-BW3Ar-uy.js.map → SettingsModal-BgKY93zh.js.map} +1 -1
  6. package/dist/__tests__/composables/useEventStream.test.d.ts +1 -0
  7. package/dist/components/AutoGroupModal.vue.d.ts +1 -1
  8. package/dist/components/BioTemplateExperimentWorkspaceView.vue.d.ts +1 -1
  9. package/dist/components/JobsStatusTray.vue.d.ts +2 -9
  10. package/dist/components/SampleLegend.vue.d.ts +1 -1
  11. package/dist/components/SampleSelector.vue.d.ts +1 -1
  12. package/dist/components/SettingsModal.vue.d.ts +1 -1
  13. package/dist/components/SmartGroupFieldRecipe.vue.d.ts +1 -1
  14. package/dist/components/index.js +4 -4
  15. package/dist/{components-DQt0tcpY.js → components-DkHddRMl.js} +10 -16
  16. package/dist/components-DkHddRMl.js.map +1 -0
  17. package/dist/composables/index.d.ts +2 -3
  18. package/dist/composables/index.js +8 -8
  19. package/dist/composables/useEventStream.d.ts +43 -0
  20. package/dist/composables/useGeneratedAnalysis.d.ts +1 -3
  21. package/dist/composables/useJobsStatusTray.d.ts +1 -25
  22. package/dist/composables/usePluginClient.d.ts +3 -18
  23. package/dist/{composables-CkXDe7nm.js → composables-C_i90sGS.js} +32 -308
  24. package/dist/composables-C_i90sGS.js.map +1 -0
  25. package/dist/{experiment-Y9Iz7KYi.js → experiment-Cq145oEW.js} +2 -2
  26. package/dist/{experiment-Y9Iz7KYi.js.map → experiment-Cq145oEW.js.map} +1 -1
  27. package/dist/index.js +12 -12
  28. package/dist/install.js +4 -4
  29. package/dist/stores/auth.d.ts +0 -6
  30. package/dist/stores/index.js +2 -2
  31. package/dist/templates/index.js +2 -2
  32. package/dist/{templates-CovvRZ-9.js → templates-DsP51i-S.js} +2 -2
  33. package/dist/{templates-CovvRZ-9.js.map → templates-DsP51i-S.js.map} +1 -1
  34. package/dist/types/auth.d.ts +0 -1
  35. package/dist/{useApi-CDBq6z23.js → useApi-J6zFkHmn.js} +1 -2
  36. package/dist/useApi-J6zFkHmn.js.map +1 -0
  37. package/dist/{useControlSchema-BhoJzWzx.js → useControlSchema-BDx2Olha.js} +239 -103
  38. package/dist/useControlSchema-BDx2Olha.js.map +1 -0
  39. package/dist/{useExperimentSelector-D0pjj9WH.js → useExperimentSelector-CV4iNfMS.js} +2 -2
  40. package/dist/{useExperimentSelector-D0pjj9WH.js.map → useExperimentSelector-CV4iNfMS.js.map} +1 -1
  41. package/dist/{useFormBuilder-DiEcDis2.js → useFormBuilder-BLxG3BOX.js} +2 -2
  42. package/dist/{useFormBuilder-DiEcDis2.js.map → useFormBuilder-BLxG3BOX.js.map} +1 -1
  43. package/dist/{useProtocolTemplates-DHwSveE6.js → useProtocolTemplates-CS_SQl6U.js} +22 -113
  44. package/dist/useProtocolTemplates-CS_SQl6U.js.map +1 -0
  45. package/package.json +1 -1
  46. package/src/__tests__/components/GeneratedPluginView.test.ts +0 -5
  47. package/src/__tests__/components/JobsStatusTray.test.ts +20 -18
  48. package/src/__tests__/composables/useAuth.test.ts +23 -0
  49. package/src/__tests__/composables/useEventStream.test.ts +140 -0
  50. package/src/__tests__/composables/useGeneratedAnalysis.test.ts +103 -0
  51. package/src/__tests__/composables/useJobsStatusTray.test.ts +25 -161
  52. package/src/__tests__/composables/usePluginClient.test.ts +8 -13
  53. package/src/__tests__/docs/frontendDocsCatalog.test.ts +22 -1
  54. package/src/components/JobsStatusTray.story.vue +6 -10
  55. package/src/components/JobsStatusTray.vue +1 -16
  56. package/src/composables/index.ts +0 -7
  57. package/src/composables/useAuth.ts +0 -3
  58. package/src/composables/useEventStream.ts +314 -0
  59. package/src/composables/useGeneratedAnalysis.ts +40 -109
  60. package/src/composables/useJobsStatusTray.ts +17 -184
  61. package/src/composables/usePluginClient.ts +45 -249
  62. package/src/stores/auth.ts +0 -1
  63. package/src/types/auth.ts +0 -1
  64. package/dist/__tests__/composables/usePluginConfig.test.d.ts +0 -15
  65. package/dist/components-DQt0tcpY.js.map +0 -1
  66. package/dist/composables/usePluginConfig.d.ts +0 -25
  67. package/dist/composables-CkXDe7nm.js.map +0 -1
  68. package/dist/useApi-CDBq6z23.js.map +0 -1
  69. package/dist/useControlSchema-BhoJzWzx.js.map +0 -1
  70. package/dist/useProtocolTemplates-DHwSveE6.js.map +0 -1
  71. package/src/__tests__/composables/usePluginConfig.test.ts +0 -497
  72. package/src/composables/usePluginConfig.ts +0 -250
@@ -1,250 +0,0 @@
1
- import { ref, computed, onMounted, type Ref, type ComputedRef } from 'vue'
2
- import { useApi } from './useApi'
3
- import { usePlatformContext } from './usePlatformContext'
4
- import { getInjectedPlatformContext } from './platformContextHelpers'
5
- import { useRequestSyncState } from './useRequestSyncState'
6
-
7
- const PLATFORM_OWNED_SETTINGS_KEY = 'allowed_experiment_types'
8
-
9
- function cloneConfig(config: Record<string, unknown>): Record<string, unknown> {
10
- return JSON.parse(JSON.stringify(config)) as Record<string, unknown>
11
- }
12
-
13
- function hasOwnConfig(config: Record<string, unknown>, key: string): boolean {
14
- return Object.prototype.hasOwnProperty.call(config, key)
15
- }
16
-
17
- function changedTopLevelKeys(
18
- current: Record<string, unknown>,
19
- saved: Record<string, unknown>,
20
- ): Set<string> {
21
- return new Set(
22
- [...new Set([...Object.keys(current), ...Object.keys(saved)])].filter(
23
- key => JSON.stringify(current[key]) !== JSON.stringify(saved[key]),
24
- ),
25
- )
26
- }
27
-
28
- function topLevelConfigPatch(
29
- current: Record<string, unknown>,
30
- keys: Iterable<string>,
31
- ): Record<string, unknown> {
32
- return Object.fromEntries(
33
- [...keys].map(key => [
34
- key,
35
- hasOwnConfig(current, key) ? current[key] : null,
36
- ]),
37
- )
38
- }
39
-
40
- function platformOwnedConfigPatch(config: Record<string, unknown>): Record<string, unknown> {
41
- return {
42
- [PLATFORM_OWNED_SETTINGS_KEY]: hasOwnConfig(config, PLATFORM_OWNED_SETTINGS_KEY)
43
- ? config[PLATFORM_OWNED_SETTINGS_KEY]
44
- : null,
45
- }
46
- }
47
-
48
- function responseConfigPayload(response: {
49
- config?: Record<string, unknown>
50
- settings?: Record<string, unknown>
51
- }): Record<string, unknown> {
52
- return response.config ?? response.settings ?? {}
53
- }
54
-
55
- export interface UsePluginConfigReturn {
56
- /** Editable plugin configuration values. */
57
- config: Ref<Record<string, unknown>>
58
- /** Whether the config is currently loading. */
59
- isLoading: Ref<boolean>
60
- /** Whether the config is currently saving. */
61
- isSaving: Ref<boolean>
62
- /** Error message from the last failed operation, or null. */
63
- error: Ref<string | null>
64
- /** Timestamp of the last successful load, or null. */
65
- lastLoadedAt: Ref<Date | null>
66
- /** Timestamp of the last successful save, or null. */
67
- lastSavedAt: Ref<Date | null>
68
- /** Whether local config differs from the last loaded or saved snapshot. */
69
- isDirty: ComputedRef<boolean>
70
- /** Load plugin configuration from the platform. */
71
- load: () => Promise<void>
72
- /** Save plugin configuration to the platform. */
73
- save: () => Promise<boolean>
74
- /** Restore the last loaded or saved configuration snapshot. */
75
- reset: () => void
76
- }
77
-
78
- /** Loads, saves, and tracks dirty state for a plugin's persistent configuration via the platform API. */
79
- export function usePluginConfig(pluginName?: string): UsePluginConfigReturn {
80
- const injectedContext = getInjectedPlatformContext()
81
- const { plugin } = usePlatformContext()
82
- const platformApi = useApi()
83
- const pluginApi = useApi({
84
- baseUrl: injectedContext?.plugin?.api_prefix ?? plugin.value?.api_prefix,
85
- })
86
-
87
- const resolvedName = computed(
88
- () => pluginName ?? plugin.value?.name ?? injectedContext?.plugin?.name ?? '',
89
- )
90
- const usesPluginSettingsApi = computed(() => {
91
- const currentPlugin = injectedContext?.plugin ?? plugin.value
92
- return currentPlugin?.settings_api === 'plugin'
93
- && resolvedName.value === currentPlugin.name
94
- })
95
-
96
- const config = ref<Record<string, unknown>>({})
97
- const savedConfig = ref<Record<string, unknown>>({})
98
- const revision = ref<string | null>(null)
99
- const request = useRequestSyncState('Plugin config request failed.')
100
- const isLoading = ref(false)
101
- const isSaving = ref(false)
102
- const error = request.error
103
- const lastLoadedAt = request.lastLoadedAt
104
- const lastSavedAt = request.lastSavedAt
105
-
106
- const isDirty = computed(() => {
107
- return JSON.stringify(config.value) !== JSON.stringify(savedConfig.value)
108
- })
109
-
110
- async function load(): Promise<void> {
111
- const name = resolvedName.value
112
- if (!name) return
113
-
114
- isLoading.value = true
115
- try {
116
- const response = usesPluginSettingsApi.value
117
- ? await request.run(
118
- () => pluginApi.get<{
119
- settings: Record<string, unknown>
120
- mode?: string
121
- revision?: string
122
- }>('/settings'),
123
- { success: 'load', errorMessage: 'Failed to load plugin config' },
124
- )
125
- : await request.run(
126
- () => platformApi.get<{
127
- plugin_name: string
128
- config: Record<string, unknown>
129
- revision?: string
130
- }>(
131
- `/plugins/${encodeURIComponent(name)}/config`,
132
- ),
133
- { success: 'load', errorMessage: 'Failed to load plugin config' },
134
- )
135
- const responseConfig = responseConfigPayload(response)
136
- config.value = cloneConfig(responseConfig)
137
- savedConfig.value = cloneConfig(responseConfig)
138
- revision.value = response.revision ?? null
139
- } catch {
140
- // Error state is handled by request.run().
141
- } finally {
142
- isLoading.value = false
143
- }
144
- }
145
-
146
- async function save(): Promise<boolean> {
147
- const name = resolvedName.value
148
- if (!name) return false
149
-
150
- isSaving.value = true
151
- try {
152
- const payload = cloneConfig(config.value)
153
- let response: {
154
- plugin_name?: string
155
- config?: Record<string, unknown>
156
- settings?: Record<string, unknown>
157
- mode?: string
158
- revision?: string
159
- } | undefined
160
- let nextRevision = revision.value
161
-
162
- if (usesPluginSettingsApi.value) {
163
- response = await request.run(
164
- () => pluginApi.put<{
165
- settings: Record<string, unknown>
166
- mode?: string
167
- revision?: string
168
- }>(
169
- '/settings',
170
- payload,
171
- revision.value
172
- ? { headers: { 'If-Match': `"${revision.value}"` } }
173
- : undefined,
174
- ),
175
- { success: 'save', errorMessage: 'Failed to save plugin config' },
176
- )
177
- nextRevision = response.revision ?? nextRevision
178
- } else {
179
- const dirtyKeys = changedTopLevelKeys(payload, savedConfig.value)
180
- const platformOwnedChanged = dirtyKeys.has(PLATFORM_OWNED_SETTINGS_KEY)
181
- const typedKeys = [...dirtyKeys].filter(
182
- key => key !== PLATFORM_OWNED_SETTINGS_KEY,
183
- )
184
-
185
- if (typedKeys.length) {
186
- response = await request.run(
187
- () => platformApi.patch<{
188
- plugin_name: string
189
- config: Record<string, unknown>
190
- revision?: string
191
- }>(
192
- `/plugins/${encodeURIComponent(name)}/config`,
193
- { config: topLevelConfigPatch(payload, typedKeys) },
194
- ),
195
- { success: 'save', errorMessage: 'Failed to save plugin config' },
196
- )
197
- nextRevision = response.revision ?? nextRevision
198
- }
199
-
200
- if (platformOwnedChanged) {
201
- response = await request.run(
202
- () => platformApi.patch<{
203
- plugin_name: string
204
- config: Record<string, unknown>
205
- revision?: string
206
- }>(
207
- `/plugins/${encodeURIComponent(name)}/config`,
208
- { config: platformOwnedConfigPatch(payload) },
209
- ),
210
- { success: 'save', errorMessage: 'Failed to save plugin config' },
211
- )
212
- nextRevision = response.revision ?? nextRevision
213
- }
214
- }
215
-
216
- if (!response) return true
217
- const responseConfig = responseConfigPayload(response)
218
- config.value = cloneConfig(responseConfig)
219
- savedConfig.value = cloneConfig(responseConfig)
220
- revision.value = response.revision ?? nextRevision
221
- return true
222
- } catch {
223
- return false
224
- } finally {
225
- isSaving.value = false
226
- }
227
- }
228
-
229
- function reset(): void {
230
- config.value = cloneConfig(savedConfig.value)
231
- request.clearError()
232
- }
233
-
234
- onMounted(() => {
235
- load()
236
- })
237
-
238
- return {
239
- config,
240
- isLoading,
241
- isSaving,
242
- error,
243
- lastLoadedAt,
244
- lastSavedAt,
245
- isDirty,
246
- load,
247
- save,
248
- reset,
249
- }
250
- }