@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.
Files changed (59) hide show
  1. package/README.md +306 -0
  2. package/dist/cli/dev.d.ts +10 -0
  3. package/dist/cli/dev.d.ts.map +1 -0
  4. package/dist/cli/dev.js +241 -0
  5. package/dist/cli/index.d.ts +3 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +22 -0
  8. package/dist/cli/init.d.ts +7 -0
  9. package/dist/cli/init.d.ts.map +1 -0
  10. package/dist/cli/init.js +108 -0
  11. package/dist/cli/publish.d.ts +9 -0
  12. package/dist/cli/publish.d.ts.map +1 -0
  13. package/dist/cli/publish.js +213 -0
  14. package/dist/cli/vite-utils.d.ts +22 -0
  15. package/dist/cli/vite-utils.d.ts.map +1 -0
  16. package/dist/cli/vite-utils.js +96 -0
  17. package/dist/client.d.ts +79 -0
  18. package/dist/client.d.ts.map +1 -0
  19. package/dist/client.js +235 -0
  20. package/dist/dev/AppGrid.svelte +246 -0
  21. package/dist/dev/AppGrid.svelte.d.ts +14 -0
  22. package/dist/dev/AppGrid.svelte.d.ts.map +1 -0
  23. package/dist/dev/DevHostController.d.ts +86 -0
  24. package/dist/dev/DevHostController.d.ts.map +1 -0
  25. package/dist/dev/DevHostController.js +395 -0
  26. package/dist/dev/HostShell.svelte +110 -0
  27. package/dist/dev/HostShell.svelte.d.ts +11 -0
  28. package/dist/dev/HostShell.svelte.d.ts.map +1 -0
  29. package/dist/dev/Sidebar.svelte +223 -0
  30. package/dist/dev/Sidebar.svelte.d.ts +19 -0
  31. package/dist/dev/Sidebar.svelte.d.ts.map +1 -0
  32. package/dist/dev/TabBar.svelte +83 -0
  33. package/dist/dev/TabBar.svelte.d.ts +14 -0
  34. package/dist/dev/TabBar.svelte.d.ts.map +1 -0
  35. package/dist/dev/app.css +1 -0
  36. package/dist/dev/host-shell.d.ts +8 -0
  37. package/dist/dev/host-shell.d.ts.map +1 -0
  38. package/dist/dev/host-shell.js +14807 -0
  39. package/dist/dev/host-shell.js.map +1 -0
  40. package/dist/dev/vite-env.d.ts +4 -0
  41. package/dist/host.d.ts +54 -0
  42. package/dist/host.d.ts.map +1 -0
  43. package/dist/host.js +171 -0
  44. package/dist/index.d.ts +10 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +8 -0
  47. package/dist/manifest.d.ts +35 -0
  48. package/dist/manifest.d.ts.map +1 -0
  49. package/dist/manifest.js +10 -0
  50. package/dist/protocol.d.ts +46 -0
  51. package/dist/protocol.d.ts.map +1 -0
  52. package/dist/protocol.js +14 -0
  53. package/dist/reactive.svelte.d.ts +100 -0
  54. package/dist/reactive.svelte.d.ts.map +1 -0
  55. package/dist/reactive.svelte.js +267 -0
  56. package/dist/types.d.ts +119 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +7 -0
  59. package/package.json +78 -0
