rastack 0.0.22 → 0.0.24

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 (60) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/cache/cache.ts +1 -1
  3. package/cache/revocation.ts +1 -1
  4. package/cache/storage.ts +1 -1
  5. package/cache/types.ts +1 -1
  6. package/define.ts +2 -0
  7. package/dist/compile/analyze.js +3 -3
  8. package/dist/compile/entities.d.ts +68 -0
  9. package/dist/compile/entities.js +496 -0
  10. package/dist/compile/index.d.ts +24 -7
  11. package/dist/compile/index.js +48 -16
  12. package/dist/compile/model.d.ts +2 -2
  13. package/dist/compile/model.js +1 -1
  14. package/dist/compile/program.d.ts +2 -1
  15. package/dist/compile/program.js +8 -3
  16. package/dist/csv-schema.js +1 -1
  17. package/dist/define/db.d.ts +73 -0
  18. package/dist/define/db.js +35 -0
  19. package/dist/define/index.d.ts +8 -8
  20. package/dist/define/index.js +10 -10
  21. package/dist/index.js +1 -1
  22. package/dist/rastack-compile.d.ts +11 -0
  23. package/dist/{rad-compile.js → rastack-compile.js} +22 -12
  24. package/dist/{rad-wasm-build.d.ts → rastack-wasm-build.d.ts} +2 -2
  25. package/dist/{rad-wasm-build.js → rastack-wasm-build.js} +6 -6
  26. package/dist/rastack.d.ts +17 -0
  27. package/dist/{rad.js → rastack.js} +24 -24
  28. package/dist/scan.d.ts +3 -3
  29. package/dist/scan.js +4 -4
  30. package/dist/seed.d.ts +2 -2
  31. package/dist/seed.js +9 -9
  32. package/hooks/real-time/pusher.ts +2 -2
  33. package/package.json +2 -3
  34. package/provider/provider.tsx +12 -12
  35. package/provider/types.ts +16 -16
  36. package/provider/warehouse.ts +1 -1
  37. package/provider/wasm.ts +10 -10
  38. package/src/compile/analyze.ts +3 -3
  39. package/src/compile/entities.ts +602 -0
  40. package/src/compile/index.ts +57 -15
  41. package/src/compile/model.ts +2 -2
  42. package/src/compile/program.ts +8 -3
  43. package/src/csv-schema.ts +1 -1
  44. package/src/define/db.ts +64 -0
  45. package/src/define/index.ts +16 -16
  46. package/src/index.ts +1 -1
  47. package/src/{rad-compile.ts → rastack-compile.ts} +30 -13
  48. package/src/{rad-wasm-build.ts → rastack-wasm-build.ts} +6 -6
  49. package/src/{rad.ts → rastack.ts} +24 -24
  50. package/src/scan.ts +4 -4
  51. package/src/seed.ts +9 -9
  52. package/sync/engine.ts +1 -1
  53. package/sync/types.ts +1 -1
  54. package/test/cache.spec.ts +1 -1
  55. package/test/compile.spec.ts +6 -6
  56. package/test/csv-schema.spec.ts +2 -2
  57. package/test/entities.spec.ts +381 -0
  58. package/types.ts +3 -0
  59. package/dist/rad-compile.d.ts +0 -11
  60. package/dist/rad.d.ts +0 -17
@@ -1,12 +1,15 @@
1
1
  import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  import { analyze } from "./analyze";
4
+ import { analyzeEntities, detectCycles, EntityDiagnostic } from "./entities";
4
5
  import { buildManifest, manifestToJson } from "./manifest";
5
6
  import { Manifest, ResourceModel } from "./model";
6
7
  import { buildOpenApi, openApiToJson } from "./openapi";
7
8
  import { createProgram } from "./program";
8
9
 
9
10
  export { analyze } from "./analyze";
11
+ export { analyzeEntities, detectCycles } from "./entities";
12
+ export type { EntityAnalysis, EntityDiagnostic } from "./entities";
10
13
  export { buildManifest } from "./manifest";
11
14
  export { buildOpenApi } from "./openapi";
12
15
  export * from "./model";
@@ -15,6 +18,7 @@ export interface CompileResult {
15
18
  manifest: Manifest;
16
19
  openapi: any;
17
20
  files: string[];
21
+ diagnostics: EntityDiagnostic[];
18
22
  }
