@weave-apps/sdk 0.1.4 → 0.1.6
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/README.md +327 -52
- package/bin/compile.js +42 -17
- package/bin/init.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,10 @@ Official SDK for building third-party applications for the Weave Platform.
|
|
|
10
10
|
- ✅ **Project Scaffolding** - Quick start with `weave-init`
|
|
11
11
|
- ✅ **DOM API** - Secure parent page DOM manipulation
|
|
12
12
|
- ✅ **Shadow DOM Isolation** - Scoped styles and encapsulation
|
|
13
|
+
- ✅ **Background Services** - Run code without user interaction
|
|
14
|
+
- ✅ **Form Integration** - Auto-fill forms with extracted data
|
|
15
|
+
- ✅ **AI Integration** - Leverage AI for smart form filling
|
|
16
|
+
- ✅ **Data Persistence** - Store app data with the Weave API
|
|
13
17
|
|
|
14
18
|
## Quick Start
|
|
15
19
|
|
|
@@ -19,6 +23,7 @@ Official SDK for building third-party applications for the Weave Platform.
|
|
|
19
23
|
npx weave-init my-custom-app
|
|
20
24
|
cd my-custom-app
|
|
21
25
|
npm install
|
|
26
|
+
npm run build
|
|
22
27
|
```
|
|
23
28
|
|
|
24
29
|
### 2. Develop Your App
|
|
@@ -26,7 +31,7 @@ npm install
|
|
|
26
31
|
Edit `src/app.ts`:
|
|
27
32
|
|
|
28
33
|
```typescript
|
|
29
|
-
import { WeaveBaseApp } from '@weave/
|
|
34
|
+
import { WeaveBaseApp } from '@weave-apps/sdk';
|
|
30
35
|
|
|
31
36
|
class MyCustomApp extends WeaveBaseApp {
|
|
32
37
|
constructor() {
|
|
@@ -39,24 +44,43 @@ class MyCustomApp extends WeaveBaseApp {
|
|
|
39
44
|
author: 'Your Name',
|
|
40
45
|
tags: ['custom']
|
|
41
46
|
});
|
|
47
|
+
|
|
48
|
+
this.state = {
|
|
49
|
+
count: 0
|
|
50
|
+
};
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
protected render(): void {
|
|
45
54
|
this.renderHTML(`
|
|
46
55
|
<style>
|
|
47
|
-
.container {
|
|
48
|
-
|
|
56
|
+
.container {
|
|
57
|
+
padding: 20px;
|
|
58
|
+
font-family: system-ui, sans-serif;
|
|
59
|
+
}
|
|
60
|
+
button {
|
|
61
|
+
padding: 10px 20px;
|
|
62
|
+
background: #3b82f6;
|
|
63
|
+
color: white;
|
|
64
|
+
border: none;
|
|
65
|
+
border-radius: 6px;
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
}
|
|
68
|
+
button:hover {
|
|
69
|
+
background: #2563eb;
|
|
70
|
+
}
|
|
49
71
|
</style>
|
|
50
72
|
<div class="container">
|
|
51
|
-
<h1>
|
|
52
|
-
<
|
|
73
|
+
<h1>Counter App</h1>
|
|
74
|
+
<p>Count: <span id="count">${this.state.count}</span></p>
|
|
75
|
+
<button id="incrementBtn">Increment</button>
|
|
53
76
|
</div>
|
|
54
77
|
`);
|
|
55
78
|
}
|
|
56
79
|
|
|
57
80
|
protected setupEventListeners(): void {
|
|
58
|
-
this.query('#
|
|
59
|
-
|
|
81
|
+
this.query('#incrementBtn')?.addEventListener('click', () => {
|
|
82
|
+
this.setState({ count: this.state.count + 1 });
|
|
83
|
+
this.render();
|
|
60
84
|
});
|
|
61
85
|
}
|
|
62
86
|
}
|
|
@@ -72,19 +96,19 @@ npm run build
|
|
|
72
96
|
|
|
73
97
|
This runs:
|
|
74
98
|
1. `tsc` - Compiles TypeScript to JavaScript
|
|
75
|
-
2. `weave-
|
|
99
|
+
2. `weave-compile` - Transpiles to clean, platform-ready JavaScript
|
|
76
100
|
|
|
77
|
-
Output: `dist/app.js` - ready for upload to Weave Platform.
|
|
101
|
+
Output: `dist/my-custom-app.js` - ready for upload to Weave Platform.
|
|
78
102
|
|
|
79
103
|
### 4. Upload to Weave
|
|
80
104
|
|
|
81
|
-
Upload the generated `dist/app.js` file to the Weave Platform via the Enterprise Management Console.
|
|
105
|
+
Upload the generated `dist/my-custom-app.js` file to the Weave Platform via the Enterprise Management Console.
|
|
82
106
|
|
|
83
107
|
## API Reference
|
|
84
108
|
|
|
85
109
|
### WeaveBaseApp
|
|
86
110
|
|
|
87
|
-
Base class for all Weave apps.
|
|
111
|
+
Base class for all Weave apps. Handles state management, rendering, and lifecycle hooks.
|
|
88
112
|
|
|
89
113
|
#### Constructor
|
|
90
114
|
|
|
@@ -99,53 +123,153 @@ constructor(appInfo: WeaveAppInfo)
|
|
|
99
123
|
- `category` (string) - App category
|
|
100
124
|
- `description` (string) - Detailed description
|
|
101
125
|
- `author` (string) - Author name
|
|
102
|
-
- `tags` (string[]) - Optional tags
|
|
126
|
+
- `tags` (string[])` - Optional tags
|
|
127
|
+
|
|
128
|
+
#### Settings & State
|
|
129
|
+
|
|
130
|
+
Define typed settings and state:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
interface MyAppSettings {
|
|
134
|
+
apiEndpoint?: string;
|
|
135
|
+
debugMode?: boolean;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
interface MyAppState {
|
|
139
|
+
isLoading: boolean;
|
|
140
|
+
data: any;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
class MyApp extends WeaveBaseApp<MyAppSettings, MyAppState> {
|
|
144
|
+
constructor() {
|
|
145
|
+
super({ /* ... */ });
|
|
146
|
+
|
|
147
|
+
this.state = {
|
|
148
|
+
isLoading: false,
|
|
149
|
+
data: null
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Access settings (injected from Enterprise Console)
|
|
153
|
+
console.log(this.appSettings?.apiEndpoint);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
103
157
|
|
|
104
158
|
#### Methods to Implement
|
|
105
159
|
|
|
106
|
-
##### `render(): void
|
|
160
|
+
##### `render(): void | Promise<void>`
|
|
107
161
|
Render your app's UI. Use `this.renderHTML()` to inject HTML.
|
|
108
162
|
|
|
163
|
+
```typescript
|
|
164
|
+
protected async render(): Promise<void> {
|
|
165
|
+
this.renderHTML(`
|
|
166
|
+
<div>Content here</div>
|
|
167
|
+
`);
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
109
171
|
##### `setupEventListeners(): void` (Optional)
|
|
110
172
|
Setup event listeners for your app's elements.
|
|
111
173
|
|
|
174
|
+
```typescript
|
|
175
|
+
protected setupEventListeners(): void {
|
|
176
|
+
this.query('#myBtn')?.addEventListener('click', () => {
|
|
177
|
+
// Handle click
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
##### `onBackgroundService(): void` (Optional)
|
|
183
|
+
Background service that runs when sidebar loads, even if app drawer isn't open.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
async onBackgroundService(): Promise<void> {
|
|
187
|
+
const url = await window.weaveDOM.getPageUrl();
|
|
188
|
+
if (url.includes('mypage.com')) {
|
|
189
|
+
// Do something on background
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
##### `onUrlChange(newUrl: string): void` (Optional)
|
|
195
|
+
Called when the page URL changes (SPA navigation).
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
async onUrlChange(newUrl: string): Promise<void> {
|
|
199
|
+
if (newUrl.includes('form')) {
|
|
200
|
+
await this.checkAndInjectButton();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
112
205
|
##### `cleanup(): void` (Optional)
|
|
113
|
-
Cleanup when app is removed from DOM.
|
|
206
|
+
Cleanup when app is removed from DOM. Clear intervals, listeners, watchers.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
protected cleanup(): void {
|
|
210
|
+
if (this.myInterval) {
|
|
211
|
+
clearInterval(this.myInterval);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
114
215
|
|
|
115
216
|
#### Helper Methods
|
|
116
217
|
|
|
117
218
|
##### `renderHTML(html: string): void`
|
|
118
219
|
Renders HTML into the shadow root.
|
|
119
220
|
|
|
221
|
+
```typescript
|
|
222
|
+
this.renderHTML('<h1>Hello</h1>');
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
##### `setState(updates: object): void`
|
|
226
|
+
Update app state (shallow merge).
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
this.setState({ isLoading: true, count: 5 });
|
|
230
|
+
```
|
|
231
|
+
|
|
120
232
|
##### `query<T>(selector: string): T | null`
|
|
121
233
|
Query a single element in shadow root.
|
|
122
234
|
|
|
235
|
+
```typescript
|
|
236
|
+
const btn = this.query<HTMLButtonElement>('#myBtn');
|
|
237
|
+
```
|
|
238
|
+
|
|
123
239
|
##### `queryAll<T>(selector: string): NodeListOf<T>`
|
|
124
240
|
Query all matching elements in shadow root.
|
|
125
241
|
|
|
126
|
-
|
|
127
|
-
|
|
242
|
+
```typescript
|
|
243
|
+
const buttons = this.queryAll<HTMLButtonElement>('button');
|
|
244
|
+
```
|
|
128
245
|
|
|
129
246
|
### WeaveDOMAPI
|
|
130
247
|
|
|
131
|
-
API for interacting with the parent page DOM.
|
|
248
|
+
API for securely interacting with the parent page DOM. Available globally as `window.weaveDOM`.
|
|
132
249
|
|
|
133
|
-
|
|
250
|
+
#### URL & Navigation
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// Get current page URL
|
|
254
|
+
const url = await window.weaveDOM.getPageUrl();
|
|
255
|
+
|
|
256
|
+
// Open new tab
|
|
257
|
+
window.open(url, '_blank');
|
|
258
|
+
```
|
|
134
259
|
|
|
135
260
|
#### Read Operations
|
|
136
261
|
|
|
137
262
|
```typescript
|
|
138
263
|
// Query element
|
|
139
264
|
const element = await window.weaveDOM.query('h1');
|
|
265
|
+
// Returns: { exists: boolean, value?: string, outerHTML?: string }
|
|
140
266
|
|
|
141
267
|
// Get text content
|
|
142
268
|
const text = await window.weaveDOM.getText('h1');
|
|
143
269
|
|
|
144
|
-
// Get
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
// Check if element has class
|
|
148
|
-
const hasClass = await window.weaveDOM.hasClass('.btn', 'active');
|
|
270
|
+
// Get form data
|
|
271
|
+
const formData = await window.weaveDOM.getFormData('form');
|
|
272
|
+
// Returns: { formId, formName, fields: [...] }
|
|
149
273
|
```
|
|
150
274
|
|
|
151
275
|
#### Write Operations
|
|
@@ -154,70 +278,221 @@ const hasClass = await window.weaveDOM.hasClass('.btn', 'active');
|
|
|
154
278
|
// Set text
|
|
155
279
|
await window.weaveDOM.setText('h1', 'New Title');
|
|
156
280
|
|
|
157
|
-
//
|
|
158
|
-
await window.weaveDOM.
|
|
281
|
+
// Set form field value
|
|
282
|
+
await window.weaveDOM.setFormFieldValue('#email', 'user@example.com', true);
|
|
159
283
|
|
|
160
|
-
//
|
|
161
|
-
await window.weaveDOM.
|
|
284
|
+
// Click element
|
|
285
|
+
await window.weaveDOM.clickElement('.submit-btn');
|
|
162
286
|
|
|
163
|
-
//
|
|
164
|
-
await window.weaveDOM.
|
|
287
|
+
// Remove element
|
|
288
|
+
await window.weaveDOM.removeElement('.old-element');
|
|
165
289
|
```
|
|
166
290
|
|
|
167
|
-
#### DOM
|
|
291
|
+
#### DOM Injection
|
|
168
292
|
|
|
169
293
|
```typescript
|
|
170
|
-
//
|
|
171
|
-
await window.weaveDOM.
|
|
294
|
+
// Inject HTML element
|
|
295
|
+
const elementId = await window.weaveDOM.injectElement(
|
|
296
|
+
'.target-container',
|
|
297
|
+
'beforeend',
|
|
298
|
+
'<button id="my-btn">Click Me</button>',
|
|
299
|
+
{
|
|
300
|
+
onClick: async () => {
|
|
301
|
+
console.log('Button clicked!');
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
);
|
|
172
305
|
|
|
173
|
-
// Remove element
|
|
174
|
-
await window.weaveDOM.
|
|
306
|
+
// Remove injected element
|
|
307
|
+
await window.weaveDOM.removeInjectedElement(elementId);
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### Event Listeners
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
// Start form click listener
|
|
314
|
+
await window.weaveDOM.startFormClickListener(async (data) => {
|
|
315
|
+
console.log('Form detected:', data.formData);
|
|
316
|
+
await window.weaveDOM.stopFormClickListener();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Watch element for changes
|
|
320
|
+
const watcherId = await window.weaveDOM.watchElement(
|
|
321
|
+
'.target',
|
|
322
|
+
(data) => {
|
|
323
|
+
console.log('Element changed:', data.changeType);
|
|
324
|
+
},
|
|
325
|
+
{ watchChildren: true, watchAttributes: true }
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
// Stop watching
|
|
329
|
+
await window.weaveDOM.unwatchElement(watcherId);
|
|
175
330
|
```
|
|
176
331
|
|
|
332
|
+
### WeaveAPIClient
|
|
333
|
+
|
|
334
|
+
API for accessing backend services. Available as `this.weaveAPI` or `window.weaveAPI`.
|
|
335
|
+
|
|
336
|
+
#### App Data Management
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
// Create app data
|
|
340
|
+
await this.weaveAPI.appData.create({
|
|
341
|
+
dataKey: 'my-key',
|
|
342
|
+
data: { content: '...', timestamp: new Date() }
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Get all app data
|
|
346
|
+
const response = await this.weaveAPI.appData.getAll();
|
|
347
|
+
const allData = response.data || [];
|
|
348
|
+
|
|
349
|
+
// Update existing data
|
|
350
|
+
await this.weaveAPI.appData.update(dataId, {
|
|
351
|
+
dataKey: 'my-key',
|
|
352
|
+
data: { content: '...', timestamp: new Date() }
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
#### AI Integration
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
// Call AI for text analysis
|
|
360
|
+
const response = await this.weaveAPI.ai.chat({
|
|
361
|
+
prompt: 'Extract patient name from this text: ...',
|
|
362
|
+
context: 'Medical form filling',
|
|
363
|
+
disableJsonExtraction: false
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
console.log(response.response);
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
#### Utilities
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
// Convert HTML to Markdown
|
|
373
|
+
const markdown = window.weaveAPI.utils.htmlToMarkdown(htmlString);
|
|
374
|
+
|
|
375
|
+
// Convert Markdown to HTML
|
|
376
|
+
const html = window.weaveAPI.utils.markdownToHtml(markdownString);
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Real-World Example: Heidi to DCP
|
|
380
|
+
|
|
381
|
+
The included `DemoWeaveHeidiDcp` app demonstrates advanced features:
|
|
382
|
+
|
|
383
|
+
- **Background Services** - Injects button on Heidi scribe pages
|
|
384
|
+
- **URL Monitoring** - Reacts to SPA navigation
|
|
385
|
+
- **DOM Injection** - Adds UI elements to parent page
|
|
386
|
+
- **Form Detection** - Captures form structure and data
|
|
387
|
+
- **Data Persistence** - Stores consultation notes
|
|
388
|
+
- **AI Integration** - Auto-fills forms using AI
|
|
389
|
+
- **State Management** - Multi-stage workflow (idle → select-form → confirm → filling → success)
|
|
390
|
+
|
|
391
|
+
Check `src/app.ts` for the complete implementation.
|
|
392
|
+
|
|
393
|
+
## Advanced Topics
|
|
394
|
+
|
|
395
|
+
### Settings & Configuration
|
|
396
|
+
|
|
397
|
+
Settings are injected from the Enterprise Console and displayed as auto-extracted form fields:
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
interface MyAppSettings {
|
|
401
|
+
/**
|
|
402
|
+
* @description API endpoint for the service
|
|
403
|
+
* @default "https://api.example.com"
|
|
404
|
+
*/
|
|
405
|
+
apiEndpoint?: string;
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* @description Enable debug logging
|
|
409
|
+
* @default false
|
|
410
|
+
*/
|
|
411
|
+
debugMode?: boolean;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
class MyApp extends WeaveBaseApp<MyAppSettings, MyAppState> {
|
|
415
|
+
constructor() {
|
|
416
|
+
super({ /* ... */ });
|
|
417
|
+
|
|
418
|
+
const endpoint = this.appSettings?.apiEndpoint || 'https://api.example.com';
|
|
419
|
+
const debug = this.appSettings?.debugMode || false;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Error Handling
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
try {
|
|
428
|
+
const data = await this.weaveAPI.appData.getAll();
|
|
429
|
+
} catch (error) {
|
|
430
|
+
console.error('Failed to fetch data:', error);
|
|
431
|
+
this.setState({ error: error.message });
|
|
432
|
+
this.render();
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Performance Tips
|
|
437
|
+
|
|
438
|
+
- Use `async/await` for API calls to avoid blocking UI
|
|
439
|
+
- Debounce rapid state changes with timers
|
|
440
|
+
- Clear intervals and listeners in `cleanup()`
|
|
441
|
+
- Use `isCheckingContainer` flags to prevent overlapping async callbacks
|
|
442
|
+
- Minimize re-renders by updating state only when needed
|
|
443
|
+
|
|
177
444
|
## Build Process
|
|
178
445
|
|
|
179
|
-
The build
|
|
446
|
+
The build pipeline has two steps:
|
|
447
|
+
|
|
448
|
+
1. **TypeScript Compilation** (`tsc`)
|
|
449
|
+
- Compiles TypeScript to ES2020 JavaScript
|
|
450
|
+
- Outputs to `dist/`
|
|
180
451
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
- Adds header comment
|
|
186
|
-
- Generates clean, readable JavaScript
|
|
452
|
+
2. **SDK Build** (`weave-compile`)
|
|
453
|
+
- Removes SDK imports (SDK is global)
|
|
454
|
+
- Replaces SDK references with `window.*`
|
|
455
|
+
- Generates final output ready for deployment
|
|
187
456
|
|
|
188
|
-
You can also run
|
|
457
|
+
You can also run manually:
|
|
189
458
|
```bash
|
|
190
459
|
tsc
|
|
191
|
-
weave-
|
|
460
|
+
weave-compile
|
|
192
461
|
```
|
|
193
462
|
|
|
194
463
|
## Security
|
|
195
464
|
|
|
196
465
|
- ✅ **Selector Validation** - Prevents dangerous selectors
|
|
197
|
-
- ✅ **HTML Sanitization** - Removes scripts and
|
|
466
|
+
- ✅ **HTML Sanitization** - Removes scripts and dangerous content
|
|
198
467
|
- ✅ **Attribute Whitelist** - Blocks dangerous attributes
|
|
199
468
|
- ✅ **Shadow DOM Isolation** - Scoped styles and encapsulation
|
|
200
|
-
- ✅ **
|
|
469
|
+
- ✅ **Secure Bridge** - All DOM operations go through security validation
|
|
201
470
|
|
|
202
471
|
## Project Structure
|
|
203
472
|
|
|
204
473
|
```
|
|
205
474
|
my-app/
|
|
206
475
|
├── src/
|
|
207
|
-
│ └── app.ts
|
|
476
|
+
│ └── app.ts # Your TypeScript app
|
|
208
477
|
├── dist/
|
|
209
|
-
│ └── app.js
|
|
478
|
+
│ └── my-app.js # Compiled output (upload this)
|
|
210
479
|
├── package.json
|
|
211
480
|
├── tsconfig.json
|
|
481
|
+
├── SIDEKICK_SPEC.md # AI assistant guide
|
|
212
482
|
└── README.md
|
|
213
483
|
```
|
|
214
484
|
|
|
215
|
-
|
|
485
|
+
## Troubleshooting
|
|
486
|
+
|
|
487
|
+
### "TypeScript not found in SDK"
|
|
488
|
+
Make sure to run `npm install` in your app directory after initialization.
|
|
216
489
|
|
|
217
|
-
|
|
490
|
+
### Form not filling
|
|
491
|
+
Check that selectors match the actual form structure. Use browser DevTools to inspect the form.
|
|
218
492
|
|
|
219
|
-
|
|
493
|
+
### Button not injecting
|
|
494
|
+
Verify the target selector exists before injection attempt. Check browser console for errors.
|
|
220
495
|
|
|
221
|
-
|
|
496
|
+
### State not persisting
|
|
497
|
+
Use `this.weaveAPI.appData.*` methods to persist data to backend. In-memory state is lost on refresh.
|
|
222
498
|
|
|
223
|
-
MIT
|
package/bin/compile.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Weave App Compiler
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Compiles TypeScript and transpiles to Weave-ready JavaScript
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -34,7 +34,35 @@ if (!fs.existsSync(path.join(appDir, 'src'))) {
|
|
|
34
34
|
|
|
35
35
|
// Get SDK directory (where this script is located)
|
|
36
36
|
const sdkDir = path.join(__dirname, '..');
|
|
37
|
-
|
|
37
|
+
|
|
38
|
+
// Look for TypeScript in multiple locations (app's node_modules first, then SDK's)
|
|
39
|
+
let tscPath = null;
|
|
40
|
+
const possibleTscPaths = [
|
|
41
|
+
// 1. App's node_modules (most common in modern npm)
|
|
42
|
+
path.join(appDir, 'node_modules', '.bin', 'tsc'),
|
|
43
|
+
// 2. SDK's node_modules (if SDK was installed with dependencies)
|
|
44
|
+
path.join(sdkDir, 'node_modules', '.bin', 'tsc'),
|
|
45
|
+
// 3. Global fallback - try npx
|
|
46
|
+
'npx tsc'
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
for (const tscCandidate of possibleTscPaths) {
|
|
50
|
+
if (tscCandidate === 'npx tsc') {
|
|
51
|
+
// Special case for npx
|
|
52
|
+
tscPath = tscCandidate;
|
|
53
|
+
break;
|
|
54
|
+
} else if (fs.existsSync(tscCandidate)) {
|
|
55
|
+
tscPath = tscCandidate;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!tscPath) {
|
|
61
|
+
console.error('❌ Error: TypeScript not found');
|
|
62
|
+
console.error('Please install TypeScript in your project:');
|
|
63
|
+
console.error(' npm install --save-dev typescript');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
38
66
|
|
|
39
67
|
// Use app's tsconfig.json (which extends SDK's config)
|
|
40
68
|
const tsconfigPath = path.join(appDir, 'tsconfig.json');
|
|
@@ -46,13 +74,6 @@ if (!fs.existsSync(tsconfigPath)) {
|
|
|
46
74
|
process.exit(1);
|
|
47
75
|
}
|
|
48
76
|
|
|
49
|
-
// Check if TypeScript exists in SDK
|
|
50
|
-
if (!fs.existsSync(tscPath)) {
|
|
51
|
-
console.error('❌ Error: TypeScript not found in SDK');
|
|
52
|
-
console.error('Run: cd SDK && npm install');
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
77
|
try {
|
|
57
78
|
// Create dist directory if it doesn't exist
|
|
58
79
|
const distDir = path.join(appDir, 'dist');
|
|
@@ -62,29 +83,33 @@ try {
|
|
|
62
83
|
|
|
63
84
|
// Step 1: Compile TypeScript using SDK's tsconfig and TypeScript
|
|
64
85
|
console.log('📦 Compiling TypeScript...');
|
|
65
|
-
|
|
86
|
+
const tscCommand = tscPath === 'npx tsc'
|
|
87
|
+
? `npx tsc --project ${tsconfigPath} --outDir ${distDir} --rootDir ${appDir}/src`
|
|
88
|
+
: `"${tscPath}" --project ${tsconfigPath} --outDir ${distDir} --rootDir ${appDir}/src`;
|
|
89
|
+
|
|
90
|
+
execSync(tscCommand, {
|
|
66
91
|
stdio: 'inherit',
|
|
67
92
|
cwd: appDir
|
|
68
93
|
});
|
|
69
|
-
|
|
94
|
+
|
|
70
95
|
// Rename app.js to {appName}.js
|
|
71
96
|
const compiledFile = path.join(distDir, 'app.js');
|
|
72
97
|
const targetFile = path.join(distDir, `${appName}.js`);
|
|
73
|
-
|
|
98
|
+
|
|
74
99
|
if (fs.existsSync(compiledFile)) {
|
|
75
100
|
fs.renameSync(compiledFile, targetFile);
|
|
76
101
|
}
|
|
77
|
-
|
|
102
|
+
|
|
78
103
|
console.log('✅ TypeScript compiled\n');
|
|
79
104
|
|
|
80
105
|
// Step 2: Extract settings schema and inject into compiled JS
|
|
81
106
|
console.log('📋 Extracting settings schema...');
|
|
82
107
|
const extractSchemaScript = path.join(sdkDir, 'scripts', 'extract-settings-schema.js');
|
|
83
108
|
const sourceFile = path.join(appDir, 'src', 'app.ts');
|
|
84
|
-
|
|
109
|
+
|
|
85
110
|
if (fs.existsSync(extractSchemaScript) && fs.existsSync(sourceFile)) {
|
|
86
111
|
try {
|
|
87
|
-
execSync(`node ${extractSchemaScript} ${sourceFile} ${targetFile}`, {
|
|
112
|
+
execSync(`node "${extractSchemaScript}" "${sourceFile}" "${targetFile}"`, {
|
|
88
113
|
stdio: 'inherit',
|
|
89
114
|
cwd: appDir
|
|
90
115
|
});
|
|
@@ -92,13 +117,13 @@ try {
|
|
|
92
117
|
console.warn('⚠️ Schema extraction failed (non-fatal)');
|
|
93
118
|
}
|
|
94
119
|
}
|
|
95
|
-
|
|
120
|
+
|
|
96
121
|
console.log('');
|
|
97
122
|
|
|
98
123
|
// Step 3: Run weave-build to transpile
|
|
99
124
|
console.log('🔧 Transpiling to Weave format...');
|
|
100
125
|
const buildScript = path.join(__dirname, 'build.js');
|
|
101
|
-
execSync(`node ${buildScript}`, {
|
|
126
|
+
execSync(`node "${buildScript}"`, {
|
|
102
127
|
stdio: 'inherit',
|
|
103
128
|
cwd: appDir
|
|
104
129
|
});
|
package/bin/init.js
CHANGED
|
@@ -131,7 +131,7 @@ fs.writeFileSync(
|
|
|
131
131
|
|
|
132
132
|
// Copy app template from SDK
|
|
133
133
|
const templateDir = path.join(__dirname, '..', 'templates');
|
|
134
|
-
const appTemplate = `import { WeaveBaseApp, WeaveAppInfo } from '@weave/
|
|
134
|
+
const appTemplate = `import { WeaveBaseApp, WeaveAppInfo } from '@weave-apps/sdk';
|
|
135
135
|
|
|
136
136
|
// Define settings type for this app, this is a way of injecting settings into the app from the Enterprise Console
|
|
137
137
|
// These will be auto-extracted and displayed in the Enterprise Console
|