package/README.md ADDED
@@ -0,0 +1,306 @@
1
+ # Rool App
2
+
3
+ Build sandboxed apps that run inside Rool Spaces. An app is a Svelte 5 component hosted in an iframe, communicating with the host via a postMessage bridge.
4
+
5
+ Apps are small, standardized, and easy to generate. An app project is just two files:
6
+
7
+ - **`App.svelte`** — Your UI component (receives a reactive channel as a prop)
8
+ - **`rool-app.json`** — Manifest with id, name, and collection access
9
+
10
+ Everything else (Vite config, entry point, HTML, Tailwind CSS) is provided by the CLI.
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ npx rool-app init my-app
16
+ cd my-app
17
+ pnpm install
18
+ npx rool-app dev
19
+ ```
20
+
21
+ This opens a dev host at `/__rool-host/` that loads your app in a sandboxed iframe, connected to a real Rool Space.
22
+
23
+ ## Manifest
24
+
25
+ `rool-app.json` declares your app's identity and collection access:
26
+
27
+ ```json
28
+ {
29
+ "id": "my-app",
30
+ "name": "My App",
31
+ "description": "What this app does",
32
+ "collections": {
33
+ "write": {
34
+ "task": [
35
+ { "name": "title", "type": { "kind": "string" } },
36
+ { "name": "done", "type": { "kind": "boolean" } }
37
+ ]
38
+ },
39
+ "read": "*"
40
+ },
41
+ "systemInstruction": "Optional system instruction for the AI"
42
+ }
43
+ ```
44
+
45
+ | Field | Required | Description |
46
+ |-------|----------|-------------|
47
+ | `id` | Yes | Unique identifier (lowercase, hyphens) |
48
+ | `name` | Yes | Display name |
49
+ | `description` | No | Short description |
50
+ | `collections` | No | Collection access declarations (see below) |
51
+ | `systemInstruction` | No | Default system instruction for the AI channel |
52
+
53
+ ### Collection Access
54
+
55
+ The `collections` field declares what collections the app works with, grouped by access level:
56
+
57
+ - **`write`** — Collections the app can create, update, and delete objects in. An object with field definitions creates the collection in the space. `"*"` grants write access to all collections.
58
+ - **`read`** — Collections the app can read from. An object with field definitions declares the expected shape. `"*"` grants read access to all collections.
59
+
60
+ `write` implies `read` — no need to list a collection under both.
61
+
62
+ ```json
63
+ // App with its own collections + read access to everything else
64
+ "collections": {
65
+ "write": {
66
+ "card": [
67
+ { "name": "front", "type": { "kind": "string" } },
68
+ { "name": "back", "type": { "kind": "string" } }
69
+ ]
70
+ },
71
+ "read": "*"
72
+ }
73
+
74
+ // Full access to all collections (chat, SQL interface, etc.)
75
+ "collections": {
76
+ "write": "*"
77
+ }
78
+
79
+ // Read-only access to all collections
80
+ "collections": {
81
+ "read": "*"
82
+ }
83
+ ```
84
+
85
+ ## App Component
86
+
87
+ `App.svelte` receives a single prop — a `ReactiveAppChannel`:
88
+
89
+ ```svelte
90
+ <script lang="ts">
91
+ import type { ReactiveAppChannel } from '@rool-dev/app';
92
+
93
+ interface Props {
94
+ channel: ReactiveAppChannel;
95
+ }
96
+
97
+ let { channel }: Props = $props();
98
+ </script>
99
+
100
+ <div>
101
+ <p>Connected to: {channel.spaceName}</p>
102
+ <p>Objects: {channel.objectIds.length}</p>
103
+ <button onclick={() => channel.prompt('Hello')}>Send</button>
104
+ </div>
105
+ ```
106
+
107
+ The component can import other `.svelte` components and `.ts` files — standard Svelte/TypeScript conventions apply. Tailwind CSS v4 is available out of the box. Add an `app.css` file to include custom styles.
108
+
109
+ ## ReactiveAppChannel
110
+
111
+ The channel is the app's interface to the host Space. It mirrors the `@rool-dev/svelte` ReactiveChannel API over a postMessage bridge.
112
+
113
+ ### Reactive State
114
+
115
+ These are Svelte 5 `$state` properties — use them directly in templates or `$effect` blocks:
116
+
117
+ | Property | Type | Description |
118
+ |----------|------|-------------|
119
+ | `interactions` | `Interaction[]` | Channel interaction history (auto-updates) |
120
+ | `objectIds` | `string[]` | All object IDs in the space (auto-updates on create/delete) |
121
+
122
+ ### Properties
123
+
124
+ | Property | Type | Description |
125
+ |----------|------|-------------|
126
+ | `channelId` | `string` | Channel ID |
127
+ | `spaceId` | `string` | Space ID |
128
+ | `spaceName` | `string` | Space name |
129
+ | `role` | `RoolUserRole` | User's role (`owner`, `admin`, `editor`, `viewer`) |
130
+ | `linkAccess` | `LinkAccess` | URL sharing level |
131
+ | `userId` | `string` | Current user's ID |
132
+ | `isReadOnly` | `boolean` | True if viewer role |
133
+
134
+ ### Object Operations
135
+
136
+ ```typescript
137
+ await channel.getObject(id)
138
+ await channel.findObjects({ where: { type: 'note' } })
139
+ await channel.createObject({ data: { type: 'note', text: '{{expand this}}' } })
140
+ await channel.updateObject(id, { data: { text: 'Updated' } })
141
+ await channel.updateObject(id, { prompt: 'Make it shorter' })
142
+ await channel.deleteObjects([id])
143
+ channel.getObjectIds({ limit: 10, order: 'desc' })
144
+ ```
145
+
146
+ See the [SDK docs](../sdk/README.md) for full details on object operations, `{{placeholder}}` syntax, and `findObjects` options.
147
+
148
+ ### AI
149
+
150
+ ```typescript
151
+ const { message, objects } = await channel.prompt('Create three tasks');
152
+ const { message } = await channel.prompt('Summarize', { readOnly: true, effort: 'QUICK' });
153
+ ```
154
+
155
+ | Option | Description |
156
+ |--------|-------------|
157
+ | `objectIds` | Focus on specific objects |
158
+ | `responseSchema` | Request structured JSON response |
159
+ | `effort` | `'QUICK'`, `'STANDARD'`, `'REASONING'`, or `'RESEARCH'` |
160
+ | `ephemeral` | Don't record in interaction history |
161
+ | `readOnly` | Disable mutation tools |
162
+
163
+ ### Schema
164
+
165
+ ```typescript
166
+ channel.getSchema()
167
+ await channel.createCollection('task', [
168
+ { name: 'title', type: { kind: 'string' } },
169
+ { name: 'done', type: { kind: 'boolean' } },
170
+ ])
171
+ await channel.alterCollection('task', [...updatedFields])
172
+ await channel.dropCollection('task')
173
+ ```
174
+
175
+ ### Undo/Redo
176
+
177
+ ```typescript
178
+ await channel.checkpoint('Before delete')
179
+ await channel.deleteObjects([id])
180
+ await channel.undo() // restores deleted object
181
+ await channel.redo() // deletes again
182
+ ```
183
+
184
+ ### Metadata
185
+
186
+ Arbitrary key-value storage on the Space (not visible to AI):
187
+
188
+ ```typescript
189
+ channel.setMetadata('viewport', { zoom: 1.5 })
190
+ channel.getMetadata('viewport')
191
+ channel.getAllMetadata()
192
+ ```
193
+
194
+ ### Interaction History
195
+
196
+ ```typescript
197
+ channel.getInteractions()
198
+ channel.getSystemInstruction()
199
+ await channel.setSystemInstruction('Respond in haiku')
200
+ ```
201
+
202
+ ### Events
203
+
204
+ ```typescript
205
+ channel.on('objectCreated', ({ objectId, object, source }) => { ... })
206
+ channel.on('objectUpdated', ({ objectId, object, source }) => { ... })
207
+ channel.on('objectDeleted', ({ objectId, source }) => { ... })
208
+ channel.on('metadataUpdated', ({ metadata, source }) => { ... })
209
+ channel.on('channelUpdated', ({ channelId, source }) => { ... })
210
+ channel.on('reset', ({ source }) => { ... })
211
+ ```
212
+
213
+ `source` is `'local_user'`, `'remote_user'`, `'remote_agent'`, or `'system'`.
214
+
215
+ ### Reactive Primitives
216
+
217
+ #### `channel.watch(options)`
218
+
219
+ Auto-updating filtered object list:
220
+
221
+ ```svelte
222
+ <script>
223
+ const tasks = channel.watch({ where: { type: 'task' } });
224
+ </script>
225
+
226
+ {#each tasks.objects as task}
227
+ <div>{task.title}</div>
228
+ {/each}
229
+ ```
230
+
231
+ | State | Description |
232
+ |-------|-------------|
233
+ | `watch.objects` | `$state<RoolObject[]>` — matching objects |
234
+ | `watch.loading` | `$state<boolean>` — loading state |
235
+
236
+ Methods: `watch.refresh()`, `watch.close()`.
237
+
238
+ #### `channel.object(id)`
239
+
240
+ Single reactive object subscription:
241
+
242
+ ```svelte
243
+ <script>
244
+ const item = channel.object('abc123');
245
+ </script>
246
+
247
+ {#if item.data}
248
+ <div>{item.data.title}</div>
249
+ {/if}
250
+ ```
251
+
252
+ | State | Description |
253
+ |-------|-------------|
254
+ | `object.data` | `$state<RoolObject | undefined>` — object data |
255
+ | `object.loading` | `$state<boolean>` — loading state |
256
+
257
+ Methods: `object.refresh()`, `object.close()`.
258
+
259
+ ## Hosting
260
+
261
+ Apps run in a sandboxed iframe (`allow-scripts allow-same-origin`). The host creates the iframe, establishes a postMessage bridge, and proxies all channel operations to a real Rool Space. The app never authenticates directly — the host handles auth and forwards operations.
262
+
263
+ The bridge protocol:
264
+ 1. App sends `rool:ready`
265
+ 2. Host responds with `rool:init` (channel metadata, schema, space info)
266
+ 3. App calls channel methods → `rool:request` → host executes → `rool:response`
267
+ 4. Host pushes real-time events → `rool:event` → app updates reactive state
268
+
269
+ ## CLI Commands
270
+
271
+ | Command | Description |
272
+ |---------|-------------|
273
+ | `rool-app init [name]` | Scaffold a new app project |
274
+ | `rool-app dev` | Start the dev server with host shell |
275
+
276
+ ## Exported Types
277
+
278
+ ```typescript
279
+ import type {
280
+ ReactiveAppChannel,
281
+ ReactiveObject,
282
+ ReactiveWatch,
283
+ WatchOptions,
284
+ RoolObject,
285
+ RoolObjectStat,
286
+ SpaceSchema,
287
+ CollectionDef,
288
+ FieldDef,
289
+ FieldType,
290
+ Interaction,
291
+ ToolCall,
292
+ PromptOptions,
293
+ PromptEffort,
294
+ FindObjectsOptions,
295
+ CreateObjectOptions,
296
+ UpdateObjectOptions,
297
+ ChangeSource,
298
+ RoolUserRole,
299
+ LinkAccess,
300
+ AppChannelEvents,
301
+ } from '@rool-dev/app';
302
+ ```
303
+
304
+ ## License
305
+
306
+ MIT - see [LICENSE](../../LICENSE) for details.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * rool-app dev
3
+ *
4
+ * Starts the app's Vite dev server with the dev host shell injected.
5
+ * The host shell is served at /__rool-host/ and the app at /.
6
+ *
7
+ * Usage: npx rool-app dev
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,241 @@
1
+ /**
2
+ * rool-app dev
3
+ *
4
+ * Starts the app's Vite dev server with the dev host shell injected.
5
+ * The host shell is served at /__rool-host/ and the app at /.
6
+ *
7
+ * Usage: npx rool-app dev
8
+ */
9
+ import { createServer } from 'vite';
10
+ import { svelte } from '@sveltejs/vite-plugin-svelte';
11
+ import tailwindcss from '@tailwindcss/vite';
12
+ import { readFileSync, existsSync, watch } from 'fs';
13
+ import { resolve, dirname } from 'path';
14
+ import { fileURLToPath } from 'url';
15
+ import { readManifest, getSvelteAliases } from './vite-utils.js';
16
+ // ---------------------------------------------------------------------------
17
+ // Paths
18
+ // ---------------------------------------------------------------------------
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const HOST_SHELL_JS_PATH = resolve(__dirname, '../dev/host-shell.js');
21
+ // ---------------------------------------------------------------------------
22
+ // HTML generation
23
+ // ---------------------------------------------------------------------------
24
+ function escapeHtml(s) {
25
+ return s.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
26
+ }
27
+ function generateHostHtml(result) {
28
+ const { manifest, error } = result;
29
+ const channelId = manifest?.id ?? 'app-dev';
30
+ const dataAttrs = {
31
+ 'data-channel-id': channelId,
32
+ 'data-app-url': '/',
33
+ };
34
+ if (manifest)
35
+ dataAttrs['data-manifest'] = escapeHtml(JSON.stringify(manifest));
36
+ if (error)
37
+ dataAttrs['data-manifest-error'] = escapeHtml(error);
38
+ const attrs = Object.entries(dataAttrs).map(([k, v]) => `${k}="${v}"`).join('\n ');
39
+ return `<!DOCTYPE html>
40
+ <html lang="en" style="height:100%">
41
+ <head>
42
+ <meta charset="UTF-8">
43
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
44
+ <title>${manifest?.name ? escapeHtml(manifest.name) + ' \u2014 ' : ''}App Dev Host</title>
45
+ <script type="module" src="/@vite/client"></script>
46
+ </head>
47
+ <body style="height:100%;margin:0">
48
+ <div id="rool-host"
49
+ style="display:flex;height:100%;background:#f8fafc"
50
+ ${attrs}
51
+ ></div>
52
+ <script type="module" src="/__rool-host/host-shell.js"></script>
53
+ </body>
54
+ </html>`;
55
+ }
56
+ // ---------------------------------------------------------------------------
57
+ // Vite plugins (internal — injected by the CLI, not user-facing)
58
+ // ---------------------------------------------------------------------------
59
+ /**
60
+ * Synthesizes index.html and the app entry module so app projects
61
+ * only need App.svelte + rool-app.json.
62
+ */
63
+ function roolAppPlugin(root, tailwindCssPath) {
64
+ const VIRTUAL_ENTRY = 'virtual:rool-app-entry';
65
+ const RESOLVED_ENTRY = '\0' + VIRTUAL_ENTRY;
66
+ const VIRTUAL_CSS = 'virtual:rool-app-tailwind.css';
67
+ const RESOLVED_CSS = '\0' + VIRTUAL_CSS;
68
+ const appPath = resolve(root, 'App.svelte');
69
+ const cssPath = resolve(root, 'app.css');
70
+ const hasCss = existsSync(cssPath);
71
+ return {
72
+ name: 'rool-app-entry',
73
+ resolveId(id) {
74
+ if (id === VIRTUAL_ENTRY)
75
+ return RESOLVED_ENTRY;
76
+ if (id === VIRTUAL_CSS)
77
+ return RESOLVED_CSS;
78
+ return undefined;
79
+ },
80
+ load(id) {
81
+ if (id === RESOLVED_CSS)
82
+ return `@import "${tailwindCssPath}";`;
83
+ if (id !== RESOLVED_ENTRY)
84
+ return;
85
+ return [
86
+ `import { initApp } from '@rool-dev/app';`,
87
+ `import { mount } from 'svelte';`,
88
+ `import App from '${appPath}';`,
89
+ `import '${VIRTUAL_CSS}';`,
90
+ hasCss ? `import '${cssPath}';` : ``,
91
+ ``,
92
+ `async function main() {`,
93
+ ` const channel = await initApp();`,
94
+ ` mount(App, {`,
95
+ ` target: document.getElementById('app'),`,
96
+ ` props: { channel },`,
97
+ ` });`,
98
+ `}`,
99
+ ``,
100
+ `main().catch((err) => {`,
101
+ ` document.getElementById('app').innerHTML =`,
102
+ ` '<div style="padding:2rem;color:red"><h2>Failed to initialize app</h2><p>' + err.message + '</p></div>';`,
103
+ `});`,
104
+ ].filter(Boolean).join('\n');
105
+ },
106
+ configureServer(server) {
107
+ server.middlewares.use((req, res, next) => {
108
+ // Serve synthesized index.html at /
109
+ if (req.url === '/' || req.url === '/index.html') {
110
+ const html = `<!DOCTYPE html>
111
+ <html lang="en" style="height:100%">
112
+ <head>
113
+ <meta charset="UTF-8">
114
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
115
+ <title>App</title>
116
+ </head>
117
+ <body style="height:100%;margin:0">
118
+ <div id="app" style="height:100%"></div>
119
+ <script type="module" src="/@id/${VIRTUAL_ENTRY}"></script>
120
+ </body>
121
+ </html>`;
122
+ server.transformIndexHtml(req.url, html).then((transformed) => {
123
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
124
+ res.end(transformed);
125
+ }).catch(next);
126
+ return;
127
+ }
128
+ next();
129
+ });
130
+ },
131
+ };
132
+ }
133
+ function roolHostPlugin(state, hostShellJs) {
134
+ return {
135
+ name: 'rool-app-host',
136
+ configureServer(server) {
137
+ server.middlewares.use((req, res, next) => {
138
+ if (!req.url?.startsWith('/__rool-host'))
139
+ return next();
140
+ if (req.url === '/__rool-host/host-shell.js') {
141
+ res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
142
+ res.end(hostShellJs);
143
+ return;
144
+ }
145
+ const html = generateHostHtml(state.current);
146
+ res.setHeader('Content-Type', 'text/html; charset=utf-8');
147
+ res.end(html);
148
+ });
149
+ },
150
+ };
151
+ }
152
+ // ---------------------------------------------------------------------------
153
+ // Manifest file watcher
154
+ // ---------------------------------------------------------------------------
155
+ function watchManifest(root, state, server) {
156
+ const manifestPath = resolve(root, 'rool-app.json');
157
+ let debounce = null;
158
+ const onChange = () => {
159
+ if (debounce)
160
+ clearTimeout(debounce);
161
+ debounce = setTimeout(() => {
162
+ const prev = state.current;
163
+ state.current = readManifest(root);
164
+ if (JSON.stringify(prev) === JSON.stringify(state.current))
165
+ return;
166
+ if (state.current.error) {
167
+ console.warn(`\n \u26a0 Manifest: ${state.current.error}\n`);
168
+ }
169
+ else {
170
+ console.log(`\n \u2713 Manifest updated \u2014 ${state.current.manifest.name}\n`);
171
+ }
172
+ server.ws.send({ type: 'full-reload', path: '*' });
173
+ }, 100);
174
+ };
175
+ // Watch the file (and parent dir so we catch creation/deletion)
176
+ try {
177
+ watch(manifestPath, onChange);
178
+ }
179
+ catch {
180
+ // File may not exist yet — watch the directory instead
181
+ }
182
+ watch(root, (_, filename) => {
183
+ if (filename === 'rool-app.json')
184
+ onChange();
185
+ });
186
+ }
187
+ // ---------------------------------------------------------------------------
188
+ // Main
189
+ // ---------------------------------------------------------------------------
190
+ async function main() {
191
+ const cwd = process.cwd();
192
+ const state = { current: readManifest(cwd) };
193
+ if (state.current.error) {
194
+ console.warn(`\n \u26a0 Manifest: ${state.current.error}\n`);
195
+ }
196
+ // Load pre-built host shell bundle
197
+ let hostShellJs;
198
+ try {
199
+ hostShellJs = readFileSync(HOST_SHELL_JS_PATH, 'utf-8');
200
+ }
201
+ catch {
202
+ console.error(`Could not find host-shell.js at ${HOST_SHELL_JS_PATH}.\n` +
203
+ `Run "pnpm build" in the @rool-dev/app package first.`);
204
+ process.exit(1);
205
+ }
206
+ // Resolve packages from the CLI's own node_modules so apps don't need them
207
+ // and to ensure a single copy of svelte (compiler + runtime must match)
208
+ const tailwindPkgDir = dirname(fileURLToPath(import.meta.resolve('tailwindcss/package.json')));
209
+ const tailwindCssPath = resolve(tailwindPkgDir, 'index.css');
210
+ const appPkgPath = resolve(__dirname, '..');
211
+ const server = await createServer({
212
+ configFile: false,
213
+ root: cwd,
214
+ server: {
215
+ open: '/__rool-host/',
216
+ },
217
+ resolve: {
218
+ alias: [
219
+ { find: '@rool-dev/app', replacement: appPkgPath },
220
+ { find: /^tailwindcss$/, replacement: tailwindCssPath },
221
+ ...getSvelteAliases(),
222
+ ],
223
+ },
224
+ plugins: [
225
+ tailwindcss(),
226
+ svelte(),
227
+ roolAppPlugin(cwd, tailwindCssPath),
228
+ roolHostPlugin(state, hostShellJs),
229
+ ],
230
+ });
231
+ await server.listen();
232
+ server.printUrls();
233
+ const name = state.current.manifest?.name ?? 'app';
234
+ console.log(`\n Dev host ready \u2014 serving ${name} via bridge\n`);
235
+ // Start watching the manifest for changes
236
+ watchManifest(cwd, state, server);
237
+ }
238
+ main().catch((err) => {
239
+ console.error('Failed to start dev server:', err);
240
+ process.exit(1);
241
+ });
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,CAAC"}
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ const command = process.argv[2];
3
+ switch (command) {
4
+ case 'dev':
5
+ await import('./dev.js');
6
+ break;
7
+ case 'init':
8
+ await import('./init.js');
9
+ break;
10
+ case 'publish':
11
+ await import('./publish.js');
12
+ break;
13
+ default:
14
+ console.log(`Usage: rool-app <command>
15
+
16
+ Commands:
17
+ init Create a new app project
18
+ dev Start the dev server
19
+ publish Build and publish the app`);
20
+ process.exit(command ? 1 : 0);
21
+ }
22
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * rool-app init [name]
3
+ *
4
+ * Scaffolds a new app project in the current directory or a named subdirectory.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}