rastack 0.0.55 → 0.0.56
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/CHANGELOG.md +7 -0
- package/admin/AdminApp.tsx +307 -50
- package/admin/ui/DetailsModal.tsx +125 -0
- package/admin/ui/Sidebar.tsx +28 -10
- package/admin/ui/icons.tsx +41 -0
- package/admin/ui/motion.tsx +292 -0
- package/admin/ui/styles.ts +277 -0
- package/admin/useAdmin.ts +91 -0
- package/dist/admin.js +295 -18
- package/dist/dev/harness.d.ts +44 -13
- package/dist/dev/harness.js +117 -16
- package/dist/rastack-dev.d.ts +9 -5
- package/dist/rastack-dev.js +49 -22
- package/dist/rastack-update.js +11 -4
- package/dist/rastack.d.ts +1 -1
- package/dist/rastack.js +5 -4
- package/dist/wasm/rastack_wasm_bg.wasm +0 -0
- package/package.json +7 -7
- package/src/dev/harness.ts +121 -15
- package/src/rastack-dev.ts +56 -24
- package/src/rastack-update.ts +11 -4
- package/src/rastack.ts +5 -4
- package/test/dev.spec.ts +42 -1
- package/wasm/rastack_wasm_bg.wasm +0 -0
package/src/dev/harness.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pure core of `rastack dev` — the local, always-WASM development server.
|
|
3
3
|
*
|
|
4
|
-
* `rastack dev`
|
|
5
|
-
*
|
|
4
|
+
* `rastack dev` runs **your app** in the browser over the WebAssembly engine
|
|
5
|
+
* (the same app-generic `rastack-api-core` the Lambda runs, fed the app's
|
|
6
6
|
* compiled manifest). No native server, no cargo: a dependency-free static
|
|
7
|
-
* server hands the browser the prebuilt wasm engine, the compiled schema,
|
|
8
|
-
*
|
|
7
|
+
* server hands the browser the prebuilt wasm engine, the compiled schema, the
|
|
8
|
+
* committed Iceberg warehouse, and the developer's own app. It does **not**
|
|
9
|
+
* serve the admin console — that is a separate command, `rastack admin`.
|
|
10
|
+
*
|
|
11
|
+
* When the project has no app to serve, `dev` shows a Django-style welcome
|
|
12
|
+
* page (`renderWelcome`) so a fresh scaffold still boots to something friendly.
|
|
9
13
|
*
|
|
10
14
|
* Everything decidable without IO lives here (arg parsing, MIME types, locating
|
|
11
|
-
* the wasm bundle, the HTML
|
|
12
|
-
* filesystem; `rastack-dev.ts` is the thin IO wrapper that
|
|
13
|
-
* the engine, and serves.
|
|
15
|
+
* the wasm bundle, resolving the app root, the HTML pages) so it is unit-tested
|
|
16
|
+
* without a socket or a filesystem; `rastack-dev.ts` is the thin IO wrapper that
|
|
17
|
+
* compiles, resolves the engine and the app, and serves.
|
|
14
18
|
*/
|
|
15
19
|
|
|
16
20
|
import * as path from "path";
|
|
@@ -22,6 +26,12 @@ export interface DevArgs {
|
|
|
22
26
|
outDir: string;
|
|
23
27
|
/** The committed Iceberg warehouse served to seed the browser engine. */
|
|
24
28
|
warehouseDir: string;
|
|
29
|
+
/**
|
|
30
|
+
* Explicit app root to serve at `/` (its `index.html`). Empty when unset —
|
|
31
|
+
* the IO wrapper then probes `appRootCandidates`, falling back to the welcome
|
|
32
|
+
* page when none holds an `index.html`.
|
|
33
|
+
*/
|
|
34
|
+
appDir: string;
|
|
25
35
|
/** Port the dev server listens on. */
|
|
26
36
|
port: number;
|
|
27
37
|
/** Open the browser on start (false with `--no-open`). */
|
|
@@ -32,22 +42,25 @@ export const DEV_DEFAULTS: DevArgs = {
|
|
|
32
42
|
resourcesDir: "resources",
|
|
33
43
|
outDir: ".rastack",
|
|
34
44
|
warehouseDir: "data/warehouse",
|
|
45
|
+
appDir: "",
|
|
35
46
|
port: 4321,
|
|
36
47
|
open: true,
|
|
37
48
|
};
|
|
38
49
|
|
|
39
50
|
/**
|
|
40
|
-
* Parse `rastack dev` args: `--port`, `--warehouse`, `--out`, `--
|
|
41
|
-
* single positional resources dir. Unknown flags are ignored
|
|
51
|
+
* Parse `rastack dev` args: `--port`, `--warehouse`, `--out`, `--app`,
|
|
52
|
+
* `--no-open`, and a single positional resources dir. Unknown flags are ignored
|
|
53
|
+
* (forgiving CLI).
|
|
42
54
|
*/
|
|
43
55
|
export function parseDevArgs(argv: string[]): DevArgs {
|
|
44
56
|
const positional: string[] = [];
|
|
45
|
-
let { resourcesDir, outDir, warehouseDir, port, open } = DEV_DEFAULTS;
|
|
57
|
+
let { resourcesDir, outDir, warehouseDir, appDir, port, open } = DEV_DEFAULTS;
|
|
46
58
|
for (let i = 0; i < argv.length; i++) {
|
|
47
59
|
const a = argv[i];
|
|
48
60
|
if (a === "--port") port = Number(argv[++i]) || port;
|
|
49
61
|
else if (a === "--warehouse") warehouseDir = argv[++i] ?? warehouseDir;
|
|
50
62
|
else if (a === "--out") outDir = argv[++i] ?? outDir;
|
|
63
|
+
else if (a === "--app") appDir = argv[++i] ?? appDir;
|
|
51
64
|
else if (a === "--no-open") open = false;
|
|
52
65
|
else if (!a.startsWith("-")) positional.push(a);
|
|
53
66
|
}
|
|
@@ -55,11 +68,27 @@ export function parseDevArgs(argv: string[]): DevArgs {
|
|
|
55
68
|
resourcesDir: positional[0] || resourcesDir,
|
|
56
69
|
outDir,
|
|
57
70
|
warehouseDir,
|
|
71
|
+
appDir,
|
|
58
72
|
port,
|
|
59
73
|
open,
|
|
60
74
|
};
|
|
61
75
|
}
|
|
62
76
|
|
|
77
|
+
/** The file that marks a directory as a servable app root. */
|
|
78
|
+
export const APP_ENTRY = "index.html";
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Ordered directories to probe for the developer's app (`index.html`). An
|
|
82
|
+
* explicit `--app <dir>` wins outright; otherwise `dev` looks in the
|
|
83
|
+
* conventional static roots and finally the project root. The IO wrapper picks
|
|
84
|
+
* the first candidate that actually holds an `index.html`; if none does, it
|
|
85
|
+
* serves the welcome page instead.
|
|
86
|
+
*/
|
|
87
|
+
export function appRootCandidates(appDir: string, cwd = process.cwd()): string[] {
|
|
88
|
+
if (appDir) return [path.resolve(cwd, appDir)];
|
|
89
|
+
return ["public", "www", "app", "."].map((d) => path.resolve(cwd, d));
|
|
90
|
+
}
|
|
91
|
+
|
|
63
92
|
/** Args for `rastack admin` — like `DevArgs`, but bound to a real server port. */
|
|
64
93
|
export interface AdminArgs {
|
|
65
94
|
resourcesDir: string;
|
|
@@ -147,12 +176,14 @@ export function wasmBundleCandidates(
|
|
|
147
176
|
export const WASM_ENTRY = "rastack_wasm.js";
|
|
148
177
|
|
|
149
178
|
/**
|
|
150
|
-
* The HTML shell served at
|
|
151
|
-
* (`/dev/admin.js`), which fetches the schema/openapi, boots the
|
|
152
|
-
* seeds it from `/data/warehouse/`, and renders the admin.
|
|
153
|
-
* all logic lives in the bundle — mirroring the
|
|
179
|
+
* The HTML shell served at `/` by **`rastack admin`**. It loads the bundled
|
|
180
|
+
* React admin (`/dev/admin.js`), which fetches the schema/openapi, boots the
|
|
181
|
+
* wasm engine, seeds it from `/data/warehouse/`, and renders the admin console.
|
|
182
|
+
* Deliberately tiny — all logic lives in the bundle — mirroring the
|
|
183
|
+
* `rastack design` studio. `rastack dev` does **not** use this: it serves the
|
|
184
|
+
* developer's own app (or the welcome page below).
|
|
154
185
|
*/
|
|
155
|
-
export function renderShell(title = "React API Stack —
|
|
186
|
+
export function renderShell(title = "React API Stack — admin"): string {
|
|
156
187
|
return `<!doctype html>
|
|
157
188
|
<html lang="en">
|
|
158
189
|
<head>
|
|
@@ -172,3 +203,78 @@ export function renderShell(title = "React API Stack — dev"): string {
|
|
|
172
203
|
</html>
|
|
173
204
|
`;
|
|
174
205
|
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The Django-style welcome page `rastack dev` serves at `/` when the project
|
|
209
|
+
* has **no app** to serve (no `index.html` in any `appRootCandidates` dir).
|
|
210
|
+
* It confirms the dev server is up, points at the live backend endpoints the
|
|
211
|
+
* WASM engine exposes, and tells you how to add an app or open the admin — the
|
|
212
|
+
* "It worked!" moment of `django-admin runserver`, on brand for the stack.
|
|
213
|
+
*/
|
|
214
|
+
export function renderWelcome(title = "React API Stack — dev"): string {
|
|
215
|
+
return `<!doctype html>
|
|
216
|
+
<html lang="en">
|
|
217
|
+
<head>
|
|
218
|
+
<meta charset="utf-8" />
|
|
219
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
220
|
+
<title>${title}</title>
|
|
221
|
+
<style>
|
|
222
|
+
:root { color-scheme: light dark; }
|
|
223
|
+
* { box-sizing: border-box; }
|
|
224
|
+
html, body { margin: 0; min-height: 100%; }
|
|
225
|
+
body {
|
|
226
|
+
font: 15px/1.6 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
227
|
+
color: #1f2328; background: #f6f8fa;
|
|
228
|
+
display: flex; align-items: center; justify-content: center; padding: 2rem;
|
|
229
|
+
}
|
|
230
|
+
.card {
|
|
231
|
+
max-width: 640px; width: 100%; background: #fff; border: 1px solid #d0d7de;
|
|
232
|
+
border-radius: 14px; padding: 2.5rem; box-shadow: 0 1px 3px rgba(0,0,0,.06);
|
|
233
|
+
}
|
|
234
|
+
.rocket { font-size: 2.5rem; line-height: 1; }
|
|
235
|
+
h1 { font-size: 1.5rem; margin: .75rem 0 .25rem; }
|
|
236
|
+
p.lead { color: #57606a; margin: 0 0 1.5rem; }
|
|
237
|
+
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
238
|
+
background: #eff1f3; padding: .1em .4em; border-radius: 5px; font-size: .9em; }
|
|
239
|
+
ul { padding-left: 1.1rem; margin: 0; }
|
|
240
|
+
li { margin: .4rem 0; }
|
|
241
|
+
a { color: #0969da; text-decoration: none; }
|
|
242
|
+
a:hover { text-decoration: underline; }
|
|
243
|
+
.foot { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid #eaeef2;
|
|
244
|
+
color: #6e7781; font-size: .85rem; }
|
|
245
|
+
@media (prefers-color-scheme: dark) {
|
|
246
|
+
body { color: #e6edf3; background: #0d1117; }
|
|
247
|
+
.card { background: #161b22; border-color: #30363d; box-shadow: none; }
|
|
248
|
+
p.lead { color: #8b949e; }
|
|
249
|
+
code { background: #21262d; }
|
|
250
|
+
.foot { border-color: #21262d; color: #8b949e; }
|
|
251
|
+
}
|
|
252
|
+
</style>
|
|
253
|
+
</head>
|
|
254
|
+
<body>
|
|
255
|
+
<div class="card">
|
|
256
|
+
<div class="rocket">🚀</div>
|
|
257
|
+
<h1>The dev server is running.</h1>
|
|
258
|
+
<p class="lead">
|
|
259
|
+
Congratulations — <strong>rastack dev</strong> is up and your API is running
|
|
260
|
+
in the browser over WebAssembly. You're seeing this page because there's no
|
|
261
|
+
app to serve yet.
|
|
262
|
+
</p>
|
|
263
|
+
<ul>
|
|
264
|
+
<li>Add an <code>index.html</code> (in <code>public/</code>, <code>www/</code>,
|
|
265
|
+
<code>app/</code>, or the project root) and reload — <code>dev</code> will serve it.
|
|
266
|
+
Point it at a different folder with <code>rastack dev --app <dir></code>.</li>
|
|
267
|
+
<li>Wire your app to the local engine with
|
|
268
|
+
<code><RAStackProvider mode="local"></code>.</li>
|
|
269
|
+
<li>Live backend endpoints:
|
|
270
|
+
<a href="/schema.rastack.json">/schema.rastack.json</a>,
|
|
271
|
+
<a href="/openapi.json">/openapi.json</a>,
|
|
272
|
+
<code>/data/warehouse/…</code>, <code>/wasm/…</code>.</li>
|
|
273
|
+
<li>Want the database console instead? Run <code>rastack admin</code>.</li>
|
|
274
|
+
</ul>
|
|
275
|
+
<div class="foot">React API Stack — a full-stack framework on object storage.</div>
|
|
276
|
+
</div>
|
|
277
|
+
</body>
|
|
278
|
+
</html>
|
|
279
|
+
`;
|
|
280
|
+
}
|
package/src/rastack-dev.ts
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* `rastack dev` — local development
|
|
4
|
+
* `rastack dev` — local development of **your app**, always over the WASM engine.
|
|
5
5
|
*
|
|
6
|
-
* rastack dev [resourcesDir] [--port <n>] [--warehouse <dir>] [--out <dir>] [--no-open]
|
|
6
|
+
* rastack dev [resourcesDir] [--port <n>] [--warehouse <dir>] [--out <dir>] [--app <dir>] [--no-open]
|
|
7
7
|
*
|
|
8
8
|
* Compiles the resources, then boots a dependency-free static server (à la
|
|
9
|
-
* `rastack design`) that hands the browser everything it needs to run the
|
|
10
|
-
*
|
|
11
|
-
* • the
|
|
9
|
+
* `rastack design`) that hands the browser everything it needs to run the
|
|
10
|
+
* developer's own app locally over WebAssembly — no native server, no cargo:
|
|
11
|
+
* • the developer's app (`index.html` from the resolved app root), or a
|
|
12
|
+
* Django-style welcome page when the project has no app yet,
|
|
12
13
|
* • the prebuilt WASM engine (`/wasm/…`) shipped in the package,
|
|
13
14
|
* • the compiled `schema.rastack.json` / `openapi.json`,
|
|
14
15
|
* • the committed Iceberg warehouse (`/data/warehouse/…`) to seed from.
|
|
15
16
|
*
|
|
17
|
+
* It does **not** serve the admin console — that is a separate command,
|
|
18
|
+
* `rastack admin`.
|
|
19
|
+
*
|
|
16
20
|
* The engine is resolved from the package's prebuilt bundle; if none is present
|
|
17
21
|
* but a Rust + wasm toolchain is, it is built from source as a fallback.
|
|
18
22
|
*/
|
|
@@ -22,10 +26,12 @@ import * as fs from "fs";
|
|
|
22
26
|
import * as http from "http";
|
|
23
27
|
import * as path from "path";
|
|
24
28
|
import {
|
|
29
|
+
APP_ENTRY,
|
|
25
30
|
WASM_ENTRY,
|
|
31
|
+
appRootCandidates,
|
|
26
32
|
contentType,
|
|
27
33
|
parseDevArgs,
|
|
28
|
-
|
|
34
|
+
renderWelcome,
|
|
29
35
|
wasmBundleCandidates,
|
|
30
36
|
} from "./dev/harness";
|
|
31
37
|
|
|
@@ -54,6 +60,14 @@ function cargoAvailable(): boolean {
|
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
|
|
63
|
+
/** First app-root candidate that holds an `index.html`, or null (→ welcome). */
|
|
64
|
+
function resolveAppRoot(appDir: string): string | null {
|
|
65
|
+
for (const dir of appRootCandidates(appDir)) {
|
|
66
|
+
if (fs.existsSync(path.join(dir, APP_ENTRY))) return dir;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
57
71
|
/** Locate the prebuilt wasm bundle dir, building from source as a fallback. */
|
|
58
72
|
function resolveWasmDir(): string | null {
|
|
59
73
|
for (const dir of wasmBundleCandidates(HERE)) {
|
|
@@ -130,7 +144,7 @@ function openBrowser(url: string): void {
|
|
|
130
144
|
function main(argv: string[]): void {
|
|
131
145
|
const args = parseDevArgs(argv);
|
|
132
146
|
|
|
133
|
-
console.log(`\n rastack dev —
|
|
147
|
+
console.log(`\n rastack dev — your app, in your browser over WASM\n`);
|
|
134
148
|
console.log(`▸ Compiling ${args.resourcesDir} → ${args.outDir}`);
|
|
135
149
|
if (!compile(args.resourcesDir, args.outDir)) {
|
|
136
150
|
console.error(" ✗ compile failed — fix the resource graph and re-run.");
|
|
@@ -149,13 +163,8 @@ function main(argv: string[]): void {
|
|
|
149
163
|
process.exit(1);
|
|
150
164
|
}
|
|
151
165
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
console.error(
|
|
155
|
-
" ✗ admin bundle (dist/admin.js) not found — reinstall rastack.",
|
|
156
|
-
);
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
166
|
+
// Serve the developer's own app when there is one; otherwise the welcome page.
|
|
167
|
+
const appRoot = resolveAppRoot(args.appDir);
|
|
159
168
|
|
|
160
169
|
const server = http.createServer((req, res) => {
|
|
161
170
|
const url = (req.url || "/").split("?")[0];
|
|
@@ -163,13 +172,7 @@ function main(argv: string[]): void {
|
|
|
163
172
|
res.writeHead(405).end("Method not allowed");
|
|
164
173
|
return;
|
|
165
174
|
}
|
|
166
|
-
|
|
167
|
-
res
|
|
168
|
-
.writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
|
|
169
|
-
.end(renderShell());
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (url === "/dev/admin.js") return serveFile(res, adminBundle);
|
|
175
|
+
// Reserved backend routes always win over the app's static files.
|
|
173
176
|
if (url.startsWith("/wasm/"))
|
|
174
177
|
return serveUnder(res, wasmDir, url.slice("/wasm/".length));
|
|
175
178
|
if (url === "/schema.rastack.json" || url === "/openapi.json")
|
|
@@ -180,14 +183,43 @@ function main(argv: string[]): void {
|
|
|
180
183
|
args.warehouseDir,
|
|
181
184
|
url.slice("/data/warehouse/".length),
|
|
182
185
|
);
|
|
183
|
-
|
|
186
|
+
|
|
187
|
+
// No app → the Django-style welcome page (root only; else 404).
|
|
188
|
+
if (!appRoot) {
|
|
189
|
+
if (url === "/" || url === "/index.html") {
|
|
190
|
+
res
|
|
191
|
+
.writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
|
|
192
|
+
.end(renderWelcome());
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
res.writeHead(404).end("Not found");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Serve the app: its index.html at `/`, an existing file as-is, and any
|
|
200
|
+
// other route back to index.html (SPA client-side routing).
|
|
201
|
+
if (url === "/" || url === "/index.html")
|
|
202
|
+
return serveFile(res, path.join(appRoot, APP_ENTRY));
|
|
203
|
+
const rel = decodeURIComponent(url).replace(/^\/+/, "");
|
|
204
|
+
const target = path.resolve(appRoot, rel);
|
|
205
|
+
const safeRoot = path.resolve(appRoot);
|
|
206
|
+
const inRoot =
|
|
207
|
+
target === safeRoot || target.startsWith(safeRoot + path.sep);
|
|
208
|
+
if (inRoot && fs.existsSync(target) && fs.statSync(target).isFile())
|
|
209
|
+
return serveFile(res, target);
|
|
210
|
+
return serveFile(res, path.join(appRoot, APP_ENTRY));
|
|
184
211
|
});
|
|
185
212
|
|
|
186
213
|
server.listen(args.port, () => {
|
|
187
214
|
const url = `http://localhost:${args.port}`;
|
|
188
215
|
console.log(` ✓ WASM engine: ${path.relative(process.cwd(), wasmDir)}`);
|
|
189
|
-
|
|
190
|
-
|
|
216
|
+
if (appRoot)
|
|
217
|
+
console.log(` ✓ app: ${path.relative(process.cwd(), appRoot) || "."}`);
|
|
218
|
+
else console.log(` ○ no app found — serving the welcome page`);
|
|
219
|
+
console.log(`\n ▸ ${url}\n`);
|
|
220
|
+
console.log(
|
|
221
|
+
" Need the database console? Run `rastack admin`. Ctrl+C to stop.\n",
|
|
222
|
+
);
|
|
191
223
|
if (args.open) openBrowser(url);
|
|
192
224
|
});
|
|
193
225
|
}
|
package/src/rastack-update.ts
CHANGED
|
@@ -64,10 +64,17 @@ function runNpm(
|
|
|
64
64
|
args: string[],
|
|
65
65
|
opts: { stdio: "inherit" | ["ignore", "pipe", "pipe"]; env?: NodeJS.ProcessEnv },
|
|
66
66
|
): Buffer | null {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
if (NPM_SHELL) {
|
|
68
|
+
// Windows: launch the `npm.cmd` shim through a shell (a bare `.cmd` spawn is
|
|
69
|
+
// rejected with EINVAL since the Node CVE-2024-27980 fix). The only
|
|
70
|
+
// non-literal token — the version — is sanitized in `parseUpdateFlags`, and
|
|
71
|
+
// every other arg is a fixed literal, so nothing injectable reaches it. The
|
|
72
|
+
// nosemgrep must sit on the line directly above the `execSync` call for the
|
|
73
|
+
// engine to honour it.
|
|
74
|
+
// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
|
|
75
|
+
return execSync(npmShellCommand(NPM, args), opts);
|
|
76
|
+
}
|
|
77
|
+
return execFileSync(NPM, args, opts);
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
/** Capture `npm <args>` stdout, or null when npm is absent / the call fails. */
|
package/src/rastack.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* rastack generate [--schema-only] Generate typed hooks from OpenAPI
|
|
12
12
|
* rastack tokens [input] [outDir] Compile design tokens → CSS vars + typed theme
|
|
13
13
|
* rastack design [input] [--port n] Live design-system studio (showcase + edit)
|
|
14
|
-
* rastack dev [--port n] [--warehouse d] Local dev: run
|
|
14
|
+
* rastack dev [--port n] [--warehouse d] Local dev: run your app in-browser over WASM
|
|
15
15
|
* rastack admin [--warehouse dir] [--port n] Full admin console — React UI (rastack/components) over the native server
|
|
16
16
|
* rastack import <file> --resource app.model Load CSV/XLSX/PDF data and validate it (state machine)
|
|
17
17
|
* rastack scan [files...] [--fail-on-pii] Scan CSVs for PII and schema info
|
|
@@ -72,8 +72,9 @@ switch (command) {
|
|
|
72
72
|
run("rastack-compile.js", [command, ...rest]);
|
|
73
73
|
break;
|
|
74
74
|
case "dev":
|
|
75
|
-
// Local development: serve
|
|
76
|
-
//
|
|
75
|
+
// Local development: serve your app in the browser over the WASM engine
|
|
76
|
+
// (no native server, no cargo). The admin console is a separate command,
|
|
77
|
+
// `rastack admin`. Replaces `serve` + `wasm`.
|
|
77
78
|
run("rastack-dev.js", rest);
|
|
78
79
|
break;
|
|
79
80
|
case "admin":
|
|
@@ -139,7 +140,7 @@ switch (command) {
|
|
|
139
140
|
` rastack generate [--schema-only]\n` +
|
|
140
141
|
` rastack tokens [input] [outDir] [--prefix p] Compile design tokens → CSS vars + typed theme\n` +
|
|
141
142
|
` rastack design [input] [--port n] Live design-system studio (showcase + edit)\n` +
|
|
142
|
-
` rastack dev [--port n] [--warehouse dir] [--no-open] Run
|
|
143
|
+
` rastack dev [--port n] [--warehouse dir] [--app dir] [--no-open] Run your app in-browser over WASM (local dev)\n` +
|
|
143
144
|
` rastack admin [--warehouse dir] [--port n] [--no-open] Full admin console (React UI over the native server; needs a built rastack-server)\n` +
|
|
144
145
|
` rastack import <file.csv|.xlsx|.pdf> --resource app.model [--related app.model=<file>] [--out cleaned.json]\n` +
|
|
145
146
|
` rastack scan [files...] [--fail-on-pii]\n` +
|
package/test/dev.spec.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ADMIN_DEFAULTS,
|
|
3
|
+
APP_ENTRY,
|
|
3
4
|
DEV_DEFAULTS,
|
|
4
5
|
WASM_ENTRY,
|
|
6
|
+
appRootCandidates,
|
|
5
7
|
contentType,
|
|
6
8
|
parseAdminArgs,
|
|
7
9
|
parseDevArgs,
|
|
8
10
|
renderShell,
|
|
11
|
+
renderWelcome,
|
|
9
12
|
wasmBundleCandidates,
|
|
10
13
|
} from "../src/dev/harness";
|
|
11
14
|
import {
|
|
@@ -57,6 +60,7 @@ describe("rastack dev — arg parsing", () => {
|
|
|
57
60
|
resourcesDir: "app/resources",
|
|
58
61
|
outDir: "build",
|
|
59
62
|
warehouseDir: "data/wh",
|
|
63
|
+
appDir: "",
|
|
60
64
|
port: 5000,
|
|
61
65
|
open: false,
|
|
62
66
|
});
|
|
@@ -114,7 +118,7 @@ describe("rastack dev — wasm bundle resolution", () => {
|
|
|
114
118
|
});
|
|
115
119
|
});
|
|
116
120
|
|
|
117
|
-
describe("rastack
|
|
121
|
+
describe("rastack admin — HTML shell", () => {
|
|
118
122
|
it("mounts #root and loads the bundled admin", () => {
|
|
119
123
|
const html = renderShell();
|
|
120
124
|
expect(html).toContain('<div id="root">');
|
|
@@ -123,6 +127,43 @@ describe("rastack dev — HTML shell", () => {
|
|
|
123
127
|
});
|
|
124
128
|
});
|
|
125
129
|
|
|
130
|
+
describe("rastack dev — app resolution", () => {
|
|
131
|
+
it("defaults appDir to empty (probe the conventional roots)", () => {
|
|
132
|
+
expect(DEV_DEFAULTS.appDir).toBe("");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("reads --app as an explicit app root", () => {
|
|
136
|
+
expect(parseDevArgs(["--app", "site"]).appDir).toBe("site");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("probes public/www/app/root when no --app is given", () => {
|
|
140
|
+
const dirs = appRootCandidates("", "/repo");
|
|
141
|
+
expect(dirs).toEqual([
|
|
142
|
+
"/repo/public",
|
|
143
|
+
"/repo/www",
|
|
144
|
+
"/repo/app",
|
|
145
|
+
"/repo",
|
|
146
|
+
]);
|
|
147
|
+
expect(APP_ENTRY).toBe("index.html");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("uses only the explicit --app dir when set", () => {
|
|
151
|
+
expect(appRootCandidates("site", "/repo")).toEqual(["/repo/site"]);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe("rastack dev — welcome page (no app)", () => {
|
|
156
|
+
it("is a self-contained page pointing at the backend and admin", () => {
|
|
157
|
+
const html = renderWelcome();
|
|
158
|
+
expect(html.startsWith("<!doctype html>")).toBe(true);
|
|
159
|
+
// Django-style "it worked" confirmation, not the admin bundle.
|
|
160
|
+
expect(html).not.toContain("/dev/admin.js");
|
|
161
|
+
expect(html).toContain("dev server is running");
|
|
162
|
+
expect(html).toContain("/schema.rastack.json");
|
|
163
|
+
expect(html).toContain("rastack admin");
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
126
167
|
// -- admin adapters ---------------------------------------------------------
|
|
127
168
|
|
|
128
169
|
/** The in-repo airline fixtures, in manifest JSON shape. */
|
|
Binary file
|