19
23
 
20
24
  /** Directories never worth walking for resource definitions. */
@@ -22,19 +26,20 @@ const IGNORE_DIRS = new Set([
22
26
  "node_modules",
23
27
  ".git",
24
28
  ".expo",
25
- ".rad",
29
+ ".rad", // legacy output dir
30
+ ".rastack",
26
31
  "dist",
27
32
  "web-build",
28
33
  ]);
29
34
 
30
35
  /**
31
- * Every `.ts` resource file under a directory (recursively), or an explicit
32
- * file. Recursion is what lets resource definitions live **co-located with the
33
- * pages that consume them** — e.g. `app/airports/_schema.rad.ts` beside
34
- * `app/airports/index.tsx` — instead of in one top-level `resources/` folder.
35
- * `.tsx` route components and `.d.ts` files are skipped; only files with a
36
- * `resource(...)` call contribute to the manifest, so co-located helpers are
37
- * harmless.
36
+ * Every `.ts`/`.tsx` source file under a directory (recursively), or an
37
+ * explicit file. Recursion is what lets resource definitions live **co-located
38
+ * with the pages that consume them** — e.g. `app/airports/_schema.rastack.ts`
39
+ * beside `app/airports/index.tsx` — instead of in one top-level `resources/`
40
+ * folder. `.tsx` files carry no `resource(...)` definitions but are scanned
41
+ * for the typed hook calls (`useEntityList<Airport>(…)`) that root the
42
+ * interface-driven entity graph; co-located helpers are harmless.
38
43
  */
