rastack 0.0.57 → 0.0.59

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 (86) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +190 -0
  3. package/admin/index.tsx +19 -13
  4. package/admin/ui/BootScreen.tsx +201 -0
  5. package/dist/admin.js +156 -18
  6. package/dist/deploy/ci.js +2 -2
  7. package/dist/deploy/io.js +2 -2
  8. package/dist/dev/harness.d.ts +1 -1
  9. package/dist/dev/harness.js +21 -5
  10. package/dist/external/cache.d.ts +28 -0
  11. package/dist/external/cache.js +35 -0
  12. package/dist/external/envelope.d.ts +35 -0
  13. package/dist/external/envelope.js +48 -0
  14. package/dist/external/graphql.d.ts +25 -0
  15. package/dist/external/graphql.js +78 -0
  16. package/dist/external/handler.d.ts +52 -0
  17. package/dist/external/handler.js +139 -0
  18. package/dist/external/index.d.ts +19 -0
  19. package/dist/external/index.js +38 -0
  20. package/dist/external/lambda.d.ts +49 -0
  21. package/dist/external/lambda.js +86 -0
  22. package/dist/external/rest.d.ts +17 -0
  23. package/dist/external/rest.js +46 -0
  24. package/dist/external/route.d.ts +12 -0
  25. package/dist/external/route.js +33 -0
  26. package/dist/external/service.d.ts +50 -0
  27. package/dist/external/service.js +59 -0
  28. package/dist/external/types.d.ts +107 -0
  29. package/dist/external/types.js +22 -0
  30. package/dist/external/util.d.ts +16 -0
  31. package/dist/external/util.js +58 -0
  32. package/dist/rastack-dev.js +200 -4
  33. package/dist/rastack-wasm-build.d.ts +1 -1
  34. package/dist/rastack-wasm-build.js +6 -6
  35. package/dist/wasm/rastack_wasm_bg.wasm +0 -0
  36. package/external.ts +10 -0
  37. package/hooks/query/delete.ts +12 -11
  38. package/hooks/query/fetch.ts +6 -8
  39. package/hooks/query/interfaces.ts +1 -4
  40. package/hooks/query/list.ts +16 -9
  41. package/hooks/query/update.ts +8 -6
  42. package/package.json +25 -2
  43. package/scripts/copy-templates.js +1 -1
  44. package/scripts/copy-wasm.js +1 -1
  45. package/scripts/dev-install.mjs +63 -0
  46. package/src/deploy/ci.ts +2 -2
  47. package/src/deploy/io.ts +2 -2
  48. package/src/dev/harness.ts +21 -5
  49. package/src/external/cache.ts +53 -0
  50. package/src/external/envelope.ts +78 -0
  51. package/src/external/graphql.ts +98 -0
  52. package/src/external/handler.ts +207 -0
  53. package/src/external/index.ts +25 -0
  54. package/src/external/lambda.ts +108 -0
  55. package/src/external/rest.ts +60 -0
  56. package/src/external/route.ts +33 -0
  57. package/src/external/service.ts +86 -0
  58. package/src/external/types.ts +121 -0
  59. package/src/external/util.ts +57 -0
  60. package/src/rastack-dev.ts +233 -6
  61. package/src/rastack-wasm-build.ts +6 -6
  62. package/jest.config.cjs +0 -12
  63. package/test/auth.spec.ts +0 -207
  64. package/test/cache.spec.ts +0 -263
  65. package/test/cognito.spec.ts +0 -281
  66. package/test/compile.spec.ts +0 -240
  67. package/test/components.spec.ts +0 -524
  68. package/test/csv-schema.spec.ts +0 -143
  69. package/test/deploy.spec.ts +0 -571
  70. package/test/dev.spec.ts +0 -414
  71. package/test/entities.spec.ts +0 -597
  72. package/test/import.spec.ts +0 -241
  73. package/test/plugin.spec.ts +0 -315
  74. package/test/runtime-manifest.spec.ts +0 -309
  75. package/test/schema-entities.spec.ts +0 -688
  76. package/test/tokens.spec.ts +0 -302
  77. package/test/transitions.spec.ts +0 -372
  78. package/test/typed-hooks.spec.ts +0 -412
  79. package/test/update.spec.ts +0 -152
  80. package/test/validate.spec.ts +0 -319
  81. package/tsconfig.json +0 -27
  82. package/wasm/package.json +0 -4
  83. package/wasm/rastack_wasm.d.ts +0 -100
  84. package/wasm/rastack_wasm.js +0 -745
  85. package/wasm/rastack_wasm_bg.wasm +0 -0
  86. package/wasm/rastack_wasm_bg.wasm.d.ts +0 -28
