rastack 0.0.64 → 0.0.69

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/CHANGELOG.md +25 -0
  2. package/README.md +19 -24
  3. package/admin/httpEngine.ts +5 -6
  4. package/admin/index.tsx +11 -91
  5. package/dist/admin.js +14 -22
  6. package/dist/compile/model.d.ts +1 -1
  7. package/dist/compile/run.d.ts +2 -2
  8. package/dist/compile/run.js +2 -2
  9. package/dist/define/index.d.ts +2 -3
  10. package/dist/deploy/workflows.js +1 -1
  11. package/dist/dev/harness.d.ts +19 -27
  12. package/dist/dev/harness.js +31 -40
  13. package/dist/dev/native-server.d.ts +29 -0
  14. package/dist/dev/native-server.js +174 -0
  15. package/dist/external/envelope.d.ts +1 -1
  16. package/dist/external/envelope.js +1 -1
  17. package/dist/external/service.d.ts +2 -2
  18. package/dist/external/service.js +2 -2
  19. package/dist/external/types.d.ts +1 -1
  20. package/dist/external/types.js +1 -1
  21. package/dist/rastack-admin.d.ts +6 -14
  22. package/dist/rastack-admin.js +60 -88
  23. package/dist/rastack-design.d.ts +1 -2
  24. package/dist/rastack-design.js +1 -2
  25. package/dist/rastack-dev.d.ts +7 -10
  26. package/dist/rastack-dev.js +68 -80
  27. package/dist/rastack.d.ts +1 -1
  28. package/dist/rastack.js +5 -6
  29. package/dist/validate/transitions.d.ts +1 -1
  30. package/dist/validate/transitions.js +1 -1
  31. package/hooks/query/api.ts +4 -6
  32. package/package.json +2 -2
  33. package/provider/index.ts +0 -1
  34. package/provider/provider.tsx +31 -157
  35. package/provider/types.ts +29 -77
  36. package/src/compile/model.ts +1 -1
  37. package/src/compile/run.ts +2 -2
  38. package/src/define/index.ts +2 -3
  39. package/src/deploy/workflows.ts +1 -1
  40. package/src/dev/harness.ts +30 -43
  41. package/src/dev/native-server.ts +164 -0
  42. package/src/external/envelope.ts +1 -1
  43. package/src/external/service.ts +2 -2
  44. package/src/external/types.ts +1 -1
  45. package/src/rastack-admin.ts +66 -107
  46. package/src/rastack-design.ts +1 -2
  47. package/src/rastack-dev.ts +79 -91
  48. package/src/rastack.ts +5 -6
  49. package/src/validate/transitions.ts +1 -1
  50. package/dist/rastack-wasm-build.d.ts +0 -19
  51. package/dist/rastack-wasm-build.js +0 -94
  52. package/dist/wasm/package.json +0 -4
  53. package/dist/wasm/rastack_wasm.d.ts +0 -100
  54. package/dist/wasm/rastack_wasm.js +0 -745
  55. package/dist/wasm/rastack_wasm_bg.wasm +0 -0
  56. package/dist/wasm/rastack_wasm_bg.wasm.d.ts +0 -28
  57. package/provider/wasm.ts +0 -126
  58. package/scripts/copy-wasm.js +0 -35
  59. package/src/rastack-wasm-build.ts +0 -113
