rastack 0.0.63 → 0.0.69
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 +32 -0
- package/README.md +19 -24
- package/admin/AdminApp.tsx +56 -3
- package/admin/httpEngine.ts +5 -6
- package/admin/index.tsx +11 -85
- package/admin/ui/icons.tsx +9 -0
- package/admin/ui/styles.ts +3 -0
- package/admin/useAdmin.ts +41 -4
- package/dist/admin.js +17 -22
- package/dist/compile/analyze.js +11 -1
- package/dist/compile/model.d.ts +1 -1
- package/dist/compile/run.d.ts +2 -2
- package/dist/compile/run.js +2 -2
- package/dist/define/index.d.ts +10 -4
- package/dist/define/index.js +2 -2
- package/dist/define/manifest.js +7 -1
- package/dist/deploy/io.d.ts +50 -0
- package/dist/deploy/io.js +124 -0
- package/dist/deploy/workflows.js +1 -1
- package/dist/dev/harness.d.ts +19 -27
- package/dist/dev/harness.js +31 -40
- package/dist/dev/native-server.d.ts +29 -0
- package/dist/dev/native-server.js +174 -0
- package/dist/external/envelope.d.ts +1 -1
- package/dist/external/envelope.js +1 -1
- package/dist/external/service.d.ts +2 -2
- package/dist/external/service.js +2 -2
- package/dist/external/types.d.ts +1 -1
- package/dist/external/types.js +1 -1
- package/dist/rastack-admin.d.ts +6 -14
- package/dist/rastack-admin.js +60 -88
- package/dist/rastack-bootstrap.js +18 -47
- package/dist/rastack-design.d.ts +1 -2
- package/dist/rastack-design.js +1 -2
- package/dist/rastack-dev.d.ts +7 -10
- package/dist/rastack-dev.js +68 -80
- package/dist/rastack-init.d.ts +6 -3
- package/dist/rastack-init.js +43 -30
- package/dist/rastack.d.ts +1 -1
- package/dist/rastack.js +5 -6
- package/dist/validate/transitions.d.ts +1 -1
- package/dist/validate/transitions.js +1 -1
- package/hooks/query/api.ts +4 -6
- package/package.json +2 -2
- package/provider/index.ts +0 -1
- package/provider/provider.tsx +31 -134
- package/provider/types.ts +30 -69
- package/src/compile/analyze.ts +11 -1
- package/src/compile/model.ts +1 -1
- package/src/compile/run.ts +2 -2
- package/src/define/index.ts +7 -4
- package/src/define/manifest.ts +7 -1
- package/src/deploy/io.ts +162 -1
- package/src/deploy/workflows.ts +1 -1
- package/src/dev/harness.ts +30 -43
- package/src/dev/native-server.ts +164 -0
- package/src/external/envelope.ts +1 -1
- package/src/external/service.ts +2 -2
- package/src/external/types.ts +1 -1
- package/src/rastack-admin.ts +66 -107
- package/src/rastack-bootstrap.ts +24 -69
- package/src/rastack-design.ts +1 -2
- package/src/rastack-dev.ts +79 -91
- package/src/rastack-init.ts +50 -41
- package/src/rastack.ts +5 -6
- package/src/validate/transitions.ts +1 -1
- package/dist/rastack-wasm-build.d.ts +0 -19
- package/dist/rastack-wasm-build.js +0 -94
- package/dist/wasm/package.json +0 -4
- package/dist/wasm/rastack_wasm.d.ts +0 -100
- package/dist/wasm/rastack_wasm.js +0 -745
- package/dist/wasm/rastack_wasm_bg.wasm +0 -0
- package/dist/wasm/rastack_wasm_bg.wasm.d.ts +0 -28
- package/provider/wasm.ts +0 -126
- package/scripts/copy-wasm.js +0 -35
- package/src/rastack-wasm-build.ts +0 -113
package/dist/compile/analyze.js
CHANGED
|
@@ -172,7 +172,17 @@ function analyzeField(name, initializer, checker) {
|
|
|
172
172
|
const app = literalTypeProp(fieldType, "__app", checker, initializer);
|
|
173
173
|
const model = literalTypeProp(fieldType, "__model", checker, initializer);
|
|
174
174
|
if (app && model) {
|
|
175
|
-
|
|
175
|
+
const fk = { name, type: "fk", relation: { app, model } };
|
|
176
|
+
// `s.ref(() => X, { null: true })` — a nullable relation (a shared secret
|
|
177
|
+
// with no project, an unassigned task). The options object is the ref
|
|
178
|
+
// call's second argument, read the same way scalar constraints are.
|
|
179
|
+
if (ts.isCallExpression(initializer)) {
|
|
180
|
+
const opts = literalToValue(initializer.arguments[1]);
|
|
181
|
+
if (opts && typeof opts === "object" && opts.null === true) {
|
|
182
|
+
fk.null = true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return fk;
|
|
176
186
|
}
|
|
177
187
|
return undefined;
|
|
178
188
|
}
|
package/dist/compile/model.d.ts
CHANGED
|
@@ -76,7 +76,7 @@ export interface TransitionEdgeModel {
|
|
|
76
76
|
/**
|
|
77
77
|
* A resource's declarative state machine, keyed off one state field. Compiled
|
|
78
78
|
* into the manifest and enforced on every write surface: the TS record machine
|
|
79
|
-
* (forms, `rastack import`) and `rastack-api-core` (
|
|
79
|
+
* (forms, `rastack import`) and `rastack-api-core` (the native server) all
|
|
80
80
|
* reject a write that jumps between states without a declared edge.
|
|
81
81
|
*/
|
|
82
82
|
export interface TransitionsModel {
|
package/dist/compile/run.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* This is the single entry point the two real modalities of use share instead
|
|
7
7
|
* of shelling out to a standalone `compile` command:
|
|
8
|
-
* • `rastack dev` / `rastack admin` — local development (compile, then
|
|
9
|
-
* the
|
|
8
|
+
* • `rastack dev` / `rastack admin` — local development (compile, then start
|
|
9
|
+
* the native rastack-server; the browser is only the UI), and
|
|
10
10
|
* • `rastack ci` — the deploy pipeline (compile the manifest the Rust backend
|
|
11
11
|
* reads + the OpenAPI contract, then build/ship).
|
|
12
12
|
*/
|
package/dist/compile/run.js
CHANGED
|
@@ -44,8 +44,8 @@ const index_1 = require("./index");
|
|
|
44
44
|
*
|
|
45
45
|
* This is the single entry point the two real modalities of use share instead
|
|
46
46
|
* of shelling out to a standalone `compile` command:
|
|
47
|
-
* • `rastack dev` / `rastack admin` — local development (compile, then
|
|
48
|
-
* the
|
|
47
|
+
* • `rastack dev` / `rastack admin` — local development (compile, then start
|
|
48
|
+
* the native rastack-server; the browser is only the UI), and
|
|
49
49
|
* • `rastack ci` — the deploy pipeline (compile the manifest the Rust backend
|
|
50
50
|
* reads + the OpenAPI contract, then build/ship).
|
|
51
51
|
*/
|
package/dist/define/index.d.ts
CHANGED
|
@@ -64,6 +64,11 @@ export interface Ref<App extends string, Model extends string> {
|
|
|
64
64
|
readonly __model: Model;
|
|
65
65
|
/** The authored `() => Resource` thunk — carried at runtime. */
|
|
66
66
|
readonly target?: () => ResourceType<App, Model, any>;
|
|
67
|
+
/** Optional FK constraints — `null: true` makes the relation nullable
|
|
68
|
+
* (e.g. a shared secret with no project, or an unassigned task). */
|
|
69
|
+
readonly options?: {
|
|
70
|
+
null?: boolean;
|
|
71
|
+
};
|
|
67
72
|
}
|
|
68
73
|
export interface StringOptions {
|
|
69
74
|
maxLength?: number;
|
|
@@ -105,7 +110,9 @@ export declare const s: {
|
|
|
105
110
|
* the relation from the *type* `Ref<App, Model>` this returns, and the
|
|
106
111
|
* runtime keeps the thunk so `manifestFromResources()` can resolve it too.
|
|
107
112
|
*/
|
|
108
|
-
ref<A extends string, M extends string>(thunk: () => ResourceType<A, M, any
|
|
113
|
+
ref<A extends string, M extends string>(thunk: () => ResourceType<A, M, any>, options?: {
|
|
114
|
+
null?: boolean;
|
|
115
|
+
}): Ref<A, M>;
|
|
109
116
|
};
|
|
110
117
|
export type AnyField = Scalar<string> | Ref<string, string>;
|
|
111
118
|
/** Local-first sync configuration (mirrors the old Python `class Sync:`). */
|
|
@@ -118,8 +125,7 @@ export interface SyncOptions {
|
|
|
118
125
|
}
|
|
119
126
|
/**
|
|
120
127
|
* Row-level access control. Enforced by `rastack-api-core`, so the rules are
|
|
121
|
-
* identical on the server (
|
|
122
|
-
* engine.
|
|
128
|
+
* identical on the native server (laptop and Lambda).
|
|
123
129
|
*
|
|
124
130
|
* - `ownerField` — the field that records the owning identity (the verified
|
|
125
131
|
* token `sub`). Rows are only listed/read/written by their owner; the field
|
|
@@ -157,7 +163,7 @@ export type Permission = "authenticatedOrReadOnly" | "authenticated" | "public";
|
|
|
157
163
|
* transition may fire from, `to` the state it enters, and `set` the effect —
|
|
158
164
|
* field patches the engine applies alongside the state change. The block is
|
|
159
165
|
* static data: it compiles into the manifest and is enforced by
|
|
160
|
-
* `rastack-api-core` on
|
|
166
|
+
* `rastack-api-core` on the native server (laptop and Lambda), so a
|
|
161
167
|
* transition behaves as a declarative, typesafe backend function with no
|
|
162
168
|
* per-resource server code.
|
|
163
169
|
*/
|
package/dist/define/index.js
CHANGED
|
@@ -56,8 +56,8 @@ exports.s = {
|
|
|
56
56
|
* the relation from the *type* `Ref<App, Model>` this returns, and the
|
|
57
57
|
* runtime keeps the thunk so `manifestFromResources()` can resolve it too.
|
|
58
58
|
*/
|
|
59
|
-
ref(thunk) {
|
|
60
|
-
return { __rastackRef: true, target: thunk };
|
|
59
|
+
ref(thunk, options) {
|
|
60
|
+
return { __rastackRef: true, target: thunk, options };
|
|
61
61
|
},
|
|
62
62
|
};
|
|
63
63
|
/** Define a resource. Capture app/model (and options) as literal types. */
|
package/dist/define/manifest.js
CHANGED
|
@@ -89,11 +89,17 @@ function runtimeField(res, name, value) {
|
|
|
89
89
|
if (!isResourceValue(target)) {
|
|
90
90
|
throw new Error(`manifestFromResources: ${res.app}.${res.model}.${name} is an s.ref(...) whose target thunk did not return a resource() value`);
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
const fk = {
|
|
93
93
|
name,
|
|
94
94
|
type: "fk",
|
|
95
95
|
relation: { app: target.app, model: target.model },
|
|
96
96
|
};
|
|
97
|
+
// A nullable FK (`s.ref(() => X, { null: true })`) — e.g. a shared secret
|
|
98
|
+
// with no project — so the engine allows and stores a null relation.
|
|
99
|
+
if (field.options?.null === true) {
|
|
100
|
+
fk.null = true;
|
|
101
|
+
}
|
|
102
|
+
return fk;
|
|
97
103
|
}
|
|
98
104
|
if (typeof field.__rastackScalar !== "string")
|
|
99
105
|
return undefined;
|
package/dist/deploy/io.d.ts
CHANGED
|
@@ -22,6 +22,27 @@ export declare function configPath(cwd?: string): string;
|
|
|
22
22
|
* repo itself still works. Returns null when no candidate exists.
|
|
23
23
|
*/
|
|
24
24
|
export declare function resolveDeployAsset(relPath: string, cwd?: string): string | null;
|
|
25
|
+
/**
|
|
26
|
+
* Base URL the framework's CloudFormation templates are published at. The same
|
|
27
|
+
* site that hosts the installer (`reactapistack.com/setup.sh`) serves the base
|
|
28
|
+
* templates, so a CLI whose bundled copy is missing (a partial install, or a
|
|
29
|
+
* globally-linked dev build) can still fetch them.
|
|
30
|
+
*/
|
|
31
|
+
export declare const TEMPLATE_BASE_URL = "https://reactapistack.com/cloudformation";
|
|
32
|
+
/**
|
|
33
|
+
* Download a published CloudFormation template to a temp file, returning its
|
|
34
|
+
* path — or null on any failure (offline, 404, write error). Used as the
|
|
35
|
+
* fallback when the template is not bundled with the install.
|
|
36
|
+
*/
|
|
37
|
+
export declare function downloadTemplate(name: string, base?: string): Promise<string | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a CloudFormation template to a local file path for `aws cloudformation
|
|
40
|
+
* deploy` (which accepts only `--template-file`). Prefers the copy bundled with
|
|
41
|
+
* the `rastack` install; when that is missing, downloads it from the React API
|
|
42
|
+
* Stack website. Returns null only when the template is neither bundled nor
|
|
43
|
+
* fetchable.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveTemplateFile(name: string, cwd?: string): Promise<string | null>;
|
|
25
46
|
/** Read + normalize the deploy config, or null when it does not exist yet. */
|
|
26
47
|
export declare function loadConfig(cwd?: string): DeployConfig | null;
|
|
27
48
|
/** Write the deploy config, creating `.rastack/` if needed. */
|
|
@@ -39,6 +60,14 @@ export declare function awsCapture(args: string[], cwd?: string): string | null;
|
|
|
39
60
|
export declare function awsRun(args: string[], cwd?: string): void;
|
|
40
61
|
/** True when the AWS CLI is on PATH. */
|
|
41
62
|
export declare function awsAvailable(): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* The AWS account id the CLI's active credentials resolve to, or null when the
|
|
65
|
+
* CLI has no usable credentials (not logged in, an expired SSO session, or no
|
|
66
|
+
* configured profile). `init`/`bootstrap` call this to fail fast with an
|
|
67
|
+
* actionable "log in" message before attempting a stack deploy, instead of
|
|
68
|
+
* letting the first `cloudformation deploy` fail with a cryptic AWS error.
|
|
69
|
+
*/
|
|
70
|
+
export declare function awsIdentity(region?: string): string | null;
|
|
42
71
|
/**
|
|
43
72
|
* A single Output value of a CloudFormation stack, or null when the stack does
|
|
44
73
|
* not exist or the key is unset. `describe-stacks` prints `None` for a missing
|
|
@@ -52,6 +81,27 @@ export declare function stackOutput(stackName: string, key: string, region: stri
|
|
|
52
81
|
* on every `init`. Returns false when the placeholder file is missing.
|
|
53
82
|
*/
|
|
54
83
|
export declare function uploadPlaceholder(artifactsBucket: string, region: string, cwd?: string): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Deploy the tier-0 bootstrap stack, retrying once against an account that
|
|
86
|
+
* already has a GitHub OIDC provider — AWS allows only one provider per URL, so
|
|
87
|
+
* a second create is rejected and the retry tells the template to reuse the
|
|
88
|
+
* existing one. Throws on failure (after the retry). Shared by `rastack init`
|
|
89
|
+
* and `rastack bootstrap` so both stand the stack up identically.
|
|
90
|
+
*/
|
|
91
|
+
export declare function deployBootstrap(cfg: DeployConfig, reuseOidc: boolean, template: string): void;
|
|
92
|
+
/**
|
|
93
|
+
* Ensure the tier-0 bootstrap stack exists for this account+region, **creating
|
|
94
|
+
* it when missing**, and return its artifacts/templates buckets. This is why
|
|
95
|
+
* `rastack init` is a single command that stands up *everything* — the
|
|
96
|
+
* account-level bootstrap stack and the per-app backend — with no separate
|
|
97
|
+
* `rastack bootstrap` step. Idempotent: an existing stack is reconciled in
|
|
98
|
+
* place. Throws with an actionable message when the template cannot be resolved,
|
|
99
|
+
* the deploy fails, or the buckets can't be read back.
|
|
100
|
+
*/
|
|
101
|
+
export declare function ensureBootstrapStack(cfg: DeployConfig, reuseOidc?: boolean): Promise<{
|
|
102
|
+
artifactsBucket: string;
|
|
103
|
+
templatesBucket: string | null;
|
|
104
|
+
}>;
|
|
55
105
|
/** True when the GitHub CLI is on PATH. */
|
|
56
106
|
export declare function ghAvailable(): boolean;
|
|
57
107
|
/**
|
package/dist/deploy/io.js
CHANGED
|
@@ -38,8 +38,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
38
38
|
};
|
|
39
39
|
})();
|
|
40
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.TEMPLATE_BASE_URL = void 0;
|
|
41
42
|
exports.configPath = configPath;
|
|
42
43
|
exports.resolveDeployAsset = resolveDeployAsset;
|
|
44
|
+
exports.downloadTemplate = downloadTemplate;
|
|
45
|
+
exports.resolveTemplateFile = resolveTemplateFile;
|
|
43
46
|
exports.loadConfig = loadConfig;
|
|
44
47
|
exports.saveConfig = saveConfig;
|
|
45
48
|
exports.gitRemoteUrl = gitRemoteUrl;
|
|
@@ -47,8 +50,11 @@ exports.canPrompt = canPrompt;
|
|
|
47
50
|
exports.awsCapture = awsCapture;
|
|
48
51
|
exports.awsRun = awsRun;
|
|
49
52
|
exports.awsAvailable = awsAvailable;
|
|
53
|
+
exports.awsIdentity = awsIdentity;
|
|
50
54
|
exports.stackOutput = stackOutput;
|
|
51
55
|
exports.uploadPlaceholder = uploadPlaceholder;
|
|
56
|
+
exports.deployBootstrap = deployBootstrap;
|
|
57
|
+
exports.ensureBootstrapStack = ensureBootstrapStack;
|
|
52
58
|
exports.ghAvailable = ghAvailable;
|
|
53
59
|
exports.ghAuthenticated = ghAuthenticated;
|
|
54
60
|
exports.ghAuthLogin = ghAuthLogin;
|
|
@@ -58,6 +64,7 @@ exports.ask = ask;
|
|
|
58
64
|
exports.withPrompt = withPrompt;
|
|
59
65
|
const child_process_1 = require("child_process");
|
|
60
66
|
const fs = __importStar(require("fs"));
|
|
67
|
+
const https = __importStar(require("https"));
|
|
61
68
|
const os = __importStar(require("os"));
|
|
62
69
|
const path = __importStar(require("path"));
|
|
63
70
|
const readline = __importStar(require("readline"));
|
|
@@ -97,6 +104,54 @@ function resolveDeployAsset(relPath, cwd = process.cwd()) {
|
|
|
97
104
|
}
|
|
98
105
|
return null;
|
|
99
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Base URL the framework's CloudFormation templates are published at. The same
|
|
109
|
+
* site that hosts the installer (`reactapistack.com/setup.sh`) serves the base
|
|
110
|
+
* templates, so a CLI whose bundled copy is missing (a partial install, or a
|
|
111
|
+
* globally-linked dev build) can still fetch them.
|
|
112
|
+
*/
|
|
113
|
+
exports.TEMPLATE_BASE_URL = "https://reactapistack.com/cloudformation";
|
|
114
|
+
/**
|
|
115
|
+
* Download a published CloudFormation template to a temp file, returning its
|
|
116
|
+
* path — or null on any failure (offline, 404, write error). Used as the
|
|
117
|
+
* fallback when the template is not bundled with the install.
|
|
118
|
+
*/
|
|
119
|
+
function downloadTemplate(name, base = exports.TEMPLATE_BASE_URL) {
|
|
120
|
+
const url = `${base}/${name}`;
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
https
|
|
123
|
+
.get(url, (res) => {
|
|
124
|
+
if (res.statusCode !== 200) {
|
|
125
|
+
res.resume();
|
|
126
|
+
resolve(null);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const chunks = [];
|
|
130
|
+
res.on("data", (c) => chunks.push(c));
|
|
131
|
+
res.on("end", () => {
|
|
132
|
+
try {
|
|
133
|
+
const dest = path.join(os.tmpdir(), `rastack-${path.basename(name)}`);
|
|
134
|
+
fs.writeFileSync(dest, Buffer.concat(chunks));
|
|
135
|
+
resolve(dest);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
resolve(null);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
})
|
|
142
|
+
.on("error", () => resolve(null));
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Resolve a CloudFormation template to a local file path for `aws cloudformation
|
|
147
|
+
* deploy` (which accepts only `--template-file`). Prefers the copy bundled with
|
|
148
|
+
* the `rastack` install; when that is missing, downloads it from the React API
|
|
149
|
+
* Stack website. Returns null only when the template is neither bundled nor
|
|
150
|
+
* fetchable.
|
|
151
|
+
*/
|
|
152
|
+
async function resolveTemplateFile(name, cwd = process.cwd()) {
|
|
153
|
+
return resolveDeployAsset(name, cwd) ?? (await downloadTemplate(name));
|
|
154
|
+
}
|
|
100
155
|
/** Read + normalize the deploy config, or null when it does not exist yet. */
|
|
101
156
|
function loadConfig(cwd = process.cwd()) {
|
|
102
157
|
const file = configPath(cwd);
|
|
@@ -163,6 +218,26 @@ function awsRun(args, cwd = process.cwd()) {
|
|
|
163
218
|
function awsAvailable() {
|
|
164
219
|
return awsCapture(["--version"]) !== null;
|
|
165
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* The AWS account id the CLI's active credentials resolve to, or null when the
|
|
223
|
+
* CLI has no usable credentials (not logged in, an expired SSO session, or no
|
|
224
|
+
* configured profile). `init`/`bootstrap` call this to fail fast with an
|
|
225
|
+
* actionable "log in" message before attempting a stack deploy, instead of
|
|
226
|
+
* letting the first `cloudformation deploy` fail with a cryptic AWS error.
|
|
227
|
+
*/
|
|
228
|
+
function awsIdentity(region) {
|
|
229
|
+
const args = [
|
|
230
|
+
"sts",
|
|
231
|
+
"get-caller-identity",
|
|
232
|
+
"--query",
|
|
233
|
+
"Account",
|
|
234
|
+
"--output",
|
|
235
|
+
"text",
|
|
236
|
+
];
|
|
237
|
+
if (region)
|
|
238
|
+
args.push("--region", region);
|
|
239
|
+
return awsCapture(args);
|
|
240
|
+
}
|
|
166
241
|
/**
|
|
167
242
|
* A single Output value of a CloudFormation stack, or null when the stack does
|
|
168
243
|
* not exist or the key is unset. `describe-stacks` prints `None` for a missing
|
|
@@ -210,6 +285,55 @@ function uploadPlaceholder(artifactsBucket, region, cwd = process.cwd()) {
|
|
|
210
285
|
]);
|
|
211
286
|
return true;
|
|
212
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Deploy the tier-0 bootstrap stack, retrying once against an account that
|
|
290
|
+
* already has a GitHub OIDC provider — AWS allows only one provider per URL, so
|
|
291
|
+
* a second create is rejected and the retry tells the template to reuse the
|
|
292
|
+
* existing one. Throws on failure (after the retry). Shared by `rastack init`
|
|
293
|
+
* and `rastack bootstrap` so both stand the stack up identically.
|
|
294
|
+
*/
|
|
295
|
+
function deployBootstrap(cfg, reuseOidc, template) {
|
|
296
|
+
try {
|
|
297
|
+
awsRun((0, config_1.bootstrapStackArgs)(cfg, reuseOidc, template));
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
if (reuseOidc)
|
|
301
|
+
throw err;
|
|
302
|
+
console.log(" • retrying bootstrap, reusing the account's existing GitHub OIDC provider");
|
|
303
|
+
awsRun((0, config_1.bootstrapStackArgs)(cfg, true, template));
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Ensure the tier-0 bootstrap stack exists for this account+region, **creating
|
|
308
|
+
* it when missing**, and return its artifacts/templates buckets. This is why
|
|
309
|
+
* `rastack init` is a single command that stands up *everything* — the
|
|
310
|
+
* account-level bootstrap stack and the per-app backend — with no separate
|
|
311
|
+
* `rastack bootstrap` step. Idempotent: an existing stack is reconciled in
|
|
312
|
+
* place. Throws with an actionable message when the template cannot be resolved,
|
|
313
|
+
* the deploy fails, or the buckets can't be read back.
|
|
314
|
+
*/
|
|
315
|
+
async function ensureBootstrapStack(cfg, reuseOidc = false) {
|
|
316
|
+
const stack = (0, config_1.bootstrapStackName)(cfg);
|
|
317
|
+
const existing = stackOutput(stack, config_1.BOOTSTRAP_ARTIFACTS_OUTPUT, cfg.region);
|
|
318
|
+
if (existing) {
|
|
319
|
+
console.log(` ✓ bootstrap stack ${stack} already exists — reconciling`);
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
console.log(`\n▸ Creating the tier-0 bootstrap stack ${stack} (@ ${cfg.region})`);
|
|
323
|
+
}
|
|
324
|
+
const template = await resolveTemplateFile("bootstrap.yaml");
|
|
325
|
+
if (!template) {
|
|
326
|
+
throw new Error("bootstrap.yaml template not found in the rastack package and could not be\n" +
|
|
327
|
+
" downloaded from reactapistack.com. Reinstall rastack (`npm i -g rastack`).");
|
|
328
|
+
}
|
|
329
|
+
deployBootstrap(cfg, reuseOidc, template);
|
|
330
|
+
const artifactsBucket = stackOutput(stack, config_1.BOOTSTRAP_ARTIFACTS_OUTPUT, cfg.region);
|
|
331
|
+
const templatesBucket = stackOutput(stack, config_1.BOOTSTRAP_TEMPLATES_OUTPUT, cfg.region);
|
|
332
|
+
if (!artifactsBucket) {
|
|
333
|
+
throw new Error(`bootstrap stack ${stack} deployed but its ArtifactsBucket output could not be read.`);
|
|
334
|
+
}
|
|
335
|
+
return { artifactsBucket, templatesBucket };
|
|
336
|
+
}
|
|
213
337
|
// ── GitHub CLI boundary ──────────────────────────────────────────────────────
|
|
214
338
|
// After the stack is up `init` wires the two repo Variables CI reads
|
|
215
339
|
// (AWS_DEPLOY_ROLE_ARN / AWS_REGION) through the developer's own `gh` — already
|
package/dist/deploy/workflows.js
CHANGED
|
@@ -124,7 +124,7 @@ jobs:
|
|
|
124
124
|
- name: Cache cargo
|
|
125
125
|
uses: Swatinem/rust-cache@v2
|
|
126
126
|
with:
|
|
127
|
-
workspaces:
|
|
127
|
+
workspaces: engine
|
|
128
128
|
# cargo-lambda cross-builds the custom-runtime binary; it auto-installs
|
|
129
129
|
# zig (via pip) for the cross-compile when needed.
|
|
130
130
|
- name: Install cargo-lambda
|
package/dist/dev/harness.d.ts
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pure core of `rastack dev` — the
|
|
2
|
+
* Pure core of `rastack dev` — local development against the native Rust server.
|
|
3
3
|
*
|
|
4
|
-
* `rastack dev`
|
|
5
|
-
* (the same
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* committed Iceberg warehouse, and the developer's own app. It does **not**
|
|
9
|
-
* serve the admin console — that is a separate command, `rastack admin`.
|
|
4
|
+
* `rastack dev` serves **your app** from Node and proxies `/api` to a native
|
|
5
|
+
* `rastack-server` process (the same `rastack-api-core` Lambda runs). The
|
|
6
|
+
* browser renders the UI. It does not execute the engine. `rastack admin` is
|
|
7
|
+
* the separate command that starts `rastack-server admin`.
|
|
10
8
|
*
|
|
11
9
|
* When the project has no app to serve, `dev` shows a Django-style welcome
|
|
12
10
|
* page (`renderWelcome`) so a fresh scaffold still boots to something friendly.
|
|
13
11
|
*
|
|
14
|
-
* Everything decidable without IO lives here (arg parsing, MIME types,
|
|
15
|
-
* the
|
|
16
|
-
* without a socket or a filesystem; `rastack-dev.ts` is the
|
|
17
|
-
*
|
|
12
|
+
* Everything decidable without IO lives here (arg parsing, MIME types, where
|
|
13
|
+
* the native binary is looked up, resolving the app root, the HTML pages) so
|
|
14
|
+
* it is unit-tested without a socket or a filesystem; `rastack-dev.ts` is the
|
|
15
|
+
* thin IO wrapper that compiles, starts the server, and serves.
|
|
18
16
|
*/
|
|
19
17
|
export interface DevArgs {
|
|
20
18
|
/** Where the TypeScript resource definitions live (compiled on start). */
|
|
21
19
|
resourcesDir: string;
|
|
22
20
|
/** Where `rastack compile` writes schema.rastack.json + openapi.json. */
|
|
23
21
|
outDir: string;
|
|
24
|
-
/** The
|
|
22
|
+
/** The on-disk Iceberg warehouse the native server opens. */
|
|
25
23
|
warehouseDir: string;
|
|
26
24
|
/**
|
|
27
25
|
* Explicit app root to serve at `/` (its `index.html`). Empty when unset —
|
|
@@ -71,21 +69,16 @@ export declare function parseAdminArgs(argv: string[]): AdminArgs;
|
|
|
71
69
|
/** MIME type for a served path, defaulting to octet-stream (Iceberg data files). */
|
|
72
70
|
export declare function contentType(filePath: string): string;
|
|
73
71
|
/**
|
|
74
|
-
* Ordered
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* cargo; the repo-local `wasm/` (from a source build) and a sibling of the
|
|
78
|
-
* running module are fallbacks for development. `dirnameDir` is the directory of
|
|
79
|
-
* the compiled `rastack-dev.js` (`dist/`), so `../wasm` and `./wasm` cover both
|
|
80
|
-
* `dist/rastack-dev.js` → `dist/wasm` and `dist/dev/…` layouts.
|
|
72
|
+
* Ordered paths for the native `rastack-server` binary inside a workspace
|
|
73
|
+
* checkout. Release wins over debug. `RASTACK_SERVER` and `PATH` are checked
|
|
74
|
+
* by the IO wrapper, not here.
|
|
81
75
|
*/
|
|
82
|
-
export declare function
|
|
83
|
-
/** The name of the file that marks a directory as a usable wasm bundle. */
|
|
84
|
-
export declare const WASM_ENTRY = "rastack_wasm.js";
|
|
76
|
+
export declare function serverBinaryCandidates(cwd: string): string[];
|
|
85
77
|
/**
|
|
86
78
|
* The HTML shell served at `/` by **`rastack admin`**. It loads the bundled
|
|
87
|
-
* React admin (`/dev/admin.js`)
|
|
88
|
-
*
|
|
79
|
+
* React admin (`/dev/admin.js`). `rastack admin` itself does not serve this
|
|
80
|
+
* page: the native `rastack-server admin` process serves `/admin/` and the
|
|
81
|
+
* bundle talks to that process over HTTP.
|
|
89
82
|
* Deliberately tiny — all logic lives in the bundle — mirroring the
|
|
90
83
|
* `rastack design` studio. `rastack dev` does **not** use this: it serves the
|
|
91
84
|
* developer's own app (or the welcome page below).
|
|
@@ -94,8 +87,7 @@ export declare function renderShell(title?: string): string;
|
|
|
94
87
|
/**
|
|
95
88
|
* The Django-style welcome page `rastack dev` serves at `/` when the project
|
|
96
89
|
* has **no app** to serve (no `index.html` in any `appRootCandidates` dir).
|
|
97
|
-
* It confirms the dev server is up, points at the
|
|
98
|
-
*
|
|
99
|
-
* "It worked!" moment of `django-admin runserver`, on brand for the stack.
|
|
90
|
+
* It confirms the dev server is up, points at the native API proxied at
|
|
91
|
+
* `/api`, and tells you how to add an app or open the admin.
|
|
100
92
|
*/
|
|
101
93
|
export declare function renderWelcome(title?: string): string;
|
package/dist/dev/harness.js
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Pure core of `rastack dev` — the
|
|
3
|
+
* Pure core of `rastack dev` — local development against the native Rust server.
|
|
4
4
|
*
|
|
5
|
-
* `rastack dev`
|
|
6
|
-
* (the same
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* committed Iceberg warehouse, and the developer's own app. It does **not**
|
|
10
|
-
* serve the admin console — that is a separate command, `rastack admin`.
|
|
5
|
+
* `rastack dev` serves **your app** from Node and proxies `/api` to a native
|
|
6
|
+
* `rastack-server` process (the same `rastack-api-core` Lambda runs). The
|
|
7
|
+
* browser renders the UI. It does not execute the engine. `rastack admin` is
|
|
8
|
+
* the separate command that starts `rastack-server admin`.
|
|
11
9
|
*
|
|
12
10
|
* When the project has no app to serve, `dev` shows a Django-style welcome
|
|
13
11
|
* page (`renderWelcome`) so a fresh scaffold still boots to something friendly.
|
|
14
12
|
*
|
|
15
|
-
* Everything decidable without IO lives here (arg parsing, MIME types,
|
|
16
|
-
* the
|
|
17
|
-
* without a socket or a filesystem; `rastack-dev.ts` is the
|
|
18
|
-
*
|
|
13
|
+
* Everything decidable without IO lives here (arg parsing, MIME types, where
|
|
14
|
+
* the native binary is looked up, resolving the app root, the HTML pages) so
|
|
15
|
+
* it is unit-tested without a socket or a filesystem; `rastack-dev.ts` is the
|
|
16
|
+
* thin IO wrapper that compiles, starts the server, and serves.
|
|
19
17
|
*/
|
|
20
18
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
19
|
if (k2 === undefined) k2 = k;
|
|
@@ -51,12 +49,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
51
49
|
};
|
|
52
50
|
})();
|
|
53
51
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
-
exports.
|
|
52
|
+
exports.ADMIN_DEFAULTS = exports.APP_ENTRY = exports.DEV_DEFAULTS = void 0;
|
|
55
53
|
exports.parseDevArgs = parseDevArgs;
|
|
56
54
|
exports.appRootCandidates = appRootCandidates;
|
|
57
55
|
exports.parseAdminArgs = parseAdminArgs;
|
|
58
56
|
exports.contentType = contentType;
|
|
59
|
-
exports.
|
|
57
|
+
exports.serverBinaryCandidates = serverBinaryCandidates;
|
|
60
58
|
exports.renderShell = renderShell;
|
|
61
59
|
exports.renderWelcome = renderWelcome;
|
|
62
60
|
const path = __importStar(require("path"));
|
|
@@ -157,7 +155,6 @@ const MIME = {
|
|
|
157
155
|
".mjs": "text/javascript; charset=utf-8",
|
|
158
156
|
".css": "text/css; charset=utf-8",
|
|
159
157
|
".json": "application/json; charset=utf-8",
|
|
160
|
-
// Critical: `WebAssembly.instantiateStreaming` requires this exact type.
|
|
161
158
|
".wasm": "application/wasm",
|
|
162
159
|
".text": "text/plain; charset=utf-8",
|
|
163
160
|
".parquet": "application/octet-stream",
|
|
@@ -168,27 +165,21 @@ function contentType(filePath) {
|
|
|
168
165
|
return MIME[ext] ?? "application/octet-stream";
|
|
169
166
|
}
|
|
170
167
|
/**
|
|
171
|
-
* Ordered
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* cargo; the repo-local `wasm/` (from a source build) and a sibling of the
|
|
175
|
-
* running module are fallbacks for development. `dirnameDir` is the directory of
|
|
176
|
-
* the compiled `rastack-dev.js` (`dist/`), so `../wasm` and `./wasm` cover both
|
|
177
|
-
* `dist/rastack-dev.js` → `dist/wasm` and `dist/dev/…` layouts.
|
|
168
|
+
* Ordered paths for the native `rastack-server` binary inside a workspace
|
|
169
|
+
* checkout. Release wins over debug. `RASTACK_SERVER` and `PATH` are checked
|
|
170
|
+
* by the IO wrapper, not here.
|
|
178
171
|
*/
|
|
179
|
-
function
|
|
172
|
+
function serverBinaryCandidates(cwd) {
|
|
180
173
|
return [
|
|
181
|
-
path.join(
|
|
182
|
-
path.join(
|
|
183
|
-
path.join(cwd, "wasm"),
|
|
174
|
+
path.join(cwd, "engine", "target", "release", "rastack-server"),
|
|
175
|
+
path.join(cwd, "engine", "target", "debug", "rastack-server"),
|
|
184
176
|
];
|
|
185
177
|
}
|
|
186
|
-
/** The name of the file that marks a directory as a usable wasm bundle. */
|
|
187
|
-
exports.WASM_ENTRY = "rastack_wasm.js";
|
|
188
178
|
/**
|
|
189
179
|
* The HTML shell served at `/` by **`rastack admin`**. It loads the bundled
|
|
190
|
-
* React admin (`/dev/admin.js`)
|
|
191
|
-
*
|
|
180
|
+
* React admin (`/dev/admin.js`). `rastack admin` itself does not serve this
|
|
181
|
+
* page: the native `rastack-server admin` process serves `/admin/` and the
|
|
182
|
+
* bundle talks to that process over HTTP.
|
|
192
183
|
* Deliberately tiny — all logic lives in the bundle — mirroring the
|
|
193
184
|
* `rastack design` studio. `rastack dev` does **not** use this: it serves the
|
|
194
185
|
* developer's own app (or the welcome page below).
|
|
@@ -233,9 +224,8 @@ function renderShell(title = "React API Stack — admin") {
|
|
|
233
224
|
/**
|
|
234
225
|
* The Django-style welcome page `rastack dev` serves at `/` when the project
|
|
235
226
|
* has **no app** to serve (no `index.html` in any `appRootCandidates` dir).
|
|
236
|
-
* It confirms the dev server is up, points at the
|
|
237
|
-
*
|
|
238
|
-
* "It worked!" moment of `django-admin runserver`, on brand for the stack.
|
|
227
|
+
* It confirms the dev server is up, points at the native API proxied at
|
|
228
|
+
* `/api`, and tells you how to add an app or open the admin.
|
|
239
229
|
*/
|
|
240
230
|
function renderWelcome(title = "React API Stack — dev") {
|
|
241
231
|
return `<!doctype html>
|
|
@@ -282,20 +272,21 @@ function renderWelcome(title = "React API Stack — dev") {
|
|
|
282
272
|
<div class="rocket">🚀</div>
|
|
283
273
|
<h1>The dev server is running.</h1>
|
|
284
274
|
<p class="lead">
|
|
285
|
-
Congratulations — <strong>rastack dev</strong> is up
|
|
286
|
-
|
|
287
|
-
app to serve yet.
|
|
275
|
+
Congratulations — <strong>rastack dev</strong> is up. The API is a native
|
|
276
|
+
Rust process (<code>rastack-server</code>) on this machine. This page is
|
|
277
|
+
only the UI. You're seeing it because there's no app to serve yet.
|
|
288
278
|
</p>
|
|
289
279
|
<ul>
|
|
290
280
|
<li>Add an <code>index.html</code> (in <code>public/</code>, <code>www/</code>,
|
|
291
281
|
<code>app/</code>, or the project root) and reload — <code>dev</code> will serve it.
|
|
292
282
|
Point it at a different folder with <code>rastack dev --app <dir></code>.</li>
|
|
293
|
-
<li>Wire your app
|
|
294
|
-
<code><RAStackProvider mode="local"></code
|
|
295
|
-
|
|
283
|
+
<li>Wire your app with
|
|
284
|
+
<code><RAStackProvider mode="local"></code>. Requests go to
|
|
285
|
+
<code>/api</code> on this origin, which is proxied to the native server.</li>
|
|
286
|
+
<li>Live files:
|
|
296
287
|
<a href="/schema.rastack.json">/schema.rastack.json</a>,
|
|
297
|
-
<a href="/openapi.json">/openapi.json</a
|
|
298
|
-
|
|
288
|
+
<a href="/openapi.json">/openapi.json</a>.
|
|
289
|
+
The warehouse stays on disk; the browser does not load it.</li>
|
|
299
290
|
<li>Want the database console instead? Run <code>rastack admin</code>.</li>
|
|
300
291
|
</ul>
|
|
301
292
|
<div class="foot">React API Stack — a full-stack framework on object storage.</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start the native `rastack-server` binary — the same `rastack-api-core`
|
|
3
|
+
* process Lambda runs. The browser never executes this binary. `rastack dev`
|
|
4
|
+
* proxies `/api` to it; `rastack admin` lets the binary serve `/admin` itself.
|
|
5
|
+
*/
|
|
6
|
+
import { type ChildProcess } from "child_process";
|
|
7
|
+
export interface NativeServerOpts {
|
|
8
|
+
/** Absolute path to the `rastack-server` binary. */
|
|
9
|
+
bin: string;
|
|
10
|
+
/** `serve` is the API. `admin` mounts the React console at `/admin`. */
|
|
11
|
+
command: "serve" | "admin";
|
|
12
|
+
manifest: string;
|
|
13
|
+
warehouse: string;
|
|
14
|
+
/** `host:port` the binary binds. */
|
|
15
|
+
addr: string;
|
|
16
|
+
/** Directory that contains `admin.js` (`RASTACK_ADMIN_ASSETS`). */
|
|
17
|
+
adminAssets?: string;
|
|
18
|
+
}
|
|
19
|
+
/** `RASTACK_SERVER`, a workspace target dir, or `rastack-server` on PATH. */
|
|
20
|
+
export declare function resolveServerBinary(cwd: string): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Locate the native binary, building it with cargo when this repo's workspace
|
|
23
|
+
* is present and the binary is not. Throws when nothing native can be started.
|
|
24
|
+
*/
|
|
25
|
+
export declare function ensureServerBinary(cwd: string): string;
|
|
26
|
+
/** Spawn `rastack-server serve|admin`. Stdio is inherited. */
|
|
27
|
+
export declare function startNativeServer(opts: NativeServerOpts): ChildProcess;
|
|
28
|
+
/** Resolve once the process accepts TCP, or reject if it exits or times out. */
|
|
29
|
+
export declare function waitForTcp(port: number, child: ChildProcess, timeoutMs?: number): Promise<void>;
|