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