rastack 0.0.48 → 0.0.49
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 +2 -0
- package/dist/rastack-admin.d.ts +14 -7
- package/dist/rastack-admin.js +85 -40
- package/dist/wasm/rastack_wasm_bg.wasm +0 -0
- package/package.json +1 -1
- package/src/rastack-admin.ts +105 -46
- package/wasm/rastack_wasm_bg.wasm +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [0.0.49](https://github.com/theserverkid/reactapistack/compare/v0.0.48...v0.0.49) (2026-07-14)
|
|
6
|
+
|
|
5
7
|
### [0.0.48](https://github.com/theserverkid/reactapistack/compare/v0.0.47...v0.0.48) (2026-07-13)
|
|
6
8
|
|
|
7
9
|
### [0.0.47](https://github.com/theserverkid/reactapistack/compare/v0.0.46...v0.0.47) (2026-07-13)
|
package/dist/rastack-admin.d.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* `rastack admin` — the full, Django-admin-style console, served
|
|
4
|
-
*
|
|
3
|
+
* `rastack admin` — the full, Django-admin-style console, served **in the
|
|
4
|
+
* browser over the prebuilt WebAssembly engine** (exactly like `rastack dev`).
|
|
5
5
|
*
|
|
6
6
|
* rastack admin [resourcesDir] [--warehouse <dir>] [--port <n>] [--out <dir>] [--no-open]
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* The console is React API Stack's own React admin (`tools/admin`, built from
|
|
9
|
+
* `rastack/components` + `rastack/theme`). This command compiles the resources,
|
|
10
|
+
* then boots a dependency-free static server that hands the browser everything
|
|
11
|
+
* it needs to run the admin locally over WASM — no native server, no cargo:
|
|
12
|
+
* • the bundled React admin (`/dev/admin.js`), which defaults to `mode:"local"`,
|
|
13
|
+
* • the prebuilt WASM engine (`/wasm/…`) shipped in the package,
|
|
14
|
+
* • the compiled `schema.rastack.json` / `openapi.json`,
|
|
15
|
+
* • the committed Iceberg warehouse (`/data/warehouse/…`) to seed from.
|
|
16
|
+
*
|
|
17
|
+
* The engine is the single prebuilt `.wasm` bundle shipped in the npm package;
|
|
18
|
+
* if it is missing the command errors and points at `rastack update` — it never
|
|
19
|
+
* falls back to a from-source cargo build.
|
|
13
20
|
*/
|
|
14
21
|
export {};
|
package/dist/rastack-admin.js
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
* `rastack admin` — the full, Django-admin-style console, served
|
|
5
|
-
*
|
|
4
|
+
* `rastack admin` — the full, Django-admin-style console, served **in the
|
|
5
|
+
* browser over the prebuilt WebAssembly engine** (exactly like `rastack dev`).
|
|
6
6
|
*
|
|
7
7
|
* rastack admin [resourcesDir] [--warehouse <dir>] [--port <n>] [--out <dir>] [--no-open]
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* The console is React API Stack's own React admin (`tools/admin`, built from
|
|
10
|
+
* `rastack/components` + `rastack/theme`). This command compiles the resources,
|
|
11
|
+
* then boots a dependency-free static server that hands the browser everything
|
|
12
|
+
* it needs to run the admin locally over WASM — no native server, no cargo:
|
|
13
|
+
* • the bundled React admin (`/dev/admin.js`), which defaults to `mode:"local"`,
|
|
14
|
+
* • the prebuilt WASM engine (`/wasm/…`) shipped in the package,
|
|
15
|
+
* • the compiled `schema.rastack.json` / `openapi.json`,
|
|
16
|
+
* • the committed Iceberg warehouse (`/data/warehouse/…`) to seed from.
|
|
17
|
+
*
|
|
18
|
+
* The engine is the single prebuilt `.wasm` bundle shipped in the npm package;
|
|
19
|
+
* if it is missing the command errors and points at `rastack update` — it never
|
|
20
|
+
* falls back to a from-source cargo build.
|
|
14
21
|
*/
|
|
15
22
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
23
|
if (k2 === undefined) k2 = k;
|
|
@@ -48,6 +55,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
48
55
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
56
|
const child_process_1 = require("child_process");
|
|
50
57
|
const fs = __importStar(require("fs"));
|
|
58
|
+
const http = __importStar(require("http"));
|
|
51
59
|
const path = __importStar(require("path"));
|
|
52
60
|
const harness_1 = require("./dev/harness");
|
|
53
61
|
const HERE = __dirname; // dist/ when installed — the bundle lives beside us.
|
|
@@ -59,6 +67,42 @@ function compile(resourcesDir, outDir) {
|
|
|
59
67
|
return false;
|
|
60
68
|
return fs.existsSync(path.join(outDir, "schema.rastack.json"));
|
|
61
69
|
}
|
|
70
|
+
/** Locate the prebuilt wasm bundle dir shipped in the package (no cargo fallback). */
|
|
71
|
+
function resolveWasmDir() {
|
|
72
|
+
for (const dir of (0, harness_1.wasmBundleCandidates)(HERE)) {
|
|
73
|
+
if (fs.existsSync(path.join(dir, harness_1.WASM_ENTRY)))
|
|
74
|
+
return dir;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
/** Serve one file if it exists and stays within `root`; else 404/403. */
|
|
79
|
+
function serveUnder(res, root, relPath) {
|
|
80
|
+
const safeRoot = path.resolve(root);
|
|
81
|
+
const rel = decodeURIComponent(relPath).replace(/^\/+/, "");
|
|
82
|
+
const target = path.resolve(safeRoot, rel);
|
|
83
|
+
// Path-traversal guard: the resolved target must stay inside the root.
|
|
84
|
+
if (target !== safeRoot && !target.startsWith(safeRoot + path.sep)) {
|
|
85
|
+
res.writeHead(403).end("Forbidden");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
fs.readFile(target, (err, data) => {
|
|
89
|
+
if (err) {
|
|
90
|
+
res.writeHead(404).end("Not found");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
res.writeHead(200, { "Content-Type": (0, harness_1.contentType)(target) }).end(data);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/** Serve an exact file (no traversal concern — a fixed internal artifact). */
|
|
97
|
+
function serveFile(res, file) {
|
|
98
|
+
fs.readFile(file, (err, data) => {
|
|
99
|
+
if (err) {
|
|
100
|
+
res.writeHead(404).end("Not found");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
res.writeHead(200, { "Content-Type": (0, harness_1.contentType)(file) }).end(data);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
62
106
|
/** Best-effort open the default browser at `url`. */
|
|
63
107
|
function openBrowser(url) {
|
|
64
108
|
const cmd = process.platform === "darwin"
|
|
@@ -81,47 +125,48 @@ function main(argv) {
|
|
|
81
125
|
console.error(" ✗ compile failed — fix the resource graph and re-run.");
|
|
82
126
|
process.exit(1);
|
|
83
127
|
}
|
|
84
|
-
const
|
|
85
|
-
if (!
|
|
86
|
-
console.error(" ✗
|
|
128
|
+
const wasmDir = resolveWasmDir();
|
|
129
|
+
if (!wasmDir) {
|
|
130
|
+
console.error("\n ✗ No prebuilt WASM engine found in this rastack install.\n" +
|
|
131
|
+
" Update rastack to pull the prebuilt bundle:\n" +
|
|
132
|
+
" rastack update\n");
|
|
87
133
|
process.exit(1);
|
|
88
134
|
}
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
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`);
|
|
135
|
+
const adminBundle = path.join(HERE, "admin.js");
|
|
136
|
+
if (!fs.existsSync(adminBundle)) {
|
|
137
|
+
console.error(" ✗ admin bundle (dist/admin.js) not found — reinstall rastack.");
|
|
115
138
|
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
const server = http.createServer((req, res) => {
|
|
141
|
+
const url = (req.url || "/").split("?")[0];
|
|
142
|
+
if (req.method !== "GET") {
|
|
143
|
+
res.writeHead(405).end("Method not allowed");
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (url === "/" || url === "/index.html") {
|
|
147
|
+
res
|
|
148
|
+
.writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
|
|
149
|
+
.end((0, harness_1.renderShell)("React API Stack — admin"));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (url === "/dev/admin.js")
|
|
153
|
+
return serveFile(res, adminBundle);
|
|
154
|
+
if (url.startsWith("/wasm/"))
|
|
155
|
+
return serveUnder(res, wasmDir, url.slice("/wasm/".length));
|
|
156
|
+
if (url === "/schema.rastack.json" || url === "/openapi.json")
|
|
157
|
+
return serveUnder(res, args.outDir, path.basename(url));
|
|
158
|
+
if (url.startsWith("/data/warehouse/"))
|
|
159
|
+
return serveUnder(res, args.warehouseDir, url.slice("/data/warehouse/".length));
|
|
160
|
+
res.writeHead(404).end("Not found");
|
|
116
161
|
});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
162
|
+
server.listen(args.port, () => {
|
|
163
|
+
const url = `http://localhost:${args.port}`;
|
|
164
|
+
console.log(` ✓ WASM engine: ${path.relative(process.cwd(), wasmDir)}`);
|
|
120
165
|
console.log(`\n ▸ ${url}\n`);
|
|
121
166
|
console.log(" Press Ctrl+C to stop.\n");
|
|
122
167
|
if (args.open)
|
|
123
168
|
openBrowser(url);
|
|
124
|
-
}
|
|
169
|
+
});
|
|
125
170
|
}
|
|
126
171
|
if (require.main === module) {
|
|
127
172
|
main(process.argv.slice(2));
|
|
Binary file
|
package/package.json
CHANGED
package/src/rastack-admin.ts
CHANGED
|
@@ -1,22 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* `rastack admin` — the full, Django-admin-style console, served
|
|
5
|
-
*
|
|
4
|
+
* `rastack admin` — the full, Django-admin-style console, served **in the
|
|
5
|
+
* browser over the prebuilt WebAssembly engine** (exactly like `rastack dev`).
|
|
6
6
|
*
|
|
7
7
|
* rastack admin [resourcesDir] [--warehouse <dir>] [--port <n>] [--out <dir>] [--no-open]
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* The console is React API Stack's own React admin (`tools/admin`, built from
|
|
10
|
+
* `rastack/components` + `rastack/theme`). This command compiles the resources,
|
|
11
|
+
* then boots a dependency-free static server that hands the browser everything
|
|
12
|
+
* it needs to run the admin locally over WASM — no native server, no cargo:
|
|
13
|
+
* • the bundled React admin (`/dev/admin.js`), which defaults to `mode:"local"`,
|
|
14
|
+
* • the prebuilt WASM engine (`/wasm/…`) shipped in the package,
|
|
15
|
+
* • the compiled `schema.rastack.json` / `openapi.json`,
|
|
16
|
+
* • the committed Iceberg warehouse (`/data/warehouse/…`) to seed from.
|
|
17
|
+
*
|
|
18
|
+
* The engine is the single prebuilt `.wasm` bundle shipped in the npm package;
|
|
19
|
+
* if it is missing the command errors and points at `rastack update` — it never
|
|
20
|
+
* falls back to a from-source cargo build.
|
|
14
21
|
*/
|
|
15
22
|
|
|
16
|
-
import { execFileSync,
|
|
23
|
+
import { execFileSync, spawnSync } from "child_process";
|
|
17
24
|
import * as fs from "fs";
|
|
25
|
+
import * as http from "http";
|
|
18
26
|
import * as path from "path";
|
|
19
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
WASM_ENTRY,
|
|
29
|
+
contentType,
|
|
30
|
+
parseAdminArgs,
|
|
31
|
+
renderShell,
|
|
32
|
+
wasmBundleCandidates,
|
|
33
|
+
} from "./dev/harness";
|
|
20
34
|
|
|
21
35
|
const HERE = __dirname; // dist/ when installed — the bundle lives beside us.
|
|
22
36
|
|
|
@@ -32,6 +46,48 @@ function compile(resourcesDir: string, outDir: string): boolean {
|
|
|
32
46
|
return fs.existsSync(path.join(outDir, "schema.rastack.json"));
|
|
33
47
|
}
|
|
34
48
|
|
|
49
|
+
/** Locate the prebuilt wasm bundle dir shipped in the package (no cargo fallback). */
|
|
50
|
+
function resolveWasmDir(): string | null {
|
|
51
|
+
for (const dir of wasmBundleCandidates(HERE)) {
|
|
52
|
+
if (fs.existsSync(path.join(dir, WASM_ENTRY))) return dir;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Serve one file if it exists and stays within `root`; else 404/403. */
|
|
58
|
+
function serveUnder(
|
|
59
|
+
res: http.ServerResponse,
|
|
60
|
+
root: string,
|
|
61
|
+
relPath: string,
|
|
62
|
+
): void {
|
|
63
|
+
const safeRoot = path.resolve(root);
|
|
64
|
+
const rel = decodeURIComponent(relPath).replace(/^\/+/, "");
|
|
65
|
+
const target = path.resolve(safeRoot, rel);
|
|
66
|
+
// Path-traversal guard: the resolved target must stay inside the root.
|
|
67
|
+
if (target !== safeRoot && !target.startsWith(safeRoot + path.sep)) {
|
|
68
|
+
res.writeHead(403).end("Forbidden");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
fs.readFile(target, (err, data) => {
|
|
72
|
+
if (err) {
|
|
73
|
+
res.writeHead(404).end("Not found");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
res.writeHead(200, { "Content-Type": contentType(target) }).end(data);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Serve an exact file (no traversal concern — a fixed internal artifact). */
|
|
81
|
+
function serveFile(res: http.ServerResponse, file: string): void {
|
|
82
|
+
fs.readFile(file, (err, data) => {
|
|
83
|
+
if (err) {
|
|
84
|
+
res.writeHead(404).end("Not found");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
res.writeHead(200, { "Content-Type": contentType(file) }).end(data);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
35
91
|
/** Best-effort open the default browser at `url`. */
|
|
36
92
|
function openBrowser(url: string): void {
|
|
37
93
|
const cmd =
|
|
@@ -57,54 +113,57 @@ function main(argv: string[]): void {
|
|
|
57
113
|
process.exit(1);
|
|
58
114
|
}
|
|
59
115
|
|
|
60
|
-
const
|
|
61
|
-
if (!
|
|
62
|
-
console.error(
|
|
116
|
+
const wasmDir = resolveWasmDir();
|
|
117
|
+
if (!wasmDir) {
|
|
118
|
+
console.error(
|
|
119
|
+
"\n ✗ No prebuilt WASM engine found in this rastack install.\n" +
|
|
120
|
+
" Update rastack to pull the prebuilt bundle:\n" +
|
|
121
|
+
" rastack update\n",
|
|
122
|
+
);
|
|
63
123
|
process.exit(1);
|
|
64
124
|
}
|
|
65
125
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
process.env.RASTACK_SERVER_BIN ||
|
|
69
|
-
path.join(repoRoot, "rust", "target", "release", "rastack-server");
|
|
70
|
-
|
|
71
|
-
const manifest = path.join(args.outDir, "schema.rastack.json");
|
|
72
|
-
const serverArgs = [
|
|
73
|
-
"admin",
|
|
74
|
-
"--manifest",
|
|
75
|
-
manifest,
|
|
76
|
-
"--warehouse",
|
|
77
|
-
args.warehouseDir,
|
|
78
|
-
"--addr",
|
|
79
|
-
`127.0.0.1:${args.port}`,
|
|
80
|
-
// Local dev context: the admin runs unauthenticated, exactly like the old
|
|
81
|
-
// native admin. Production admins run the server with --jwks/--admin-group.
|
|
82
|
-
"--insecure-dev-auth",
|
|
83
|
-
];
|
|
84
|
-
|
|
85
|
-
const url = `http://127.0.0.1:${args.port}/admin/`;
|
|
86
|
-
// The server serves the React bundle from here; hand the path via env so the
|
|
87
|
-
// Rust side stays free of JS-toolchain knowledge.
|
|
88
|
-
const child = spawn(binary, serverArgs, {
|
|
89
|
-
stdio: "inherit",
|
|
90
|
-
env: { ...process.env, RASTACK_ADMIN_ASSETS: HERE },
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
child.on("error", () => {
|
|
126
|
+
const adminBundle = path.join(HERE, "admin.js");
|
|
127
|
+
if (!fs.existsSync(adminBundle)) {
|
|
94
128
|
console.error(
|
|
95
|
-
|
|
96
|
-
` cd rust && cargo build --release\n`,
|
|
129
|
+
" ✗ admin bundle (dist/admin.js) not found — reinstall rastack.",
|
|
97
130
|
);
|
|
98
131
|
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const server = http.createServer((req, res) => {
|
|
135
|
+
const url = (req.url || "/").split("?")[0];
|
|
136
|
+
if (req.method !== "GET") {
|
|
137
|
+
res.writeHead(405).end("Method not allowed");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (url === "/" || url === "/index.html") {
|
|
141
|
+
res
|
|
142
|
+
.writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
|
|
143
|
+
.end(renderShell("React API Stack — admin"));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (url === "/dev/admin.js") return serveFile(res, adminBundle);
|
|
147
|
+
if (url.startsWith("/wasm/"))
|
|
148
|
+
return serveUnder(res, wasmDir, url.slice("/wasm/".length));
|
|
149
|
+
if (url === "/schema.rastack.json" || url === "/openapi.json")
|
|
150
|
+
return serveUnder(res, args.outDir, path.basename(url));
|
|
151
|
+
if (url.startsWith("/data/warehouse/"))
|
|
152
|
+
return serveUnder(
|
|
153
|
+
res,
|
|
154
|
+
args.warehouseDir,
|
|
155
|
+
url.slice("/data/warehouse/".length),
|
|
156
|
+
);
|
|
157
|
+
res.writeHead(404).end("Not found");
|
|
99
158
|
});
|
|
100
|
-
child.on("exit", (code) => process.exit(code ?? 0));
|
|
101
159
|
|
|
102
|
-
|
|
103
|
-
|
|
160
|
+
server.listen(args.port, () => {
|
|
161
|
+
const url = `http://localhost:${args.port}`;
|
|
162
|
+
console.log(` ✓ WASM engine: ${path.relative(process.cwd(), wasmDir)}`);
|
|
104
163
|
console.log(`\n ▸ ${url}\n`);
|
|
105
164
|
console.log(" Press Ctrl+C to stop.\n");
|
|
106
165
|
if (args.open) openBrowser(url);
|
|
107
|
-
}
|
|
166
|
+
});
|
|
108
167
|
}
|
|
109
168
|
|
|
110
169
|
if (require.main === module) {
|
|
Binary file
|