@morscherlab/mld-sdk 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/composables/useExperimentSelector.js +18 -3
- package/dist/composables/useExperimentSelector.js.map +1 -1
- package/dist/composables/usePlatformContext.d.ts +2 -0
- package/dist/types/platform.d.ts +1 -0
- package/package.json +1 -1
- package/src/composables/useExperimentSelector.ts +22 -4
- package/src/types/platform.ts +3 -0
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { ref, computed, reactive, watch, onScopeDispose } from "vue";
|
|
2
2
|
import { useApi } from "./useApi.js";
|
|
3
|
+
function getPlatformContext() {
|
|
4
|
+
if (typeof window === "undefined") return void 0;
|
|
5
|
+
return window.__MLD_PLATFORM__;
|
|
6
|
+
}
|
|
7
|
+
function getPlatformApiUrl() {
|
|
8
|
+
var _a;
|
|
9
|
+
return (_a = getPlatformContext()) == null ? void 0 : _a.platformApiUrl;
|
|
10
|
+
}
|
|
3
11
|
function useExperimentSelector(options = {}) {
|
|
4
12
|
const { limit = 100, immediate = false, experimentType, apiBaseUrl } = options;
|
|
5
|
-
const
|
|
13
|
+
const platformBase = apiBaseUrl ?? getPlatformApiUrl();
|
|
14
|
+
const api = useApi();
|
|
6
15
|
const experiments = ref([]);
|
|
7
16
|
const total = ref(0);
|
|
8
17
|
const selectedExperiment = ref(null);
|
|
@@ -15,14 +24,20 @@ function useExperimentSelector(options = {}) {
|
|
|
15
24
|
error.value = null;
|
|
16
25
|
try {
|
|
17
26
|
const params = new URLSearchParams();
|
|
18
|
-
|
|
27
|
+
const effectiveType = experimentType ?? (() => {
|
|
28
|
+
var _a;
|
|
29
|
+
const types = (_a = getPlatformContext()) == null ? void 0 : _a.allowedExperimentTypes;
|
|
30
|
+
return (types == null ? void 0 : types.length) === 1 ? types[0] : void 0;
|
|
31
|
+
})();
|
|
32
|
+
if (effectiveType) params.set("experiment_type", effectiveType);
|
|
19
33
|
if (filters.status) params.set("status", filters.status);
|
|
20
34
|
if (filters.search) params.set("search", filters.search);
|
|
21
35
|
if (filters.project) params.set("project", filters.project);
|
|
22
36
|
params.set("limit", String(limit));
|
|
23
37
|
params.set("skip", String(page.value * limit));
|
|
24
38
|
const query = params.toString();
|
|
25
|
-
const
|
|
39
|
+
const base = platformBase ?? "/api";
|
|
40
|
+
const url = `${base}/experiments${query ? `?${query}` : ""}`;
|
|
26
41
|
const data = await api.get(url);
|
|
27
42
|
if (page.value === 0) {
|
|
28
43
|
experiments.value = data.experiments;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useExperimentSelector.js","sources":["../../src/composables/useExperimentSelector.ts"],"sourcesContent":["import { ref, reactive, computed, watch, onScopeDispose, type Ref, type ComputedRef } from 'vue'\nimport { useApi } from './useApi'\nimport type { ExperimentSummary, ExperimentListResponse, ExperimentFilters } from '../types'\n\nexport interface UseExperimentSelectorOptions {\n experimentType?: string\n apiBaseUrl?: string\n limit?: number\n immediate?: boolean\n}\n\nexport interface UseExperimentSelectorReturn {\n experiments: Ref<ExperimentSummary[]>\n total: Ref<number>\n selectedExperiment: Ref<ExperimentSummary | null>\n filters: ExperimentFilters\n isLoading: Ref<boolean>\n error: Ref<string | null>\n page: Ref<number>\n hasMore: ComputedRef<boolean>\n fetch: () => Promise<void>\n loadMore: () => Promise<void>\n reset: () => void\n select: (experiment: ExperimentSummary) => void\n clear: () => void\n}\n\nexport function useExperimentSelector(\n options: UseExperimentSelectorOptions = {},\n): UseExperimentSelectorReturn {\n const { limit = 100, immediate = false, experimentType, apiBaseUrl } = options\n const
|
|
1
|
+
{"version":3,"file":"useExperimentSelector.js","sources":["../../src/composables/useExperimentSelector.ts"],"sourcesContent":["import { ref, reactive, computed, watch, onScopeDispose, type Ref, type ComputedRef } from 'vue'\nimport { useApi } from './useApi'\nimport type { ExperimentSummary, ExperimentListResponse, ExperimentFilters, PlatformContext } from '../types'\n\nfunction getPlatformContext(): PlatformContext | undefined {\n if (typeof window === 'undefined') return undefined\n return (window as unknown as { __MLD_PLATFORM__?: PlatformContext }).__MLD_PLATFORM__\n}\n\nfunction getPlatformApiUrl(): string | undefined {\n return getPlatformContext()?.platformApiUrl\n}\n\nexport interface UseExperimentSelectorOptions {\n experimentType?: string\n apiBaseUrl?: string\n limit?: number\n immediate?: boolean\n}\n\nexport interface UseExperimentSelectorReturn {\n experiments: Ref<ExperimentSummary[]>\n total: Ref<number>\n selectedExperiment: Ref<ExperimentSummary | null>\n filters: ExperimentFilters\n isLoading: Ref<boolean>\n error: Ref<string | null>\n page: Ref<number>\n hasMore: ComputedRef<boolean>\n fetch: () => Promise<void>\n loadMore: () => Promise<void>\n reset: () => void\n select: (experiment: ExperimentSummary) => void\n clear: () => void\n}\n\nexport function useExperimentSelector(\n options: UseExperimentSelectorOptions = {},\n): UseExperimentSelectorReturn {\n const { limit = 100, immediate = false, experimentType, apiBaseUrl } = options\n const platformBase = apiBaseUrl ?? getPlatformApiUrl()\n const api = useApi()\n\n const experiments = ref<ExperimentSummary[]>([])\n const total = ref(0)\n const selectedExperiment = ref<ExperimentSummary | null>(null)\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n const page = ref(0)\n\n const hasMore = computed(() => experiments.value.length < total.value)\n\n async function fetchExperiments(): Promise<void> {\n isLoading.value = true\n error.value = null\n try {\n const params = new URLSearchParams()\n // Priority: explicit option > platform context (single type) > no filter\n const effectiveType = experimentType\n ?? (() => {\n const types = getPlatformContext()?.allowedExperimentTypes\n return types?.length === 1 ? types[0] : undefined\n })()\n if (effectiveType) params.set('experiment_type', effectiveType)\n if (filters.status) params.set('status', filters.status)\n if (filters.search) params.set('search', filters.search)\n if (filters.project) params.set('project', filters.project)\n params.set('limit', String(limit))\n params.set('skip', String(page.value * limit))\n\n const query = params.toString()\n // Use absolute platform URL in integrated mode to bypass plugin's baseURL\n const base = platformBase ?? '/api'\n const url = `${base}/experiments${query ? `?${query}` : ''}`\n const data = await api.get<ExperimentListResponse>(url)\n\n if (page.value === 0) {\n experiments.value = data.experiments\n } else {\n experiments.value = [...experiments.value, ...data.experiments]\n }\n total.value = data.total\n } catch (e) {\n error.value = e instanceof Error ? e.message : 'Failed to fetch experiments'\n if (page.value === 0) {\n experiments.value = []\n total.value = 0\n }\n } finally {\n isLoading.value = false\n }\n }\n\n async function loadMore(): Promise<void> {\n if (!hasMore.value || isLoading.value) return\n page.value++\n await fetchExperiments()\n }\n\n function reset(): void {\n page.value = 0\n experiments.value = []\n total.value = 0\n fetchExperiments()\n }\n\n function select(experiment: ExperimentSummary): void {\n selectedExperiment.value = experiment\n }\n\n function clear(): void {\n selectedExperiment.value = null\n filters.search = undefined\n filters.status = undefined\n filters.project = undefined\n page.value = 0\n }\n\n const filters: ExperimentFilters = reactive({\n search: undefined,\n status: undefined,\n project: undefined,\n })\n\n // Debounced watch on search filter\n let debounceTimer: ReturnType<typeof setTimeout> | null = null\n watch(\n () => filters.search,\n () => {\n if (debounceTimer) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n page.value = 0\n fetchExperiments()\n }, 300)\n },\n )\n\n // Immediate watch on status/project filters (no debounce needed)\n watch(\n () => [filters.status, filters.project],\n () => {\n page.value = 0\n fetchExperiments()\n },\n )\n\n onScopeDispose(() => {\n if (debounceTimer) clearTimeout(debounceTimer)\n })\n\n if (immediate) {\n fetchExperiments()\n }\n\n return {\n experiments,\n total,\n selectedExperiment,\n filters,\n isLoading,\n error,\n page,\n hasMore,\n fetch: fetchExperiments,\n loadMore,\n reset,\n select,\n clear,\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -52,6 +52,7 @@ export declare function usePlatformContext(options?: PlatformContextOptions): {
|
|
|
52
52
|
role: string;
|
|
53
53
|
} | undefined;
|
|
54
54
|
theme: "light" | "dark" | "system";
|
|
55
|
+
allowedExperimentTypes?: string[] | null | undefined;
|
|
55
56
|
features?: {
|
|
56
57
|
experiments?: boolean | undefined;
|
|
57
58
|
passkey?: boolean | undefined;
|
|
@@ -84,6 +85,7 @@ export declare function usePlatformContext(options?: PlatformContextOptions): {
|
|
|
84
85
|
role: string;
|
|
85
86
|
} | undefined;
|
|
86
87
|
theme: "light" | "dark" | "system";
|
|
88
|
+
allowedExperimentTypes?: string[] | null | undefined;
|
|
87
89
|
features?: {
|
|
88
90
|
experiments?: boolean | undefined;
|
|
89
91
|
passkey?: boolean | undefined;
|
package/dist/types/platform.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { ref, reactive, computed, watch, onScopeDispose, type Ref, type ComputedRef } from 'vue'
|
|
2
2
|
import { useApi } from './useApi'
|
|
3
|
-
import type { ExperimentSummary, ExperimentListResponse, ExperimentFilters } from '../types'
|
|
3
|
+
import type { ExperimentSummary, ExperimentListResponse, ExperimentFilters, PlatformContext } from '../types'
|
|
4
|
+
|
|
5
|
+
function getPlatformContext(): PlatformContext | undefined {
|
|
6
|
+
if (typeof window === 'undefined') return undefined
|
|
7
|
+
return (window as unknown as { __MLD_PLATFORM__?: PlatformContext }).__MLD_PLATFORM__
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getPlatformApiUrl(): string | undefined {
|
|
11
|
+
return getPlatformContext()?.platformApiUrl
|
|
12
|
+
}
|
|
4
13
|
|
|
5
14
|
export interface UseExperimentSelectorOptions {
|
|
6
15
|
experimentType?: string
|
|
@@ -29,7 +38,8 @@ export function useExperimentSelector(
|
|
|
29
38
|
options: UseExperimentSelectorOptions = {},
|
|
30
39
|
): UseExperimentSelectorReturn {
|
|
31
40
|
const { limit = 100, immediate = false, experimentType, apiBaseUrl } = options
|
|
32
|
-
const
|
|
41
|
+
const platformBase = apiBaseUrl ?? getPlatformApiUrl()
|
|
42
|
+
const api = useApi()
|
|
33
43
|
|
|
34
44
|
const experiments = ref<ExperimentSummary[]>([])
|
|
35
45
|
const total = ref(0)
|
|
@@ -45,7 +55,13 @@ export function useExperimentSelector(
|
|
|
45
55
|
error.value = null
|
|
46
56
|
try {
|
|
47
57
|
const params = new URLSearchParams()
|
|
48
|
-
|
|
58
|
+
// Priority: explicit option > platform context (single type) > no filter
|
|
59
|
+
const effectiveType = experimentType
|
|
60
|
+
?? (() => {
|
|
61
|
+
const types = getPlatformContext()?.allowedExperimentTypes
|
|
62
|
+
return types?.length === 1 ? types[0] : undefined
|
|
63
|
+
})()
|
|
64
|
+
if (effectiveType) params.set('experiment_type', effectiveType)
|
|
49
65
|
if (filters.status) params.set('status', filters.status)
|
|
50
66
|
if (filters.search) params.set('search', filters.search)
|
|
51
67
|
if (filters.project) params.set('project', filters.project)
|
|
@@ -53,7 +69,9 @@ export function useExperimentSelector(
|
|
|
53
69
|
params.set('skip', String(page.value * limit))
|
|
54
70
|
|
|
55
71
|
const query = params.toString()
|
|
56
|
-
|
|
72
|
+
// Use absolute platform URL in integrated mode to bypass plugin's baseURL
|
|
73
|
+
const base = platformBase ?? '/api'
|
|
74
|
+
const url = `${base}/experiments${query ? `?${query}` : ''}`
|
|
57
75
|
const data = await api.get<ExperimentListResponse>(url)
|
|
58
76
|
|
|
59
77
|
if (page.value === 0) {
|
package/src/types/platform.ts
CHANGED
|
@@ -64,6 +64,9 @@ export interface PlatformContext {
|
|
|
64
64
|
// Theme preference
|
|
65
65
|
theme: 'light' | 'dark' | 'system'
|
|
66
66
|
|
|
67
|
+
// Allowed experiment types for this plugin (from admin config)
|
|
68
|
+
allowedExperimentTypes?: string[] | null
|
|
69
|
+
|
|
67
70
|
// Features enabled in platform
|
|
68
71
|
features?: {
|
|
69
72
|
experiments?: boolean
|