@weave-apps/sdk 0.4.0 → 0.6.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/README.md +3 -1
- package/bin/compile.js +58 -18
- package/bin/init.js +9 -16
- package/bin/postinstall.js +65 -0
- package/bin/postinstall.test.js +103 -0
- package/package.json +5 -2
- package/templates/WEAVE_SPEC.md +3 -0
package/README.md
CHANGED
|
@@ -82,12 +82,14 @@ Official SDK for building third-party applications for the Weave Platform.
|
|
|
82
82
|
### 1. Initialize a New App
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
|
-
npx weave-
|
|
85
|
+
npx @weave-apps/sdk my-custom-app
|
|
86
86
|
cd my-custom-app
|
|
87
87
|
npm install
|
|
88
88
|
npm run build
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
> **Note:** Running `npm install` will automatically create a `WEAVE_SPEC.md` file in your project root. This is an AI assistant guide for building Weave apps and is kept up to date each time you install or update the SDK.
|
|
92
|
+
|
|
91
93
|
### 2. Develop Your App
|
|
92
94
|
|
|
93
95
|
Edit `src/app.ts`:
|
package/bin/compile.js
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Weave App Compiler
|
|
5
5
|
*
|
|
6
|
-
* Compiles TypeScript and transpiles to Weave-ready JavaScript
|
|
6
|
+
* Compiles TypeScript and transpiles to Weave-ready JavaScript.
|
|
7
|
+
* Supports multi-file apps by bundling all imports into a single output file.
|
|
8
|
+
*
|
|
9
|
+
* Uses esbuild for bundling and tsc for type-checking only.
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
12
|
const { execSync } = require('child_process');
|
|
@@ -74,6 +77,27 @@ if (!fs.existsSync(tsconfigPath)) {
|
|
|
74
77
|
process.exit(1);
|
|
75
78
|
}
|
|
76
79
|
|
|
80
|
+
// Look for esbuild in multiple locations
|
|
81
|
+
let esbuildPath = null;
|
|
82
|
+
const possibleEsbuildPaths = [
|
|
83
|
+
path.join(appDir, 'node_modules', '.bin', 'esbuild'),
|
|
84
|
+
path.join(sdkDir, 'node_modules', '.bin', 'esbuild'),
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
for (const candidate of possibleEsbuildPaths) {
|
|
88
|
+
if (fs.existsSync(candidate)) {
|
|
89
|
+
esbuildPath = candidate;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!esbuildPath) {
|
|
95
|
+
console.error('❌ Error: esbuild not found');
|
|
96
|
+
console.error('It should be installed as part of @weave-apps/sdk.');
|
|
97
|
+
console.error('Try running: npm install');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
77
101
|
try {
|
|
78
102
|
// Create dist directory if it doesn't exist
|
|
79
103
|
const distDir = path.join(appDir, 'dist');
|
|
@@ -81,35 +105,51 @@ try {
|
|
|
81
105
|
fs.mkdirSync(distDir, { recursive: true });
|
|
82
106
|
}
|
|
83
107
|
|
|
84
|
-
|
|
85
|
-
|
|
108
|
+
const entryPoint = path.join(appDir, 'src', 'app.ts');
|
|
109
|
+
const targetFile = path.join(distDir, `${appName}.js`);
|
|
110
|
+
|
|
111
|
+
// Step 1: Type-check with tsc (no emit)
|
|
112
|
+
console.log('� Type-checking with TypeScript...');
|
|
86
113
|
const tscCommand = tscPath === 'npx tsc'
|
|
87
|
-
? `npx tsc --project ${tsconfigPath} --
|
|
88
|
-
: `"${tscPath}" --project ${tsconfigPath} --
|
|
114
|
+
? `npx tsc --project ${tsconfigPath} --noEmit`
|
|
115
|
+
: `"${tscPath}" --project ${tsconfigPath} --noEmit`;
|
|
89
116
|
|
|
90
117
|
execSync(tscCommand, {
|
|
91
118
|
stdio: 'inherit',
|
|
92
119
|
cwd: appDir
|
|
93
120
|
});
|
|
94
121
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
122
|
+
console.log('✅ Type-check passed\n');
|
|
123
|
+
|
|
124
|
+
// Step 2: Bundle with esbuild (all imports resolved into single file)
|
|
125
|
+
console.log('📦 Bundling with esbuild...');
|
|
126
|
+
const esbuildCommand = [
|
|
127
|
+
`"${esbuildPath}"`,
|
|
128
|
+
`"${entryPoint}"`,
|
|
129
|
+
'--bundle',
|
|
130
|
+
`--outfile="${targetFile}"`,
|
|
131
|
+
'--format=esm',
|
|
132
|
+
'--target=es2020',
|
|
133
|
+
'--platform=browser',
|
|
134
|
+
// Mark SDK as external — build.js will replace with window globals
|
|
135
|
+
'--external:@weave-apps/sdk',
|
|
136
|
+
'--external:@weave/app-sdk',
|
|
137
|
+
].join(' ');
|
|
138
|
+
|
|
139
|
+
execSync(esbuildCommand, {
|
|
140
|
+
stdio: 'inherit',
|
|
141
|
+
cwd: appDir
|
|
142
|
+
});
|
|
102
143
|
|
|
103
|
-
console.log('✅
|
|
144
|
+
console.log('✅ Bundle complete\n');
|
|
104
145
|
|
|
105
|
-
// Step
|
|
146
|
+
// Step 3: Extract settings schema and inject into compiled JS
|
|
106
147
|
console.log('📋 Extracting settings schema...');
|
|
107
148
|
const extractSchemaScript = path.join(sdkDir, 'scripts', 'extract-settings-schema.js');
|
|
108
|
-
const sourceFile = path.join(appDir, 'src', 'app.ts');
|
|
109
149
|
|
|
110
|
-
if (fs.existsSync(extractSchemaScript) && fs.existsSync(
|
|
150
|
+
if (fs.existsSync(extractSchemaScript) && fs.existsSync(entryPoint)) {
|
|
111
151
|
try {
|
|
112
|
-
execSync(`node "${extractSchemaScript}" "${
|
|
152
|
+
execSync(`node "${extractSchemaScript}" "${entryPoint}" "${targetFile}"`, {
|
|
113
153
|
stdio: 'inherit',
|
|
114
154
|
cwd: appDir
|
|
115
155
|
});
|
|
@@ -120,7 +160,7 @@ try {
|
|
|
120
160
|
|
|
121
161
|
console.log('');
|
|
122
162
|
|
|
123
|
-
// Step
|
|
163
|
+
// Step 4: Run weave-build to transpile (strip imports, add window globals)
|
|
124
164
|
console.log('🔧 Transpiling to Weave format...');
|
|
125
165
|
const buildScript = path.join(__dirname, 'build.js');
|
|
126
166
|
execSync(`node "${buildScript}"`, {
|
package/bin/init.js
CHANGED
|
@@ -33,13 +33,13 @@ if (appName === '--help') {
|
|
|
33
33
|
🚀 Weave Apps SDK Help
|
|
34
34
|
|
|
35
35
|
Usage:
|
|
36
|
-
weave-
|
|
37
|
-
weave-
|
|
36
|
+
npx @weave-apps/sdk <app-name> Create a new Weave app
|
|
37
|
+
npx @weave-apps/sdk --help Show this help message
|
|
38
38
|
|
|
39
39
|
Examples:
|
|
40
|
-
weave-
|
|
41
|
-
weave-
|
|
42
|
-
weave-
|
|
40
|
+
npx @weave-apps/sdk my-app
|
|
41
|
+
npx @weave-apps/sdk my-custom-app
|
|
42
|
+
npx @weave-apps/sdk dashboard
|
|
43
43
|
|
|
44
44
|
Requirements:
|
|
45
45
|
• App name must be in kebab-case (lowercase with hyphens)
|
|
@@ -59,7 +59,7 @@ Learn more:
|
|
|
59
59
|
|
|
60
60
|
if (!appName) {
|
|
61
61
|
console.error('❌ Error: App name is required');
|
|
62
|
-
console.log('Usage: npx weave-
|
|
62
|
+
console.log('Usage: npx @weave-apps/sdk <app-name>');
|
|
63
63
|
process.exit(1);
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -254,14 +254,8 @@ if (fs.existsSync(readmePath)) {
|
|
|
254
254
|
console.warn('⚠️ Warning: README.md template not found in SDK templates');
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
if (fs.existsSync(specPath)) {
|
|
260
|
-
const specContent = fs.readFileSync(specPath, 'utf8');
|
|
261
|
-
fs.writeFileSync(path.join(projectDir, 'WEAVE_SPEC.md'), specContent);
|
|
262
|
-
} else {
|
|
263
|
-
console.warn('⚠️ Warning: WEAVE_SPEC.md not found in SDK templates');
|
|
264
|
-
}
|
|
257
|
+
// WEAVE_SPEC.md is automatically copied/updated by the SDK's postinstall script
|
|
258
|
+
// each time the consumer runs npm install. It does not need to be copied here.
|
|
265
259
|
|
|
266
260
|
// Create .gitignore
|
|
267
261
|
const gitignore = `node_modules/
|
|
@@ -279,11 +273,10 @@ console.log(' ├── src/');
|
|
|
279
273
|
console.log(' │ └── app.ts');
|
|
280
274
|
console.log(' ├── package.json');
|
|
281
275
|
console.log(' ├── tsconfig.json (extends SDK config)');
|
|
282
|
-
console.log(' ├── WEAVE_SPEC.md (AI assistant guide)');
|
|
283
276
|
console.log(' └── README.md\n');
|
|
284
277
|
console.log('🚀 Next steps:');
|
|
285
278
|
console.log(` cd ${appName}`);
|
|
286
|
-
console.log(' npm install');
|
|
279
|
+
console.log(' npm install # also creates WEAVE_SPEC.md (AI assistant guide)');
|
|
287
280
|
console.log(' npm run build\n');
|
|
288
281
|
|
|
289
282
|
// Helper functions
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Postinstall script for @weave-apps/sdk
|
|
5
|
+
*
|
|
6
|
+
* Automatically copies/updates WEAVE_SPEC.md into the consumer's project
|
|
7
|
+
* root every time the SDK is installed or updated.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Core postinstall logic. Copies WEAVE_SPEC.md from the SDK templates
|
|
15
|
+
* directory into the consumer project root.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} sdkRoot - Absolute path to the SDK package root
|
|
18
|
+
* @returns {{ status: string, message: string }}
|
|
19
|
+
*/
|
|
20
|
+
function runPostinstall(sdkRoot) {
|
|
21
|
+
// When running as a postinstall hook inside node_modules/@weave-apps/sdk,
|
|
22
|
+
// the project root is three levels up: node_modules/@weave-apps/sdk/ -> project root
|
|
23
|
+
const projectRoot = path.resolve(sdkRoot, '..', '..', '..');
|
|
24
|
+
|
|
25
|
+
// Safety check: only run when installed as a dependency (not during SDK development)
|
|
26
|
+
const isInstalledAsDependency = sdkRoot.includes('node_modules');
|
|
27
|
+
if (!isInstalledAsDependency) {
|
|
28
|
+
return { status: 'skipped', message: 'Not installed as a dependency' };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Safety check: ensure we resolved to a real project (has a package.json)
|
|
32
|
+
const consumerPackageJsonPath = path.join(projectRoot, 'package.json');
|
|
33
|
+
if (!fs.existsSync(consumerPackageJsonPath)) {
|
|
34
|
+
return { status: 'skipped', message: 'No consumer package.json found' };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const specSource = path.join(sdkRoot, 'templates', 'WEAVE_SPEC.md');
|
|
38
|
+
const specDest = path.join(projectRoot, 'WEAVE_SPEC.md');
|
|
39
|
+
|
|
40
|
+
if (!fs.existsSync(specSource)) {
|
|
41
|
+
return { status: 'skipped', message: 'WEAVE_SPEC.md template not found' };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const specContent = fs.readFileSync(specSource, 'utf8');
|
|
46
|
+
fs.writeFileSync(specDest, specContent);
|
|
47
|
+
return { status: 'copied', message: 'WEAVE_SPEC.md updated to latest version' };
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return { status: 'error', message: err.message };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Run when executed directly (postinstall hook)
|
|
54
|
+
if (require.main === module) {
|
|
55
|
+
const sdkRoot = path.join(__dirname, '..');
|
|
56
|
+
const result = runPostinstall(sdkRoot);
|
|
57
|
+
|
|
58
|
+
if (result.status === 'copied') {
|
|
59
|
+
console.log(`📋 @weave-apps/sdk: ${result.message}`);
|
|
60
|
+
} else if (result.status === 'error') {
|
|
61
|
+
console.warn(`⚠️ @weave-apps/sdk: Could not update WEAVE_SPEC.md: ${result.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = { runPostinstall };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const { describe, it, beforeEach, afterEach } = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const { runPostinstall } = require('./postinstall');
|
|
8
|
+
|
|
9
|
+
const SPEC_CONTENT = '# Test WEAVE_SPEC content\nThis is the latest spec.';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Creates a fake node_modules/@weave-apps/sdk directory structure
|
|
13
|
+
* inside a temporary directory to simulate a real consumer project.
|
|
14
|
+
*
|
|
15
|
+
* Returns { projectRoot, sdkRoot, cleanup }
|
|
16
|
+
*/
|
|
17
|
+
function createFakeProject({ withPackageJson = true, withSpecTemplate = true } = {}) {
|
|
18
|
+
const projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'weave-postinstall-test-'));
|
|
19
|
+
const sdkRoot = path.join(projectRoot, 'node_modules', '@weave-apps', 'sdk');
|
|
20
|
+
|
|
21
|
+
// Create SDK directory structure
|
|
22
|
+
fs.mkdirSync(path.join(sdkRoot, 'templates'), { recursive: true });
|
|
23
|
+
|
|
24
|
+
if (withPackageJson) {
|
|
25
|
+
fs.writeFileSync(
|
|
26
|
+
path.join(projectRoot, 'package.json'),
|
|
27
|
+
JSON.stringify({ name: 'test-consumer', version: '1.0.0' })
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (withSpecTemplate) {
|
|
32
|
+
fs.writeFileSync(path.join(sdkRoot, 'templates', 'WEAVE_SPEC.md'), SPEC_CONTENT);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const cleanup = () => fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
36
|
+
|
|
37
|
+
return { projectRoot, sdkRoot, cleanup };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('postinstall', () => {
|
|
41
|
+
let project;
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
if (project) {
|
|
45
|
+
project.cleanup();
|
|
46
|
+
project = null;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('copies WEAVE_SPEC.md to consumer project root when installed as dependency', () => {
|
|
51
|
+
project = createFakeProject();
|
|
52
|
+
const result = runPostinstall(project.sdkRoot);
|
|
53
|
+
|
|
54
|
+
assert.equal(result.status, 'copied');
|
|
55
|
+
const dest = path.join(project.projectRoot, 'WEAVE_SPEC.md');
|
|
56
|
+
assert.ok(fs.existsSync(dest), 'WEAVE_SPEC.md should exist in project root');
|
|
57
|
+
assert.equal(fs.readFileSync(dest, 'utf8'), SPEC_CONTENT);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('overwrites existing WEAVE_SPEC.md with latest version', () => {
|
|
61
|
+
project = createFakeProject();
|
|
62
|
+
const dest = path.join(project.projectRoot, 'WEAVE_SPEC.md');
|
|
63
|
+
|
|
64
|
+
// Write an old version first
|
|
65
|
+
fs.writeFileSync(dest, '# Old spec content');
|
|
66
|
+
|
|
67
|
+
const result = runPostinstall(project.sdkRoot);
|
|
68
|
+
|
|
69
|
+
assert.equal(result.status, 'copied');
|
|
70
|
+
assert.equal(fs.readFileSync(dest, 'utf8'), SPEC_CONTENT);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('skips when not installed as a dependency (SDK development)', () => {
|
|
74
|
+
// Use a path that does NOT contain "node_modules"
|
|
75
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'weave-postinstall-dev-'));
|
|
76
|
+
const fakeSdkRoot = path.join(tmpDir, 'frontends', 'app-sdk');
|
|
77
|
+
fs.mkdirSync(path.join(fakeSdkRoot, 'templates'), { recursive: true });
|
|
78
|
+
fs.writeFileSync(path.join(fakeSdkRoot, 'templates', 'WEAVE_SPEC.md'), SPEC_CONTENT);
|
|
79
|
+
|
|
80
|
+
const result = runPostinstall(fakeSdkRoot);
|
|
81
|
+
|
|
82
|
+
assert.equal(result.status, 'skipped');
|
|
83
|
+
assert.match(result.message, /Not installed as a dependency/);
|
|
84
|
+
|
|
85
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('skips when no consumer package.json found', () => {
|
|
89
|
+
project = createFakeProject({ withPackageJson: false });
|
|
90
|
+
const result = runPostinstall(project.sdkRoot);
|
|
91
|
+
|
|
92
|
+
assert.equal(result.status, 'skipped');
|
|
93
|
+
assert.match(result.message, /No consumer package\.json found/);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('skips when WEAVE_SPEC.md template is missing from SDK', () => {
|
|
97
|
+
project = createFakeProject({ withSpecTemplate: false });
|
|
98
|
+
const result = runPostinstall(project.sdkRoot);
|
|
99
|
+
|
|
100
|
+
assert.equal(result.status, 'skipped');
|
|
101
|
+
assert.match(result.message, /template not found/);
|
|
102
|
+
});
|
|
103
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weave-apps/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "SDK for building Weave Micro Apps",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"build": "tsc",
|
|
20
20
|
"dev": "tsc --watch",
|
|
21
21
|
"prepare": "npm run build",
|
|
22
|
-
"prepublishOnly": "npm run build"
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"postinstall": "node bin/postinstall.js",
|
|
24
|
+
"test": "node --test bin/*.test.js"
|
|
23
25
|
},
|
|
24
26
|
"keywords": [
|
|
25
27
|
"weave",
|
|
@@ -30,6 +32,7 @@
|
|
|
30
32
|
"author": "Weave Platform",
|
|
31
33
|
"license": "MIT",
|
|
32
34
|
"dependencies": {
|
|
35
|
+
"esbuild": "^0.24.0",
|
|
33
36
|
"typescript": "^5.9.3"
|
|
34
37
|
},
|
|
35
38
|
"devDependencies": {
|
package/templates/WEAVE_SPEC.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> **AI Assistant Guide**: This document provides the complete specification for building Weave applications. Use this as your reference when helping developers create apps.
|
|
4
4
|
|
|
5
|
+
> **Note**: When you update the SDK in your project, this SPEC sheet will be overwritten with the latest version.
|
|
6
|
+
|
|
7
|
+
|
|
5
8
|
## Architecture Overview
|
|
6
9
|
|
|
7
10
|
Weave apps are **Web Components** that run in an **iframe** inside a browser extension sidebar. They interact with two secure APIs:
|