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
@@ -14,23 +14,10 @@ import {
14
14
  RastackEngine,
15
15
  RAStackProviderConfig,
16
16
  RadStatus,
17
- WarehouseSource,
18
17
  } from "./types";
19
- import { createEngine, createWasmAdapter, defaultEngineLoader } from "./wasm";
20
18
  import {
21
- debounce,
22
- downloadBlob,
23
- manifestResources,
24
- seedFromIceberg,
25
- WarehousePersistence,
26
- } from "./warehouse";
27
- import {
28
- CacheStorage,
29
19
  defaultCacheStorage,
30
20
  deleteIndexedDb,
31
- httpFileSource,
32
- IcebergFileCache,
33
- isAccessRevoked,
34
21
  purgeIdentityCache,
35
22
  } from "../cache";
36
23
  import {
@@ -45,13 +32,18 @@ import { getGlobalManifest } from "../hooks/manifest";
45
32
  const RastackContext = createContext<RastackClient | null>(null);
46
33
 
47
34
  /**
48
- * The one place a React app configures *where the API runs* — over HTTP
49
- * (`remote`), in the browser via WebAssembly (`local`), or in the browser
50
- * against S3 (`s3`). It reconfigures the shared axios client in place, so every
51
- * generated hook keeps working with no changes.
35
+ * The one place a React app configures *where the API runs*. Every mode talks
36
+ * HTTP to a native Rust server (`rastack-server` locally, Lambda in
37
+ * production). The browser does not execute the engine.
38
+ *
39
+ * - `local` — same origin (or `baseURL`). `rastack dev` proxies `/api` to the
40
+ * native process whose warehouse is the on-disk directory.
41
+ * - `s3` — the same HTTP client. The native process owns an `s3://` warehouse.
42
+ * The page does not open the bucket.
43
+ * - `remote` — HTTP to `baseURL` (Lambda or a server you already started).
52
44
  *
53
45
  * ```tsx
54
- * <RAStackProvider mode="local" manifest={schema} warehouseBaseUrl="/data/warehouse/">
46
+ * <RAStackProvider mode="local" manifest={schema}>
55
47
  * <App />
56
48
  * </RAStackProvider>
57
49
  * ```
@@ -62,8 +54,6 @@ export function RAStackProvider({
62
54
  }: RAStackProviderConfig & { children: ReactNode }): JSX.Element {
63
55
  const [status, setStatus] = useState<RadStatus>("idle");
64
56
  const engineRef = useRef<RastackEngine | null>(null);
65
- const persistRef = useRef<(() => void) & { flush: () => void }>();
66
- const cacheStorageRef = useRef<CacheStorage | null>(null);
67
57
 
68
58
  // The `manifest` prop is optional: when the rastack dev-server plugin is
69
59
  // in the bundler it registers the compiled schema globally before the app
@@ -90,7 +80,6 @@ export function RAStackProvider({
90
80
  ? userAuth(config.identity, config.groups ?? [])
91
81
  : undefined);
92
82
  const identity = config.identity ?? auth?.sub;
93
- const groups = config.groups ?? auth?.groups ?? [];
94
83
 
95
84
  // Locally persisted state is namespaced per identity, so two accounts on
96
85
  // one device never share bytes.
@@ -99,10 +88,12 @@ export function RAStackProvider({
99
88
  identity,
100
89
  );
101
90
 
102
- /** Delete everything this identity persisted locally. */
91
+ /** Delete leftover device bytes (file cache, old warehouse snapshots). */
103
92
  const purgeLocal = async () => {
104
- if (cacheStorageRef.current) {
105
- await purgeIdentityCache(cacheStorageRef.current, identity);
93
+ try {
94
+ await purgeIdentityCache(defaultCacheStorage(), identity);
95
+ } catch {
96
+ /* no IndexedDB in this runtime */
106
97
  }
107
98
  await deleteIndexedDb(warehouseDb);
108
99
  // Replicated rows in the sync engine's store are derived from the same
@@ -132,110 +123,15 @@ export function RAStackProvider({
132
123
  });
133
124
  }
134
125
 
135
- if (config.mode === "remote") {
126
+ // Every mode is HTTP to a native server. `local` uses this origin
127
+ // (`rastack dev` proxies `/api`). `s3` is the same client — the native
128
+ // process holds the `s3://` warehouse. `remote` with no baseURL keeps
129
+ // the historical default (NEXT_PUBLIC_BASE_URL or 127.0.0.1:8000).
130
+ if (config.mode === "remote" && config.baseURL === undefined) {
131
+ resetApi();
132
+ } else {
136
133
  configureApi({ baseURL: config.baseURL ?? "", adapter: null });
137
- if (config.baseURL === undefined) resetApi();
138
- setStatusSafe("ready");
139
- return;
140
- }
141
-
142
- // -- WASM modes (local / s3) --
143
- if (!manifest) {
144
- throw new Error(
145
- "RAStackProvider: no manifest found for local/s3 mode — add rastackPlugin() to your bundler (rastack/plugin), or pass the `manifest` prop.",
146
- );
147
- }
148
-
149
- setStatusSafe("loading");
150
- const loader = config.loadEngine ?? defaultEngineLoader;
151
- const mod = await loader();
152
- if (cancelled) return;
153
- const engine = createEngine(mod, manifest, config.openapi);
154
-
155
- // Run the in-browser engine as the signed-in identity so owner stamping
156
- // and row-level security match the server exactly.
157
- if (identity) {
158
- engine.setAuth?.(identity, groups);
159
- }
160
-
161
- // Warehouse persistence: IndexedDB for `local`, an S3 sink for `s3`.
162
- const persistence = pickPersistence(config, warehouseDb);
163
-
164
- setStatusSafe("seeding");
165
- try {
166
- // A persisted local copy (with the user's edits) wins; otherwise seed
167
- // from the warehouse's own Iceberg files — through the local file
168
- // cache by default, so a warm client revalidates one small pointer
169
- // per table and reads everything else from the device.
170
- const existing = await persistence.load();
171
- if (existing) {
172
- await engine.importWarehouse(existing);
173
- } else if (config.seedWarehouse) {
174
- // Seed from a bundled warehouse blob (exportWarehouse format) rather
175
- // than loose Iceberg files.
176
- const seed = await config.seedWarehouse();
177
- if (seed) await engine.importWarehouse(seed);
178
- } else {
179
- const base = config.warehouseBaseUrl ?? "/data/warehouse/";
180
- const resources = manifestResources(manifest);
181
- if (config.cache === false) {
182
- await seedFromIceberg(engine, base, resources);
183
- } else {
184
- const storage =
185
- typeof config.cache === "object"
186
- ? config.cache
187
- : defaultCacheStorage();
188
- cacheStorageRef.current = storage;
189
- const cache = new IcebergFileCache({
190
- storage,
191
- source: config.fileSource ?? httpFileSource(base),
192
- identity,
193
- maxOfflineMs: config.maxOfflineMs,
194
- });
195
- await cache.seed(engine, resources);
196
- }
197
- }
198
- } catch (error) {
199
- if (isAccessRevoked(error)) {
200
- // Access is gone ⇒ so is every locally persisted byte it fetched.
201
- await handleAccessRevoked();
202
- return;
203
- }
204
- throw error;
205
134
  }
206
- if (cancelled) return;
207
-
208
- const persist = debounce(() => {
209
- void engine
210
- .exportWarehouse()
211
- .then((blob) => persistence.save(blob))
212
- .catch((error) => config.onError?.(error));
213
- }, 400);
214
- persistRef.current = persist;
215
-
216
- // Persist at the *engine* level, not just in the axios adapter. Consumers
217
- // that talk to the engine directly (the admin console calls
218
- // `engine.handle("DELETE", …)` straight, bypassing the generated hooks /
219
- // axios seam) must still have their writes saved to IndexedDB — otherwise
220
- // every reload re-seeds from the committed warehouse and silently
221
- // resurrects deleted rows. Wrapping `handle` here makes persistence fire
222
- // for both paths (the adapter's `onWrite` below then coalesces into the
223
- // same debounce, so an app-hook write persists exactly once).
224
- const MUTATING = new Set(["POST", "PUT", "PATCH", "DELETE"]);
225
- const rawHandle = engine.handle.bind(engine);
226
- engine.handle = ((method: string, path: string, query?: any, body?: any) =>
227
- Promise.resolve(rawHandle(method, path, query, body)).then((res: any) => {
228
- if (MUTATING.has(String(method).toUpperCase()) && res && res.status < 300) {
229
- persist();
230
- }
231
- return res;
232
- })) as typeof engine.handle;
233
-
234
- engineRef.current = engine;
235
- configureApi({
236
- baseURL: "",
237
- adapter: createWasmAdapter(engine, persist),
238
- });
239
135
  setStatusSafe("ready");
240
136
  }
241
137
 
@@ -247,7 +143,6 @@ export function RAStackProvider({
247
143
 
248
144
  return () => {
249
145
  cancelled = true;
250
- persistRef.current?.flush();
251
146
  if (ejectToken !== undefined) API.interceptors.request.eject(ejectToken);
252
147
  resetApi();
253
148
  engineRef.current = null;
@@ -264,19 +159,17 @@ export function RAStackProvider({
264
159
  return engineRef.current;
265
160
  },
266
161
  async exportWarehouse() {
267
- if (!engineRef.current) {
268
- throw new Error(
269
- "exportWarehouse is only available in local/s3 mode.",
270
- );
271
- }
272
- return engineRef.current.exportWarehouse();
162
+ throw new Error(
163
+ "The warehouse lives in the native rastack-server process. The browser does not snapshot it.",
164
+ );
273
165
  },
274
- async downloadWarehouse(filename = "warehouse-snapshot.json") {
275
- const blob = await this.exportWarehouse();
276
- downloadBlob(blob, filename);
166
+ async downloadWarehouse() {
167
+ throw new Error(
168
+ "The warehouse lives in the native rastack-server process. The browser does not snapshot it.",
169
+ );
277
170
  },
278
171
  async persistNow() {
279
- persistRef.current?.flush();
172
+ /* The native server writes the warehouse as it handles each request. */
280
173
  },
281
174
  async purgeLocal() {
282
175
  await purgeLocal();
@@ -303,25 +196,6 @@ export function RAStackProvider({
303
196
  );
304
197
  }
305
198
 
306
- function pickPersistence(
307
- config: RAStackProviderConfig,
308
- databaseName: string,
309
- ): WarehouseSource {
310
- if (config.mode === "s3") {
311
- if (!config.warehouse) {
312
- throw new Error(
313
- "RAStackProvider: `warehouse` (a WarehouseSource over S3) is required in s3 mode.",
314
- );
315
- }
316
- return config.warehouse;
317
- }
318
- if (config.persist === false) {
319
- return { load: async () => null, save: async () => {} };
320
- }
321
- const store = new WarehousePersistence(databaseName);
322
- return { load: () => store.load(), save: (blob) => store.save(blob) };
323
- }
324
-
325
199
  /** Suffix a local database/cache name with the identity that owns it. */
326
200
  function withIdentity(name: string, identity?: string): string {
327
201
  return identity ? `${name}::${identity}` : name;
@@ -338,7 +212,7 @@ export function useRastackClient(): RastackClient {
338
212
  return client;
339
213
  }
340
214
 
341
- /** Just the current status — handy for a loading gate while WASM boots. */
215
+ /** Just the current status — handy for a loading gate. */
342
216
  export function useRadStatus(): RadStatus {
343
217
  return useRastackClient().status;
344
218
  }
package/provider/types.ts CHANGED
@@ -2,19 +2,17 @@
2
2
  * Configuration for `<RAStackProvider>` — the single place a React app chooses
3
3
  * *where the API runs*:
4
4
  *
5
- * - `remote` — talk to the Rust API over HTTP (Lambda in prod, `rastack serve`
6
- * locally). The classic client/server split.
7
- * - `local` — compile-free local development: the entire API runs in the
8
- * browser via WebAssembly over an in-memory Iceberg warehouse, seeded from a
9
- * repo `data/` folder and persisted to IndexedDB. No server, no network,
10
- * instant reads/writes.
11
- * - `s3` — full client mode: the same WASM engine, but the warehouse is
12
- * loaded from and saved back to S3, so a static site reads and writes the
13
- * object-storage database directly.
5
+ * - `remote` — talk to the Rust API over HTTP (Lambda in prod, a
6
+ * `rastack-server` you already started).
7
+ * - `local` — HTTP to the native `rastack-server` that `rastack dev` starts
8
+ * and proxies at `/api` on this origin. The warehouse is that process's
9
+ * on-disk directory. The browser does not run the engine.
10
+ * - `s3` — the same HTTP client. The native server (Lambda, or
11
+ * `rastack-server` pointed at `s3://`) owns the bucket. The page does not.
14
12
  */
15
13
  export type RadMode = "remote" | "local" | "s3";
16
14
 
17
- /** The subset of the WASM `RastackApi` class the provider uses. */
15
+ /** The `handle` seam the admin console uses. Backed by HTTP to the native server. */
18
16
  export interface RastackEngine {
19
17
  handle(
20
18
  method: string,
@@ -22,34 +20,14 @@ export interface RastackEngine {
22
20
  query: Record<string, string> | undefined,
23
21
  body: unknown,
24
22
  ): Promise<{ status: number; body: any }>;
25
- /** Seed one Iceberg file (metadata or `.parquet` data file) at its key. */
23
+ /** Not available. The native server owns warehouse files. */
26
24
  seedFile(key: string, bytes: Uint8Array): Promise<void>;
27
25
  importWarehouse(blob: Uint8Array): Promise<void>;
28
26
  exportWarehouse(): Promise<Uint8Array>;
29
- /**
30
- * Run requests as a signed-in identity (Cognito `sub` + `cognito:groups`),
31
- * so owner stamping / row-level security in the in-browser engine match the
32
- * server. Optional for older engine bundles.
33
- */
27
+ /** Not used. Identity is the bearer token the native server verifies. */
34
28
  setAuth?(subject: string | undefined, groups?: string[]): void;
35
29
  }
36
30
 
37
- /** Constructor of the generated `RastackApi` class. */
38
- export interface RastackEngineCtor {
39
- new (manifestJson: string, openapiJson?: string): RastackEngine;
40
- }
41
-
42
- /** What a WASM bundle loader must resolve to. */
43
- export interface RastackWasmModule {
44
- /** wasm-pack `web` target exposes an init fn that fetches the `.wasm`. */
45
- default?: (input?: any) => Promise<any>;
46
- init?: (input?: any) => Promise<any>;
47
- RastackApi: RastackEngineCtor;
48
- }
49
-
50
- /** Load and initialise the compiled WASM engine module. */
51
- export type RastackEngineLoader = () => Promise<RastackWasmModule>;
52
-
53
31
  /** Where to read/write the warehouse blob in `s3` mode (or any custom sink). */
54
32
  export interface WarehouseSource {
55
33
  /** Fetch the current warehouse blob, or `null` if none exists yet. */
@@ -71,7 +49,7 @@ export interface RAStackProviderConfig {
71
49
  /**
72
50
  * The signed-in identity as a typed {@link import("../auth").RastackAuth} —
73
51
  * the one auth object the whole stack shares. Supplies `identity`/`groups`
74
- * when those aren't set, runs the WASM engine as this identity, and is
52
+ * when those aren't set, and is
75
53
  * provided to the component tree (via `rastack/auth`'s context) so
76
54
  * `<AutoForm>` / `<DataTable>` gate their affordances against each
77
55
  * resource's `permission` + `access` policy automatically.
@@ -81,7 +59,7 @@ export interface RAStackProviderConfig {
81
59
  * The signed-in identity — the verified Cognito `sub`. It namespaces every
82
60
  * locally persisted byte (Iceberg file cache, warehouse snapshot DB), so
83
61
  * accounts sharing a device can't read each other's data, and it is the
84
- * subject the WASM engine stamps/filters owner rows with. Defaults to
62
+ * subject the native server stamps/filters owner rows with. Defaults to
85
63
  * `auth.sub`.
86
64
  */
87
65
  identity?: string;
@@ -115,59 +93,33 @@ export interface RAStackProviderConfig {
115
93
  * `schema.rastack.json` (object or JSON string), the
116
94
  * `virtual:rastack-manifest` module, or `resource()` definitions (an array
117
95
  * or a module namespace), from which the provider builds the identical
118
- * manifest at runtime. One of the two (plugin or prop) is required for
119
- * WASM modes (the manifest drives the in-browser engine); it is also what
120
- * lets `useData(Entity)` / `useForm(Entity)` resolve a plain TypeScript
121
- * type to its resource at runtime.
96
+ * manifest at runtime. The native server reads the compiled manifest from
97
+ * disk; this copy is what lets `useData(Entity)` / `useForm(Entity)`
98
+ * resolve a plain TypeScript type to its resource in the browser.
122
99
  */
123
100
  manifest?: object | readonly object[] | string;
124
101
  /** `openapi.json` — object or JSON string. Optional (`/api/schema/`). */
125
102
  openapi?: object | string;
126
103
  /**
127
- * Base URL of the committed Iceberg warehouse (a *directory of files*, not a
128
- * blob) to seed `local` mode from. The provider walks the tree —
129
- * `version-hint.text` → snapshot metadata → `.parquet` data files — exactly
130
- * as the server walks it over object storage. Defaults to `/data/warehouse/`.
131
- * Ignored once a persisted local copy exists.
104
+ * Unused by the request path. The native server opens the warehouse
105
+ * directory itself. Kept so existing call sites still type-check.
132
106
  */
133
107
  warehouseBaseUrl?: string;
134
- /** Persist the local warehouse to IndexedDB between reloads (default true). */
108
+ /** Unused. The native server writes the warehouse itself. */
135
109
  persist?: boolean;
136
110
  /** IndexedDB database name for the local warehouse. */
137
111
  databaseName?: string;
138
112
  /**
139
- * Cache the warehouse's Iceberg files locally (IndexedDB on web; supply a
140
- * `CacheStorage` for React Native) so repeat loads revalidate one tiny
141
- * pointer per table and read everything else from the device. `false`
142
- * disables caching; default on. Immutable Parquet/metadata files are cached
143
- * forever by key — invalidation is driven by Iceberg's own snapshot pointer.
113
+ * Unused by the request path. The native server reads the warehouse from
114
+ * disk or S3. Kept so existing call sites still type-check.
144
115
  */
145
116
  cache?: boolean | import("../cache").CacheStorage;
146
- /**
147
- * Where warehouse files are fetched from — defaults to HTTP GETs against
148
- * `warehouseBaseUrl`. Supply a SigV4-signing source (AWS SDK S3 client over
149
- * Cognito Identity Pool credentials) for `s3` mode. Must throw
150
- * `AccessRevokedError` on 401/403 so revocation deletes local data.
151
- */
117
+ /** Unused. The browser does not fetch warehouse files into an engine. */
152
118
  fileSource?: import("../cache").WarehouseFileSource;
153
- /** `s3` mode: how to load/save the warehouse blob from object storage. */
119
+ /** Unused. The native server owns the `s3://` warehouse. */
154
120
  warehouse?: WarehouseSource;
155
- /**
156
- * Seed `local` mode from a **bundled warehouse blob** — the format
157
- * `exportWarehouse()` produces and `importWarehouse()` consumes — instead of
158
- * walking loose Iceberg files under `warehouseBaseUrl`. Use this when the app
159
- * commits a single warehouse blob asset (e.g. bundled JSON) rather than a
160
- * directory of parquet files. Takes precedence over `warehouseBaseUrl` /
161
- * `fileSource` seeding, and is ignored once a persisted local copy exists.
162
- */
121
+ /** Unused. The native server opens the warehouse directory on disk. */
163
122
  seedWarehouse?: () => Uint8Array | null | Promise<Uint8Array | null>;
164
- /**
165
- * Override how the WASM module is loaded. Supply this from app code (where
166
- * your bundler can resolve the generated bundle) if the default loader can't
167
- * find it. Defaults to importing `rastack/wasm/rastack_wasm.js`.
168
- */
169
- loadEngine?: RastackEngineLoader;
170
-
171
123
  // -- lifecycle --
172
124
  onStatusChange?: (status: RadStatus) => void;
173
125
  onError?: (error: unknown) => void;
@@ -175,8 +127,8 @@ export interface RAStackProviderConfig {
175
127
 
176
128
  export type RadStatus =
177
129
  | "idle"
178
- | "loading" // fetching/instantiating the WASM engine
179
- | "seeding" // importing the warehouse
130
+ | "loading" // contacting the native server
131
+ | "seeding" // unused; the native server opens the warehouse itself
180
132
  | "ready"
181
133
  | "revoked" // access revoked — local data has been deleted
182
134
  | "error";
@@ -187,13 +139,13 @@ export interface RastackClient {
187
139
  status: RadStatus;
188
140
  /** The resolved identity this client runs as (from `auth` or `identity`). */
189
141
  auth?: import("../auth").RastackAuth;
190
- /** The live WASM engine (null in `remote` mode). */
142
+ /** Always null. The engine is the native server, not an object in the page. */
191
143
  engine: RastackEngine | null;
192
- /** Snapshot the local warehouse to a portable blob (WASM modes only). */
144
+ /** Throws. The native server owns the warehouse. */
193
145
  exportWarehouse(): Promise<Uint8Array>;
194
- /** Download that snapshot as a file — a portable backup of local edits. */
146
+ /** Throws. The native server owns the warehouse. */
195
147
  downloadWarehouse(filename?: string): Promise<void>;
196
- /** Force-persist now (IndexedDB and/or the `s3` sink). */
148
+ /** No-op. The native server writes each request as it handles it. */
197
149
  persistNow(): Promise<void>;
198
150
  /**
199
151
  * Delete everything this identity has persisted on the device — the Iceberg
@@ -87,7 +87,7 @@ export interface TransitionEdgeModel {
87
87
  /**
88
88
  * A resource's declarative state machine, keyed off one state field. Compiled
89
89
  * into the manifest and enforced on every write surface: the TS record machine
90
- * (forms, `rastack import`) and `rastack-api-core` (server, Lambda, WASM) all
90
+ * (forms, `rastack import`) and `rastack-api-core` (the native server) all
91
91
  * reject a write that jumps between states without a declared edge.
92
92
  */
93
93
  export interface TransitionsModel {
@@ -9,8 +9,8 @@ import { compile } from "./index";
9
9
  *
10
10
  * This is the single entry point the two real modalities of use share instead
11
11
  * of shelling out to a standalone `compile` command:
12
- * • `rastack dev` / `rastack admin` — local development (compile, then serve
13
- * the app + admin in the browser over WASM), and
12
+ * • `rastack dev` / `rastack admin` — local development (compile, then start
13
+ * the native rastack-server; the browser is only the UI), and
14
14
  * • `rastack ci` — the deploy pipeline (compile the manifest the Rust backend
15
15
  * reads + the OpenAPI contract, then build/ship).
16
16
  */
@@ -175,8 +175,7 @@ export interface SyncOptions {
175
175
 
176
176
  /**
177
177
  * Row-level access control. Enforced by `rastack-api-core`, so the rules are
178
- * identical on the server (Lambda / `rastack serve`) and in the in-browser WASM
179
- * engine.
178
+ * identical on the native server (laptop and Lambda).
180
179
  *
181
180
  * - `ownerField` — the field that records the owning identity (the verified
182
181
  * token `sub`). Rows are only listed/read/written by their owner; the field
@@ -216,7 +215,7 @@ export type Permission = "authenticatedOrReadOnly" | "authenticated" | "public";
216
215
  * transition may fire from, `to` the state it enters, and `set` the effect —
217
216
  * field patches the engine applies alongside the state change. The block is
218
217
  * static data: it compiles into the manifest and is enforced by
219
- * `rastack-api-core` on every surface (server, Lambda, in-browser WASM), so a
218
+ * `rastack-api-core` on the native server (laptop and Lambda), so a
220
219
  * transition behaves as a declarative, typesafe backend function with no
221
220
  * per-resource server code.
222
221
  */
@@ -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,20 +1,18 @@
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
 
20
18
  import * as path from "path";
@@ -24,7 +22,7 @@ export interface DevArgs {
24
22
  resourcesDir: string;
25
23
  /** Where `rastack compile` writes schema.rastack.json + openapi.json. */
26
24
  outDir: string;
27
- /** The committed Iceberg warehouse served to seed the browser engine. */
25
+ /** The on-disk Iceberg warehouse the native server opens. */
28
26
  warehouseDir: string;
29
27
  /**
30
28
  * Explicit app root to serve at `/` (its `index.html`). Empty when unset —
@@ -139,7 +137,6 @@ const MIME: Record<string, string> = {
139
137
  ".mjs": "text/javascript; charset=utf-8",
140
138
  ".css": "text/css; charset=utf-8",
141
139
  ".json": "application/json; charset=utf-8",
142
- // Critical: `WebAssembly.instantiateStreaming` requires this exact type.
143
140
  ".wasm": "application/wasm",
144
141
  ".text": "text/plain; charset=utf-8",
145
142
  ".parquet": "application/octet-stream",
@@ -152,32 +149,22 @@ export function contentType(filePath: string): string {
152
149
  }
153
150
 
154
151
  /**
155
- * Ordered directories to look for the prebuilt wasm bundle (`rastack_wasm.js` +
156
- * `rastack_wasm_bg.wasm`). The bundle is shipped inside the package
157
- * (`dist/wasm/`, copied at build), so `rastack dev` works from any repo with no
158
- * cargo; the repo-local `wasm/` (from a source build) and a sibling of the
159
- * running module are fallbacks for development. `dirnameDir` is the directory of
160
- * the compiled `rastack-dev.js` (`dist/`), so `../wasm` and `./wasm` cover both
161
- * `dist/rastack-dev.js` → `dist/wasm` and `dist/dev/…` layouts.
152
+ * Ordered paths for the native `rastack-server` binary inside a workspace
153
+ * checkout. Release wins over debug. `RASTACK_SERVER` and `PATH` are checked
154
+ * by the IO wrapper, not here.
162
155
  */
163
- export function wasmBundleCandidates(
164
- dirnameDir: string,
165
- cwd = process.cwd(),
166
- ): string[] {
156
+ export function serverBinaryCandidates(cwd: string): string[] {
167
157
  return [
168
- path.join(dirnameDir, "wasm"),
169
- path.join(dirnameDir, "..", "wasm"),
170
- path.join(cwd, "wasm"),
158
+ path.join(cwd, "engine", "target", "release", "rastack-server"),
159
+ path.join(cwd, "engine", "target", "debug", "rastack-server"),
171
160
  ];
172
161
  }
173
162
 
174
- /** The name of the file that marks a directory as a usable wasm bundle. */
175
- export const WASM_ENTRY = "rastack_wasm.js";
176
-
177
163
  /**
178
164
  * The HTML shell served at `/` by **`rastack admin`**. It loads the bundled
179
- * React admin (`/dev/admin.js`), which fetches the schema/openapi, boots the
180
- * wasm engine, seeds it from `/data/warehouse/`, and renders the admin console.
165
+ * React admin (`/dev/admin.js`). `rastack admin` itself does not serve this
166
+ * page: the native `rastack-server admin` process serves `/admin/` and the
167
+ * bundle talks to that process over HTTP.
181
168
  * Deliberately tiny — all logic lives in the bundle — mirroring the
182
169
  * `rastack design` studio. `rastack dev` does **not** use this: it serves the
183
170
  * developer's own app (or the welcome page below).
@@ -223,9 +210,8 @@ export function renderShell(title = "React API Stack — admin"): string {
223
210
  /**
224
211
  * The Django-style welcome page `rastack dev` serves at `/` when the project
225
212
  * has **no app** to serve (no `index.html` in any `appRootCandidates` dir).
226
- * It confirms the dev server is up, points at the live backend endpoints the
227
- * WASM engine exposes, and tells you how to add an app or open the admin — the
228
- * "It worked!" moment of `django-admin runserver`, on brand for the stack.
213
+ * It confirms the dev server is up, points at the native API proxied at
214
+ * `/api`, and tells you how to add an app or open the admin.
229
215
  */
230
216
  export function renderWelcome(title = "React API Stack — dev"): string {
231
217
  return `<!doctype html>
@@ -272,20 +258,21 @@ export function renderWelcome(title = "React API Stack — dev"): string {
272
258
  <div class="rocket">🚀</div>
273
259
  <h1>The dev server is running.</h1>
274
260
  <p class="lead">
275
- Congratulations — <strong>rastack dev</strong> is up and your API is running
276
- in the browser over WebAssembly. You're seeing this page because there's no
277
- app to serve yet.
261
+ Congratulations — <strong>rastack dev</strong> is up. The API is a native
262
+ Rust process (<code>rastack-server</code>) on this machine. This page is
263
+ only the UI. You're seeing it because there's no app to serve yet.
278
264
  </p>
279
265
  <ul>
280
266
  <li>Add an <code>index.html</code> (in <code>public/</code>, <code>www/</code>,
281
267
  <code>app/</code>, or the project root) and reload — <code>dev</code> will serve it.
282
268
  Point it at a different folder with <code>rastack dev --app &lt;dir&gt;</code>.</li>
283
- <li>Wire your app to the local engine with
284
- <code>&lt;RAStackProvider mode="local"&gt;</code>.</li>
285
- <li>Live backend endpoints:
269
+ <li>Wire your app with
270
+ <code>&lt;RAStackProvider mode="local"&gt;</code>. Requests go to
271
+ <code>/api</code> on this origin, which is proxied to the native server.</li>
272
+ <li>Live files:
286
273
  <a href="/schema.rastack.json">/schema.rastack.json</a>,
287
- <a href="/openapi.json">/openapi.json</a>,
288
- <code>/data/warehouse/…</code>, <code>/wasm/…</code>.</li>
274
+ <a href="/openapi.json">/openapi.json</a>.
275
+ The warehouse stays on disk; the browser does not load it.</li>
289
276
  <li>Want the database console instead? Run <code>rastack admin</code>.</li>
290
277
  </ul>
291
278
  <div class="foot">React API Stack — a full-stack framework on object storage.</div>