Skip to content

Commit 8586aef

Browse files
committed
Migrate to new niwrap deployment system
1 parent f40ff02 commit 8586aef

File tree

8 files changed

+178
-53
lines changed

8 files changed

+178
-53
lines changed

package-lock.json

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
},
4747
"dependencies": {
4848
"mode-watcher": "^1.1.0",
49-
"niwrap": "^0.6.3",
5049
"shell-quote": "^1.8.3",
5150
"shiki": "^3.11.0",
5251
"shlex": "^3.0.0",

src/lib/components/Contents.svelte

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,18 @@
2323
Copy,
2424
Check,
2525
RefreshCw,
26-
Package,
2726
Loader2,
2827
Code
2928
} from '@lucide/svelte';
3029
import { getSchemaMetadata } from '$lib/services/schema/schemaPath';
3130
import { getAppInputSchema, getAppOutputSchema } from '$lib/services/packages.svelte';
3231
3332
interface Props {
34-
packageId: string;
33+
packageName: string;
3534
descriptorId: string;
3635
}
3736
38-
let { packageId, descriptorId }: Props = $props();
37+
let { packageName, descriptorId }: Props = $props();
3938
4039
// State management
4140
let descriptorInputSchema: object | null = $state(null);
@@ -45,16 +44,26 @@
4544
let error = $state<string | null>(null);
4645
let activeTab = $state('command');
4746
let commandCopied = $state(false);
47+
let niwrapExecutionData = $state<{
48+
success: true;
49+
cargs: string[];
50+
outputObject: any;
51+
} | {
52+
success: false;
53+
error: string;
54+
} | null>(null);
4855
4956
// Memoized computations
50-
const niwrapExecutionData = $derived.by(() => {
57+
$effect(() => {
5158
if (!descriptorConfig || Object.keys(descriptorConfig).length === 0) {
52-
return { success: false, error: 'No configuration provided', cargs: [], outputFiles: [], outputObject: {} };
59+
niwrapExecutionData = { success: false, error: 'No configuration provided' };
5360
}
54-
return niwrapExecute(descriptorConfig);
61+
niwrapExecute(descriptorConfig).then((d) => niwrapExecutionData = d);
5562
});
5663
5764
const commandArgs = $derived(() => {
65+
if (!niwrapExecutionData) return [];
66+
5867
if (!niwrapExecutionData.success) {
5968
return niwrapDeathMessage();
6069
}
@@ -67,13 +76,12 @@
6776
});
6877
6978
const outputFiles = $derived.by(() => {
79+
if (!niwrapExecutionData) return [];
7080
const ret = [];
7181
if (!niwrapExecutionData.success) return [];
7282
for (const [key, value] of Object.entries(niwrapExecutionData.outputObject)) {
7383
if (!(typeof value === 'string')) continue;
7484
const keyMetadata = descriptorOutputSchema && getSchemaMetadata(descriptorOutputSchema, key);
75-
console.log(descriptorOutputSchema)
76-
console.log(key)
7785
ret.push({
7886
path: '/outputs/' + value,
7987
title: keyMetadata?.title ?? 'Title',
@@ -83,20 +91,20 @@
8391
}
8492
return ret;
8593
});
86-
const niwrapError = $derived(niwrapExecutionData.success ? null : niwrapExecutionData.error);
94+
const niwrapError = $derived(niwrapExecutionData?.success ? null : niwrapExecutionData?.error ?? "???");
8795
const hasConfig = $derived(Object.keys(descriptorConfig).length > 0);
8896
const hasOutputs = $derived(outputFiles.length > 0);
8997
9098
// Schema fetching with retry logic
9199
async function fetchSchema() {
92-
if (!packageId || !descriptorId) return;
100+
if (!packageName || !descriptorId) return;
93101
94102
isLoading = true;
95103
error = null;
96104
97105
try {
98-
const inputSchema = await getAppInputSchema(packageId, descriptorId);
99-
const outputSchema = await getAppOutputSchema(packageId, descriptorId);
106+
const inputSchema = await getAppInputSchema(packageName, descriptorId);
107+
const outputSchema = await getAppOutputSchema(packageName, descriptorId);
100108
descriptorInputSchema = inputSchema;
101109
descriptorOutputSchema = outputSchema;
102110
} catch (err) {

src/lib/components/FileTreeView.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
7575
// Reactive state using Svelte 5 runes
7676
const tree = $derived(buildTree(files));
77+
// svelte-ignore state_referenced_locally
7778
let expanded = $state(getAllFolderPaths(tree));
7879
7980
// Re-expand all folders when files change

src/lib/components/QuickSelector.svelte

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@
139139
<div class="flex min-w-0 items-center">
140140
<PackageIcon class={cn('mr-2 flex-shrink-0', compact ? 'h-3 w-3' : 'h-4 w-4')} />
141141
<span class={cn('truncate', compact ? 'text-sm' : 'text-base')}>
142-
{selectedPackage?.name || 'Choose a package...'}
142+
{selectedPackage?.docs.title || 'Choose a package...'}
143143
</span>
144144
</div>
145145
<ChevronsUpDownIcon
@@ -156,7 +156,7 @@
156156
<Command.Group>
157157
{#each packages as pkg (pkg.name)}
158158
<Command.Item
159-
value={pkg.name}
159+
value={pkg.docs.title ?? pkg.name}
160160
onSelect={() => selectPackage(pkg.name)}
161161
class="flex cursor-pointer items-center justify-between py-3"
162162
>
@@ -253,7 +253,7 @@
253253
<Command.Group>
254254
{#each apps as app (app.id)}
255255
<Command.Item
256-
value={app.id}
256+
value={app.name}
257257
onSelect={() => selectApp(app)}
258258
class="flex cursor-pointer items-center py-2"
259259
>

src/lib/services/niwrapExecution.ts

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,61 @@
1-
import * as niwrap from 'niwrap';
21
import * as styxdefs from 'styxdefs';
32

3+
// Cache for the dynamically loaded module
4+
let niwrapModule: any = null;
5+
let loadingPromise: Promise<any> | null = null;
6+
7+
/**
8+
* Dynamically loads the niwrap module from the hosted build artifacts
9+
* Uses browser detection to choose appropriate module format
10+
*/
11+
async function loadNiwrap(): Promise<any> {
12+
if (niwrapModule) {
13+
return niwrapModule;
14+
}
15+
16+
// If already loading, return the same promise to avoid multiple loads
17+
if (loadingPromise) {
18+
return loadingPromise;
19+
}
20+
21+
loadingPromise = (async () => {
22+
try {
23+
// Try ESM first (preferred for modern browsers)
24+
try {
25+
const module = await import('https://styx-api.github.io/niwrap/js/index.esm.js');
26+
niwrapModule = module;
27+
return niwrapModule;
28+
} catch (esmError) {
29+
console.warn('Failed to load ESM module, falling back to CJS:', esmError);
30+
31+
// Fallback to CJS - though this might need special handling in browser
32+
const module = await import('https://styx-api.github.io/niwrap/js/index.cjs.js');
33+
niwrapModule = module;
34+
return niwrapModule;
35+
}
36+
} catch (error) {
37+
console.error('Failed to load niwrap module:', error);
38+
throw new Error(`Failed to load niwrap: ${error instanceof Error ? error.message : String(error)}`);
39+
} finally {
40+
loadingPromise = null;
41+
}
42+
})();
43+
44+
return loadingPromise;
45+
}
46+
47+
/**
48+
* Preload the niwrap module - call this early in your app lifecycle
49+
*/
50+
export async function preloadNiwrap(): Promise<void> {
51+
try {
52+
await loadNiwrap();
53+
console.log('NiWrap module preloaded successfully');
54+
} catch (error) {
55+
console.error('Failed to preload NiWrap module:', error);
56+
}
57+
}
58+
459
export function niwrapDeathMessage(): string[] {
560
const deathMessages = [
661
['💀', 'NiWrap died of dysentery'],
@@ -22,37 +77,49 @@ export function niwrapDeathMessage(): string[] {
2277
return deathMessages[Math.floor(Math.random() * deathMessages.length)];
2378
}
2479

25-
export function niwrapExecute(config: object):
80+
export async function niwrapExecute(config: object): Promise<
2681
| {
27-
success: true;
28-
cargs: string[];
29-
outputObject: any;
30-
}
82+
success: true;
83+
cargs: string[];
84+
outputObject: any;
85+
}
3186
| {
32-
success: false;
33-
error: string;
34-
} {
87+
success: false;
88+
error: string;
89+
}
90+
> {
3591
if (!config || !('@type' in config)) {
3692
return {
3793
success: false,
3894
error: 'Invalid config: missing config or @type'
3995
};
4096
}
97+
4198
try {
99+
// Dynamically load the niwrap module
100+
const niwrap = await loadNiwrap();
101+
42102
const runner = new styxdefs.DryRunner();
43103
const outputs = niwrap.execute(config, runner);
104+
44105
return {
45106
success: true,
46107
cargs: runner.lastCargs ?? [],
47108
outputObject: outputs
48109
};
49110
} catch (error) {
50111
const errorString = error instanceof Error ? error.message : String(error);
51-
console.log(error)
112+
console.log(error);
52113
console.error('NiWrap execution failed:', errorString);
114+
53115
return {
54116
success: false,
55117
error: errorString
56118
};
57119
}
58120
}
121+
122+
export async function niwrapVersion() {
123+
const niwrap = await loadNiwrap();
124+
return niwrap.version;
125+
}

src/lib/services/packages.svelte.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@ export type App = {
1414

1515
let index: Index | null = $state(null);
1616
let loading = $state(false);
17-
let initialized = false;
17+
let loadPromise: Promise<void> | null = null; // Cache loading promise
1818

1919
export async function getIndex() {
20-
if (!initialized) {
21-
initialized = true;
22-
await loadData(); // Auto-load on first access
20+
if (index !== null) {
21+
return index; // Already loaded
2322
}
23+
24+
if (loadPromise === null) {
25+
// First caller starts the loading
26+
loadPromise = loadData();
27+
}
28+
29+
// All callers wait for the same promise
30+
await loadPromise;
2431
return index;
2532
}
2633

@@ -40,6 +47,9 @@ export async function loadData() {
4047
}
4148
} catch (err) {
4249
console.error('Failed to load indices:', err);
50+
// Reset loadPromise on error so it can be retried
51+
loadPromise = null;
52+
throw err; // Re-throw to let callers handle the error
4353
} finally {
4454
loading = false;
4555
}

0 commit comments

Comments
 (0)