rastack 0.0.43 → 0.0.45
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 +14 -0
- package/admin/AdminApp.tsx +461 -0
- package/admin/adapters.ts +346 -0
- package/admin/httpEngine.ts +56 -0
- package/admin/index.tsx +140 -0
- package/admin/ui/FilterBar.tsx +231 -0
- package/admin/ui/Popover.tsx +54 -0
- package/admin/ui/Sidebar.tsx +86 -0
- package/admin/ui/icons.tsx +146 -0
- package/admin/ui/styles.ts +398 -0
- package/admin/useAdmin.ts +434 -0
- package/dist/admin.js +459 -0
- package/dist/dev/harness.d.ts +70 -0
- package/dist/dev/harness.js +189 -0
- package/dist/rastack-admin.d.ts +14 -0
- package/dist/rastack-admin.js +128 -0
- package/dist/rastack-dev.d.ts +18 -0
- package/dist/rastack-dev.js +197 -0
- package/dist/rastack-wasm-build.d.ts +9 -4
- package/dist/rastack-wasm-build.js +44 -33
- package/dist/rastack.d.ts +2 -3
- package/dist/rastack.js +13 -32
- package/dist/wasm/package.json +4 -0
- package/dist/wasm/rastack_wasm.d.ts +100 -0
- package/dist/wasm/rastack_wasm.js +745 -0
- package/dist/wasm/rastack_wasm_bg.wasm +0 -0
- package/dist/wasm/rastack_wasm_bg.wasm.d.ts +28 -0
- package/package.json +6 -2
- package/scripts/build-admin.js +48 -0
- package/scripts/copy-wasm.js +35 -0
- package/src/dev/harness.ts +174 -0
- package/src/rastack-admin.ts +112 -0
- package/src/rastack-dev.ts +197 -0
- package/src/rastack-wasm-build.ts +52 -41
- package/src/rastack.ts +14 -37
- package/test/dev.spec.ts +373 -0
- package/wasm/package.json +4 -0
- package/wasm/rastack_wasm.d.ts +100 -0
- package/wasm/rastack_wasm.js +745 -0
- package/wasm/rastack_wasm_bg.wasm +0 -0
- package/wasm/rastack_wasm_bg.wasm.d.ts +28 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure core of `rastack dev` — the local, always-WASM development server.
|
|
3
|
+
*
|
|
4
|
+
* `rastack dev` boots the app + admin **in the browser** over the WebAssembly
|
|
5
|
+
* engine (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, and
|
|
8
|
+
* the committed Iceberg warehouse, and a bundled React admin drives it.
|
|
9
|
+
*
|
|
10
|
+
* Everything decidable without IO lives here (arg parsing, MIME types, locating
|
|
11
|
+
* the wasm bundle, the HTML shell) so it is unit-tested without a socket or a
|
|
12
|
+
* filesystem; `rastack-dev.ts` is the thin IO wrapper that compiles, resolves
|
|
13
|
+
* the engine, and serves.
|
|
14
|
+
*/
|
|
15
|
+
export interface DevArgs {
|
|
16
|
+
/** Where the TypeScript resource definitions live (compiled on start). */
|
|
17
|
+
resourcesDir: string;
|
|
18
|
+
/** Where `rastack compile` writes schema.rastack.json + openapi.json. */
|
|
19
|
+
outDir: string;
|
|
20
|
+
/** The committed Iceberg warehouse served to seed the browser engine. */
|
|
21
|
+
warehouseDir: string;
|
|
22
|
+
/** Port the dev server listens on. */
|
|
23
|
+
port: number;
|
|
24
|
+
/** Open the browser on start (false with `--no-open`). */
|
|
25
|
+
open: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare const DEV_DEFAULTS: DevArgs;
|
|
28
|
+
/**
|
|
29
|
+
* Parse `rastack dev` args: `--port`, `--warehouse`, `--out`, `--no-open`, and a
|
|
30
|
+
* single positional resources dir. Unknown flags are ignored (forgiving CLI).
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseDevArgs(argv: string[]): DevArgs;
|
|
33
|
+
/** Args for `rastack admin` — like `DevArgs`, but bound to a real server port. */
|
|
34
|
+
export interface AdminArgs {
|
|
35
|
+
resourcesDir: string;
|
|
36
|
+
outDir: string;
|
|
37
|
+
warehouseDir: string;
|
|
38
|
+
/** TCP port the native `rastack-server` binds (the admin lives at `/admin/`). */
|
|
39
|
+
port: number;
|
|
40
|
+
open: boolean;
|
|
41
|
+
}
|
|
42
|
+
export declare const ADMIN_DEFAULTS: AdminArgs;
|
|
43
|
+
/**
|
|
44
|
+
* Parse `rastack admin` args: `--port`, `--warehouse`, `--out`, `--no-open`, and
|
|
45
|
+
* a single positional resources dir. Mirrors `parseDevArgs` but defaults to the
|
|
46
|
+
* server's port 8000 (the React admin is served by the native server, not a
|
|
47
|
+
* node static server).
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseAdminArgs(argv: string[]): AdminArgs;
|
|
50
|
+
/** MIME type for a served path, defaulting to octet-stream (Iceberg data files). */
|
|
51
|
+
export declare function contentType(filePath: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* Ordered directories to look for the prebuilt wasm bundle (`rastack_wasm.js` +
|
|
54
|
+
* `rastack_wasm_bg.wasm`). The bundle is shipped inside the package
|
|
55
|
+
* (`dist/wasm/`, copied at build), so `rastack dev` works from any repo with no
|
|
56
|
+
* cargo; the repo-local `tools/wasm/` (from a source build) and a sibling of the
|
|
57
|
+
* running module are fallbacks for development. `dirnameDir` is the directory of
|
|
58
|
+
* the compiled `rastack-dev.js` (`dist/`), so `../wasm` and `./wasm` cover both
|
|
59
|
+
* `dist/rastack-dev.js` → `dist/wasm` and `dist/dev/…` layouts.
|
|
60
|
+
*/
|
|
61
|
+
export declare function wasmBundleCandidates(dirnameDir: string, cwd?: string): string[];
|
|
62
|
+
/** The name of the file that marks a directory as a usable wasm bundle. */
|
|
63
|
+
export declare const WASM_ENTRY = "rastack_wasm.js";
|
|
64
|
+
/**
|
|
65
|
+
* The HTML shell served at `/`. It loads the bundled React admin
|
|
66
|
+
* (`/dev/admin.js`), which fetches the schema/openapi, boots the wasm engine,
|
|
67
|
+
* seeds it from `/data/warehouse/`, and renders the admin. Deliberately tiny —
|
|
68
|
+
* all logic lives in the bundle — mirroring the `rastack design` studio.
|
|
69
|
+
*/
|
|
70
|
+
export declare function renderShell(title?: string): string;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pure core of `rastack dev` — the local, always-WASM development server.
|
|
4
|
+
*
|
|
5
|
+
* `rastack dev` boots the app + admin **in the browser** over the WebAssembly
|
|
6
|
+
* engine (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, and
|
|
9
|
+
* the committed Iceberg warehouse, and a bundled React admin drives it.
|
|
10
|
+
*
|
|
11
|
+
* Everything decidable without IO lives here (arg parsing, MIME types, locating
|
|
12
|
+
* the wasm bundle, the HTML shell) so it is unit-tested without a socket or a
|
|
13
|
+
* filesystem; `rastack-dev.ts` is the thin IO wrapper that compiles, resolves
|
|
14
|
+
* the engine, and serves.
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.WASM_ENTRY = exports.ADMIN_DEFAULTS = exports.DEV_DEFAULTS = void 0;
|
|
51
|
+
exports.parseDevArgs = parseDevArgs;
|
|
52
|
+
exports.parseAdminArgs = parseAdminArgs;
|
|
53
|
+
exports.contentType = contentType;
|
|
54
|
+
exports.wasmBundleCandidates = wasmBundleCandidates;
|
|
55
|
+
exports.renderShell = renderShell;
|
|
56
|
+
const path = __importStar(require("path"));
|
|
57
|
+
exports.DEV_DEFAULTS = {
|
|
58
|
+
resourcesDir: "resources",
|
|
59
|
+
outDir: ".rastack",
|
|
60
|
+
warehouseDir: "data/warehouse",
|
|
61
|
+
port: 4321,
|
|
62
|
+
open: true,
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Parse `rastack dev` args: `--port`, `--warehouse`, `--out`, `--no-open`, and a
|
|
66
|
+
* single positional resources dir. Unknown flags are ignored (forgiving CLI).
|
|
67
|
+
*/
|
|
68
|
+
function parseDevArgs(argv) {
|
|
69
|
+
const positional = [];
|
|
70
|
+
let { resourcesDir, outDir, warehouseDir, port, open } = exports.DEV_DEFAULTS;
|
|
71
|
+
for (let i = 0; i < argv.length; i++) {
|
|
72
|
+
const a = argv[i];
|
|
73
|
+
if (a === "--port")
|
|
74
|
+
port = Number(argv[++i]) || port;
|
|
75
|
+
else if (a === "--warehouse")
|
|
76
|
+
warehouseDir = argv[++i] ?? warehouseDir;
|
|
77
|
+
else if (a === "--out")
|
|
78
|
+
outDir = argv[++i] ?? outDir;
|
|
79
|
+
else if (a === "--no-open")
|
|
80
|
+
open = false;
|
|
81
|
+
else if (!a.startsWith("-"))
|
|
82
|
+
positional.push(a);
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
resourcesDir: positional[0] || resourcesDir,
|
|
86
|
+
outDir,
|
|
87
|
+
warehouseDir,
|
|
88
|
+
port,
|
|
89
|
+
open,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
exports.ADMIN_DEFAULTS = {
|
|
93
|
+
resourcesDir: "resources",
|
|
94
|
+
outDir: ".rastack",
|
|
95
|
+
warehouseDir: "data/warehouse",
|
|
96
|
+
port: 8000,
|
|
97
|
+
open: true,
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Parse `rastack admin` args: `--port`, `--warehouse`, `--out`, `--no-open`, and
|
|
101
|
+
* a single positional resources dir. Mirrors `parseDevArgs` but defaults to the
|
|
102
|
+
* server's port 8000 (the React admin is served by the native server, not a
|
|
103
|
+
* node static server).
|
|
104
|
+
*/
|
|
105
|
+
function parseAdminArgs(argv) {
|
|
106
|
+
const positional = [];
|
|
107
|
+
let { resourcesDir, outDir, warehouseDir, port, open } = exports.ADMIN_DEFAULTS;
|
|
108
|
+
for (let i = 0; i < argv.length; i++) {
|
|
109
|
+
const a = argv[i];
|
|
110
|
+
if (a === "--port")
|
|
111
|
+
port = Number(argv[++i]) || port;
|
|
112
|
+
else if (a === "--warehouse")
|
|
113
|
+
warehouseDir = argv[++i] ?? warehouseDir;
|
|
114
|
+
else if (a === "--out")
|
|
115
|
+
outDir = argv[++i] ?? outDir;
|
|
116
|
+
else if (a === "--no-open")
|
|
117
|
+
open = false;
|
|
118
|
+
else if (!a.startsWith("-"))
|
|
119
|
+
positional.push(a);
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
resourcesDir: positional[0] || resourcesDir,
|
|
123
|
+
outDir,
|
|
124
|
+
warehouseDir,
|
|
125
|
+
port,
|
|
126
|
+
open,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const MIME = {
|
|
130
|
+
".html": "text/html; charset=utf-8",
|
|
131
|
+
".js": "text/javascript; charset=utf-8",
|
|
132
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
133
|
+
".css": "text/css; charset=utf-8",
|
|
134
|
+
".json": "application/json; charset=utf-8",
|
|
135
|
+
// Critical: `WebAssembly.instantiateStreaming` requires this exact type.
|
|
136
|
+
".wasm": "application/wasm",
|
|
137
|
+
".text": "text/plain; charset=utf-8",
|
|
138
|
+
".parquet": "application/octet-stream",
|
|
139
|
+
};
|
|
140
|
+
/** MIME type for a served path, defaulting to octet-stream (Iceberg data files). */
|
|
141
|
+
function contentType(filePath) {
|
|
142
|
+
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
143
|
+
return MIME[ext] ?? "application/octet-stream";
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Ordered directories to look for the prebuilt wasm bundle (`rastack_wasm.js` +
|
|
147
|
+
* `rastack_wasm_bg.wasm`). The bundle is shipped inside the package
|
|
148
|
+
* (`dist/wasm/`, copied at build), so `rastack dev` works from any repo with no
|
|
149
|
+
* cargo; the repo-local `tools/wasm/` (from a source build) and a sibling of the
|
|
150
|
+
* running module are fallbacks for development. `dirnameDir` is the directory of
|
|
151
|
+
* the compiled `rastack-dev.js` (`dist/`), so `../wasm` and `./wasm` cover both
|
|
152
|
+
* `dist/rastack-dev.js` → `dist/wasm` and `dist/dev/…` layouts.
|
|
153
|
+
*/
|
|
154
|
+
function wasmBundleCandidates(dirnameDir, cwd = process.cwd()) {
|
|
155
|
+
return [
|
|
156
|
+
path.join(dirnameDir, "wasm"),
|
|
157
|
+
path.join(dirnameDir, "..", "wasm"),
|
|
158
|
+
path.join(cwd, "tools", "wasm"),
|
|
159
|
+
path.join(cwd, "wasm"),
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
/** The name of the file that marks a directory as a usable wasm bundle. */
|
|
163
|
+
exports.WASM_ENTRY = "rastack_wasm.js";
|
|
164
|
+
/**
|
|
165
|
+
* The HTML shell served at `/`. It loads the bundled React admin
|
|
166
|
+
* (`/dev/admin.js`), which fetches the schema/openapi, boots the wasm engine,
|
|
167
|
+
* seeds it from `/data/warehouse/`, and renders the admin. Deliberately tiny —
|
|
168
|
+
* all logic lives in the bundle — mirroring the `rastack design` studio.
|
|
169
|
+
*/
|
|
170
|
+
function renderShell(title = "React API Stack — dev") {
|
|
171
|
+
return `<!doctype html>
|
|
172
|
+
<html lang="en">
|
|
173
|
+
<head>
|
|
174
|
+
<meta charset="utf-8" />
|
|
175
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
176
|
+
<title>${title}</title>
|
|
177
|
+
<style>
|
|
178
|
+
html, body { margin: 0; height: 100%; }
|
|
179
|
+
#root { min-height: 100%; }
|
|
180
|
+
.rastack-boot { font: 14px system-ui, sans-serif; padding: 2rem; color: #555; }
|
|
181
|
+
</style>
|
|
182
|
+
</head>
|
|
183
|
+
<body>
|
|
184
|
+
<div id="root"><div class="rastack-boot">Loading the WASM engine…</div></div>
|
|
185
|
+
<script src="/dev/admin.js"></script>
|
|
186
|
+
</body>
|
|
187
|
+
</html>
|
|
188
|
+
`;
|
|
189
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `rastack admin` — the full, Django-admin-style console, served by the native
|
|
4
|
+
* `rastack-server` against the real Iceberg warehouse.
|
|
5
|
+
*
|
|
6
|
+
* rastack admin [resourcesDir] [--warehouse <dir>] [--port <n>] [--out <dir>] [--no-open]
|
|
7
|
+
*
|
|
8
|
+
* Unlike the old server-rendered `/admin`, the console is React API Stack's own
|
|
9
|
+
* React admin (`tools/admin`, built from `rastack/components` + `rastack/theme`)
|
|
10
|
+
* — this command compiles the resources, points the server at the prebuilt
|
|
11
|
+
* bundle (`RASTACK_ADMIN_ASSETS`), and boots it. The bundle talks back to the
|
|
12
|
+
* same server's `/api` over HTTP, so edits persist to the real warehouse.
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* `rastack admin` — the full, Django-admin-style console, served by the native
|
|
5
|
+
* `rastack-server` against the real Iceberg warehouse.
|
|
6
|
+
*
|
|
7
|
+
* rastack admin [resourcesDir] [--warehouse <dir>] [--port <n>] [--out <dir>] [--no-open]
|
|
8
|
+
*
|
|
9
|
+
* Unlike the old server-rendered `/admin`, the console is React API Stack's own
|
|
10
|
+
* React admin (`tools/admin`, built from `rastack/components` + `rastack/theme`)
|
|
11
|
+
* — this command compiles the resources, points the server at the prebuilt
|
|
12
|
+
* bundle (`RASTACK_ADMIN_ASSETS`), and boots it. The bundle talks back to the
|
|
13
|
+
* same server's `/api` over HTTP, so edits persist to the real warehouse.
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
const child_process_1 = require("child_process");
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
const harness_1 = require("./dev/harness");
|
|
53
|
+
const HERE = __dirname; // dist/ when installed — the bundle lives beside us.
|
|
54
|
+
/** Compile the resources to schema.rastack.json + openapi.json in outDir. */
|
|
55
|
+
function compile(resourcesDir, outDir) {
|
|
56
|
+
const compiler = path.join(HERE, "rastack-compile.js");
|
|
57
|
+
const result = (0, child_process_1.spawnSync)(process.execPath, [compiler, "compile", resourcesDir, outDir], { stdio: "inherit" });
|
|
58
|
+
if (result.status !== 0)
|
|
59
|
+
return false;
|
|
60
|
+
return fs.existsSync(path.join(outDir, "schema.rastack.json"));
|
|
61
|
+
}
|
|
62
|
+
/** Best-effort open the default browser at `url`. */
|
|
63
|
+
function openBrowser(url) {
|
|
64
|
+
const cmd = process.platform === "darwin"
|
|
65
|
+
? "open"
|
|
66
|
+
: process.platform === "win32"
|
|
67
|
+
? "start"
|
|
68
|
+
: "xdg-open";
|
|
69
|
+
try {
|
|
70
|
+
(0, child_process_1.execFileSync)(cmd, [url], { stdio: "ignore" });
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
/* non-fatal — the URL is printed anyway */
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function main(argv) {
|
|
77
|
+
const args = (0, harness_1.parseAdminArgs)(argv);
|
|
78
|
+
console.log(`\n rastack admin — the database console, built with React API Stack\n`);
|
|
79
|
+
console.log(`▸ Compiling ${args.resourcesDir} → ${args.outDir}`);
|
|
80
|
+
if (!compile(args.resourcesDir, args.outDir)) {
|
|
81
|
+
console.error(" ✗ compile failed — fix the resource graph and re-run.");
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
const bundle = path.join(HERE, "admin.js");
|
|
85
|
+
if (!fs.existsSync(bundle)) {
|
|
86
|
+
console.error(" ✗ admin bundle (dist/admin.js) not found — reinstall rastack.");
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
const repoRoot = path.resolve(HERE, "..", "..");
|
|
90
|
+
const binary = process.env.RASTACK_SERVER_BIN ||
|
|
91
|
+
path.join(repoRoot, "rust", "target", "release", "rastack-server");
|
|
92
|
+
const manifest = path.join(args.outDir, "schema.rastack.json");
|
|
93
|
+
const serverArgs = [
|
|
94
|
+
"admin",
|
|
95
|
+
"--manifest",
|
|
96
|
+
manifest,
|
|
97
|
+
"--warehouse",
|
|
98
|
+
args.warehouseDir,
|
|
99
|
+
"--addr",
|
|
100
|
+
`127.0.0.1:${args.port}`,
|
|
101
|
+
// Local dev context: the admin runs unauthenticated, exactly like the old
|
|
102
|
+
// native admin. Production admins run the server with --jwks/--admin-group.
|
|
103
|
+
"--insecure-dev-auth",
|
|
104
|
+
];
|
|
105
|
+
const url = `http://127.0.0.1:${args.port}/admin/`;
|
|
106
|
+
// The server serves the React bundle from here; hand the path via env so the
|
|
107
|
+
// Rust side stays free of JS-toolchain knowledge.
|
|
108
|
+
const child = (0, child_process_1.spawn)(binary, serverArgs, {
|
|
109
|
+
stdio: "inherit",
|
|
110
|
+
env: { ...process.env, RASTACK_ADMIN_ASSETS: HERE },
|
|
111
|
+
});
|
|
112
|
+
child.on("error", () => {
|
|
113
|
+
console.error(` ✗ Could not run the Rust server (${binary}). Build it first:\n` +
|
|
114
|
+
` cd rust && cargo build --release\n`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
});
|
|
117
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
118
|
+
// Give the server a moment to bind, then print + open.
|
|
119
|
+
setTimeout(() => {
|
|
120
|
+
console.log(`\n ▸ ${url}\n`);
|
|
121
|
+
console.log(" Press Ctrl+C to stop.\n");
|
|
122
|
+
if (args.open)
|
|
123
|
+
openBrowser(url);
|
|
124
|
+
}, 700);
|
|
125
|
+
}
|
|
126
|
+
if (require.main === module) {
|
|
127
|
+
main(process.argv.slice(2));
|
|
128
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `rastack dev` — local development, always over the WASM engine.
|
|
4
|
+
*
|
|
5
|
+
* rastack dev [resourcesDir] [--port <n>] [--warehouse <dir>] [--out <dir>] [--no-open]
|
|
6
|
+
*
|
|
7
|
+
* Compiles the resources, then boots a dependency-free static server (à la
|
|
8
|
+
* `rastack design`) that hands the browser everything it needs to run the app +
|
|
9
|
+
* admin locally over WebAssembly — no native server, no cargo:
|
|
10
|
+
* • the bundled React admin (`/dev/admin.js`),
|
|
11
|
+
* • the prebuilt WASM engine (`/wasm/…`) shipped in the package,
|
|
12
|
+
* • the compiled `schema.rastack.json` / `openapi.json`,
|
|
13
|
+
* • the committed Iceberg warehouse (`/data/warehouse/…`) to seed from.
|
|
14
|
+
*
|
|
15
|
+
* The engine is resolved from the package's prebuilt bundle; if none is present
|
|
16
|
+
* but a Rust + wasm toolchain is, it is built from source as a fallback.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* `rastack dev` — local development, always over the WASM engine.
|
|
5
|
+
*
|
|
6
|
+
* rastack dev [resourcesDir] [--port <n>] [--warehouse <dir>] [--out <dir>] [--no-open]
|
|
7
|
+
*
|
|
8
|
+
* Compiles the resources, then boots a dependency-free static server (à la
|
|
9
|
+
* `rastack design`) that hands the browser everything it needs to run the app +
|
|
10
|
+
* admin locally over WebAssembly — no native server, no cargo:
|
|
11
|
+
* • the bundled React admin (`/dev/admin.js`),
|
|
12
|
+
* • the prebuilt WASM engine (`/wasm/…`) shipped in the package,
|
|
13
|
+
* • the compiled `schema.rastack.json` / `openapi.json`,
|
|
14
|
+
* • the committed Iceberg warehouse (`/data/warehouse/…`) to seed from.
|
|
15
|
+
*
|
|
16
|
+
* The engine is resolved from the package's prebuilt bundle; if none is present
|
|
17
|
+
* but a Rust + wasm toolchain is, it is built from source as a fallback.
|
|
18
|
+
*/
|
|
19
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
22
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
23
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
24
|
+
}
|
|
25
|
+
Object.defineProperty(o, k2, desc);
|
|
26
|
+
}) : (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
o[k2] = m[k];
|
|
29
|
+
}));
|
|
30
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
31
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
32
|
+
}) : function(o, v) {
|
|
33
|
+
o["default"] = v;
|
|
34
|
+
});
|
|
35
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
36
|
+
var ownKeys = function(o) {
|
|
37
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
48
|
+
__setModuleDefault(result, mod);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
const child_process_1 = require("child_process");
|
|
54
|
+
const fs = __importStar(require("fs"));
|
|
55
|
+
const http = __importStar(require("http"));
|
|
56
|
+
const path = __importStar(require("path"));
|
|
57
|
+
const harness_1 = require("./dev/harness");
|
|
58
|
+
const HERE = __dirname; // dist/ when installed
|
|
59
|
+
/** Compile the resources to schema.rastack.json + openapi.json in outDir. */
|
|
60
|
+
function compile(resourcesDir, outDir) {
|
|
61
|
+
const compiler = path.join(HERE, "rastack-compile.js");
|
|
62
|
+
const result = (0, child_process_1.spawnSync)(process.execPath, [compiler, "compile", resourcesDir, outDir], { stdio: "inherit" });
|
|
63
|
+
if (result.status !== 0)
|
|
64
|
+
return false;
|
|
65
|
+
return fs.existsSync(path.join(outDir, "schema.rastack.json"));
|
|
66
|
+
}
|
|
67
|
+
/** True when `cargo` is on PATH (the from-source wasm fallback needs it). */
|
|
68
|
+
function cargoAvailable() {
|
|
69
|
+
try {
|
|
70
|
+
return ((0, child_process_1.spawnSync)("cargo", ["--version"], { stdio: "ignore" }).status === 0);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Locate the prebuilt wasm bundle dir, building from source as a fallback. */
|
|
77
|
+
function resolveWasmDir() {
|
|
78
|
+
for (const dir of (0, harness_1.wasmBundleCandidates)(HERE)) {
|
|
79
|
+
if (fs.existsSync(path.join(dir, harness_1.WASM_ENTRY)))
|
|
80
|
+
return dir;
|
|
81
|
+
}
|
|
82
|
+
if (cargoAvailable()) {
|
|
83
|
+
console.log("\n▸ No prebuilt WASM engine found — building it from source (cargo)…\n");
|
|
84
|
+
try {
|
|
85
|
+
// Lazy require so a normal `dev` with a prebuilt bundle never loads it.
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
87
|
+
require("./rastack-wasm-build").buildWasm();
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
console.error(` ✗ wasm build failed: ${err.message}`);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
for (const dir of (0, harness_1.wasmBundleCandidates)(HERE)) {
|
|
94
|
+
if (fs.existsSync(path.join(dir, harness_1.WASM_ENTRY)))
|
|
95
|
+
return dir;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
/** Serve one file if it exists and stays within `root`; else 404/403. */
|
|
101
|
+
function serveUnder(res, root, relPath) {
|
|
102
|
+
const safeRoot = path.resolve(root);
|
|
103
|
+
const rel = decodeURIComponent(relPath).replace(/^\/+/, "");
|
|
104
|
+
const target = path.resolve(safeRoot, rel);
|
|
105
|
+
// Path-traversal guard: the resolved target must stay inside the root.
|
|
106
|
+
if (target !== safeRoot && !target.startsWith(safeRoot + path.sep)) {
|
|
107
|
+
res.writeHead(403).end("Forbidden");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
fs.readFile(target, (err, data) => {
|
|
111
|
+
if (err) {
|
|
112
|
+
res.writeHead(404).end("Not found");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
res.writeHead(200, { "Content-Type": (0, harness_1.contentType)(target) }).end(data);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** Serve an exact file (no traversal concern — a fixed internal artifact). */
|
|
119
|
+
function serveFile(res, file) {
|
|
120
|
+
fs.readFile(file, (err, data) => {
|
|
121
|
+
if (err) {
|
|
122
|
+
res.writeHead(404).end("Not found");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
res.writeHead(200, { "Content-Type": (0, harness_1.contentType)(file) }).end(data);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/** Best-effort open the default browser at `url`. */
|
|
129
|
+
function openBrowser(url) {
|
|
130
|
+
const cmd = process.platform === "darwin"
|
|
131
|
+
? "open"
|
|
132
|
+
: process.platform === "win32"
|
|
133
|
+
? "start"
|
|
134
|
+
: "xdg-open";
|
|
135
|
+
try {
|
|
136
|
+
(0, child_process_1.execFileSync)(cmd, [url], { stdio: "ignore" });
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
/* non-fatal — the URL is printed anyway */
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function main(argv) {
|
|
143
|
+
const args = (0, harness_1.parseDevArgs)(argv);
|
|
144
|
+
console.log(`\n rastack dev — the app + admin, in your browser over WASM\n`);
|
|
145
|
+
console.log(`▸ Compiling ${args.resourcesDir} → ${args.outDir}`);
|
|
146
|
+
if (!compile(args.resourcesDir, args.outDir)) {
|
|
147
|
+
console.error(" ✗ compile failed — fix the resource graph and re-run.");
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
const wasmDir = resolveWasmDir();
|
|
151
|
+
if (!wasmDir) {
|
|
152
|
+
console.error("\n ✗ No WASM engine available.\n" +
|
|
153
|
+
" Upgrade rastack for the prebuilt bundle (`rastack update`), or install\n" +
|
|
154
|
+
" the Rust toolchain to build it from source:\n" +
|
|
155
|
+
" rustup target add wasm32-unknown-unknown\n" +
|
|
156
|
+
" cargo install wasm-bindgen-cli\n");
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
const adminBundle = path.join(HERE, "admin.js");
|
|
160
|
+
if (!fs.existsSync(adminBundle)) {
|
|
161
|
+
console.error(" ✗ admin bundle (dist/admin.js) not found — reinstall rastack.");
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
const server = http.createServer((req, res) => {
|
|
165
|
+
const url = (req.url || "/").split("?")[0];
|
|
166
|
+
if (req.method !== "GET") {
|
|
167
|
+
res.writeHead(405).end("Method not allowed");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (url === "/" || url === "/index.html") {
|
|
171
|
+
res
|
|
172
|
+
.writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
|
|
173
|
+
.end((0, harness_1.renderShell)());
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (url === "/dev/admin.js")
|
|
177
|
+
return serveFile(res, adminBundle);
|
|
178
|
+
if (url.startsWith("/wasm/"))
|
|
179
|
+
return serveUnder(res, wasmDir, url.slice("/wasm/".length));
|
|
180
|
+
if (url === "/schema.rastack.json" || url === "/openapi.json")
|
|
181
|
+
return serveUnder(res, args.outDir, path.basename(url));
|
|
182
|
+
if (url.startsWith("/data/warehouse/"))
|
|
183
|
+
return serveUnder(res, args.warehouseDir, url.slice("/data/warehouse/".length));
|
|
184
|
+
res.writeHead(404).end("Not found");
|
|
185
|
+
});
|
|
186
|
+
server.listen(args.port, () => {
|
|
187
|
+
const url = `http://localhost:${args.port}`;
|
|
188
|
+
console.log(` ✓ WASM engine: ${path.relative(process.cwd(), wasmDir)}`);
|
|
189
|
+
console.log(`\n ▸ ${url} (admin at ${url}/#/)\n`);
|
|
190
|
+
console.log(" Press Ctrl+C to stop.\n");
|
|
191
|
+
if (args.open)
|
|
192
|
+
openBrowser(url);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
if (require.main === module) {
|
|
196
|
+
main(process.argv.slice(2));
|
|
197
|
+
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Compile the Rust API to WebAssembly for in-browser local dev — the engine
|
|
4
|
+
* `rastack dev` serves and `<RAStackProvider mode="local">` loads.
|
|
4
5
|
*
|
|
5
6
|
* Pipeline:
|
|
6
7
|
* 1. cargo build -p rastack-wasm --release --target wasm32-unknown-unknown
|
|
7
|
-
* 2. wasm-bindgen --target web →
|
|
8
|
+
* 2. wasm-bindgen --target web → tools/wasm/rastack_wasm.js + rastack_wasm_bg.wasm
|
|
8
9
|
* 3. wasm-opt -Oz (if available) to shrink the binary
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
+
* This is invoked by the release CI (to bundle a prebuilt engine into the npm
|
|
12
|
+
* package) and by `rastack dev` as a from-source fallback when no prebuilt
|
|
13
|
+
* bundle is present but the toolchain is. Prereqs:
|
|
11
14
|
* rustup target add wasm32-unknown-unknown
|
|
12
15
|
* cargo install wasm-bindgen-cli (version must match the wasm-bindgen crate)
|
|
13
16
|
*/
|
|
14
|
-
|
|
17
|
+
/** Run the full compile → wasm-bindgen → optimise pipeline. Throws via `run`'s
|
|
18
|
+
* `process.exit` on a missing tool or a non-zero step. */
|
|
19
|
+
export declare function buildWasm(): void;
|