@@ -57,6 +57,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
57
57
  const child_process_1 = require("child_process");
58
58
  const fs = __importStar(require("fs"));
59
59
  const http = __importStar(require("http"));
60
+ const net = __importStar(require("net"));
60
61
  const path = __importStar(require("path"));
61
62
  const compile_1 = require("./compile");
62
63
  const harness_1 = require("./dev/harness");
@@ -144,14 +145,208 @@ function openBrowser(url) {
144
145
  /* non-fatal — the URL is printed anyway */
145
146
  }
146
147
  }
147
- function main(argv) {
148
+ // ---------------------------------------------------------------------------
149
+ // Live-dev mode — `rastack dev` for an Expo / react-native-web app.
150
+ //
151
+ // A React Native (Expo) app has no static `index.html`: its web build is a
152
+ // Metro bundle. Rather than make the developer run a second tool, `dev` starts
153
+ // Expo's Metro dev server itself and reverse-proxies to it, so `rastack dev`
154
+ // stays the single command *and* keeps Metro's fast-refresh. The WASM
155
+ // static-serve path remains the default for apps that ship a built index.html.
156
+ // ---------------------------------------------------------------------------
157
+ /** True when the project is an Expo app (so `dev` should drive Metro). */
158
+ function isExpoProject(cwd) {
159
+ try {
160
+ const appJson = JSON.parse(fs.readFileSync(path.join(cwd, "app.json"), "utf-8"));
161
+ if (appJson && appJson.expo)
162
+ return true;
163
+ }
164
+ catch {
165
+ /* no app.json — fall through to package.json */
166
+ }
167
+ try {
168
+ const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf-8"));
169
+ if (pkg?.dependencies?.expo || pkg?.devDependencies?.expo)
170
+ return true;
171
+ }
172
+ catch {
173
+ /* no package.json */
174
+ }
175
+ return false;
176
+ }
177
+ /** Locate the prebuilt wasm bundle without the cargo fallback (optional here). */
178
+ function findWasmDir() {
179
+ for (const dir of (0, harness_1.wasmBundleCandidates)(HERE)) {
180
+ if (fs.existsSync(path.join(dir, harness_1.WASM_ENTRY)))
181
+ return dir;
182
+ }
183
+ return null;
184
+ }
185
+ /** Grab an ephemeral free TCP port for Metro (avoids a fixed-port clash). */
186
+ function getFreePort() {
187
+ return new Promise((resolve, reject) => {
188
+ const srv = net.createServer();
189
+ srv.on("error", reject);
190
+ srv.listen(0, "127.0.0.1", () => {
191
+ const addr = srv.address();
192
+ const port = addr && typeof addr === "object" ? addr.port : 0;
193
+ srv.close(() => (port ? resolve(port) : reject(new Error("no free port"))));
194
+ });
195
+ });
196
+ }
197
+ /** Start `expo start --web` on `port`, with a reload-enabled environment. */
198
+ function startMetro(cwd, port) {
199
+ // A live dev server needs watch mode, so we must NOT set CI (Expo reads it as
200
+ // "reloads disabled"). BROWSER=none stops Expo opening its own tab — we front
201
+ // it on the rastack port. `shell:true` keeps the npx / npx.cmd split portable.
202
+ const env = {
203
+ ...process.env,
204
+ BROWSER: "none",
205
+ EXPO_NO_TELEMETRY: "1",
206
+ };
207
+ delete env.CI;
208
+ // nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
209
+ const child = (0, child_process_1.spawn)(`npx expo start --web --port ${port}`, {
210
+ cwd,
211
+ env,
212
+ stdio: ["ignore", "pipe", "pipe"],
213
+ shell: true,
214
+ });
215
+ // Surface Metro's own errors (bundling failures, port issues) with a prefix.
216
+ child.stderr?.on("data", (d) => process.stderr.write(` [metro] ${d}`));
217
+ return child;
218
+ }
219
+ /** Poll Metro's `/status` until it answers `packager-status:running`. */
220
+ function waitForMetro(port, timeoutMs = 120000) {
221
+ const deadline = Date.now() + timeoutMs;
222
+ return new Promise((resolve) => {
223
+ const retry = () => Date.now() > deadline ? resolve(false) : setTimeout(tick, 400);
224
+ const tick = () => {
225
+ const req = http.get({ host: "127.0.0.1", port, path: "/status", timeout: 2000 }, (res) => {
226
+ let body = "";
227
+ res.on("data", (c) => (body += c));
228
+ res.on("end", () => body.includes("packager-status:running") ? resolve(true) : retry());
229
+ });
230
+ req.on("error", retry);
231
+ req.on("timeout", () => req.destroy());
232
+ };
233
+ tick();
234
+ });
235
+ }
236
+ /** Reverse-proxy one HTTP request/response pair to Metro. */
237
+ function proxyToMetro(req, res, port) {
238
+ const proxyReq = http.request({ host: "127.0.0.1", port, method: req.method, path: req.url, headers: req.headers }, (proxyRes) => {
239
+ res.writeHead(proxyRes.statusCode || 502, proxyRes.headers);
240
+ proxyRes.pipe(res);
241
+ });
242
+ proxyReq.on("error", () => {
243
+ if (!res.headersSent)
244
+ res.writeHead(502);
245
+ res.end("Metro is not reachable.");
246
+ });
247
+ req.pipe(proxyReq);
248
+ }
249
+ /** Reverse-proxy a WebSocket upgrade (Metro HMR / fast-refresh) to Metro. */
250
+ function proxyUpgrade(req, clientSocket, head, port) {
251
+ const upstream = net.connect(port, "127.0.0.1", () => {
252
+ const lines = [`${req.method} ${req.url} HTTP/1.1`];
253
+ for (const [k, v] of Object.entries(req.headers)) {
254
+ if (Array.isArray(v))
255
+ v.forEach((vv) => lines.push(`${k}: ${vv}`));
256
+ else if (v != null)
257
+ lines.push(`${k}: ${v}`);
258
+ }
259
+ upstream.write(lines.join("\r\n") + "\r\n\r\n");
260
+ if (head && head.length)
261
+ upstream.write(head);
262
+ upstream.pipe(clientSocket);
263
+ clientSocket.pipe(upstream);
264
+ });
265
+ upstream.on("error", () => clientSocket.destroy());
266
+ clientSocket.on("error", () => upstream.destroy());
267
+ }
268
+ /** `rastack dev` for an Expo app: start Metro and reverse-proxy to it. */
269
+ async function runMetroMode(args, cwd) {
270
+ // The wasm engine is optional here — an Expo/RN-web app runs its own engine —
271
+ // so a missing bundle is fine. When present, the reserved backend routes work.
272
+ const wasmDir = findWasmDir();
273
+ const metroPort = await getFreePort();
274
+ console.log(`▸ Starting Metro bundler (Expo) on :${metroPort} …`);
275
+ const metro = startMetro(cwd, metroPort);
276
+ let shuttingDown = false;
277
+ const stop = (code = 0) => {
278
+ if (shuttingDown)
279
+ return;
280
+ shuttingDown = true;
281
+ try {
282
+ metro.kill();
283
+ }
284
+ catch {
285
+ /* already gone */
286
+ }
287
+ process.exit(code);
288
+ };
289
+ process.on("SIGINT", () => stop(0));
290
+ process.on("SIGTERM", () => stop(0));
291
+ metro.on("exit", (code) => {
292
+ if (!shuttingDown) {
293
+ console.error(`\n ✗ Metro exited (code ${code ?? "?"}).`);
294
+ process.exit(code ?? 1);
295
+ }
296
+ });
297
+ if (!(await waitForMetro(metroPort))) {
298
+ console.error(" ✗ Metro did not come up in time.");
299
+ return stop(1);
300
+ }
301
+ const server = http.createServer((req, res) => {
302
+ const url = (req.url || "/").split("?")[0];
303
+ // Reserved backend routes win when the engine bundle is available.
304
+ if (wasmDir && url.startsWith("/wasm/"))
305
+ return serveUnder(res, wasmDir, url.slice("/wasm/".length));
306
+ if (url === "/schema.rastack.json" || url === "/openapi.json")
307
+ return serveUnder(res, args.outDir, path.basename(url));
308
+ if (url.startsWith("/data/warehouse/"))
309
+ return serveUnder(res, args.warehouseDir, url.slice("/data/warehouse/".length));
310
+ // Everything else → the live Metro bundle (HTML, JS, assets, HMR polls).
311
+ return proxyToMetro(req, res, metroPort);
312
+ });
313
+ server.on("upgrade", (req, socket, head) => proxyUpgrade(req, socket, head, metroPort));
314
+ server.on("error", (err) => {
315
+ if (err.code === "EADDRINUSE") {
316
+ console.error(`\n ✗ Port ${args.port} is already in use — is \`rastack dev\` already running?\n` +
317
+ ` Stop it, or pick another port: rastack dev --port <n>\n`);
318
+ }
319
+ else {
320
+ console.error(`\n ✗ dev server error: ${err.message}`);
321
+ }
322
+ stop(1);
323
+ });
324
+ server.listen(args.port, () => {
325
+ const url = `http://localhost:${args.port}`;
326
+ console.log(` ✓ Metro live on :${metroPort} — fast refresh on`);
327
+ if (wasmDir)
328
+ console.log(` ✓ WASM engine: ${path.relative(cwd, wasmDir)}`);
329
+ console.log(`\n ▸ ${url}\n`);
330
+ console.log(" Edits reload live. Need the database console? Run `rastack admin`. Ctrl+C to stop.\n");
331
+ if (args.open)
332
+ openBrowser(url);
333
+ });
334
+ }
335
+ async function main(argv) {
148
336
  const args = (0, harness_1.parseDevArgs)(argv);
337
+ const cwd = process.cwd();
149
338
  console.log(`\n rastack dev — your app, in your browser over WASM\n`);
150
339
  console.log(`▸ Compiling ${args.resourcesDir} → ${args.outDir}`);
151
340
  if (!(0, compile_1.runCompile)(args.resourcesDir, args.outDir)) {
152
341
  console.error(" ✗ compile failed — fix the resource graph and re-run.");
153
342
  process.exit(1);
154
343
  }
344
+ // Serve the developer's own app when there is one; otherwise the welcome page.
345
+ const appRoot = resolveAppRoot(args.appDir);
346
+ // No static build, but an Expo source tree → drive Metro live (fast refresh).
347
+ if (!appRoot && isExpoProject(cwd)) {
348
+ return runMetroMode(args, cwd);
349
+ }
155
350
  const wasmDir = resolveWasmDir();
156
351
  if (!wasmDir) {
157
352
  console.error("\n ✗ No WASM engine available.\n" +
@@ -161,8 +356,6 @@ function main(argv) {
161
356
  " cargo install wasm-bindgen-cli\n");
162
357
  process.exit(1);
163
358
  }
164
- // Serve the developer's own app when there is one; otherwise the welcome page.
165
- const appRoot = resolveAppRoot(args.appDir);
166
359
  const server = http.createServer((req, res) => {
167
360
  const url = (req.url || "/").split("?")[0];
168
361
  if (req.method !== "GET") {
@@ -213,5 +406,8 @@ function main(argv) {
213
406
  });
214
407
  }
215
408
  if (require.main === module) {
216
- main(process.argv.slice(2));
409
+ main(process.argv.slice(2)).catch((err) => {
410
+ console.error(err);
411
+ process.exit(1);
412
+ });
217
413
  }
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Pipeline:
7
7
  * 1. cargo build -p rastack-wasm --release --target wasm32-unknown-unknown
8
- * 2. wasm-bindgen --target web → tools/wasm/rastack_wasm.js + rastack_wasm_bg.wasm
8
+ * 2. wasm-bindgen --target web → wasm/rastack_wasm.js + rastack_wasm_bg.wasm
9
9
  * 3. wasm-opt -Oz (if available) to shrink the binary
10
10
  *
11
11
  * This is invoked by the release CI (to bundle a prebuilt engine into the npm
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Pipeline:
8
8
  * 1. cargo build -p rastack-wasm --release --target wasm32-unknown-unknown
9
- * 2. wasm-bindgen --target web → tools/wasm/rastack_wasm.js + rastack_wasm_bg.wasm
9
+ * 2. wasm-bindgen --target web → wasm/rastack_wasm.js + rastack_wasm_bg.wasm
10
10
  * 3. wasm-opt -Oz (if available) to shrink the binary
11
11
  *
12
12
  * This is invoked by the release CI (to bundle a prebuilt engine into the npm
@@ -23,9 +23,9 @@ exports.buildWasm = buildWasm;
23
23
  const child_process_1 = require("child_process");
24
24
  const fs_1 = __importDefault(require("fs"));
25
25
  const path_1 = __importDefault(require("path"));
26
- const repoRoot = path_1.default.resolve(__dirname, "..", "..");
27
- const rustDir = path_1.default.join(repoRoot, "rust");
28
- const outDir = path_1.default.join(repoRoot, "tools", "wasm");
26
+ const repoRoot = path_1.default.resolve(__dirname, "..");
27
+ const rustDir = path_1.default.join(repoRoot, "engine");
28
+ const outDir = path_1.default.join(repoRoot, "wasm");
29
29
  const wasmPath = path_1.default.join(rustDir, "target", "wasm32-unknown-unknown", "release", "rastack_wasm.wasm");
30
30
  function run(cmd, args, cwd = repoRoot) {
31
31
  // Local build tool: `cmd`/`args` are the fixed toolchain invocations below
@@ -65,7 +65,7 @@ function buildWasm() {
65
65
  "--target",
66
66
  "wasm32-unknown-unknown",
67
67
  ], rustDir);
68
- console.log("→ wasm-bindgen → tools/wasm/");
68
+ console.log("→ wasm-bindgen → wasm/");
69
69
  run("wasm-bindgen", [
70
70
  "--target",
71
71
  "web",
@@ -87,7 +87,7 @@ function buildWasm() {
87
87
  console.log("· wasm-opt not found — skipping size optimisation (optional)");
88
88
  }
89
89
  const size = fs_1.default.statSync(path_1.default.join(outDir, "rastack_wasm_bg.wasm")).size;
90
- console.log(`✓ wasm engine built: tools/wasm/ (${(size / 1024).toFixed(0)} KB)`);
90
+ console.log(`✓ wasm engine built: wasm/ (${(size / 1024).toFixed(0)} KB)`);
91
91
  }
92
92
  if (require.main === module) {
93
93
  buildWasm();
Binary file
package/external.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `rastack/external` — the external-API proxy core.
3
+ *
4
+ * Serve external REST/GraphQL APIs (GitHub, Expo, AWS) over the rastack
5
+ * `/api/{app}/v1/{model}/` contract so the normal typed hooks consume them
6
+ * unchanged. The same core runs in two adapters: the mini service inside
7
+ * `rastack dev` ({@link createExternalService}) and an AWS Lambda behind API
8
+ * Gateway ({@link handleLambdaEvent}).
9
+ */
10
+ export * from "./src/external";
@@ -1,6 +1,9 @@
1
1
  import { AxiosResponse } from "axios";
2
- import { UseMutationResult } from "react-query/types/react/types";
3
- import { useMutation, useQueryClient } from "react-query";
2
+ import {
3
+ useMutation,
4
+ useQueryClient,
5
+ type UseMutationResult,
6
+ } from "@tanstack/react-query";
4
7
  import { IDeleteQueryResult } from "./interfaces";
5
8
  import { API } from "./api";
6
9
 
@@ -15,8 +18,8 @@ export function useDeleteQuery<TResult>({
15
18
  }: DeleteQueryProps): IDeleteQueryResult<TResult> {
16
19
  const queryClient = useQueryClient();
17
20
 
18
- return useMutation(
19
- async (params: { id?: string }) => {
21
+ return useMutation({
22
+ mutationFn: async (params: { id?: string }) => {
20
23
  if (params.id === undefined) {
21
24
  throw new Error("ID is required for delete operation");
22
25
  }
@@ -36,12 +39,10 @@ export function useDeleteQuery<TResult>({
36
39
  // assume `.results` exists on an empty body.
37
40
  return response.data?.results ?? response.data ?? null;
38
41
  },
39
- {
40
- onSuccess: () => {
41
- if (listQueryCacheKey) {
42
- queryClient.invalidateQueries(listQueryCacheKey);
43
- }
44
- },
42
+ onSuccess: () => {
43
+ if (listQueryCacheKey) {
44
+ queryClient.invalidateQueries({ queryKey: [listQueryCacheKey] });
45
+ }
45
46
  },
46
- ) as UseMutationResult<TResult>;
47
+ }) as UseMutationResult<TResult>;
47
48
  }
@@ -1,5 +1,5 @@
1
1
  import { AxiosResponse } from "axios";
2
- import { useQuery } from "react-query";
2
+ import { useQuery } from "@tanstack/react-query";
3
3
  import { API } from "./api";
4
4
  import { stringifyQueryParams } from "./url";
5
5
  import { GetQueryOptions, IGetQueryResult } from "./interfaces";
@@ -31,9 +31,9 @@ export function useGetQuery<TParam, TResult>({
31
31
 
32
32
  const params = options?.params;
33
33
 
34
- return useQuery(
35
- [queryKey, getUrl, params],
36
- async () => {
34
+ return useQuery({
35
+ queryKey: [queryKey, getUrl, params],
36
+ queryFn: async () => {
37
37
  const urlParams = stringifyQueryParams(pathParameters, params);
38
38
  const serverURL = `${getUrl}${urlParams ? `?${urlParams}` : ""}`;
39
39
 
@@ -41,8 +41,6 @@ export function useGetQuery<TParam, TResult>({
41
41
 
42
42
  return response.data;
43
43
  },
44
- {
45
- enabled: isAuthenticated && isRequiredParamsPresent,
46
- },
47
- );
44
+ enabled: isAuthenticated && isRequiredParamsPresent,
45
+ });
48
46
  }
@@ -1,7 +1,4 @@
1
- import {
2
- UseMutationResult,
3
- UseQueryResult,
4
- } from "react-query/types/react/types";
1
+ import { UseMutationResult, UseQueryResult } from "@tanstack/react-query";
5
2
  import { ExtractKey } from "../../types";
6
3
 
7
4
  export interface IPaginationActions {
@@ -1,5 +1,5 @@
1
1
  import { AxiosResponse } from "axios";
2
- import { useQuery } from "react-query";
2
+ import { useQuery } from "@tanstack/react-query";
3
3
  import { useEffect, useState } from "react";
4
4
  import {
5
5
  IFilterActions,
@@ -125,14 +125,21 @@ export function useGetPaginatedQueryList<TParam, TResult>({
125
125
  }
126
126
  };
127
127
 
128
- const query = useQuery<IPaginatedResponse<TResult>>(
129
- [queryKey, getUrl, options?.params, serverQueryParamState],
130
- fetchData,
131
- {
132
- enabled: isRequiredQueryParamsPresent && isAuthenticated,
133
- onSuccess: options?.onSuccess,
134
- },
135
- );
128
+ const query = useQuery<IPaginatedResponse<TResult>>({
129
+ queryKey: [queryKey, getUrl, options?.params, serverQueryParamState],
130
+ queryFn: fetchData,
131
+ enabled: isRequiredQueryParamsPresent && isAuthenticated,
132
+ });
133
+
134
+ // react-query v5 dropped the query-level `onSuccess` callback; mirror it with
135
+ // an effect so callers that pass `options.onSuccess` keep firing on each new
136
+ // successful result.
137
+ useEffect(() => {
138
+ if (query.isSuccess && query.data) {
139
+ options?.onSuccess?.(query.data);
140
+ }
141
+ // eslint-disable-next-line react-hooks/exhaustive-deps
142
+ }, [query.isSuccess, query.data]);
136
143
 
137
144
  // The list envelope is either nested (`{ pagination: { count, last } }`, the
138
145
  // Django/DRF-paginated shape) or flat DRF-classic (`{ count, next, previous,
@@ -1,5 +1,5 @@
1
1
  import { AxiosResponse } from "axios";
2
- import { useMutation, useQueryClient } from "react-query";
2
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
3
3
  import { IUpdateQueryResult, WriteQueryVariables } from "./interfaces";
4
4
  import { API } from "./api";
5
5
  import { stringifyQueryParams } from "./url";
@@ -29,8 +29,9 @@ export function useUpdateQuery<TParams, TPayload, TResult>({
29
29
  unknown,
30
30
  WriteQueryVariables<TParams, TPayload>,
31
31
  unknown
32
- >(({ params, payload }) => {
33
- return new Promise((resolve, reject) => {
32
+ >({
33
+ mutationFn: ({ params, payload }) => {
34
+ return new Promise((resolve, reject) => {
34
35
  let urlType: "put" | "patch" | "post" = "patch";
35
36
  let url: string | undefined;
36
37
 
@@ -73,16 +74,17 @@ export function useUpdateQuery<TParams, TPayload, TResult>({
73
74
  getAPIFunction(urlType, url, payload)
74
75
  .then((response: AxiosResponse<TResult>) => {
75
76
  if (itemQueryCacheKey) {
76
- queryClient.invalidateQueries([itemQueryCacheKey]);
77
+ queryClient.invalidateQueries({ queryKey: [itemQueryCacheKey] });
77
78
  }
78
79
  if (listQueryCacheKey) {
79
- queryClient.invalidateQueries(listQueryCacheKey);
80
+ queryClient.invalidateQueries({ queryKey: [listQueryCacheKey] });
80
81
  }
81
82
  resolve(response.data);
82
83
  })
83
84
  .catch((error) => {
84
85
  reject(error);
85
86
  });
86
- });
87
+ });
88
+ },
87
89
  });
88
90
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rastack",
3
- "version": "0.0.57",
3
+ "version": "0.0.59",
4
4
  "description": "",
5
5
  "main": "runtime.ts",
6
6
  "types": "runtime.ts",
@@ -11,9 +11,32 @@
11
11
  "publishConfig": {
12
12
  "access": "public"
13
13
  },
14
+ "files": [
15
+ "admin",
16
+ "auth",
17
+ "cache",
18
+ "components",
19
+ "hooks",
20
+ "provider",
21
+ "sync",
22
+ "theme",
23
+ "src",
24
+ "scripts",
25
+ "dist",
26
+ "define.ts",
27
+ "db.ts",
28
+ "validate.ts",
29
+ "plugin.ts",
30
+ "external.ts",
31
+ "tokens.ts",
32
+ "types.ts",
33
+ "runtime.ts",
34
+ "CHANGELOG.md"
35
+ ],
14
36
  "scripts": {
15
37
  "format": "prettier --write \"**/*.{ts,tsx,md}\"",
16
38
  "build": "tsc && cp src/.prettierrc dist/.prettierrc && node scripts/copy-templates.js && node scripts/build-admin.js && node scripts/copy-wasm.js",
39
+ "dev:install": "node scripts/dev-install.mjs",
17
40
  "start": "node dist/index.js",
18
41
  "test": "jest"
19
42
  },
@@ -38,7 +61,6 @@
38
61
  "luxon": "^3.7.2",
39
62
  "minimatch": "^10.2.5",
40
63
  "prettier": "^3.9.5",
41
- "react-query": "^3.39.3",
42
64
  "typescript": "~5.9.3",
43
65
  "usehooks-ts": "^3.1.1"
44
66
  },
@@ -46,6 +68,7 @@
46
68
  "react": "^19.2.7"
47
69
  },
48
70
  "devDependencies": {
71
+ "@testing-library/react-hooks": "8.0.1",
49
72
  "@types/jest": "^30.0.0",
50
73
  "@types/js-yaml": "^4.0.9",
51
74
  "@types/mocha": "^10.0.6",
@@ -13,7 +13,7 @@
13
13
  const fs = require("fs");
14
14
  const path = require("path");
15
15
 
16
- const repoRoot = path.resolve(__dirname, "..", "..");
16
+ const repoRoot = path.resolve(__dirname, "..");
17
17
  const srcDir = path.join(repoRoot, "deploy", "cloudformation");
18
18
  const destDir = path.join(__dirname, "..", "dist", "templates", "cloudformation");
19
19
 
@@ -17,7 +17,7 @@ const entry = path.join(src, "rastack_wasm.js");
17
17
 
18
18
  if (!fs.existsSync(entry)) {
19
19
  console.log(
20
- "copy-wasm: no prebuilt engine at tools/wasm (skipping — build it with " +
20
+ "copy-wasm: no prebuilt engine at wasm/ (skipping — build it with " +
21
21
  "rastack-wasm-build.js to ship it).",
22
22
  );
23
23
  process.exit(0);
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * dev-install — fast local build + install of the rastack CLI.
4
+ *
5
+ * Compiles `src/ → dist/` with the local TypeScript, then merge-copies the fresh
6
+ * dist over the globally installed copy (`~/.rastack/node_modules/rastack/dist`)
7
+ * so `rastack` and `ras` on the PATH immediately run the new code. The copy
8
+ * merges (it never deletes), which preserves the shipped runtime artifacts that
9
+ * already live there — the wasm engine, the admin bundle, and the templates —
10
+ * none of which a tsc-only build emits.
11
+ *
12
+ * This is the fast local iteration loop; a full `npm run build && npm pack`
13
+ * publish is unchanged. Usage:
14
+ *
15
+ * node scripts/dev-install.mjs [--prefix <dir>] # defaults to ~/.rastack
16
+ */
17
+ import { execFileSync } from "node:child_process";
18
+ import * as fs from "node:fs";
19
+ import * as os from "node:os";
20
+ import * as path from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+
23
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
24
+ const TOOLS = path.resolve(HERE, "..");
25
+
26
+ function flag(name, fallback) {
27
+ const i = process.argv.indexOf(name);
28
+ return i !== -1 && process.argv[i + 1] ? process.argv[i + 1] : fallback;
29
+ }
30
+
31
+ // The install prefix rastack ships into (the hosted installer uses ~/.rastack).
32
+ const prefix = flag("--prefix", path.join(os.homedir(), ".rastack"));
33
+ const target = path.join(prefix, "node_modules", "rastack");
34
+
35
+ if (!fs.existsSync(target)) {
36
+ console.error(
37
+ ` ✗ no rastack install found at ${target}\n` +
38
+ " Install it first (hosted installer / npm install -g rastack), or pass --prefix <dir>.",
39
+ );
40
+ process.exit(1);
41
+ }
42
+
43
+ // 1) Compile TypeScript → dist/. Run tsc through node (avoids the Windows .cmd
44
+ // spawn EINVAL) so this loop is identical on every platform.
45
+ console.log("▸ tsc — compiling src → dist");
46
+ const tscJs = path.join(TOOLS, "node_modules", "typescript", "bin", "tsc");
47
+ execFileSync(process.execPath, [tscJs, "-p", path.join(TOOLS, "tsconfig.json")], {
48
+ stdio: "inherit",
49
+ cwd: TOOLS,
50
+ });
51
+
52
+ // 2) Merge-copy the fresh dist over the global install (never delete → the wasm
53
+ // engine, admin bundle and templates already there are left untouched).
54
+ console.log(`▸ installing → ${target}`);
55
+ fs.cpSync(path.join(TOOLS, "dist"), path.join(target, "dist"), { recursive: true });
56
+ fs.copyFileSync(path.join(TOOLS, "package.json"), path.join(target, "package.json"));
57
+
58
+ const version = JSON.parse(
59
+ fs.readFileSync(path.join(target, "package.json"), "utf-8"),
60
+ ).version;
61
+ console.log(
62
+ ` ✓ installed rastack ${version} — \`rastack\` and \`ras\` now run this local build`,
63
+ );
package/src/deploy/ci.ts CHANGED
@@ -83,7 +83,7 @@ function stackOutput(
83
83
 
84
84
  /** The Lambda staging directory `cargo lambda build` writes the binary into. */
85
85
  function stageDir(cfg: DeployConfig): string {
86
- return `rust/target/lambda/${cfg.lambda.bin}`;
86
+ return `engine/target/lambda/${cfg.lambda.bin}`;
87
87
  }
88
88
 
89
89
  /** `check` — validate the resource graph and compile the manifest. A CI gate. */
@@ -118,7 +118,7 @@ export function buildSteps(
118
118
  {
119
119
  name: "Cross-build the Lambda binary",
120
120
  run: `cargo lambda build --release ${archFlag} --bin ${cfg.lambda.bin} --features ${cfg.lambda.features}`,
121
- cwd: "rust",
121
+ cwd: "engine",
122
122
  },
123
123
  {
124
124
  name: "Bake the Cognito JWKS into the bundle",
package/src/deploy/io.ts CHANGED
@@ -39,8 +39,8 @@ export function resolveDeployAsset(
39
39
  const candidates = [
40
40
  // Installed package: dist/deploy/io.js → dist/templates/cloudformation/…
41
41
  path.join(__dirname, "..", "templates", "cloudformation", relPath),
42
- // Running from TS source: tools/src/deploy/io.ts → repo-root deploy/…
43
- path.join(__dirname, "..", "..", "..", "deploy", "cloudformation", relPath),
42
+ // Running from TS source: src/deploy/io.ts → repo-root deploy/…
43
+ path.join(__dirname, "..", "..", "deploy", "cloudformation", relPath),
44
44
  // Invoked inside the framework repo itself (cwd is the repo root).
45
45
  path.join(cwd, "deploy", "cloudformation", relPath),
46
46
  ];