@rool-dev/app 0.3.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 +306 -0
- package/dist/cli/dev.d.ts +10 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dev.js +241 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +22 -0
- package/dist/cli/init.d.ts +7 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +108 -0
- package/dist/cli/publish.d.ts +9 -0
- package/dist/cli/publish.d.ts.map +1 -0
- package/dist/cli/publish.js +213 -0
- package/dist/cli/vite-utils.d.ts +22 -0
- package/dist/cli/vite-utils.d.ts.map +1 -0
- package/dist/cli/vite-utils.js +96 -0
- package/dist/client.d.ts +79 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +235 -0
- package/dist/dev/AppGrid.svelte +246 -0
- package/dist/dev/AppGrid.svelte.d.ts +14 -0
- package/dist/dev/AppGrid.svelte.d.ts.map +1 -0
- package/dist/dev/DevHostController.d.ts +86 -0
- package/dist/dev/DevHostController.d.ts.map +1 -0
- package/dist/dev/DevHostController.js +395 -0
- package/dist/dev/HostShell.svelte +110 -0
- package/dist/dev/HostShell.svelte.d.ts +11 -0
- package/dist/dev/HostShell.svelte.d.ts.map +1 -0
- package/dist/dev/Sidebar.svelte +223 -0
- package/dist/dev/Sidebar.svelte.d.ts +19 -0
- package/dist/dev/Sidebar.svelte.d.ts.map +1 -0
- package/dist/dev/TabBar.svelte +83 -0
- package/dist/dev/TabBar.svelte.d.ts +14 -0
- package/dist/dev/TabBar.svelte.d.ts.map +1 -0
- package/dist/dev/app.css +1 -0
- package/dist/dev/host-shell.d.ts +8 -0
- package/dist/dev/host-shell.d.ts.map +1 -0
- package/dist/dev/host-shell.js +14807 -0
- package/dist/dev/host-shell.js.map +1 -0
- package/dist/dev/vite-env.d.ts +4 -0
- package/dist/host.d.ts +54 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +171 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/manifest.d.ts +35 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +10 -0
- package/dist/protocol.d.ts +46 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +14 -0
- package/dist/reactive.svelte.d.ts +100 -0
- package/dist/reactive.svelte.d.ts.map +1 -0
- package/dist/reactive.svelte.js +267 -0
- package/dist/types.d.ts +119 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/package.json +78 -0
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rool-app init [name]
|
|
3
|
+
*
|
|
4
|
+
* Scaffolds a new app project in the current directory or a named subdirectory.
|
|
5
|
+
*/
|
|
6
|
+
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
7
|
+
import { resolve, basename, dirname } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
function toId(name) {
|
|
11
|
+
return name.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
12
|
+
}
|
|
13
|
+
function init() {
|
|
14
|
+
const name = process.argv[3];
|
|
15
|
+
const dir = name ? resolve(process.cwd(), name) : process.cwd();
|
|
16
|
+
const appName = name ?? basename(dir);
|
|
17
|
+
const appId = toId(appName);
|
|
18
|
+
if (name) {
|
|
19
|
+
if (existsSync(dir)) {
|
|
20
|
+
console.error(`Directory "${name}" already exists.`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
mkdirSync(dir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
if (existsSync(resolve(dir, 'rool-app.json'))) {
|
|
26
|
+
console.error('rool-app.json already exists in this directory.');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
// Resolve the path to @rool-dev/app for a local link
|
|
30
|
+
const appPkgDir = resolve(__dirname, '../..');
|
|
31
|
+
const manifest = {
|
|
32
|
+
id: appId,
|
|
33
|
+
name: appName,
|
|
34
|
+
};
|
|
35
|
+
const appSvelte = `<script lang="ts">
|
|
36
|
+
import type { ReactiveAppChannel } from '@rool-dev/app';
|
|
37
|
+
|
|
38
|
+
interface Props {
|
|
39
|
+
channel: ReactiveAppChannel;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let { channel }: Props = $props();
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<div class="h-full flex items-center justify-center">
|
|
46
|
+
<div class="text-center">
|
|
47
|
+
<h1 class="text-2xl font-bold text-slate-800 mb-2">${appName}</h1>
|
|
48
|
+
<p class="text-slate-500">Edit App.svelte to get started</p>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
`;
|
|
52
|
+
const packageJson = {
|
|
53
|
+
name: appId,
|
|
54
|
+
private: true,
|
|
55
|
+
version: '0.0.0',
|
|
56
|
+
type: 'module',
|
|
57
|
+
scripts: {
|
|
58
|
+
dev: 'rool-app dev',
|
|
59
|
+
},
|
|
60
|
+
dependencies: {
|
|
61
|
+
'@rool-dev/app': `link:${appPkgDir}`,
|
|
62
|
+
},
|
|
63
|
+
devDependencies: {
|
|
64
|
+
svelte: '^5.0.0',
|
|
65
|
+
typescript: '^5.0.0',
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
const agentsMd = `# ${appName}
|
|
69
|
+
|
|
70
|
+
This is a Rool App — a sandboxed Svelte 5 component that runs inside a Rool Space.
|
|
71
|
+
|
|
72
|
+
## Documentation
|
|
73
|
+
|
|
74
|
+
Read the app SDK documentation before making changes:
|
|
75
|
+
|
|
76
|
+
\`\`\`
|
|
77
|
+
cat node_modules/@rool-dev/app/README.md
|
|
78
|
+
\`\`\`
|
|
79
|
+
|
|
80
|
+
## Project structure
|
|
81
|
+
|
|
82
|
+
- \`App.svelte\` — Main component (receives \`channel: ReactiveAppChannel\` as a prop)
|
|
83
|
+
- \`rool-app.json\` — Manifest (id, name, collections)
|
|
84
|
+
- \`app.css\` — Optional custom styles (Tailwind v4 is available by default)
|
|
85
|
+
|
|
86
|
+
Additional \`.svelte\` and \`.ts\` files can be imported from \`App.svelte\`.
|
|
87
|
+
|
|
88
|
+
## Dev server
|
|
89
|
+
|
|
90
|
+
\`\`\`
|
|
91
|
+
npx rool-app dev
|
|
92
|
+
\`\`\`
|
|
93
|
+
`;
|
|
94
|
+
writeFileSync(resolve(dir, 'rool-app.json'), JSON.stringify(manifest, null, 2) + '\n');
|
|
95
|
+
writeFileSync(resolve(dir, 'App.svelte'), appSvelte);
|
|
96
|
+
writeFileSync(resolve(dir, 'package.json'), JSON.stringify(packageJson, null, 2) + '\n');
|
|
97
|
+
writeFileSync(resolve(dir, 'AGENTS.md'), agentsMd);
|
|
98
|
+
const relDir = name ?? '.';
|
|
99
|
+
console.log(`
|
|
100
|
+
Created app "${appName}" in ${relDir}/
|
|
101
|
+
|
|
102
|
+
Next steps:
|
|
103
|
+
${name ? `cd ${name}` : ''}
|
|
104
|
+
npm install
|
|
105
|
+
npx rool-app dev
|
|
106
|
+
`);
|
|
107
|
+
}
|
|
108
|
+
init();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/cli/publish.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rool-app publish
|
|
3
|
+
*
|
|
4
|
+
* Builds the app with Vite and publishes it to the Rool app platform.
|
|
5
|
+
*
|
|
6
|
+
* Usage: npx rool-app publish [--env dev|prod]
|
|
7
|
+
*/
|
|
8
|
+
import { build } from 'vite';
|
|
9
|
+
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
10
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
11
|
+
import { existsSync, readdirSync, statSync, copyFileSync, writeFileSync } from 'fs';
|
|
12
|
+
import { resolve, dirname } from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { RoolClient } from '@rool-dev/sdk';
|
|
15
|
+
import { NodeAuthProvider } from '@rool-dev/sdk/node';
|
|
16
|
+
import archiver from 'archiver';
|
|
17
|
+
import { ENV_URLS } from '../manifest.js';
|
|
18
|
+
import { readManifestOrExit, getSvelteAliases } from './vite-utils.js';
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Paths
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Vite build plugin (production version of the dev virtual entry)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
function roolAppBuildPlugin(root, tailwindCssPath) {
|
|
27
|
+
const VIRTUAL_ENTRY = 'virtual:rool-app-entry';
|
|
28
|
+
const RESOLVED_ENTRY = '\0' + VIRTUAL_ENTRY;
|
|
29
|
+
const VIRTUAL_CSS = 'virtual:rool-app-tailwind.css';
|
|
30
|
+
const RESOLVED_CSS = '\0' + VIRTUAL_CSS;
|
|
31
|
+
const appPath = resolve(root, 'App.svelte');
|
|
32
|
+
const cssPath = resolve(root, 'app.css');
|
|
33
|
+
const hasCss = existsSync(cssPath);
|
|
34
|
+
return {
|
|
35
|
+
name: 'rool-app-build',
|
|
36
|
+
resolveId(id) {
|
|
37
|
+
if (id === VIRTUAL_ENTRY)
|
|
38
|
+
return RESOLVED_ENTRY;
|
|
39
|
+
if (id === VIRTUAL_CSS)
|
|
40
|
+
return RESOLVED_CSS;
|
|
41
|
+
return undefined;
|
|
42
|
+
},
|
|
43
|
+
load(id) {
|
|
44
|
+
if (id === RESOLVED_CSS)
|
|
45
|
+
return `@import "${tailwindCssPath}";`;
|
|
46
|
+
if (id !== RESOLVED_ENTRY)
|
|
47
|
+
return;
|
|
48
|
+
return [
|
|
49
|
+
`import { initApp } from '@rool-dev/app';`,
|
|
50
|
+
`import { mount } from 'svelte';`,
|
|
51
|
+
`import App from '${appPath}';`,
|
|
52
|
+
`import '${VIRTUAL_CSS}';`,
|
|
53
|
+
hasCss ? `import '${cssPath}';` : ``,
|
|
54
|
+
``,
|
|
55
|
+
`async function main() {`,
|
|
56
|
+
` const channel = await initApp();`,
|
|
57
|
+
` mount(App, {`,
|
|
58
|
+
` target: document.getElementById('app'),`,
|
|
59
|
+
` props: { channel },`,
|
|
60
|
+
` });`,
|
|
61
|
+
`}`,
|
|
62
|
+
``,
|
|
63
|
+
`main().catch((err) => {`,
|
|
64
|
+
` document.getElementById('app').innerHTML =`,
|
|
65
|
+
` '<div style="padding:2rem;color:red"><h2>Failed to initialize app</h2><p>' + err.message + '</p></div>';`,
|
|
66
|
+
`});`,
|
|
67
|
+
].filter(Boolean).join('\n');
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Zip
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
function zipDirectory(dirPath) {
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
77
|
+
const chunks = [];
|
|
78
|
+
archive.on('data', (chunk) => chunks.push(chunk));
|
|
79
|
+
archive.on('end', () => resolve(Buffer.concat(chunks)));
|
|
80
|
+
archive.on('error', reject);
|
|
81
|
+
archive.directory(dirPath, false);
|
|
82
|
+
archive.finalize();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// CLI args
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
function parseArgs() {
|
|
89
|
+
const args = process.argv.slice(3); // after 'publish'
|
|
90
|
+
let env = 'prod';
|
|
91
|
+
for (let i = 0; i < args.length; i++) {
|
|
92
|
+
if (args[i] === '--env' && args[i + 1]) {
|
|
93
|
+
const val = args[i + 1];
|
|
94
|
+
if (val !== 'dev' && val !== 'prod') {
|
|
95
|
+
console.error(`Invalid environment: ${val}. Use 'dev' or 'prod'.`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
env = val;
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { env };
|
|
103
|
+
}
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Main
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
async function publish() {
|
|
108
|
+
const cwd = process.cwd();
|
|
109
|
+
const { env } = parseArgs();
|
|
110
|
+
const manifest = readManifestOrExit(cwd);
|
|
111
|
+
console.log(`\n Building ${manifest.name}...\n`);
|
|
112
|
+
// Resolve packages from the CLI's own node_modules
|
|
113
|
+
const tailwindPkgDir = dirname(fileURLToPath(import.meta.resolve('tailwindcss/package.json')));
|
|
114
|
+
const tailwindCssPath = resolve(tailwindPkgDir, 'index.css');
|
|
115
|
+
const appPkgPath = resolve(__dirname, '..');
|
|
116
|
+
const outDir = resolve(cwd, 'dist');
|
|
117
|
+
// Run Vite production build
|
|
118
|
+
await build({
|
|
119
|
+
configFile: false,
|
|
120
|
+
root: cwd,
|
|
121
|
+
build: {
|
|
122
|
+
outDir,
|
|
123
|
+
emptyOutDir: true,
|
|
124
|
+
rollupOptions: {
|
|
125
|
+
input: 'virtual:rool-app-entry',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
resolve: {
|
|
129
|
+
alias: [
|
|
130
|
+
{ find: '@rool-dev/app', replacement: appPkgPath },
|
|
131
|
+
{ find: /^tailwindcss$/, replacement: tailwindCssPath },
|
|
132
|
+
...getSvelteAliases(),
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
plugins: [
|
|
136
|
+
tailwindcss(),
|
|
137
|
+
svelte(),
|
|
138
|
+
roolAppBuildPlugin(cwd, tailwindCssPath),
|
|
139
|
+
],
|
|
140
|
+
});
|
|
141
|
+
// Copy rool-app.json into dist
|
|
142
|
+
copyFileSync(resolve(cwd, 'rool-app.json'), resolve(outDir, 'rool-app.json'));
|
|
143
|
+
// Write index.html (Vite build doesn't generate one from virtual entry)
|
|
144
|
+
const assets = readdirSync(resolve(outDir, 'assets')).filter(f => f.endsWith('.js') || f.endsWith('.css'));
|
|
145
|
+
const jsFiles = assets.filter(f => f.endsWith('.js'));
|
|
146
|
+
const cssFiles = assets.filter(f => f.endsWith('.css'));
|
|
147
|
+
const indexHtml = `<!DOCTYPE html>
|
|
148
|
+
<html lang="en" style="height:100%">
|
|
149
|
+
<head>
|
|
150
|
+
<meta charset="UTF-8">
|
|
151
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
152
|
+
<title>${manifest.name}</title>
|
|
153
|
+
${cssFiles.map(f => ` <link rel="stylesheet" href="/assets/${f}">`).join('\n')}
|
|
154
|
+
</head>
|
|
155
|
+
<body style="height:100%;margin:0">
|
|
156
|
+
<div id="app" style="height:100%"></div>
|
|
157
|
+
${jsFiles.map(f => ` <script type="module" src="/assets/${f}"></script>`).join('\n')}
|
|
158
|
+
</body>
|
|
159
|
+
</html>`;
|
|
160
|
+
writeFileSync(resolve(outDir, 'index.html'), indexHtml);
|
|
161
|
+
// Calculate size
|
|
162
|
+
let totalSize = 0;
|
|
163
|
+
function walkDir(dir) {
|
|
164
|
+
for (const entry of readdirSync(dir)) {
|
|
165
|
+
const full = resolve(dir, entry);
|
|
166
|
+
const stat = statSync(full);
|
|
167
|
+
if (stat.isDirectory())
|
|
168
|
+
walkDir(full);
|
|
169
|
+
else
|
|
170
|
+
totalSize += stat.size;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
walkDir(outDir);
|
|
174
|
+
console.log(`\n Build complete — ${formatBytes(totalSize)}\n`);
|
|
175
|
+
// Zip
|
|
176
|
+
console.log(` Packaging...`);
|
|
177
|
+
const zipBuffer = await zipDirectory(outDir);
|
|
178
|
+
console.log(` Bundle: ${formatBytes(zipBuffer.length)}\n`);
|
|
179
|
+
// Authenticate
|
|
180
|
+
const urls = ENV_URLS[env];
|
|
181
|
+
const client = new RoolClient({
|
|
182
|
+
baseUrl: urls.baseUrl,
|
|
183
|
+
authUrl: urls.authUrl,
|
|
184
|
+
authProvider: new NodeAuthProvider(),
|
|
185
|
+
});
|
|
186
|
+
if (!await client.isAuthenticated()) {
|
|
187
|
+
console.log(' Opening browser to authenticate...');
|
|
188
|
+
await client.login('Rool App CLI');
|
|
189
|
+
}
|
|
190
|
+
// Publish
|
|
191
|
+
console.log(` Publishing ${manifest.id} to ${env}...`);
|
|
192
|
+
const blob = new Blob([new Uint8Array(zipBuffer)], { type: 'application/zip' });
|
|
193
|
+
const result = await client.publishApp(manifest.id, {
|
|
194
|
+
name: manifest.name,
|
|
195
|
+
bundle: blob,
|
|
196
|
+
spa: false,
|
|
197
|
+
});
|
|
198
|
+
console.log(`\n Published: ${result.name}`);
|
|
199
|
+
console.log(` URL: ${result.url}`);
|
|
200
|
+
console.log(` Size: ${formatBytes(result.sizeBytes)}\n`);
|
|
201
|
+
client.destroy();
|
|
202
|
+
}
|
|
203
|
+
function formatBytes(bytes) {
|
|
204
|
+
if (bytes < 1024)
|
|
205
|
+
return `${bytes} B`;
|
|
206
|
+
if (bytes < 1024 * 1024)
|
|
207
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
208
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
209
|
+
}
|
|
210
|
+
publish().catch((err) => {
|
|
211
|
+
console.error('Publish failed:', err.message || err);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Vite utilities for the CLI (dev server and publish).
|
|
3
|
+
*
|
|
4
|
+
* Node.js only — not included in the browser bundle.
|
|
5
|
+
*/
|
|
6
|
+
import type { AppManifest, ManifestResult } from '../manifest.js';
|
|
7
|
+
export declare function readManifest(root: string): ManifestResult;
|
|
8
|
+
/**
|
|
9
|
+
* Strict manifest reading for publish — exits on error.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readManifestOrExit(root: string): AppManifest;
|
|
12
|
+
/**
|
|
13
|
+
* Builds resolve.alias entries that map every `svelte` and `svelte/*` import
|
|
14
|
+
* to the exact file in the CLI's own svelte copy. This ensures the compiler
|
|
15
|
+
* (loaded from the CLI) and the browser runtime always use the same svelte
|
|
16
|
+
* instance — even when the app lives outside the monorepo.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getSvelteAliases(): {
|
|
19
|
+
find: RegExp;
|
|
20
|
+
replacement: string;
|
|
21
|
+
}[];
|
|
22
|
+
//# sourceMappingURL=vite-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-utils.d.ts","sourceRoot":"","sources":["../../src/cli/vite-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAMlE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAwBzD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAY5D;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,CAgB1E"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Vite utilities for the CLI (dev server and publish).
|
|
3
|
+
*
|
|
4
|
+
* Node.js only — not included in the browser bundle.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, existsSync } from 'fs';
|
|
7
|
+
import { resolve, dirname } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Manifest reading
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
export function readManifest(root) {
|
|
13
|
+
const path = resolve(root, 'rool-app.json');
|
|
14
|
+
if (!existsSync(path)) {
|
|
15
|
+
return { manifest: null, error: 'rool-app.json not found' };
|
|
16
|
+
}
|
|
17
|
+
let raw;
|
|
18
|
+
try {
|
|
19
|
+
raw = readFileSync(path, 'utf-8');
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return { manifest: null, error: `Cannot read rool-app.json: ${e instanceof Error ? e.message : String(e)}` };
|
|
23
|
+
}
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(raw);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return { manifest: null, error: 'rool-app.json contains invalid JSON' };
|
|
30
|
+
}
|
|
31
|
+
const missing = [];
|
|
32
|
+
if (!parsed.id)
|
|
33
|
+
missing.push('id');
|
|
34
|
+
if (!parsed.name)
|
|
35
|
+
missing.push('name');
|
|
36
|
+
if (missing.length > 0) {
|
|
37
|
+
return { manifest: null, error: `rool-app.json missing required fields: ${missing.join(', ')}` };
|
|
38
|
+
}
|
|
39
|
+
return { manifest: parsed, error: null };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Strict manifest reading for publish — exits on error.
|
|
43
|
+
*/
|
|
44
|
+
export function readManifestOrExit(root) {
|
|
45
|
+
const path = resolve(root, 'rool-app.json');
|
|
46
|
+
if (!existsSync(path)) {
|
|
47
|
+
console.error('rool-app.json not found');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
51
|
+
if (!parsed.id || !parsed.name) {
|
|
52
|
+
console.error('rool-app.json missing required fields: id, name');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
return parsed;
|
|
56
|
+
}
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Svelte resolution
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
/**
|
|
61
|
+
* Builds resolve.alias entries that map every `svelte` and `svelte/*` import
|
|
62
|
+
* to the exact file in the CLI's own svelte copy. This ensures the compiler
|
|
63
|
+
* (loaded from the CLI) and the browser runtime always use the same svelte
|
|
64
|
+
* instance — even when the app lives outside the monorepo.
|
|
65
|
+
*/
|
|
66
|
+
export function getSvelteAliases() {
|
|
67
|
+
const svelteDir = dirname(fileURLToPath(import.meta.resolve('svelte/package.json')));
|
|
68
|
+
const pkg = JSON.parse(readFileSync(resolve(svelteDir, 'package.json'), 'utf-8'));
|
|
69
|
+
const aliases = [];
|
|
70
|
+
for (const [exportPath, conditions] of Object.entries(pkg.exports)) {
|
|
71
|
+
const file = pickExport(conditions, ['svelte', 'browser', 'default']);
|
|
72
|
+
if (!file)
|
|
73
|
+
continue;
|
|
74
|
+
const specifier = exportPath === '.' ? 'svelte' : 'svelte' + exportPath.slice(1);
|
|
75
|
+
aliases.push({
|
|
76
|
+
find: new RegExp(`^${specifier.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`),
|
|
77
|
+
replacement: resolve(svelteDir, file),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return aliases;
|
|
81
|
+
}
|
|
82
|
+
/** Walk a conditional exports value, picking the first matching condition. */
|
|
83
|
+
function pickExport(value, conditions) {
|
|
84
|
+
if (typeof value === 'string')
|
|
85
|
+
return value;
|
|
86
|
+
if (typeof value !== 'object' || value === null)
|
|
87
|
+
return null;
|
|
88
|
+
for (const c of conditions) {
|
|
89
|
+
if (c in value) {
|
|
90
|
+
const r = pickExport(value[c], conditions);
|
|
91
|
+
if (r)
|
|
92
|
+
return r;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App-side bridge client.
|
|
3
|
+
*
|
|
4
|
+
* `initApp()` waits for the host handshake, then returns an `AppChannel`
|
|
5
|
+
* that mirrors the RoolChannel API over postMessage.
|
|
6
|
+
*/
|
|
7
|
+
import type { BridgeInit } from './protocol.js';
|
|
8
|
+
import type { RoolObject, RoolObjectStat, SpaceSchema, CollectionDef, FieldDef, Interaction, PromptOptions, FindObjectsOptions, CreateObjectOptions, UpdateObjectOptions, RoolUserRole, LinkAccess, AppChannelEvents } from './types.js';
|
|
9
|
+
type EventName = keyof AppChannelEvents;
|
|
10
|
+
export declare class AppChannel {
|
|
11
|
+
private _pending;
|
|
12
|
+
private _listeners;
|
|
13
|
+
readonly channelId: string;
|
|
14
|
+
readonly spaceId: string;
|
|
15
|
+
readonly spaceName: string;
|
|
16
|
+
readonly role: RoolUserRole;
|
|
17
|
+
readonly linkAccess: LinkAccess;
|
|
18
|
+
readonly userId: string;
|
|
19
|
+
private _schema;
|
|
20
|
+
private _metadata;
|
|
21
|
+
constructor(init: BridgeInit);
|
|
22
|
+
get isReadOnly(): boolean;
|
|
23
|
+
on<E extends EventName>(event: E, callback: (data: AppChannelEvents[E]) => void): void;
|
|
24
|
+
off<E extends EventName>(event: E, callback: (data: AppChannelEvents[E]) => void): void;
|
|
25
|
+
private _emit;
|
|
26
|
+
private _call;
|
|
27
|
+
private _onMessage;
|
|
28
|
+
getObject(objectId: string): Promise<RoolObject | undefined>;
|
|
29
|
+
stat(objectId: string): Promise<RoolObjectStat | undefined>;
|
|
30
|
+
findObjects(options: FindObjectsOptions): Promise<{
|
|
31
|
+
objects: RoolObject[];
|
|
32
|
+
message: string;
|
|
33
|
+
}>;
|
|
34
|
+
getObjectIds(options?: {
|
|
35
|
+
limit?: number;
|
|
36
|
+
order?: 'asc' | 'desc';
|
|
37
|
+
}): Promise<string[]>;
|
|
38
|
+
createObject(options: CreateObjectOptions): Promise<{
|
|
39
|
+
object: RoolObject;
|
|
40
|
+
message: string;
|
|
41
|
+
}>;
|
|
42
|
+
updateObject(objectId: string, options: UpdateObjectOptions): Promise<{
|
|
43
|
+
object: RoolObject;
|
|
44
|
+
message: string;
|
|
45
|
+
}>;
|
|
46
|
+
deleteObjects(objectIds: string[]): Promise<void>;
|
|
47
|
+
getSchema(): SpaceSchema;
|
|
48
|
+
createCollection(name: string, fields: FieldDef[]): Promise<CollectionDef>;
|
|
49
|
+
alterCollection(name: string, fields: FieldDef[]): Promise<CollectionDef>;
|
|
50
|
+
dropCollection(name: string): Promise<void>;
|
|
51
|
+
getInteractions(): Promise<Interaction[]>;
|
|
52
|
+
getSystemInstruction(): Promise<string | undefined>;
|
|
53
|
+
setSystemInstruction(instruction: string | null): Promise<void>;
|
|
54
|
+
setMetadata(key: string, value: unknown): Promise<void>;
|
|
55
|
+
getMetadata(key: string): unknown;
|
|
56
|
+
getAllMetadata(): Record<string, unknown>;
|
|
57
|
+
prompt(text: string, options?: PromptOptions): Promise<{
|
|
58
|
+
message: string;
|
|
59
|
+
objects: RoolObject[];
|
|
60
|
+
}>;
|
|
61
|
+
checkpoint(label?: string): Promise<string>;
|
|
62
|
+
canUndo(): Promise<boolean>;
|
|
63
|
+
canRedo(): Promise<boolean>;
|
|
64
|
+
undo(): Promise<boolean>;
|
|
65
|
+
redo(): Promise<boolean>;
|
|
66
|
+
clearHistory(): Promise<void>;
|
|
67
|
+
destroy(): void;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Initialize the app bridge. Call this once at startup.
|
|
71
|
+
*
|
|
72
|
+
* Sends `rool:ready` to the host and waits for `rool:init` with channel metadata.
|
|
73
|
+
* Returns an `AppChannel` that mirrors the RoolChannel API over postMessage.
|
|
74
|
+
*
|
|
75
|
+
* @param timeout - How long to wait for the handshake (ms). Default: 10000.
|
|
76
|
+
*/
|
|
77
|
+
export declare function initApp(timeout?: number): Promise<AppChannel>;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|