39
44
  export function resolveResourceFiles(input: string): string[] {
40
45
  const stat = fs.existsSync(input) ? fs.statSync(input) : undefined;
@@ -45,7 +50,7 @@ export function resolveResourceFiles(input: string): string[] {
45
50
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
46
51
  if (entry.isDirectory()) {
47
52
  if (!IGNORE_DIRS.has(entry.name)) walk(path.join(dir, entry.name));
48
- } else if (entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts")) {
53
+ } else if (/\.tsx?$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
49
54
  files.push(path.resolve(dir, entry.name));
50
55
  }
51
56
  }
@@ -56,26 +61,63 @@ export function resolveResourceFiles(input: string): string[] {
56
61
  throw new Error(`No such resources path: ${input}`);
57
62
  }
58
63
 
59
- /** Analyse resource definitions into the canonical model (no file output). */
60
- export function analyzeResources(input: string): ResourceModel[] {
64
+ export interface ProjectAnalysis {
65
+ resources: ResourceModel[];
66
+ diagnostics: EntityDiagnostic[];
67
+ }
68
+
69
+ /**
70
+ * Analyse a project into the canonical model: explicit `resource()` DSL
71
+ * definitions plus resources inferred from hook-rooted TypeScript interfaces.
72
+ * When both surfaces define the same `app.model`, the explicit DSL definition
73
+ * wins — hook usage of a generated `IAirport` interface resolves back onto the
74
+ * authored resource instead of creating a duplicate. Cycle detection runs over
75
+ * the merged FK graph.
76
+ */
77
+ export function analyzeProject(input: string): ProjectAnalysis {
61
78
  const files = resolveResourceFiles(input);
62
79
  const program = createProgram(files);
63
- return analyze(program, files);
80
+
81
+ const fromDsl = analyze(program, files);
82
+ const fromInterfaces = analyzeEntities(program, files);
83
+
84
+ const defined = new Set(fromDsl.map((r) => `${r.app}.${r.model}`));
85
+ const resources = [
86
+ ...fromDsl,
87
+ ...fromInterfaces.resources.filter((r) => !defined.has(`${r.app}.${r.model}`)),
88
+ ];
89
+ resources.sort((a, b) =>
90
+ a.app === b.app ? a.model.localeCompare(b.model) : a.app.localeCompare(b.app),
91
+ );
92
+
93
+ return {
94
+ resources,
95
+ diagnostics: [...fromInterfaces.diagnostics, ...detectCycles(resources)],
96
+ };
97
+ }
98
+
99
+ /** Analyse resource definitions into the canonical model (no file output). */
100
+ export function analyzeResources(input: string): ResourceModel[] {
101
+ return analyzeProject(input).resources;
64
102
  }
65
103
 
66
104
  /** Compile resource definitions to a manifest + OpenAPI, writing both to disk. */
67
105
  export function compile(input: string, outDir: string): CompileResult {
68
- const resources = analyzeResources(input);
106
+ const { resources, diagnostics } = analyzeProject(input);
107
+ const errors = diagnostics.filter((d) => d.severity === "error");
108
+ if (errors.length) {
109
+ throw new Error(errors.map((e) => e.message).join("\n"));
110
+ }
69
111
  const manifest = buildManifest(resources);
70
112
  const openapi = buildOpenApi(resources);
71
113
 
72
114
  fs.mkdirSync(outDir, { recursive: true });
73
- const manifestPath = path.join(outDir, "schema.rad.json");
115
+ const manifestPath = path.join(outDir, "schema.rastack.json");
74
116
  const openapiPath = path.join(outDir, "openapi.json");
75
117
  fs.writeFileSync(manifestPath, manifestToJson(manifest), { flag: "w" });
76
118
  fs.writeFileSync(openapiPath, openApiToJson(openapi), { flag: "w" });
77
119
 
78
- return { manifest, openapi, files: [manifestPath, openapiPath] };
120
+ return { manifest, openapi, files: [manifestPath, openapiPath], diagnostics };
79
121
  }
80
122
 
81
123
  // -- introspection (replaces `manage.py rastack check|list|urls`) -----------
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The canonical, backend-agnostic data model produced by the compiler. This is
3
- * what `schema.rad.json` serializes and what both the OpenAPI emitter and the
3
+ * what `schema.rastack.json` serializes and what both the OpenAPI emitter and the
4
4
  * Rust API consume.
5
5
  */
6
6
 
@@ -52,7 +52,7 @@ export interface AdminModel {
52
52
  }
53
53
 
54
54
  /**
55
- * Row-level access control, enforced by `rad-api-core` on every surface.
55
+ * Row-level access control, enforced by `rastack-api-core` on every surface.
56
56
  * `ownerField` names the field stamped with the authenticated subject;
57
57
  * `scope: "owner"` places the table under a per-identity warehouse prefix
58
58
  * (`tenants/{sub}/…`) so object-storage IAM can fence data per user.
@@ -3,7 +3,8 @@ import * as ts from "typescript";
3
3
 
4
4
  /**
5
5
  * Build a `ts.Program` (and thus a `TypeChecker`) over a set of resource
6
- * definition files. The DSL package (`rad-tools/define`) is mapped to the
6
+ * definition files. The authoring subpaths (`rastack/define` for the DSL,
7
+ * `rastack/types` for the `DbConfig` column brands) are mapped to the
7
8
  * in-tree/installed sources via `paths` so both the shipped package name and
8
9
  * the local demo/tests resolve without extra linking.
9
10
  */
@@ -18,10 +19,14 @@ export function createProgram(fileNames: string[]): ts.Program {
18
19
  skipLibCheck: true,
19
20
  esModuleInterop: true,
20
21
  noEmit: true,
22
+ // `.tsx` app files are scanned for the typed hook calls that root the
23
+ // interface-driven entity graph.
24
+ jsx: ts.JsxEmit.ReactJSX,
21
25
  baseUrl: process.cwd(),
22
26
  paths: {
23
- "rad-tools/define": [defineDir],
24
- "rad-tools/define/*": [defineDir + "/*"],
27
+ "rastack/define": [defineDir],
28
+ "rastack/define/*": [defineDir + "/*"],
29
+ "rastack/types": [path.join(defineDir, "db")],
25
30
  },
26
31
  };
27
32
 
package/src/csv-schema.ts CHANGED
@@ -47,7 +47,7 @@ const DEFAULT_PII_PATTERNS = [
47
47
  ];
48
48
 
49
49
  function loadPiiConfig(repoRoot: string): PiiConfig {
50
- const configPath = path.join(repoRoot, ".rad", "pii-patterns.yml");
50
+ const configPath = path.join(repoRoot, ".rastack", "pii-patterns.yml");
51
51
  if (!fs.existsSync(configPath)) {
52
52
  return { patterns: DEFAULT_PII_PATTERNS, luhn_check: true, require_policy: true };
53
53
  }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Type-level column configuration for interface-driven entities.
3
+ *
4
+ * Interface entities are plain TypeScript, so their column constraints are
5
+ * expressed *in the type system*: intersect a property type with one or more
6
+ * {@link DbConfig} brands and the compiler reads the configuration straight
7
+ * off the type — the same way it reads foreign keys.
8
+ *
9
+ * ```ts
10
+ * import { Default, Int, MaxLength, PrimaryKey, Unique, UUID } from "rastack/types";
11
+ *
12
+ * export interface Passenger {
13
+ * id: UUID & PrimaryKey;
14
+ * seat: string & MaxLength<4> & Unique;
15
+ * age: Int; // number maps to float; Int refines it
16
+ * status: string & Default<"active">;
17
+ * }
18
+ * ```
19
+ *
20
+ * Recurring configurations can extend {@link DbConfig} once and be reused:
21
+ *
22
+ * ```ts
23
+ * interface Registration extends DbConfig<{ maxLength: 10; unique: true }> {}
24
+ *
25
+ * export interface Aircraft {
26
+ * registration: string & Registration;
27
+ * }
28
+ * ```
29
+ *
30
+ * The brands are "flavors", not hard brands: the phantom `__rastackDb` property is
31
+ * optional and never exists at runtime, so a plain `"3F"` stays assignable to
32
+ * `string & MaxLength<4> & Unique` — no casts anywhere in app code.
33
+ */
34
+
35
+ /**
36
+ * The phantom carrier every constraint type extends. `C` holds the column
37
+ * configuration as literal types; the compiler recovers it from the `__rastackDb`
38
+ * property of the intersected field type.
39
+ */
40
+ export interface DbConfig<C = unknown> {
41
+ readonly __rastackDb?: C;
42
+ }
43
+
44
+ /** The column value must be unique. */
45
+ export interface Unique extends DbConfig<{ unique: true }> {}
46
+
47
+ /** The column is the primary key. */
48
+ export interface PrimaryKey extends DbConfig<{ primaryKey: true }> {}
49
+
50
+ /** Maximum length for string columns: `string & MaxLength<120>`. */
51
+ export interface MaxLength<N extends number> extends DbConfig<{ maxLength: N }> {}
52
+
53
+ /** Server-side default value: `string & Default<"scheduled">`, `number & Default<0>`. */
54
+ export interface Default<V extends string | number | boolean>
55
+ extends DbConfig<{ default: V }> {}
56
+
57
+ /** An integer column — a bare `number` property maps to `float`. */
58
+ export type Int = number & DbConfig<{ int: true }>;
59
+
60
+ /** A UUID column, stored as a string. */
61
+ export type UUID = string & DbConfig<{ uuid: true }>;
62
+
63
+ /** A datetime column authored as a string (a `Date` property maps directly). */
64
+ export type DateTimeString = string & DbConfig<{ datetime: true }>;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * rad-tools authoring DSL — the TypeScript-first source of truth.
2
+ * rastack authoring DSL — the TypeScript-first source of truth.
3
3
  *
4
4
  * Resources are authored as plain TypeScript. The compiler
5
5
  * (`tools/src/compile`) reads these definitions and, crucially, **infers
@@ -9,7 +9,7 @@
9
9
  * `Ref`'s string-literal type arguments.
10
10
  *
11
11
  * ```ts
12
- * import { resource, s } from "rad-tools/define";
12
+ * import { resource, s } from "rastack/define";
13
13
  *
14
14
  * export const Airport = resource("airports", "airport", {
15
15
  * code: s.string({ maxLength: 3, unique: true }),
@@ -23,9 +23,9 @@
23
23
  * ```
24
24
  */
25
25
 
26
- /** A scalar field. The literal `__radScalar` type is what the compiler reads. */
26
+ /** A scalar field. The literal `__rastackScalar` type is what the compiler reads. */
27
27
  export interface Scalar<T extends string> {
28
- readonly __radScalar: T;
28
+ readonly __rastackScalar: T;
29
29
  }
30
30
 
31
31
  /**
@@ -33,7 +33,7 @@ export interface Scalar<T extends string> {
33
33
  * arguments so the compiler can recover the relation target purely from types.
34
34
  */
35
35
  export interface Ref<App extends string, Model extends string> {
36
- readonly __radRef: true;
36
+ readonly __rastackRef: true;
37
37
  readonly __app: App;
38
38
  readonly __model: Model;
39
39
  }
@@ -72,22 +72,22 @@ export interface DateOptions {
72
72
  */
73
73
  export const s = {
74
74
  string(_opts?: StringOptions): Scalar<"string"> {
75
- return { __radScalar: "string" } as Scalar<"string">;
75
+ return { __rastackScalar: "string" } as Scalar<"string">;
76
76
  },
77
77
  int(_opts?: NumberOptions): Scalar<"int"> {
78
- return { __radScalar: "int" } as Scalar<"int">;
78
+ return { __rastackScalar: "int" } as Scalar<"int">;
79
79
  },
80
80
  float(_opts?: NumberOptions): Scalar<"float"> {
81
- return { __radScalar: "float" } as Scalar<"float">;
81
+ return { __rastackScalar: "float" } as Scalar<"float">;
82
82
  },
83
83
  bool(_opts?: BoolOptions): Scalar<"bool"> {
84
- return { __radScalar: "bool" } as Scalar<"bool">;
84
+ return { __rastackScalar: "bool" } as Scalar<"bool">;
85
85
  },
86
86
  datetime(_opts?: DateOptions): Scalar<"datetime"> {
87
- return { __radScalar: "datetime" } as Scalar<"datetime">;
87
+ return { __rastackScalar: "datetime" } as Scalar<"datetime">;
88
88
  },
89
89
  uuid(_opts?: StringOptions): Scalar<"uuid"> {
90
- return { __radScalar: "uuid" } as Scalar<"uuid">;
90
+ return { __rastackScalar: "uuid" } as Scalar<"uuid">;
91
91
  },
92
92
  /**
93
93
  * A foreign key. The target is `() => SomeResource`; the compiler resolves
@@ -97,7 +97,7 @@ export const s = {
97
97
  ref<A extends string, M extends string>(
98
98
  _thunk: () => ResourceType<A, M, any>,
99
99
  ): Ref<A, M> {
100
- return { __radRef: true } as Ref<A, M>;
100
+ return { __rastackRef: true } as Ref<A, M>;
101
101
  },
102
102
  };
103
103
 
@@ -113,8 +113,8 @@ export interface SyncOptions {
113
113
  }
114
114
 
115
115
  /**
116
- * Row-level access control. Enforced by `rad-api-core`, so the rules are
117
- * identical on the server (Lambda / `rad serve`) and in the in-browser WASM
116
+ * Row-level access control. Enforced by `rastack-api-core`, so the rules are
117
+ * identical on the server (Lambda / `rastack serve`) and in the in-browser WASM
118
118
  * engine.
119
119
  *
120
120
  * - `ownerField` — the field that records the owning identity (the verified
@@ -152,7 +152,7 @@ export interface ResourceOptions {
152
152
  * arguments so a {@link Ref} to this resource carries a recoverable target.
153
153
  */
154
154
  export interface ResourceType<App extends string, Model extends string, F> {
155
- readonly __rad: "resource";
155
+ readonly __rastack: "resource";
156
156
  readonly __app: App;
157
157
  readonly __model: Model;
158
158
  readonly app: App;
@@ -173,7 +173,7 @@ export function resource<
173
173
  options: ResourceOptions = {},
174
174
  ): ResourceType<App, Model, F> {
175
175
  return {
176
- __rad: "resource",
176
+ __rastack: "resource",
177
177
  __app: app,
178
178
  __model: model,
179
179
  app,
package/src/index.ts CHANGED
@@ -9,7 +9,7 @@ const schemaOnly = args.includes("--schema-only");
9
9
 
10
10
  const currentDir = process.cwd(); // Get the current working directory
11
11
  const kodeDir = path.join(currentDir, ".kode");
12
- const configFile = path.join(kodeDir, "generate-rad-sdk.json");
12
+ const configFile = path.join(kodeDir, "generate-rastack-sdk.json");
13
13
  let config: any = {};
14
14
 
15
15
  // Create the .kode directory if it doesn't exist
@@ -1,19 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * `rad compile | check | list | urls` — the TypeScript-first replacement for the
4
+ * `rastack compile | check | list | urls` — the TypeScript-first replacement for the
5
5
  * old Django `manage.py rastack` command.
6
6
  *
7
- * rad compile [resourcesDir] [outDir] defs → schema.rad.json + openapi.json
8
- * rad check [resourcesDir] validate the resource graph (exit != 0 on error)
9
- * rad list [resourcesDir] print every resource + its fields/relations
10
- * rad urls [resourcesDir] print the generated /api/{app}/v1/{model}/ routes
7
+ * rastack compile [resourcesDir] [outDir] defs → schema.rastack.json + openapi.json
8
+ * rastack check [resourcesDir] validate the resource graph (exit != 0 on error)
9
+ * rastack list [resourcesDir] print every resource + its fields/relations
10
+ * rastack urls [resourcesDir] print the generated /api/{app}/v1/{model}/ routes
11
11
  */
12
12
 
13
- import { analyzeResources, collisions, compile, urlsFor } from "./compile";
13
+ import {
14
+ analyzeProject,
15
+ analyzeResources,
16
+ collisions,
17
+ compile,
18
+ urlsFor,
19
+ } from "./compile";
14
20
 
15
21
  const DEFAULT_RESOURCES = "resources";
16
- const DEFAULT_OUT = ".rad";
22
+ const DEFAULT_OUT = ".rastack";
17
23
 
18
24
  function main(argv: string[]): void {
19
25
  const [sub, ...rest] = argv;
@@ -23,14 +29,25 @@ function main(argv: string[]): void {
23
29
  const resourcesDir = rest[0] || DEFAULT_RESOURCES;
24
30
  const outDir = rest[1] || DEFAULT_OUT;
25
31
  const result = compile(resourcesDir, outDir);
32
+ for (const d of result.diagnostics) console.warn(` ⚠ ${d.message}`);
26
33
  console.log(
27
34
  `✓ compiled ${result.manifest.resources.length} resource(s) → ${result.files.join(", ")}`,
28
35
  );
29
36
  break;
30
37
  }
31
38
  case "check": {
32
- const resources = analyzeResources(rest[0] || DEFAULT_RESOURCES);
33
- const errors = collisions(resources);
39
+ const { resources, diagnostics } = analyzeProject(
40
+ rest[0] || DEFAULT_RESOURCES,
41
+ );
42
+ const errors = [
43
+ ...collisions(resources),
44
+ ...diagnostics
45
+ .filter((d) => d.severity === "error")
46
+ .map((d) => d.message),
47
+ ];
48
+ for (const d of diagnostics.filter((d) => d.severity === "warning")) {
49
+ console.warn(` ⚠ ${d.message}`);
50
+ }
34
51
  if (errors.length) {
35
52
  for (const e of errors) console.error(` ✗ ${e}`);
36
53
  console.error(`${errors.length} error(s) found.`);
@@ -74,10 +91,10 @@ function main(argv: string[]): void {
74
91
  console.error(
75
92
  `Unknown compile subcommand: "${sub}"\n\n` +
76
93
  `Usage:\n` +
77
- ` rad compile [resourcesDir] [outDir]\n` +
78
- ` rad check [resourcesDir]\n` +
79
- ` rad list [resourcesDir]\n` +
80
- ` rad urls [resourcesDir]\n`,
94
+ ` rastack compile [resourcesDir] [outDir]\n` +
95
+ ` rastack check [resourcesDir]\n` +
96
+ ` rastack list [resourcesDir]\n` +
97
+ ` rastack urls [resourcesDir]\n`,
81
98
  );
82
99
  process.exit(1);
83
100
  }
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * `rad wasm` — compile the Rust API to WebAssembly for in-browser local dev.
4
+ * `rastack wasm` — compile the Rust API to WebAssembly for in-browser local dev.
5
5
  *
6
6
  * Pipeline:
7
7
  * 1. cargo build -p rad-wasm --release --target wasm32-unknown-unknown
8
- * 2. wasm-bindgen --target web → stack/wasm/rad_wasm.js + rad_wasm_bg.wasm
8
+ * 2. wasm-bindgen --target web → stack/wasm/rastack_wasm.js + rastack_wasm_bg.wasm
9
9
  * 3. wasm-opt -Oz (if available) to shrink the binary
10
10
  *
11
11
  * The generated bundle is what `<RAStackProvider mode="local">` loads. Prereqs:
@@ -25,7 +25,7 @@ const wasmPath = path.join(
25
25
  "target",
26
26
  "wasm32-unknown-unknown",
27
27
  "release",
28
- "rad_wasm.wasm",
28
+ "rastack_wasm.wasm",
29
29
  );
30
30
 
31
31
  function run(cmd: string, args: string[], cwd = repoRoot): void {
@@ -77,7 +77,7 @@ run("wasm-bindgen", [
77
77
  "--out-dir",
78
78
  outDir,
79
79
  "--out-name",
80
- "rad_wasm",
80
+ "rastack_wasm",
81
81
  wasmPath,
82
82
  ]);
83
83
 
@@ -90,13 +90,13 @@ fs.writeFileSync(
90
90
 
91
91
  if (has("wasm-opt")) {
92
92
  console.log("→ wasm-opt -Oz");
93
- const bg = path.join(outDir, "rad_wasm_bg.wasm");
93
+ const bg = path.join(outDir, "rastack_wasm_bg.wasm");
94
94
  run("wasm-opt", ["-Oz", "-o", bg, bg]);
95
95
  } else {
96
96
  console.log("· wasm-opt not found — skipping size optimisation (optional)");
97
97
  }
98
98
 
99
- const size = fs.statSync(path.join(outDir, "rad_wasm_bg.wasm")).size;
99
+ const size = fs.statSync(path.join(outDir, "rastack_wasm_bg.wasm")).size;
100
100
  console.log(
101
101
  `✓ wasm engine built: stack/wasm/ (${(size / 1024).toFixed(0)} KB)`,
102
102
  );
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * rad — unified CLI for React API Stack
4
+ * rastack — unified CLI for React API Stack
5
5
  *
6
6
  * Commands:
7
- * rad compile [resourcesDir] [outDir] TypeScript resources → schema.rad.json + openapi.json
8
- * rad check [resourcesDir] Validate the resource graph
9
- * rad list [resourcesDir] Print resources, fields and relations
10
- * rad urls [resourcesDir] Print the generated /api/{app}/v1/{model}/ routes
11
- * rad generate [--schema-only] Generate typed hooks from OpenAPI
12
- * rad serve [--warehouse dir] Run the Rust API over an Iceberg warehouse
13
- * rad admin [--warehouse dir] Run the Rust admin (Django-admin-style DB browser)
14
- * rad wasm Compile the API to WebAssembly (in-browser local dev)
15
- * rad scan [files...] [--fail-on-pii] Scan CSVs for PII and schema info
16
- * rad seed [--env dev] [--source gs://] Load CSVs into Iceberg namespace
7
+ * rastack compile [resourcesDir] [outDir] TypeScript resources → schema.rastack.json + openapi.json
8
+ * rastack check [resourcesDir] Validate the resource graph
9
+ * rastack list [resourcesDir] Print resources, fields and relations
10
+ * rastack urls [resourcesDir] Print the generated /api/{app}/v1/{model}/ routes
11
+ * rastack generate [--schema-only] Generate typed hooks from OpenAPI
12
+ * rastack serve [--warehouse dir] Run the Rust API over an Iceberg warehouse
13
+ * rastack admin [--warehouse dir] Run the Rust admin (Django-admin-style DB browser)
14
+ * rastack wasm Compile the API to WebAssembly (in-browser local dev)
15
+ * rastack scan [files...] [--fail-on-pii] Scan CSVs for PII and schema info
16
+ * rastack seed [--env dev] [--source gs://] Load CSVs into Iceberg namespace
17
17
  */
18
18
 
19
19
  import { execSync, spawnSync } from "child_process";
@@ -22,7 +22,7 @@ import path from "path";
22
22
  const [command, ...rest] = process.argv.slice(2);
23
23
 
24
24
  function run(script: string, args: string[] = rest) {
25
- // `rad` is a local developer CLI: `script` is a fixed internal dist filename
25
+ // `rastack` is a local developer CLI: `script` is a fixed internal dist filename
26
26
  // and `args` come from the developer's own shell (process.argv), not from an
27
27
  // untrusted network request — there is no command-injection surface here.
28
28
  // nosemgrep: javascript.lang.security.detect-child-process.detect-child-process
@@ -36,8 +36,8 @@ function run(script: string, args: string[] = rest) {
36
36
  function runRust(subcommand: string) {
37
37
  const repoRoot = path.resolve(__dirname, "..", "..");
38
38
  const binary =
39
- process.env.RAD_SERVER_BIN ||
40
- path.join(repoRoot, "rust", "target", "release", "rad-server");
39
+ process.env.RASTACK_SERVER_BIN ||
40
+ path.join(repoRoot, "rust", "target", "release", "rastack-server");
41
41
  // Runs the locally-built Rust server binary with a fixed internal subcommand
42
42
  // ("serve"/"admin"); `rest` is the developer's own CLI args, not untrusted
43
43
  // input — no command-injection surface.
@@ -61,8 +61,8 @@ switch (command) {
61
61
  case "check":
62
62
  case "list":
63
63
  case "urls":
64
- // rad-compile dispatches on its own first argument, so pass the command.
65
- run("rad-compile.js", [command, ...rest]);
64
+ // rastack-compile dispatches on its own first argument, so pass the command.
65
+ run("rastack-compile.js", [command, ...rest]);
66
66
  break;
67
67
  case "serve":
68
68
  runRust("serve");
@@ -72,7 +72,7 @@ switch (command) {
72
72
  break;
73
73
  case "wasm":
74
74
  // Compile the API to WebAssembly for in-browser local dev.
75
- run("rad-wasm-build.js", rest);
75
+ run("rastack-wasm-build.js", rest);
76
76
  break;
77
77
  case "scan":
78
78
  run("scan.js");
@@ -89,13 +89,13 @@ switch (command) {
89
89
  console.error(
90
90
  `Unknown command: "${command}"\n\n` +
91
91
  `Usage:\n` +
92
- ` rad compile [resourcesDir] [outDir]\n` +
93
- ` rad check | list | urls [resourcesDir]\n` +
94
- ` rad generate [--schema-only]\n` +
95
- ` rad serve | admin [--warehouse dir]\n` +
96
- ` rad wasm Compile the API to WebAssembly (browser local dev)\n` +
97
- ` rad scan [files...] [--fail-on-pii]\n` +
98
- ` rad seed [--env dev|staging] [--source gs://bucket/ | ./myapp-dev/]\n`,
92
+ ` rastack compile [resourcesDir] [outDir]\n` +
93
+ ` rastack check | list | urls [resourcesDir]\n` +
94
+ ` rastack generate [--schema-only]\n` +
95
+ ` rastack serve | admin [--warehouse dir]\n` +
96
+ ` rastack wasm Compile the API to WebAssembly (browser local dev)\n` +
97
+ ` rastack scan [files...] [--fail-on-pii]\n` +
98
+ ` rastack seed [--env dev|staging] [--source gs://bucket/ | ./myapp-dev/]\n`,
99
99
  );
100
100
  process.exit(1);
101
101
  }
package/src/scan.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * rad scan
4
+ * rastack scan
5
5
  *
6
6
  * Scans CSV files in myapp-dev/ for PII column patterns and schema issues.
7
7
  * Exits non-zero if violations are found and --fail-on-pii is passed.
8
8
  *
9
9
  * Usage:
10
- * rad scan [files...] [--fail-on-pii]
11
- * rad scan myapp-dev/passengers.csv myapp-dev/flights.csv --fail-on-pii
10
+ * rastack scan [files...] [--fail-on-pii]
11
+ * rastack scan myapp-dev/passengers.csv myapp-dev/flights.csv --fail-on-pii
12
12
  */
13
13
 
14
14
  import path from "path";
@@ -80,7 +80,7 @@ if (totalViolations > 0) {
80
80
  );
81
81
  if (failOnPii) {
82
82
  console.error(
83
- "Failing because --fail-on-pii is set. Remove PII data or add access policies in .rad/policies/.",
83
+ "Failing because --fail-on-pii is set. Remove PII data or add access policies in .rastack/policies/.",
84
84
  );
85
85
  process.exit(1);
86
86
  }