@@ -76,7 +76,7 @@ export interface TransitionEdgeModel {
76
76
  /**
77
77
  * A resource's declarative state machine, keyed off one state field. Compiled
78
78
  * into the manifest and enforced on every write surface: the TS record machine
79
- * (forms, `rastack import`) and `rastack-api-core` (server, Lambda, WASM) all
79
+ * (forms, `rastack import`) and `rastack-api-core` (the native server) all
80
80
  * reject a write that jumps between states without a declared edge.
81
81
  */
82
82
  export interface TransitionsModel {
@@ -5,8 +5,8 @@
5
5
  *
6
6
  * This is the single entry point the two real modalities of use share instead
7
7
  * of shelling out to a standalone `compile` command:
8
- * • `rastack dev` / `rastack admin` — local development (compile, then serve
9
- * the app + admin in the browser over WASM), and
8
+ * • `rastack dev` / `rastack admin` — local development (compile, then start
9
+ * the native rastack-server; the browser is only the UI), and
10
10
  * • `rastack ci` — the deploy pipeline (compile the manifest the Rust backend
11
11
  * reads + the OpenAPI contract, then build/ship).
12
12
  */
@@ -44,8 +44,8 @@ const index_1 = require("./index");
44
44
  *
45
45
  * This is the single entry point the two real modalities of use share instead
46
46
  * of shelling out to a standalone `compile` command:
47
- * • `rastack dev` / `rastack admin` — local development (compile, then serve
48
- * the app + admin in the browser over WASM), and
47
+ * • `rastack dev` / `rastack admin` — local development (compile, then start
48
+ * the native rastack-server; the browser is only the UI), and
49
49
  * • `rastack ci` — the deploy pipeline (compile the manifest the Rust backend
50
50
  * reads + the OpenAPI contract, then build/ship).
51
51
  */
@@ -125,8 +125,7 @@ export interface SyncOptions {
125
125
  }
126
126
  /**
127
127
  * Row-level access control. Enforced by `rastack-api-core`, so the rules are
128
- * identical on the server (Lambda / `rastack serve`) and in the in-browser WASM
129
- * engine.
128
+ * identical on the native server (laptop and Lambda).
130
129
  *
131
130
  * - `ownerField` — the field that records the owning identity (the verified
132
131
  * token `sub`). Rows are only listed/read/written by their owner; the field
@@ -164,7 +163,7 @@ export type Permission = "authenticatedOrReadOnly" | "authenticated" | "public";
164
163
  * transition may fire from, `to` the state it enters, and `set` the effect —
165
164
  * field patches the engine applies alongside the state change. The block is
166
165
  * static data: it compiles into the manifest and is enforced by
167
- * `rastack-api-core` on every surface (server, Lambda, in-browser WASM), so a
166
+ * `rastack-api-core` on the native server (laptop and Lambda), so a
168
167
  * transition behaves as a declarative, typesafe backend function with no
169
168
  * per-resource server code.
170
169
  */
@@ -124,7 +124,7 @@ jobs:
124
124
  - name: Cache cargo
125
125
  uses: Swatinem/rust-cache@v2
126
126
  with:
127
- workspaces: rust
127
+ workspaces: engine
128
128
  # cargo-lambda cross-builds the custom-runtime binary; it auto-installs
129
129
  # zig (via pip) for the cross-compile when needed.
130
130
  - name: Install cargo-lambda
@@ -1,27 +1,25 @@
1
1
  /**
2
- * Pure core of `rastack dev` — the local, always-WASM development server.
2
+ * Pure core of `rastack dev` — local development against the native Rust server.
3
3
  *
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
- * compiled manifest). No native server, no cargo: a dependency-free static
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`.
4
+ * `rastack dev` serves **your app** from Node and proxies `/api` to a native
5
+ * `rastack-server` process (the same `rastack-api-core` Lambda runs). The
6
+ * browser renders the UI. It does not execute the engine. `rastack admin` is
7
+ * the separate command that starts `rastack-server admin`.
10
8
  *
11
9
  * When the project has no app to serve, `dev` shows a Django-style welcome
12
10
  * page (`renderWelcome`) so a fresh scaffold still boots to something friendly.
13
11
  *
14
- * Everything decidable without IO lives here (arg parsing, MIME types, locating
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.
12
+ * Everything decidable without IO lives here (arg parsing, MIME types, where
13
+ * the native binary is looked up, resolving the app root, the HTML pages) so
14
+ * it is unit-tested without a socket or a filesystem; `rastack-dev.ts` is the
15
+ * thin IO wrapper that compiles, starts the server, and serves.
18
16
  */
19
17
  export interface DevArgs {
20
18
  /** Where the TypeScript resource definitions live (compiled on start). */
21
19
  resourcesDir: string;
22
20
  /** Where `rastack compile` writes schema.rastack.json + openapi.json. */
23
21
  outDir: string;
24
- /** The committed Iceberg warehouse served to seed the browser engine. */
22
+ /** The on-disk Iceberg warehouse the native server opens. */
25
23
  warehouseDir: string;
26
24
  /**
27
25
  * Explicit app root to serve at `/` (its `index.html`). Empty when unset —
@@ -71,21 +69,16 @@ export declare function parseAdminArgs(argv: string[]): AdminArgs;
71
69
  /** MIME type for a served path, defaulting to octet-stream (Iceberg data files). */
72
70
  export declare function contentType(filePath: string): string;
73
71
  /**
74
- * Ordered directories to look for the prebuilt wasm bundle (`rastack_wasm.js` +
75
- * `rastack_wasm_bg.wasm`). The bundle is shipped inside the package
76
- * (`dist/wasm/`, copied at build), so `rastack dev` works from any repo with no
77
- * cargo; the repo-local `wasm/` (from a source build) and a sibling of the
78
- * running module are fallbacks for development. `dirnameDir` is the directory of
79
- * the compiled `rastack-dev.js` (`dist/`), so `../wasm` and `./wasm` cover both
80
- * `dist/rastack-dev.js` → `dist/wasm` and `dist/dev/…` layouts.
72
+ * Ordered paths for the native `rastack-server` binary inside a workspace
73
+ * checkout. Release wins over debug. `RASTACK_SERVER` and `PATH` are checked
74
+ * by the IO wrapper, not here.
81
75
  */
82
- export declare function wasmBundleCandidates(dirnameDir: string, cwd?: string): string[];
83
- /** The name of the file that marks a directory as a usable wasm bundle. */
84
- export declare const WASM_ENTRY = "rastack_wasm.js";
76
+ export declare function serverBinaryCandidates(cwd: string): string[];
85
77
  /**
86
78
  * The HTML shell served at `/` by **`rastack admin`**. It loads the bundled
87
- * React admin (`/dev/admin.js`), which fetches the schema/openapi, boots the
88
- * wasm engine, seeds it from `/data/warehouse/`, and renders the admin console.
79
+ * React admin (`/dev/admin.js`). `rastack admin` itself does not serve this
80
+ * page: the native `rastack-server admin` process serves `/admin/` and the
81
+ * bundle talks to that process over HTTP.
89
82
  * Deliberately tiny — all logic lives in the bundle — mirroring the
90
83
  * `rastack design` studio. `rastack dev` does **not** use this: it serves the
91
84
  * developer's own app (or the welcome page below).
@@ -94,8 +87,7 @@ export declare function renderShell(title?: string): string;
94
87
  /**
95
88
  * The Django-style welcome page `rastack dev` serves at `/` when the project
96
89
  * has **no app** to serve (no `index.html` in any `appRootCandidates` dir).
97
- * It confirms the dev server is up, points at the live backend endpoints the
98
- * WASM engine exposes, and tells you how to add an app or open the admin — the
99
- * "It worked!" moment of `django-admin runserver`, on brand for the stack.
90
+ * It confirms the dev server is up, points at the native API proxied at
91
+ * `/api`, and tells you how to add an app or open the admin.
100
92
  */
101
93
  export declare function renderWelcome(title?: string): string;
@@ -1,21 +1,19 @@
1
1
  "use strict";
2
2
  /**
3
- * Pure core of `rastack dev` — the local, always-WASM development server.
3
+ * Pure core of `rastack dev` — local development against the native Rust server.
4
4
  *
5
- * `rastack dev` runs **your app** in the browser over the WebAssembly engine
6
- * (the same app-generic `rastack-api-core` the Lambda runs, fed the app's
7
- * compiled manifest). No native server, no cargo: a dependency-free static
8
- * server hands the browser the prebuilt wasm engine, the compiled schema, the
9
- * committed Iceberg warehouse, and the developer's own app. It does **not**
10
- * serve the admin console — that is a separate command, `rastack admin`.
5
+ * `rastack dev` serves **your app** from Node and proxies `/api` to a native
6
+ * `rastack-server` process (the same `rastack-api-core` Lambda runs). The
7
+ * browser renders the UI. It does not execute the engine. `rastack admin` is
8
+ * the separate command that starts `rastack-server admin`.
11
9
  *
12
10
  * When the project has no app to serve, `dev` shows a Django-style welcome
13
11
  * page (`renderWelcome`) so a fresh scaffold still boots to something friendly.
14
12
  *
15
- * Everything decidable without IO lives here (arg parsing, MIME types, locating
16
- * the wasm bundle, resolving the app root, the HTML pages) so it is unit-tested
17
- * without a socket or a filesystem; `rastack-dev.ts` is the thin IO wrapper that
18
- * compiles, resolves the engine and the app, and serves.
13
+ * Everything decidable without IO lives here (arg parsing, MIME types, where
14
+ * the native binary is looked up, resolving the app root, the HTML pages) so
15
+ * it is unit-tested without a socket or a filesystem; `rastack-dev.ts` is the
16
+ * thin IO wrapper that compiles, starts the server, and serves.
19
17
  */
20
18
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
21
19
  if (k2 === undefined) k2 = k;
@@ -51,12 +49,12 @@ var __importStar = (this && this.__importStar) || (function () {
51
49
  };
52
50
  })();
53
51
  Object.defineProperty(exports, "__esModule", { value: true });
54
- exports.WASM_ENTRY = exports.ADMIN_DEFAULTS = exports.APP_ENTRY = exports.DEV_DEFAULTS = void 0;
52
+ exports.ADMIN_DEFAULTS = exports.APP_ENTRY = exports.DEV_DEFAULTS = void 0;
55
53
  exports.parseDevArgs = parseDevArgs;
56
54
  exports.appRootCandidates = appRootCandidates;
57
55
  exports.parseAdminArgs = parseAdminArgs;
58
56
  exports.contentType = contentType;
59
- exports.wasmBundleCandidates = wasmBundleCandidates;
57
+ exports.serverBinaryCandidates = serverBinaryCandidates;
60
58
  exports.renderShell = renderShell;
61
59
  exports.renderWelcome = renderWelcome;
62
60
  const path = __importStar(require("path"));
@@ -157,7 +155,6 @@ const MIME = {
157
155
  ".mjs": "text/javascript; charset=utf-8",
158
156
  ".css": "text/css; charset=utf-8",
159
157
  ".json": "application/json; charset=utf-8",
160
- // Critical: `WebAssembly.instantiateStreaming` requires this exact type.
161
158
  ".wasm": "application/wasm",
162
159
  ".text": "text/plain; charset=utf-8",
163
160
  ".parquet": "application/octet-stream",
@@ -168,27 +165,21 @@ function contentType(filePath) {
168
165
  return MIME[ext] ?? "application/octet-stream";
169
166
  }
170
167
  /**
171
- * Ordered directories to look for the prebuilt wasm bundle (`rastack_wasm.js` +
172
- * `rastack_wasm_bg.wasm`). The bundle is shipped inside the package
173
- * (`dist/wasm/`, copied at build), so `rastack dev` works from any repo with no
174
- * cargo; the repo-local `wasm/` (from a source build) and a sibling of the
175
- * running module are fallbacks for development. `dirnameDir` is the directory of
176
- * the compiled `rastack-dev.js` (`dist/`), so `../wasm` and `./wasm` cover both
177
- * `dist/rastack-dev.js` → `dist/wasm` and `dist/dev/…` layouts.
168
+ * Ordered paths for the native `rastack-server` binary inside a workspace
169
+ * checkout. Release wins over debug. `RASTACK_SERVER` and `PATH` are checked
170
+ * by the IO wrapper, not here.
178
171
  */
179
- function wasmBundleCandidates(dirnameDir, cwd = process.cwd()) {
172
+ function serverBinaryCandidates(cwd) {
180
173
  return [
181
- path.join(dirnameDir, "wasm"),
182
- path.join(dirnameDir, "..", "wasm"),
183
- path.join(cwd, "wasm"),
174
+ path.join(cwd, "engine", "target", "release", "rastack-server"),
175
+ path.join(cwd, "engine", "target", "debug", "rastack-server"),
184
176
  ];
185
177
  }
186
- /** The name of the file that marks a directory as a usable wasm bundle. */
187
- exports.WASM_ENTRY = "rastack_wasm.js";
188
178
  /**
189
179
  * The HTML shell served at `/` by **`rastack admin`**. It loads the bundled
190
- * React admin (`/dev/admin.js`), which fetches the schema/openapi, boots the
191
- * wasm engine, seeds it from `/data/warehouse/`, and renders the admin console.
180
+ * React admin (`/dev/admin.js`). `rastack admin` itself does not serve this
181
+ * page: the native `rastack-server admin` process serves `/admin/` and the
182
+ * bundle talks to that process over HTTP.
192
183
  * Deliberately tiny — all logic lives in the bundle — mirroring the
193
184
  * `rastack design` studio. `rastack dev` does **not** use this: it serves the
194
185
  * developer's own app (or the welcome page below).
@@ -233,9 +224,8 @@ function renderShell(title = "React API Stack — admin") {
233
224
  /**
234
225
  * The Django-style welcome page `rastack dev` serves at `/` when the project
235
226
  * has **no app** to serve (no `index.html` in any `appRootCandidates` dir).
236
- * It confirms the dev server is up, points at the live backend endpoints the
237
- * WASM engine exposes, and tells you how to add an app or open the admin — the
238
- * "It worked!" moment of `django-admin runserver`, on brand for the stack.
227
+ * It confirms the dev server is up, points at the native API proxied at
228
+ * `/api`, and tells you how to add an app or open the admin.
239
229
  */
240
230
  function renderWelcome(title = "React API Stack — dev") {
241
231
  return `<!doctype html>
@@ -282,20 +272,21 @@ function renderWelcome(title = "React API Stack — dev") {
282
272
  <div class="rocket">🚀</div>
283
273
  <h1>The dev server is running.</h1>
284
274
  <p class="lead">
285
- Congratulations — <strong>rastack dev</strong> is up and your API is running
286
- in the browser over WebAssembly. You're seeing this page because there's no
287
- app to serve yet.
275
+ Congratulations — <strong>rastack dev</strong> is up. The API is a native
276
+ Rust process (<code>rastack-server</code>) on this machine. This page is
277
+ only the UI. You're seeing it because there's no app to serve yet.
288
278
  </p>
289
279
  <ul>
290
280
  <li>Add an <code>index.html</code> (in <code>public/</code>, <code>www/</code>,
291
281
  <code>app/</code>, or the project root) and reload — <code>dev</code> will serve it.
292
282
  Point it at a different folder with <code>rastack dev --app &lt;dir&gt;</code>.</li>
293
- <li>Wire your app to the local engine with
294
- <code>&lt;RAStackProvider mode="local"&gt;</code>.</li>
295
- <li>Live backend endpoints:
283
+ <li>Wire your app with
284
+ <code>&lt;RAStackProvider mode="local"&gt;</code>. Requests go to
285
+ <code>/api</code> on this origin, which is proxied to the native server.</li>
286
+ <li>Live files:
296
287
  <a href="/schema.rastack.json">/schema.rastack.json</a>,
297
- <a href="/openapi.json">/openapi.json</a>,
298
- <code>/data/warehouse/…</code>, <code>/wasm/…</code>.</li>
288
+ <a href="/openapi.json">/openapi.json</a>.
289
+ The warehouse stays on disk; the browser does not load it.</li>
299
290
  <li>Want the database console instead? Run <code>rastack admin</code>.</li>
300
291
  </ul>
301
292
  <div class="foot">React API Stack — a full-stack framework on object storage.</div>
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Start the native `rastack-server` binary — the same `rastack-api-core`
3
+ * process Lambda runs. The browser never executes this binary. `rastack dev`
4
+ * proxies `/api` to it; `rastack admin` lets the binary serve `/admin` itself.
5
+ */
6
+ import { type ChildProcess } from "child_process";
7
+ export interface NativeServerOpts {
8
+ /** Absolute path to the `rastack-server` binary. */
9
+ bin: string;
10
+ /** `serve` is the API. `admin` mounts the React console at `/admin`. */
11
+ command: "serve" | "admin";
12
+ manifest: string;
13
+ warehouse: string;
14
+ /** `host:port` the binary binds. */
15
+ addr: string;
16
+ /** Directory that contains `admin.js` (`RASTACK_ADMIN_ASSETS`). */
17
+ adminAssets?: string;
18
+ }
19
+ /** `RASTACK_SERVER`, a workspace target dir, or `rastack-server` on PATH. */
20
+ export declare function resolveServerBinary(cwd: string): string | null;
21
+ /**
22
+ * Locate the native binary, building it with cargo when this repo's workspace
23
+ * is present and the binary is not. Throws when nothing native can be started.
24
+ */
25
+ export declare function ensureServerBinary(cwd: string): string;
26
+ /** Spawn `rastack-server serve|admin`. Stdio is inherited. */
27
+ export declare function startNativeServer(opts: NativeServerOpts): ChildProcess;
28
+ /** Resolve once the process accepts TCP, or reject if it exits or times out. */
29
+ export declare function waitForTcp(port: number, child: ChildProcess, timeoutMs?: number): Promise<void>;
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ /**
3
+ * Start the native `rastack-server` binary — the same `rastack-api-core`
4
+ * process Lambda runs. The browser never executes this binary. `rastack dev`
5
+ * proxies `/api` to it; `rastack admin` lets the binary serve `/admin` itself.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.resolveServerBinary = resolveServerBinary;
42
+ exports.ensureServerBinary = ensureServerBinary;
43
+ exports.startNativeServer = startNativeServer;
44
+ exports.waitForTcp = waitForTcp;
45
+ const child_process_1 = require("child_process");
46
+ const fs = __importStar(require("fs"));
47
+ const net = __importStar(require("net"));
48
+ const path = __importStar(require("path"));
49
+ const harness_1 = require("./harness");
50
+ const MISSING = `
51
+ ✗ No native rastack-server binary.
52
+ The API does not run in the browser. rastack dev and rastack admin start
53
+ the native Rust server (rastack-api-core) and the page talks to it over HTTP.
54
+ Build it from this repo:
55
+
56
+ cargo build -p rastack-server --manifest-path engine/Cargo.toml
57
+
58
+ Or set RASTACK_SERVER to the binary. There is no in-browser engine.
59
+ `;
60
+ /**
61
+ * `rastack-server` on PATH. Walks the directories ourselves so the lookup
62
+ * never goes through a shell (`command -v` with `shell: true`).
63
+ */
64
+ function serverOnPath() {
65
+ const entries = (process.env.PATH ?? "").split(path.delimiter);
66
+ for (const dir of entries) {
67
+ if (!dir)
68
+ continue;
69
+ const candidate = path.join(dir, "rastack-server");
70
+ try {
71
+ if (fs.existsSync(candidate))
72
+ return candidate;
73
+ }
74
+ catch {
75
+ // An unreadable PATH entry is not a candidate.
76
+ }
77
+ }
78
+ return null;
79
+ }
80
+ /** `RASTACK_SERVER`, a workspace target dir, or `rastack-server` on PATH. */
81
+ function resolveServerBinary(cwd) {
82
+ const fromEnv = process.env.RASTACK_SERVER;
83
+ if (fromEnv && fs.existsSync(fromEnv))
84
+ return fromEnv;
85
+ for (const candidate of (0, harness_1.serverBinaryCandidates)(cwd)) {
86
+ if (fs.existsSync(candidate))
87
+ return candidate;
88
+ }
89
+ return serverOnPath();
90
+ }
91
+ function cargoAvailable() {
92
+ try {
93
+ return (0, child_process_1.spawnSync)("cargo", ["--version"], { stdio: "ignore" }).status === 0;
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ }
99
+ /**
100
+ * Locate the native binary, building it with cargo when this repo's workspace
101
+ * is present and the binary is not. Throws when nothing native can be started.
102
+ */
103
+ function ensureServerBinary(cwd) {
104
+ const ready = resolveServerBinary(cwd);
105
+ if (ready)
106
+ return ready;
107
+ const manifest = path.join(cwd, "engine", "Cargo.toml");
108
+ if (fs.existsSync(manifest) && cargoAvailable()) {
109
+ console.log("\n▸ Building native rastack-server (cargo build -p rastack-server)…\n");
110
+ const built = (0, child_process_1.spawnSync)("cargo", ["build", "-p", "rastack-server", "--manifest-path", manifest], { stdio: "inherit" });
111
+ if (built.status === 0) {
112
+ const after = resolveServerBinary(cwd);
113
+ if (after)
114
+ return after;
115
+ }
116
+ }
117
+ throw new Error(MISSING.trim());
118
+ }
119
+ /** Spawn `rastack-server serve|admin`. Stdio is inherited. */
120
+ function startNativeServer(opts) {
121
+ const env = { ...process.env };
122
+ if (opts.adminAssets)
123
+ env.RASTACK_ADMIN_ASSETS = opts.adminAssets;
124
+ // `bin` is resolveServerBinary's result: RASTACK_SERVER (a file that exists),
125
+ // a workspace target path, or a PATH entry named rastack-server. It is not
126
+ // request input. The command cannot be a string literal because the path
127
+ // depends on which of those three resolved. The nosemgrep comment has to sit
128
+ // on the line directly above the tainted argument — that is the line Semgrep
129
+ // reports, not the `spawn(` line.
130
+ const bin = opts.bin;
131
+ return (0, child_process_1.spawn)(
132
+ // nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
133
+ bin, [
134
+ opts.command,
135
+ "--manifest",
136
+ opts.manifest,
137
+ "--warehouse",
138
+ opts.warehouse,
139
+ "--addr",
140
+ opts.addr,
141
+ "--insecure-dev-auth",
142
+ ], { stdio: "inherit", env });
143
+ }
144
+ /** Resolve once the process accepts TCP, or reject if it exits or times out. */
145
+ function waitForTcp(port, child, timeoutMs = 30000) {
146
+ return new Promise((resolve, reject) => {
147
+ const deadline = Date.now() + timeoutMs;
148
+ let settled = false;
149
+ const finish = (err) => {
150
+ if (settled)
151
+ return;
152
+ settled = true;
153
+ if (err)
154
+ reject(err);
155
+ else
156
+ resolve();
157
+ };
158
+ child.once("exit", (code) => finish(new Error(`rastack-server exited (${code ?? "?"}) before it accepted connections`)));
159
+ const tick = () => {
160
+ if (settled)
161
+ return;
162
+ if (Date.now() > deadline) {
163
+ finish(new Error(`rastack-server did not listen on 127.0.0.1:${port}`));
164
+ return;
165
+ }
166
+ const socket = net.connect(port, "127.0.0.1", () => {
167
+ socket.end();
168
+ finish();
169
+ });
170
+ socket.on("error", () => setTimeout(tick, 100));
171
+ };
172
+ tick();
173
+ });
174
+ }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Normalise an upstream response into rastack's REST contract — the DRF-shaped
3
3
  * list envelope and bare detail object the typed hooks and `<DataTable>` expect,
4
- * identical to what the Rust API and the WASM/local engine emit.
4
+ * identical to what the native Rust API emits.
5
5
  *
6
6
  * External APIs rarely page the way rastack does (GitHub uses `per_page` + Link
7
7
  * headers; a GraphQL list just returns an array), so the envelope is synthesised
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * Normalise an upstream response into rastack's REST contract — the DRF-shaped
4
4
  * list envelope and bare detail object the typed hooks and `<DataTable>` expect,
5
- * identical to what the Rust API and the WASM/local engine emit.
5
+ * identical to what the native Rust API emits.
6
6
  *
7
7
  * External APIs rarely page the way rastack does (GitHub uses `per_page` + Link
8
8
  * headers; a GraphQL list just returns an array), so the envelope is synthesised
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * The mini local proxy service — the "lambda you run while iterating".
3
3
  *
4
- * `rastack dev` already serves the app + admin over WASM for the stack's *own*
5
- * resources. This adds the other half for **external** resources: a request
4
+ * `rastack dev` proxies the stack's own resources to the native
5
+ * `rastack-server`. This adds the other half for **external** resources: a request
6
6
  * handler it mounts under `/api/`, so `useData("github.repo")` works locally,
7
7
  * cached, before any real Lambda is deployed. It is a thin adapter over the pure
8
8
  * {@link handleExternalRequest} core — parse the path, resolve dev auth from env
@@ -2,8 +2,8 @@
2
2
  /**
3
3
  * The mini local proxy service — the "lambda you run while iterating".
4
4
  *
5
- * `rastack dev` already serves the app + admin over WASM for the stack's *own*
6
- * resources. This adds the other half for **external** resources: a request
5
+ * `rastack dev` proxies the stack's own resources to the native
6
+ * `rastack-server`. This adds the other half for **external** resources: a request
7
7
  * handler it mounts under `/api/`, so `useData("github.repo")` works locally,
8
8
  * cached, before any real Lambda is deployed. It is a thin adapter over the pure
9
9
  * {@link handleExternalRequest} core — parse the path, resolve dev auth from env
@@ -2,7 +2,7 @@
2
2
  * External-API bindings — how a rastack `app.model` is served from an upstream
3
3
  * REST or GraphQL API.
4
4
  *
5
- * The stack's own resources are served by the Rust API / WASM engine over the
5
+ * The stack's own resources are served by the native Rust API over the
6
6
  * `/api/{app}/v1/{model}/` contract. An **external** resource (a GitHub repo, an
7
7
  * Expo project, an AWS stack) is served by the proxy instead: it speaks the same
8
8
  * contract to the client, and translates each request into an upstream call,
@@ -3,7 +3,7 @@
3
3
  * External-API bindings — how a rastack `app.model` is served from an upstream
4
4
  * REST or GraphQL API.
5
5
  *
6
- * The stack's own resources are served by the Rust API / WASM engine over the
6
+ * The stack's own resources are served by the native Rust API over the
7
7
  * `/api/{app}/v1/{model}/` contract. An **external** resource (a GitHub repo, an
8
8
  * Expo project, an AWS stack) is served by the proxy instead: it speaks the same
9
9
  * contract to the client, and translates each request into an upstream call,
@@ -1,21 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * `rastack admin` — the full, Django-admin-style console, served **in the
4
- * browser over the prebuilt WebAssembly engine** (exactly like `rastack dev`).
3
+ * `rastack admin` — the Django-admin-style console, served by the native
4
+ * `rastack-server` process.
5
5
  *
6
6
  * rastack admin [resourcesDir] [--warehouse <dir>] [--port <n>] [--out <dir>] [--no-open]
7
7
  *
8
- * The console is React API Stack's own React admin (`tools/admin`, built from
9
- * `rastack/components` + `rastack/theme`). This command compiles the resources,
10
- * then boots a dependency-free static server that hands the browser everything
11
- * it needs to run the admin locally over WASM — no native server, no cargo:
12
- * • the bundled React admin (`/dev/admin.js`), which defaults to `mode:"local"`,
13
- * • the prebuilt WASM engine (`/wasm/…`) shipped in the package,
14
- * • the compiled `schema.rastack.json` / `openapi.json`,
15
- * • the committed Iceberg warehouse (`/data/warehouse/…`) to seed from.
16
- *
17
- * The engine is the single prebuilt `.wasm` bundle shipped in the npm package;
18
- * if it is missing the command errors and points at `rastack update` — it never
19
- * falls back to a from-source cargo build.
8
+ * Compiles the resources, then starts `rastack-server admin`. That process is
9
+ * the API (`rastack-api-core`) and it serves the React bundle at `/admin/`.
10
+ * The browser renders the console and calls `/api` on that same origin.
11
+ * The engine does not run in the browser.
20
12
  */
21
13
  export {};