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.
- package/CHANGELOG.md +25 -0
- package/README.md +19 -24
- package/admin/httpEngine.ts +5 -6
- package/admin/index.tsx +11 -91
- package/dist/admin.js +14 -22
- package/dist/compile/model.d.ts +1 -1
- package/dist/compile/run.d.ts +2 -2
- package/dist/compile/run.js +2 -2
- package/dist/define/index.d.ts +2 -3
- package/dist/deploy/workflows.js +1 -1
- package/dist/dev/harness.d.ts +19 -27
- package/dist/dev/harness.js +31 -40
- package/dist/dev/native-server.d.ts +29 -0
- package/dist/dev/native-server.js +174 -0
- package/dist/external/envelope.d.ts +1 -1
- package/dist/external/envelope.js +1 -1
- package/dist/external/service.d.ts +2 -2
- package/dist/external/service.js +2 -2
- package/dist/external/types.d.ts +1 -1
- package/dist/external/types.js +1 -1
- package/dist/rastack-admin.d.ts +6 -14
- package/dist/rastack-admin.js +60 -88
- package/dist/rastack-design.d.ts +1 -2
- package/dist/rastack-design.js +1 -2
- package/dist/rastack-dev.d.ts +7 -10
- package/dist/rastack-dev.js +68 -80
- package/dist/rastack.d.ts +1 -1
- package/dist/rastack.js +5 -6
- package/dist/validate/transitions.d.ts +1 -1
- package/dist/validate/transitions.js +1 -1
- package/hooks/query/api.ts +4 -6
- package/package.json +2 -2
- package/provider/index.ts +0 -1
- package/provider/provider.tsx +31 -157
- package/provider/types.ts +29 -77
- package/src/compile/model.ts +1 -1
- package/src/compile/run.ts +2 -2
- package/src/define/index.ts +2 -3
- package/src/deploy/workflows.ts +1 -1
- package/src/dev/harness.ts +30 -43
- package/src/dev/native-server.ts +164 -0
- package/src/external/envelope.ts +1 -1
- package/src/external/service.ts +2 -2
- package/src/external/types.ts +1 -1
- package/src/rastack-admin.ts +66 -107
- package/src/rastack-design.ts +1 -2
- package/src/rastack-dev.ts +79 -91
- package/src/rastack.ts +5 -6
- package/src/validate/transitions.ts +1 -1
- package/dist/rastack-wasm-build.d.ts +0 -19
- package/dist/rastack-wasm-build.js +0 -94
- package/dist/wasm/package.json +0 -4
- package/dist/wasm/rastack_wasm.d.ts +0 -100
- package/dist/wasm/rastack_wasm.js +0 -745
- package/dist/wasm/rastack_wasm_bg.wasm +0 -0
- package/dist/wasm/rastack_wasm_bg.wasm.d.ts +0 -28
- package/provider/wasm.ts +0 -126
- package/scripts/copy-wasm.js +0 -35
- package/src/rastack-wasm-build.ts +0 -113
package/provider/provider.tsx
CHANGED
|
@@ -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
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
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}
|
|
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
|
|
91
|
+
/** Delete leftover device bytes (file cache, old warehouse snapshots). */
|
|
103
92
|
const purgeLocal = async () => {
|
|
104
|
-
|
|
105
|
-
await purgeIdentityCache(
|
|
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
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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(
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
6
|
-
*
|
|
7
|
-
* - `local` —
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
|
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
|
-
/**
|
|
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,
|
|
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
|
|
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.
|
|
119
|
-
*
|
|
120
|
-
*
|
|
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
|
-
*
|
|
128
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
140
|
-
*
|
|
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
|
-
/**
|
|
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" //
|
|
179
|
-
| "seeding" //
|
|
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
|
-
/**
|
|
142
|
+
/** Always null. The engine is the native server, not an object in the page. */
|
|
191
143
|
engine: RastackEngine | null;
|
|
192
|
-
/**
|
|
144
|
+
/** Throws. The native server owns the warehouse. */
|
|
193
145
|
exportWarehouse(): Promise<Uint8Array>;
|
|
194
|
-
/**
|
|
146
|
+
/** Throws. The native server owns the warehouse. */
|
|
195
147
|
downloadWarehouse(filename?: string): Promise<void>;
|
|
196
|
-
/**
|
|
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
|
package/src/compile/model.ts
CHANGED
|
@@ -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` (
|
|
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 {
|
package/src/compile/run.ts
CHANGED
|
@@ -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
|
|
13
|
-
* the
|
|
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
|
*/
|
package/src/define/index.ts
CHANGED
|
@@ -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 (
|
|
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
|
|
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
|
*/
|
package/src/deploy/workflows.ts
CHANGED
|
@@ -124,7 +124,7 @@ jobs:
|
|
|
124
124
|
- name: Cache cargo
|
|
125
125
|
uses: Swatinem/rust-cache@v2
|
|
126
126
|
with:
|
|
127
|
-
workspaces:
|
|
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
|
package/src/dev/harness.ts
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pure core of `rastack dev` — the
|
|
2
|
+
* Pure core of `rastack dev` — local development against the native Rust server.
|
|
3
3
|
*
|
|
4
|
-
* `rastack dev`
|
|
5
|
-
* (the same
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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,
|
|
15
|
-
* the
|
|
16
|
-
* without a socket or a filesystem; `rastack-dev.ts` is the
|
|
17
|
-
*
|
|
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
|
|
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
|
|
156
|
-
*
|
|
157
|
-
*
|
|
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
|
|
164
|
-
dirnameDir: string,
|
|
165
|
-
cwd = process.cwd(),
|
|
166
|
-
): string[] {
|
|
156
|
+
export function serverBinaryCandidates(cwd: string): string[] {
|
|
167
157
|
return [
|
|
168
|
-
path.join(
|
|
169
|
-
path.join(
|
|
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`)
|
|
180
|
-
*
|
|
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
|
|
227
|
-
*
|
|
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
|
|
276
|
-
|
|
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 <dir></code>.</li>
|
|
283
|
-
<li>Wire your app
|
|
284
|
-
<code><RAStackProvider mode="local"></code
|
|
285
|
-
|
|
269
|
+
<li>Wire your app with
|
|
270
|
+
<code><RAStackProvider mode="local"></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
|
-
|
|
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>
|