rastack 0.0.55 → 0.0.57

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.
@@ -20,10 +20,11 @@
20
20
  * falls back to a from-source cargo build.
21
21
  */
22
22
 
23
- import { execFileSync, spawnSync } from "child_process";
23
+ import { execFileSync } from "child_process";
24
24
  import * as fs from "fs";
25
25
  import * as http from "http";
26
26
  import * as path from "path";
27
+ import { runCompile } from "./compile";
27
28
  import {
28
29
  WASM_ENTRY,
29
30
  contentType,
@@ -34,18 +35,6 @@ import {
34
35
 
35
36
  const HERE = __dirname; // dist/ when installed — the bundle lives beside us.
36
37
 
37
- /** Compile the resources to schema.rastack.json + openapi.json in outDir. */
38
- function compile(resourcesDir: string, outDir: string): boolean {
39
- const compiler = path.join(HERE, "rastack-compile.js");
40
- const result = spawnSync(
41
- process.execPath,
42
- [compiler, "compile", resourcesDir, outDir],
43
- { stdio: "inherit" },
44
- );
45
- if (result.status !== 0) return false;
46
- return fs.existsSync(path.join(outDir, "schema.rastack.json"));
47
- }
48
-
49
38
  /** Locate the prebuilt wasm bundle dir shipped in the package (no cargo fallback). */
50
39
  function resolveWasmDir(): string | null {
51
40
  for (const dir of wasmBundleCandidates(HERE)) {
@@ -106,9 +95,11 @@ function openBrowser(url: string): void {
106
95
  function main(argv: string[]): void {
107
96
  const args = parseAdminArgs(argv);
108
97
 
109
- console.log(`\n rastack admin — the database console, built with React API Stack\n`);
98
+ console.log(
99
+ `\n rastack admin — the database console, built with React API Stack\n`,
100
+ );
110
101
  console.log(`▸ Compiling ${args.resourcesDir} → ${args.outDir}`);
111
- if (!compile(args.resourcesDir, args.outDir)) {
102
+ if (!runCompile(args.resourcesDir, args.outDir)) {
112
103
  console.error(" ✗ compile failed — fix the resource graph and re-run.");
113
104
  process.exit(1);
114
105
  }
package/src/rastack-ci.ts CHANGED
@@ -18,6 +18,8 @@
18
18
  */
19
19
 
20
20
  import { execSync } from "child_process";
21
+ import { runCompile } from "./compile";
22
+ import { DeployConfig } from "./deploy/config";
21
23
  import { CiPhase, CiPlanOptions, CiStep, ciPlan } from "./deploy/ci";
22
24
  import { loadConfig } from "./deploy/io";
23
25
 
@@ -85,13 +87,24 @@ function printPlan(phase: CiPhase, steps: CiStep[]): void {
85
87
  steps.forEach((s, i) => {
86
88
  const at = s.cwd ? ` (in ${s.cwd}/)` : "";
87
89
  console.log(` ${i + 1}. ${s.name}${at}`);
88
- console.log(` $ ${s.run}\n`);
90
+ // In-process steps have no shell command; `run` is a description.
91
+ console.log(` ${s.action ? "» " : "$ "}${s.run}\n`);
89
92
  });
90
93
  }
91
94
 
92
- function runSteps(phase: CiPhase, steps: CiStep[]): void {
95
+ function runSteps(phase: CiPhase, steps: CiStep[], cfg: DeployConfig): void {
93
96
  for (const step of steps) {
94
97
  console.log(`\n▸ ${step.name}${step.cwd ? ` (in ${step.cwd}/)` : ""}`);
98
+ if (step.action === "compile") {
99
+ // Compilation runs in-process — it lives inside `ci` (and `dev`), not in a
100
+ // separately-invoked `compile` command.
101
+ console.log(` » ${step.run}`);
102
+ if (!runCompile(cfg.resourcesDir, cfg.outDir)) {
103
+ console.error(`\n ✗ step failed: ${step.name}`);
104
+ process.exit(1);
105
+ }
106
+ continue;
107
+ }
95
108
  console.log(` $ ${step.run}`);
96
109
  try {
97
110
  // Runs a CI pipeline step derived from the developer's own deploy config
@@ -123,7 +136,7 @@ function main(argv: string[]): void {
123
136
  }
124
137
  const steps = ciPlan(phase, cfg, opts);
125
138
  if (plan) printPlan(phase, steps);
126
- else runSteps(phase, steps);
139
+ else runSteps(phase, steps, cfg);
127
140
  }
128
141
 
129
142
  if (require.main === module) {
@@ -1,40 +1,31 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * `rastack compile | check | list | urls` — the TypeScript-first replacement for the
5
- * old Django `manage.py rastack` command.
4
+ * `rastack check | list | urls` — introspection over the TypeScript-first
5
+ * resource graph.
6
6
  *
7
- * rastack compile [resourcesDir] [outDir] defs → schema.rastack.json + openapi.json
8
7
  * rastack check [resourcesDir] validate the resource graph (exit != 0 on error)
9
8
  * rastack list [resourcesDir] print every resource + its fields/relations
10
9
  * rastack urls [resourcesDir] print the generated /api/{app}/v1/{model}/ routes
10
+ *
11
+ * Compilation itself (defs → schema.rastack.json + openapi.json) is no longer a
12
+ * command here: it runs in-process inside the two modalities of use — `rastack
13
+ * dev`/`admin` (local) and `rastack ci` (deploy) — via the shared `runCompile`.
11
14
  */
12
15
 
13
16
  import {
14
17
  analyzeProject,
15
18
  analyzeResources,
16
19
  collisions,
17
- compile,
18
20
  urlsFor,
19
21
  } from "./compile";
20
22
 
21
23
  const DEFAULT_RESOURCES = "resources";
22
- const DEFAULT_OUT = ".rastack";
23
24
 
24
25
  function main(argv: string[]): void {
25
26
  const [sub, ...rest] = argv;
26
27
 
27
28
  switch (sub) {
28
- case "compile": {
29
- const resourcesDir = rest[0] || DEFAULT_RESOURCES;
30
- const outDir = rest[1] || DEFAULT_OUT;
31
- const result = compile(resourcesDir, outDir);
32
- for (const d of result.diagnostics) console.warn(` ⚠ ${d.message}`);
33
- console.log(
34
- `✓ compiled ${result.manifest.resources.length} resource(s) → ${result.files.join(", ")}`,
35
- );
36
- break;
37
- }
38
29
  case "check": {
39
30
  const { resources, diagnostics } = analyzeProject(
40
31
  rest[0] || DEFAULT_RESOURCES,
@@ -65,7 +56,9 @@ function main(argv: string[]): void {
65
56
  for (const r of resources) {
66
57
  console.log(`${r.app}.${r.model}`);
67
58
  for (const f of r.fields) {
68
- const rel = f.relation ? ` → ${f.relation.app}.${f.relation.model}` : "";
59
+ const rel = f.relation
60
+ ? ` → ${f.relation.app}.${f.relation.model}`
61
+ : "";
69
62
  console.log(` ${f.name}: ${f.type}${rel}`);
70
63
  }
71
64
  if (r.sync) {
@@ -89,9 +82,8 @@ function main(argv: string[]): void {
89
82
  }
90
83
  default:
91
84
  console.error(
92
- `Unknown compile subcommand: "${sub}"\n\n` +
85
+ `Unknown subcommand: "${sub}"\n\n` +
93
86
  `Usage:\n` +
94
- ` rastack compile [resourcesDir] [outDir]\n` +
95
87
  ` rastack check [resourcesDir]\n` +
96
88
  ` rastack list [resourcesDir]\n` +
97
89
  ` rastack urls [resourcesDir]\n`,
@@ -1,18 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * `rastack dev` — local development, always over the WASM engine.
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 app +
10
- * admin locally over WebAssembly — no native server, no cargo:
11
- * • the bundled React admin (`/dev/admin.js`),
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
  */
@@ -21,39 +25,36 @@ import { execFileSync, spawnSync } from "child_process";
21
25
  import * as fs from "fs";
22
26
  import * as http from "http";
23
27
  import * as path from "path";
28
+ import { runCompile } from "./compile";
24
29
  import {
30
+ APP_ENTRY,
25
31
  WASM_ENTRY,
32
+ appRootCandidates,
26
33
  contentType,
27
34
  parseDevArgs,
28
- renderShell,
35
+ renderWelcome,
29
36
  wasmBundleCandidates,
30
37
  } from "./dev/harness";
31
38
 
32
39
  const HERE = __dirname; // dist/ when installed
33
40
 
34
- /** Compile the resources to schema.rastack.json + openapi.json in outDir. */
35
- function compile(resourcesDir: string, outDir: string): boolean {
36
- const compiler = path.join(HERE, "rastack-compile.js");
37
- const result = spawnSync(
38
- process.execPath,
39
- [compiler, "compile", resourcesDir, outDir],
40
- { stdio: "inherit" },
41
- );
42
- if (result.status !== 0) return false;
43
- return fs.existsSync(path.join(outDir, "schema.rastack.json"));
44
- }
45
-
46
41
  /** True when `cargo` is on PATH (the from-source wasm fallback needs it). */
47
42
  function cargoAvailable(): boolean {
48
43
  try {
49
- return (
50
- spawnSync("cargo", ["--version"], { stdio: "ignore" }).status === 0
51
- );
44
+ return spawnSync("cargo", ["--version"], { stdio: "ignore" }).status === 0;
52
45
  } catch {
53
46
  return false;
54
47
  }
55
48
  }
56
49
 
50
+ /** First app-root candidate that holds an `index.html`, or null (→ welcome). */
51
+ function resolveAppRoot(appDir: string): string | null {
52
+ for (const dir of appRootCandidates(appDir)) {
53
+ if (fs.existsSync(path.join(dir, APP_ENTRY))) return dir;
54
+ }
55
+ return null;
56
+ }
57
+
57
58
  /** Locate the prebuilt wasm bundle dir, building from source as a fallback. */
58
59
  function resolveWasmDir(): string | null {
59
60
  for (const dir of wasmBundleCandidates(HERE)) {
@@ -130,9 +131,9 @@ function openBrowser(url: string): void {
130
131
  function main(argv: string[]): void {
131
132
  const args = parseDevArgs(argv);
132
133
 
133
- console.log(`\n rastack dev — the app + admin, in your browser over WASM\n`);
134
+ console.log(`\n rastack dev — your app, in your browser over WASM\n`);
134
135
  console.log(`▸ Compiling ${args.resourcesDir} → ${args.outDir}`);
135
- if (!compile(args.resourcesDir, args.outDir)) {
136
+ if (!runCompile(args.resourcesDir, args.outDir)) {
136
137
  console.error(" ✗ compile failed — fix the resource graph and re-run.");
137
138
  process.exit(1);
138
139
  }
@@ -149,13 +150,8 @@ function main(argv: string[]): void {
149
150
  process.exit(1);
150
151
  }
151
152
 
152
- const adminBundle = path.join(HERE, "admin.js");
153
- if (!fs.existsSync(adminBundle)) {
154
- console.error(
155
- " ✗ admin bundle (dist/admin.js) not found — reinstall rastack.",
156
- );
157
- process.exit(1);
158
- }
153
+ // Serve the developer's own app when there is one; otherwise the welcome page.
154
+ const appRoot = resolveAppRoot(args.appDir);
159
155
 
160
156
  const server = http.createServer((req, res) => {
161
157
  const url = (req.url || "/").split("?")[0];
@@ -163,13 +159,7 @@ function main(argv: string[]): void {
163
159
  res.writeHead(405).end("Method not allowed");
164
160
  return;
165
161
  }
166
- if (url === "/" || url === "/index.html") {
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);
162
+ // Reserved backend routes always win over the app's static files.
173
163
  if (url.startsWith("/wasm/"))
174
164
  return serveUnder(res, wasmDir, url.slice("/wasm/".length));
175
165
  if (url === "/schema.rastack.json" || url === "/openapi.json")
@@ -180,14 +170,43 @@ function main(argv: string[]): void {
180
170
  args.warehouseDir,
181
171
  url.slice("/data/warehouse/".length),
182
172
  );
183
- res.writeHead(404).end("Not found");
173
+
174
+ // No app → the Django-style welcome page (root only; else 404).
175
+ if (!appRoot) {
176
+ if (url === "/" || url === "/index.html") {
177
+ res
178
+ .writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
179
+ .end(renderWelcome());
180
+ return;
181
+ }
182
+ res.writeHead(404).end("Not found");
183
+ return;
184
+ }
185
+
186
+ // Serve the app: its index.html at `/`, an existing file as-is, and any
187
+ // other route back to index.html (SPA client-side routing).
188
+ if (url === "/" || url === "/index.html")
189
+ return serveFile(res, path.join(appRoot, APP_ENTRY));
190
+ const rel = decodeURIComponent(url).replace(/^\/+/, "");
191
+ const target = path.resolve(appRoot, rel);
192
+ const safeRoot = path.resolve(appRoot);
193
+ const inRoot =
194
+ target === safeRoot || target.startsWith(safeRoot + path.sep);
195
+ if (inRoot && fs.existsSync(target) && fs.statSync(target).isFile())
196
+ return serveFile(res, target);
197
+ return serveFile(res, path.join(appRoot, APP_ENTRY));
184
198
  });
185
199
 
186
200
  server.listen(args.port, () => {
187
201
  const url = `http://localhost:${args.port}`;
188
202
  console.log(` ✓ WASM engine: ${path.relative(process.cwd(), wasmDir)}`);
189
- console.log(`\n ▸ ${url} (admin at ${url}/#/)\n`);
190
- console.log(" Press Ctrl+C to stop.\n");
203
+ if (appRoot)
204
+ console.log(` ✓ app: ${path.relative(process.cwd(), appRoot) || "."}`);
205
+ else console.log(` ○ no app found — serving the welcome page`);
206
+ console.log(`\n ▸ ${url}\n`);
207
+ console.log(
208
+ " Need the database console? Run `rastack admin`. Ctrl+C to stop.\n",
209
+ );
191
210
  if (args.open) openBrowser(url);
192
211
  });
193
212
  }
@@ -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
- // nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
68
- return NPM_SHELL
69
- ? execSync(npmShellCommand(NPM, args), opts)
70
- : execFileSync(NPM, args, opts);
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
@@ -3,15 +3,17 @@
3
3
  /**
4
4
  * rastack — unified CLI for React API Stack
5
5
  *
6
+ * Compilation is not a command of its own: it runs in-process inside the two
7
+ * modalities of use — `rastack dev`/`admin` (local) and `rastack ci` (deploy).
8
+ *
6
9
  * Commands:
7
- * rastack compile [resourcesDir] [outDir] TypeScript resources → schema.rastack.json + openapi.json
8
10
  * rastack check [resourcesDir] Validate the resource graph
9
11
  * rastack list [resourcesDir] Print resources, fields and relations
10
12
  * rastack urls [resourcesDir] Print the generated /api/{app}/v1/{model}/ routes
11
13
  * rastack generate [--schema-only] Generate typed hooks from OpenAPI
12
14
  * rastack tokens [input] [outDir] Compile design tokens → CSS vars + typed theme
13
15
  * rastack design [input] [--port n] Live design-system studio (showcase + edit)
14
- * rastack dev [--port n] [--warehouse d] Local dev: run the app + admin in-browser over WASM
16
+ * rastack dev [--port n] [--warehouse d] Local dev: run your app in-browser over WASM
15
17
  * rastack admin [--warehouse dir] [--port n] Full admin console — React UI (rastack/components) over the native server
16
18
  * rastack import <file> --resource app.model Load CSV/XLSX/PDF data and validate it (state machine)
17
19
  * rastack scan [files...] [--fail-on-pii] Scan CSVs for PII and schema info
@@ -65,6 +67,17 @@ switch (command) {
65
67
  printVersion();
66
68
  break;
67
69
  case "compile":
70
+ // Compilation is no longer a standalone command — it runs in-process as
71
+ // part of the two modalities of use. Point the way rather than 404.
72
+ console.error(
73
+ "`rastack compile` has been removed — compilation now runs automatically\n" +
74
+ "inside the two modalities of use:\n" +
75
+ " • rastack dev local development (compiles, then serves over WASM)\n" +
76
+ " • rastack ci the deploy pipeline (compiles the manifest + OpenAPI)\n\n" +
77
+ "Introspect the resource graph with: rastack check | list | urls\n",
78
+ );
79
+ process.exit(1);
80
+ break;
68
81
  case "check":
69
82
  case "list":
70
83
  case "urls":
@@ -72,8 +85,9 @@ switch (command) {
72
85
  run("rastack-compile.js", [command, ...rest]);
73
86
  break;
74
87
  case "dev":
75
- // Local development: serve the app + admin in the browser over the WASM
76
- // engine (no native server, no cargo). Replaces `serve` + `wasm`.
88
+ // Local development: serve your app in the browser over the WASM engine
89
+ // (no native server, no cargo). The admin console is a separate command,
90
+ // `rastack admin`. Replaces `serve` + `wasm`.
77
91
  run("rastack-dev.js", rest);
78
92
  break;
79
93
  case "admin":
@@ -134,12 +148,11 @@ switch (command) {
134
148
  console.error(
135
149
  `Unknown command: "${command}"\n\n` +
136
150
  `Usage:\n` +
137
- ` rastack compile [resourcesDir] [outDir]\n` +
138
151
  ` rastack check | list | urls [resourcesDir]\n` +
139
152
  ` rastack generate [--schema-only]\n` +
140
153
  ` rastack tokens [input] [outDir] [--prefix p] Compile design tokens → CSS vars + typed theme\n` +
141
154
  ` rastack design [input] [--port n] Live design-system studio (showcase + edit)\n` +
142
- ` rastack dev [--port n] [--warehouse dir] [--no-open] Run the app + admin in-browser over WASM (local dev)\n` +
155
+ ` rastack dev [--port n] [--warehouse dir] [--app dir] [--no-open] Run your app in-browser over WASM (local dev)\n` +
143
156
  ` rastack admin [--warehouse dir] [--port n] [--no-open] Full admin console (React UI over the native server; needs a built rastack-server)\n` +
144
157
  ` rastack import <file.csv|.xlsx|.pdf> --resource app.model [--related app.model=<file>] [--out cleaned.json]\n` +
145
158
  ` rastack scan [files...] [--fail-on-pii]\n` +
@@ -419,12 +419,15 @@ describe("repo variables + lambda env", () => {
419
419
  });
420
420
 
421
421
  describe("ci plan — check", () => {
422
- it("validates then compiles, honouring the resources dir + bin", () => {
422
+ it("validates then compiles in-process, honouring the resources dir + bin", () => {
423
423
  const steps = checkSteps(base({ resourcesDir: "res" }), "npx rastack");
424
424
  expect(steps.map((s) => s.run)).toEqual([
425
425
  "npx rastack check res",
426
- "npx rastack compile res .rastack",
426
+ "compile res .rastack (in-process)",
427
427
  ]);
428
+ // The compile step runs in-process (no shell command), so the CLI can drop
429
+ // the standalone `compile` command entirely.
430
+ expect(steps[1].action).toBe("compile");
428
431
  expect(ciPlan("check", base(), { bin: "rastack" })).toEqual(
429
432
  checkSteps(base(), "rastack"),
430
433
  );
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 dev — HTML shell", () => {
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