@weave-apps/sdk 0.1.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/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@weave-apps/sdk",
3
+ "version": "0.1.0",
4
+ "description": "SDK for building Weave Micro Apps",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "bin": {
11
+ "weave-init": "./bin/init.js",
12
+ "weave-build": "./bin/build.js",
13
+ "weave-compile": "./bin/compile.js"
14
+ },
15
+ "scripts": {
16
+ "copy-sdk": "node scripts/copy-sdk-files.js",
17
+ "prebuild": "npm run copy-sdk",
18
+ "build": "tsc",
19
+ "dev": "tsc --watch",
20
+ "prepare": "npm run build",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "weave",
25
+ "browser-extension",
26
+ "app-sdk",
27
+ "typescript"
28
+ ],
29
+ "author": "Weave Platform",
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "typescript": "^5.9.3"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^20.0.0"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "bin",
40
+ "scripts",
41
+ "templates",
42
+ "tsconfig.app.json",
43
+ "README.md"
44
+ ]
45
+ }
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Copy SDK files from sidebar-loader to app-sdk
5
+ * This ensures the app-sdk has its own copy of the SDK files for distribution
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const sourceDir = path.join(__dirname, '../../sidebar-loader/src/sdk');
12
+ const targetDir = path.join(__dirname, '../src');
13
+
14
+ // Files to copy
15
+ const filesToCopy = [
16
+ 'WeaveBaseApp.ts',
17
+ 'WeaveDOMAPI.ts',
18
+ 'WeaveAPIClient.ts',
19
+ 'global.ts',
20
+ 'app-template.js',
21
+ 'README.md',
22
+ 'API_GUIDE.md'
23
+ ];
24
+
25
+ console.log('📦 Copying SDK files from sidebar-loader...\n');
26
+
27
+ filesToCopy.forEach(file => {
28
+ const sourcePath = path.join(sourceDir, file);
29
+ const targetPath = path.join(targetDir, file);
30
+
31
+ if (fs.existsSync(sourcePath)) {
32
+ // Special handling for global.ts - remove utils imports and references
33
+ if (file === 'global.ts') {
34
+ let content = fs.readFileSync(sourcePath, 'utf8');
35
+
36
+ // Remove utils imports
37
+ content = content.replace(/import htmlToMarkdown from ['"]\.\.\/utils\/htmlToMarkdown['"];?\n?/g, '');
38
+ content = content.replace(/import markdownToHtml from ['"]\.\.\/utils\/markdownToHtml['"];?\n?/g, '');
39
+
40
+ // Remove __weaveUtils from Window interface
41
+ content = content.replace(/\s+__weaveUtils:\s*\{[^}]+\};?\n?/g, '');
42
+
43
+ // Remove __weaveUtils assignment
44
+ content = content.replace(/\/\/ Make utilities available globally.*\n?window\.__weaveUtils\s*=\s*\{[^}]+\};?\n?/gs, '');
45
+
46
+ fs.writeFileSync(targetPath, content, 'utf8');
47
+ console.log(`✅ Copied (modified): ${file}`);
48
+ } else {
49
+ fs.copyFileSync(sourcePath, targetPath);
50
+ console.log(`✅ Copied: ${file}`);
51
+ }
52
+ } else {
53
+ console.warn(`⚠️ Warning: ${file} not found in sidebar-loader/src/sdk`);
54
+ }
55
+ });
56
+
57
+ console.log('\n✨ SDK files copied successfully!\n');
@@ -0,0 +1,235 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Extract TypeScript interfaces and embed them as JSDoc comments
5
+ * This allows the enterprise console to extract settings schema without executing code
6
+ */
7
+
8
+ const ts = require('typescript');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ /**
13
+ * Extract interface definition from TypeScript AST
14
+ */
15
+ function extractInterface(node, sourceFile, interfaceName) {
16
+ if (!ts.isInterfaceDeclaration(node) || node.name.text !== interfaceName) {
17
+ return null;
18
+ }
19
+
20
+ const schema = [];
21
+
22
+ node.members.forEach((member) => {
23
+ if (ts.isPropertySignature(member) && member.name) {
24
+ const propertyName = member.name.getText(sourceFile);
25
+ const isOptional = !!member.questionToken;
26
+
27
+ // Extract type
28
+ let propertyType = 'string';
29
+ let options = undefined;
30
+
31
+ if (member.type) {
32
+ const typeText = member.type.getText(sourceFile);
33
+
34
+ // Handle union types (e.g., 'top' | 'bottom')
35
+ if (ts.isUnionTypeNode(member.type)) {
36
+ const literals = member.type.types
37
+ .filter(t => ts.isLiteralTypeNode(t))
38
+ .map(t => t.literal.getText(sourceFile).replace(/['"]/g, ''));
39
+
40
+ if (literals.length > 0) {
41
+ propertyType = 'select';
42
+ options = literals.map(val => ({ label: val, value: val }));
43
+ }
44
+ } else if (typeText === 'string') {
45
+ propertyType = 'string';
46
+ } else if (typeText === 'number') {
47
+ propertyType = 'number';
48
+ } else if (typeText === 'boolean') {
49
+ propertyType = 'boolean';
50
+ }
51
+ }
52
+
53
+ // Extract JSDoc comment for description
54
+ const jsDocTags = ts.getJSDocTags(member);
55
+ let description = '';
56
+ let defaultValue = undefined;
57
+
58
+ jsDocTags.forEach(tag => {
59
+ if (tag.tagName.text === 'description' && tag.comment) {
60
+ description = typeof tag.comment === 'string' ? tag.comment : tag.comment.map(c => c.text).join('');
61
+ }
62
+ if (tag.tagName.text === 'default' && tag.comment) {
63
+ const defaultText = typeof tag.comment === 'string' ? tag.comment : tag.comment.map(c => c.text).join('');
64
+ // Try to parse as JSON, but handle string values carefully
65
+ try {
66
+ defaultValue = JSON.parse(defaultText);
67
+ } catch {
68
+ // If JSON.parse fails, it might be an unquoted string value
69
+ // Remove surrounding quotes if present (from JSDoc)
70
+ const trimmed = defaultText.trim();
71
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
72
+ (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
73
+ // Remove outer quotes and use the inner value as-is
74
+ defaultValue = trimmed.slice(1, -1);
75
+ } else {
76
+ defaultValue = trimmed;
77
+ }
78
+ }
79
+ }
80
+ });
81
+
82
+ // Generate human-readable label from property name
83
+ // Handles: camelCase, UPPER_CASE, PascalCase
84
+ let label = propertyName;
85
+
86
+ // Check if it's UPPER_CASE (all uppercase with underscores)
87
+ if (propertyName === propertyName.toUpperCase() && propertyName.includes('_')) {
88
+ // UPPER_CASE -> Upper Case
89
+ label = propertyName
90
+ .split('_')
91
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
92
+ .join(' ');
93
+ } else {
94
+ // camelCase or PascalCase -> Camel Case or Pascal Case
95
+ label = propertyName
96
+ .replace(/([A-Z])/g, ' $1') // Add space before capitals
97
+ .replace(/^./, str => str.toUpperCase()) // Capitalize first letter
98
+ .trim();
99
+ }
100
+
101
+ schema.push({
102
+ key: propertyName,
103
+ label: label,
104
+ type: propertyType,
105
+ description: description || undefined,
106
+ required: !isOptional,
107
+ defaultValue,
108
+ options
109
+ });
110
+ }
111
+ });
112
+
113
+ return schema;
114
+ }
115
+
116
+ /**
117
+ * Process TypeScript file and extract settings schema
118
+ */
119
+ function processFile(filePath) {
120
+ const sourceCode = fs.readFileSync(filePath, 'utf-8');
121
+ const sourceFile = ts.createSourceFile(
122
+ filePath,
123
+ sourceCode,
124
+ ts.ScriptTarget.Latest,
125
+ true
126
+ );
127
+
128
+ let settingsSchema = null;
129
+ let settingsInterfaceName = null;
130
+
131
+ // Find the class that extends WeaveBaseApp and extract the generic type
132
+ function visit(node) {
133
+ // Find class declaration
134
+ if (ts.isClassDeclaration(node) && node.heritageClauses) {
135
+ node.heritageClauses.forEach(clause => {
136
+ clause.types.forEach(type => {
137
+ const exprText = type.expression.getText(sourceFile);
138
+ if (exprText === 'WeaveBaseApp' && type.typeArguments && type.typeArguments.length > 0) {
139
+ // First generic is TSettings
140
+ settingsInterfaceName = type.typeArguments[0].getText(sourceFile);
141
+ }
142
+ });
143
+ });
144
+ }
145
+
146
+ ts.forEachChild(node, visit);
147
+ }
148
+
149
+ visit(sourceFile);
150
+
151
+ // Now extract the interface
152
+ if (settingsInterfaceName) {
153
+ function findInterface(node) {
154
+ const extracted = extractInterface(node, sourceFile, settingsInterfaceName);
155
+ if (extracted) {
156
+ settingsSchema = extracted;
157
+ }
158
+ ts.forEachChild(node, findInterface);
159
+ }
160
+ findInterface(sourceFile);
161
+ }
162
+
163
+ return settingsSchema;
164
+ }
165
+
166
+ /**
167
+ * Inject schema as JSDoc comment into compiled JavaScript
168
+ */
169
+ function injectSchemaIntoJS(jsFilePath, schema) {
170
+ if (!schema || schema.length === 0) {
171
+ return;
172
+ }
173
+
174
+ let jsCode = fs.readFileSync(jsFilePath, 'utf-8');
175
+
176
+ // Find the class definition
177
+ const classMatch = jsCode.match(/class\s+(\w+)\s+extends\s+WeaveBaseApp/);
178
+ if (!classMatch) {
179
+ console.warn('⚠️ Could not find class extending WeaveBaseApp');
180
+ return;
181
+ }
182
+
183
+ const className = classMatch[1];
184
+
185
+ // Create JSDoc comment with schema
186
+ const schemaComment = `
187
+ /**
188
+ * @weave-settings-schema ${JSON.stringify(schema, null, 2)}
189
+ */`;
190
+
191
+ // Inject before class definition
192
+ jsCode = jsCode.replace(
193
+ new RegExp(`(class\\s+${className}\\s+extends\\s+WeaveBaseApp)`, 'g'),
194
+ `${schemaComment}\n$1`
195
+ );
196
+
197
+ fs.writeFileSync(jsFilePath, jsCode, 'utf-8');
198
+ console.log(`✅ Injected settings schema into ${jsFilePath}`);
199
+ }
200
+
201
+ /**
202
+ * Main execution
203
+ */
204
+ function main() {
205
+ const args = process.argv.slice(2);
206
+
207
+ if (args.length < 2) {
208
+ console.error('Usage: extract-settings-schema.js <input.ts> <output.js>');
209
+ process.exit(1);
210
+ }
211
+
212
+ const [tsFile, jsFile] = args;
213
+
214
+ if (!fs.existsSync(tsFile)) {
215
+ console.error(`❌ TypeScript file not found: ${tsFile}`);
216
+ process.exit(1);
217
+ }
218
+
219
+ if (!fs.existsSync(jsFile)) {
220
+ console.error(`❌ JavaScript file not found: ${jsFile}`);
221
+ process.exit(1);
222
+ }
223
+
224
+ console.log(`📝 Extracting settings schema from ${tsFile}...`);
225
+ const schema = processFile(tsFile);
226
+
227
+ if (schema && schema.length > 0) {
228
+ console.log(`📦 Found ${schema.length} settings:`, schema.map(s => s.key).join(', '));
229
+ injectSchemaIntoJS(jsFile, schema);
230
+ } else {
231
+ console.log('ℹ️ No settings schema found');
232
+ }
233
+ }
234
+
235
+ main();
@@ -0,0 +1,202 @@
1
+ # {{APP_NAME_TITLE}}
2
+
3
+ A Weave app built with TypeScript.
4
+
5
+ ## 🤖 AI-Powered Development
6
+
7
+ This project includes **SIDEKICK_SPEC.md** - a comprehensive specification document designed for AI assistants like Windsurf, Cursor, GitHub Copilot, and ChatGPT.
8
+
9
+ ### How to Use with AI:
10
+
11
+ 1. **Reference the spec in your prompts:**
12
+ ```
13
+ "Read SIDEKICK_SPEC.md and create a feature that..."
14
+ "@SIDEKICK_SPEC.md help me build a form that updates the page title"
15
+ ```
16
+
17
+ 2. **Let AI read it automatically:**
18
+ - In **Windsurf**: The spec is automatically available in context
19
+ - In **Cursor**: Use `@SIDEKICK_SPEC.md` to reference it
20
+ - In **GitHub Copilot**: Open the file to add it to context
21
+
22
+ 3. **Get accurate code generation:**
23
+ - AI will understand the architecture constraints
24
+ - Generate code using proper TypeScript imports from `@weave/app-sdk`
25
+ - Follow security best practices automatically
26
+ - Include proper error handling and TypeScript types
27
+
28
+ **Pro Tip:** Keep SIDEKICK_SPEC.md open in a tab while developing. AI assistants work best when they can see the specification!
29
+
30
+ ---
31
+
32
+ ## 📝 TypeScript Development Guide
33
+
34
+ ### ✅ CORRECT: Import Types from SDK
35
+
36
+ **ALWAYS** import types and classes from the SDK package:
37
+
38
+ ```typescript
39
+ import {
40
+ WeaveBaseApp,
41
+ type WeaveAppInfo,
42
+ type AIChatRequest,
43
+ type AIChatResponse,
44
+ type AppData,
45
+ type CreateAppDataRequest,
46
+ type UpdateAppDataRequest
47
+ } from '@weave/app-sdk';
48
+ ```
49
+
50
+ ### ❌ WRONG: Do NOT Copy/Paste Type Definitions
51
+
52
+ **NEVER** copy type definitions into your app code. The SDK provides all types you need.
53
+
54
+ ---
55
+
56
+ ## API Usage Guide
57
+
58
+ Your Weave app has access to two distinct APIs:
59
+
60
+ ### 1. 🧠 Weave Backend API (`weaveAPI`)
61
+
62
+ Use `weaveAPI` to interact with **Weave backend services**:
63
+
64
+ #### AI Chat Service
65
+ ```typescript
66
+ import { type AIChatRequest } from '@weave/app-sdk';
67
+
68
+ // Call the Weave AI service
69
+ // Note: Your app's UUID is automatically injected
70
+ const request: AIChatRequest = {
71
+ prompt: 'Summarize this text...',
72
+ context: 'User is working on a document'
73
+ };
74
+
75
+ const response = await weaveAPI.ai.chat(request);
76
+ console.log(response.response);
77
+ ```
78
+
79
+ #### App Data Storage
80
+ ```typescript
81
+ import { type CreateAppDataRequest } from '@weave/app-sdk';
82
+
83
+ // Save data to Weave backend
84
+ // Note: Your app's UUID is automatically injected
85
+ const request: CreateAppDataRequest = {
86
+ dataKey: 'user-preferences',
87
+ data: { theme: 'dark', language: 'en' }
88
+ };
89
+
90
+ const saved = await weaveAPI.appData.create(request);
91
+ console.log('Saved with ID:', saved._id);
92
+
93
+ // Load all app data
94
+ const allData = await weaveAPI.appData.getAll();
95
+
96
+ // Update specific data
97
+ await weaveAPI.appData.update(saved._id, {
98
+ data: { theme: 'light' }
99
+ });
100
+
101
+ // Delete data
102
+ await weaveAPI.appData.delete(saved._id);
103
+ ```
104
+
105
+ ### 2. 🌐 DOM Bridge API (`weaveDOM`)
106
+
107
+ Use `weaveDOM` to interact with the **parent page DOM**:
108
+
109
+ ```typescript
110
+ // Read from parent page
111
+ const title = await weaveDOM.getText('h1');
112
+ const hasClass = await weaveDOM.hasClass('.button', 'active');
113
+
114
+ // Write to parent page
115
+ await weaveDOM.setText('.status', 'Updated!');
116
+ await weaveDOM.addClass('.button', 'active');
117
+ await weaveDOM.setAttribute('input[name="email"]', 'value', 'test@example.com');
118
+ ```
119
+
120
+ ### ⚠️ CRITICAL: Know Which API to Use
121
+
122
+ - **`weaveAPI`** → Weave backend (AI, data storage)
123
+ - **`weaveDOM`** → Parent page DOM manipulation
124
+
125
+ **Example - WRONG:**
126
+ ```typescript
127
+ // ❌ DO NOT use weaveDOM to call AI
128
+ await weaveDOM.callAI(...); // This doesn't exist!
129
+ ```
130
+
131
+ **Example - CORRECT:**
132
+ ```typescript
133
+ // ✅ Use weaveAPI for AI calls (app UUID auto-injected)
134
+ await weaveAPI.ai.chat({ prompt: '...' });
135
+
136
+ // ✅ Use weaveDOM for page manipulation
137
+ await weaveDOM.setText('h1', 'New Title');
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Development
143
+
144
+ ```bash
145
+ # Install dependencies
146
+ npm install
147
+
148
+ # Build the app
149
+ npm run build
150
+ ```
151
+
152
+ ## Upload to Weave
153
+
154
+ After building, upload the generated `dist/{{APP_NAME}}.js` file to the Weave Platform.
155
+
156
+ ## Project Structure
157
+
158
+ - `src/app.ts` - Your app source code (TypeScript)
159
+ - `dist/{{APP_NAME}}.js` - Compiled JavaScript (upload this)
160
+ - `package.json` - Project configuration
161
+ - `tsconfig.json` - TypeScript config (extends SDK)
162
+ - `SIDEKICK_SPEC.md` - Complete specification for AI assistants
163
+
164
+ ## Build Process
165
+
166
+ Running `npm run build` executes `weave-compile` which:
167
+ 1. Compiles TypeScript using SDK's configuration
168
+ 2. Removes import statements (runtime uses window globals)
169
+ 3. Replaces SDK references with window globals
170
+ 4. Generates clean, readable JavaScript
171
+
172
+ **Note:** During development, use TypeScript imports. The build process converts them to window globals for runtime.
173
+
174
+ ## Runtime Globals
175
+
176
+ After compilation, your app accesses these via window globals:
177
+
178
+ - **`window.WeaveBaseApp`** - Base class for creating apps
179
+ - **`window.weaveDOM`** - DOM manipulation API for parent page
180
+ - **`window.weaveAPI`** - Backend API client
181
+ - `weaveAPI.ai.chat()` - AI chat service
182
+ - `weaveAPI.appData.create()` - Store app data
183
+ - `weaveAPI.appData.getAll()` - Retrieve app data
184
+ - `weaveAPI.appData.update()` - Update app data
185
+ - `weaveAPI.appData.delete()` - Delete app data
186
+
187
+ ## Documentation
188
+
189
+ - **SIDEKICK_SPEC.md** - Complete specification for AI assistants (Windsurf, Cursor, etc.)
190
+ - [Weave App SDK](https://github.com/your-org/weave-app-sdk) - Full SDK documentation
191
+
192
+ ---
193
+
194
+ ## 🎯 Quick Reference for AI Assistants
195
+
196
+ When building features:
197
+
198
+ 1. **Import types from SDK** - Never copy/paste type definitions
199
+ 2. **Use `weaveAPI`** - For AI calls and data storage (backend)
200
+ 3. **Use `weaveDOM`** - For parent page DOM manipulation only
201
+ 4. **Extend `WeaveBaseApp`** - All apps must extend this class
202
+ 5. **Follow TypeScript patterns** - Use proper imports during development