@rebasepro/cli 0.10.1-canary.8650626 → 0.10.1-canary.8d27c46
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/dist/bundle.d.ts +53 -0
- package/dist/commands/cloud/bundle-deploy.d.ts +25 -0
- package/dist/fold-static.d.ts +46 -0
- package/dist/index.es.js +218 -5
- package/dist/index.es.js.map +1 -1
- package/package.json +7 -7
- package/templates/overlays/baas/backend/src/index.ts +7 -0
- package/templates/overlays/baas/backend/src/storage.ts +71 -0
- package/templates/template/backend/src/index.ts +7 -0
- package/templates/template/config/admin.d.ts +9 -0
- package/templates/template/config/collections/authors.ts +11 -9
- package/templates/template/config/collections/posts.ts +6 -4
- package/templates/template/config/collections/presets/ecommerce/categories.ts +7 -5
- package/templates/template/config/collections/presets/ecommerce/orders.ts +32 -20
- package/templates/template/config/collections/presets/ecommerce/products.ts +21 -13
- package/templates/template/config/collections/tags.ts +5 -3
- package/templates/template/config/collections/users.ts +34 -29
- package/templates/template/config/index.ts +6 -0
- package/templates/template/config/package.json +26 -25
- package/templates/template/config/storage.ts +88 -0
package/dist/bundle.d.ts
CHANGED
|
@@ -20,6 +20,24 @@ export interface BuildBundleResult {
|
|
|
20
20
|
manifest: RebaseBundleManifest;
|
|
21
21
|
collectionCount: number;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Whether the compiled config package exports a `storageAuthorize` hook.
|
|
25
|
+
*
|
|
26
|
+
* Recorded in the manifest so a host can refuse a deploy that would enable file
|
|
27
|
+
* storage with no access model, rather than let the runtime's boot guard turn it
|
|
28
|
+
* into a crash loop the developer cannot read.
|
|
29
|
+
*
|
|
30
|
+
* Read from the *compiled* index, deliberately: that is the exact module the
|
|
31
|
+
* runtime imports and reads the export off, so this cannot disagree with what
|
|
32
|
+
* actually happens at boot. It is a textual check rather than an import because
|
|
33
|
+
* a freshly built bundle cannot resolve its own dependencies until it is
|
|
34
|
+
* deployed — the same reason schema hashing reads source.
|
|
35
|
+
*
|
|
36
|
+
* Errs toward `false`: a missed detection costs a deploy rejection whose message
|
|
37
|
+
* says exactly how to proceed, while a false positive would hand back the crash
|
|
38
|
+
* loop this exists to prevent.
|
|
39
|
+
*/
|
|
40
|
+
export declare function detectStorageAuthorize(compiledConfigDir: string): boolean;
|
|
23
41
|
/**
|
|
24
42
|
* Detect native code in the dependency closure.
|
|
25
43
|
*
|
|
@@ -80,6 +98,41 @@ export declare function buildBundle(options: BuildBundleOptions): Promise<BuildB
|
|
|
80
98
|
* manifest — no compilation, no dependency closure (a static bundle installs
|
|
81
99
|
* nothing at boot).
|
|
82
100
|
*/
|
|
101
|
+
/**
|
|
102
|
+
* Fold a built static app into a backend bundle, so one runtime serves both.
|
|
103
|
+
*
|
|
104
|
+
* ## Why this exists
|
|
105
|
+
*
|
|
106
|
+
* A managed tenant runs one pod, and `bootFromBundle` on the backend path already
|
|
107
|
+
* knows how to serve a SPA — it looks for `entry.static` and mounts `serveSPA`
|
|
108
|
+
* last, behind `REBASE_SERVE_STATIC`. What was missing was anything putting the
|
|
109
|
+
* assets there.
|
|
110
|
+
*
|
|
111
|
+
* The consequence was not subtle. A project whose custom image served its website
|
|
112
|
+
* at `/` and its API at `/api` — the shape the scaffolded template produces — lost
|
|
113
|
+
* the website the moment it moved to the managed runtime: the API answered
|
|
114
|
+
* perfectly and every page 404'd. Managed could not be a drop-in replacement for
|
|
115
|
+
* custom while the frontend simply vanished.
|
|
116
|
+
*
|
|
117
|
+
* Folding restores parity with the container it replaces, which is the only
|
|
118
|
+
* honest baseline. It is deliberately the FIRST implementation and not the last:
|
|
119
|
+
* a static app on its own bucket behind a CDN is better for cache behaviour and
|
|
120
|
+
* lets the frontend deploy independently. But that needs infrastructure that does
|
|
121
|
+
* not exist yet, and "your site is gone" is not an acceptable state to leave a
|
|
122
|
+
* project in while it gets built.
|
|
123
|
+
*
|
|
124
|
+
* The trade it makes, stated plainly: frontend and backend now deploy together
|
|
125
|
+
* and the bundle carries the built assets. For a project that was shipping both
|
|
126
|
+
* in one image already, that is exactly what it had.
|
|
127
|
+
*/
|
|
128
|
+
export declare function foldStaticIntoBundle(options: {
|
|
129
|
+
/** The backend bundle directory, already written. */
|
|
130
|
+
bundleDir: string;
|
|
131
|
+
/** Directory of built frontend assets (the static app's `output`). */
|
|
132
|
+
assetsDir: string;
|
|
133
|
+
}): {
|
|
134
|
+
fileCount: number;
|
|
135
|
+
};
|
|
83
136
|
export declare function buildStaticBundle(options: {
|
|
84
137
|
projectRoot: string;
|
|
85
138
|
appName: string;
|
|
@@ -23,6 +23,31 @@ export declare function bundleDeployBody(input: {
|
|
|
23
23
|
manifest: RebaseBundleManifest;
|
|
24
24
|
app?: string;
|
|
25
25
|
message?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Every app this repository declares in `rebase.json`, so the platform can
|
|
28
|
+
* register the whole set rather than only the one being deployed.
|
|
29
|
+
*/
|
|
30
|
+
declaredApps?: DeclaredApp[];
|
|
26
31
|
}): Record<string, unknown>;
|
|
32
|
+
/** An app as `rebase.json` declares it, reduced to what the registry stores. */
|
|
33
|
+
export interface DeclaredApp {
|
|
34
|
+
name: string;
|
|
35
|
+
type: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The apps a project manifest declares.
|
|
39
|
+
*
|
|
40
|
+
* A deploy only ever ships ONE app's bundle, so the trigger alone could never
|
|
41
|
+
* tell the platform that the repository also contains a web frontend and an
|
|
42
|
+
* admin panel — and the Apps page, whose whole job is to show the set, listed a
|
|
43
|
+
* single entry called "backend". Sending the declared set fixes that without
|
|
44
|
+
* pretending the others are deployed: the platform registers them, and their
|
|
45
|
+
* status says what is actually true.
|
|
46
|
+
*/
|
|
47
|
+
export declare function declaredAppsFrom(manifest: {
|
|
48
|
+
apps?: Record<string, {
|
|
49
|
+
type?: string;
|
|
50
|
+
}>;
|
|
51
|
+
} | null | undefined): DeclaredApp[];
|
|
27
52
|
/** Upload a bundle archive; returns the control-plane bundle id. */
|
|
28
53
|
export declare function uploadBundle(url: string, token: string, projectId: string, tarPath: string): Promise<string>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** The apps section of a project manifest, as much of it as folding needs. */
|
|
2
|
+
export interface FoldableManifest {
|
|
3
|
+
apps?: Record<string, {
|
|
4
|
+
type?: string;
|
|
5
|
+
build?: string;
|
|
6
|
+
output?: string;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
export interface FoldOptions {
|
|
10
|
+
projectRoot: string;
|
|
11
|
+
manifest: FoldableManifest;
|
|
12
|
+
/** The backend bundle directory, already written. */
|
|
13
|
+
bundleDir: string;
|
|
14
|
+
/** Skip running the app's own build command; fold what is already built. */
|
|
15
|
+
skipBuild?: boolean;
|
|
16
|
+
log?: (message: string) => void;
|
|
17
|
+
}
|
|
18
|
+
export interface FoldOutcome {
|
|
19
|
+
appName: string;
|
|
20
|
+
fileCount: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Which static app, if any, should be served by the backend.
|
|
24
|
+
*
|
|
25
|
+
* Exactly one `static` app is folded. With several, folding would have to choose,
|
|
26
|
+
* and silently picking one of two websites is worse than doing nothing — so it
|
|
27
|
+
* declines and names what it saw. Pure, so the decision is testable without a
|
|
28
|
+
* filesystem.
|
|
29
|
+
*/
|
|
30
|
+
export declare function selectFoldableApp(manifest: FoldableManifest): {
|
|
31
|
+
app?: {
|
|
32
|
+
name: string;
|
|
33
|
+
build?: string;
|
|
34
|
+
output?: string;
|
|
35
|
+
};
|
|
36
|
+
/** Why nothing will be folded, when that is the answer. */
|
|
37
|
+
reason?: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Build the project's frontend and fold it into the backend bundle.
|
|
41
|
+
*
|
|
42
|
+
* Throws rather than exiting, so the caller decides whether a missing frontend
|
|
43
|
+
* should fail its command — a `build` may reasonably want to stop, and so should
|
|
44
|
+
* a deploy, but that is not this function's call to make.
|
|
45
|
+
*/
|
|
46
|
+
export declare function foldFrontendIntoBundle(options: FoldOptions): Promise<FoldOutcome | null>;
|
package/dist/index.es.js
CHANGED
|
@@ -2981,6 +2981,43 @@ async function writeBundleTsconfig(projectRoot, outDir, includes, skipTypeCheck)
|
|
|
2981
2981
|
return tsconfigPath;
|
|
2982
2982
|
}
|
|
2983
2983
|
/**
|
|
2984
|
+
* Whether the compiled config package exports a `storageAuthorize` hook.
|
|
2985
|
+
*
|
|
2986
|
+
* Recorded in the manifest so a host can refuse a deploy that would enable file
|
|
2987
|
+
* storage with no access model, rather than let the runtime's boot guard turn it
|
|
2988
|
+
* into a crash loop the developer cannot read.
|
|
2989
|
+
*
|
|
2990
|
+
* Read from the *compiled* index, deliberately: that is the exact module the
|
|
2991
|
+
* runtime imports and reads the export off, so this cannot disagree with what
|
|
2992
|
+
* actually happens at boot. It is a textual check rather than an import because
|
|
2993
|
+
* a freshly built bundle cannot resolve its own dependencies until it is
|
|
2994
|
+
* deployed — the same reason schema hashing reads source.
|
|
2995
|
+
*
|
|
2996
|
+
* Errs toward `false`: a missed detection costs a deploy rejection whose message
|
|
2997
|
+
* says exactly how to proceed, while a false positive would hand back the crash
|
|
2998
|
+
* loop this exists to prevent.
|
|
2999
|
+
*/
|
|
3000
|
+
function detectStorageAuthorize(compiledConfigDir) {
|
|
3001
|
+
const indexPath = [
|
|
3002
|
+
".js",
|
|
3003
|
+
".mjs",
|
|
3004
|
+
".ts"
|
|
3005
|
+
].map((ext) => path.join(compiledConfigDir, `index${ext}`)).find((candidate) => fs.existsSync(candidate));
|
|
3006
|
+
if (!indexPath) return false;
|
|
3007
|
+
let source;
|
|
3008
|
+
try {
|
|
3009
|
+
source = fs.readFileSync(indexPath, "utf8");
|
|
3010
|
+
} catch {
|
|
3011
|
+
return false;
|
|
3012
|
+
}
|
|
3013
|
+
if (/\bexport\s+(?:async\s+)?(?:const|let|var|function)\s+storageAuthorize\b/.test(source)) return true;
|
|
3014
|
+
for (const clause of source.matchAll(/\bexport\s*\{([^}]*)\}/g)) if (clause[1].split(",").map((entry) => {
|
|
3015
|
+
const parts = entry.split(/\bas\b/);
|
|
3016
|
+
return parts[parts.length - 1].trim();
|
|
3017
|
+
}).includes("storageAuthorize")) return true;
|
|
3018
|
+
return false;
|
|
3019
|
+
}
|
|
3020
|
+
/**
|
|
2984
3021
|
* Detect native code in the dependency closure.
|
|
2985
3022
|
*
|
|
2986
3023
|
* Walks declared runtime dependencies breadth-first through `node_modules`,
|
|
@@ -3297,6 +3334,7 @@ async function buildBundle(options) {
|
|
|
3297
3334
|
}
|
|
3298
3335
|
const declared = collectDeclaredDependencies(projectRoot);
|
|
3299
3336
|
const nativeModules = detectNativeDependencies(projectRoot, declared);
|
|
3337
|
+
const declaresStorageAuthorize = detectStorageAuthorize(path.join(outDir, paths.config));
|
|
3300
3338
|
const schemaOut = paths.schema.replace(/\.ts$/, ".js");
|
|
3301
3339
|
const relative = (target) => fs.existsSync(path.join(outDir, target)) ? target : void 0;
|
|
3302
3340
|
const manifest = {
|
|
@@ -3322,6 +3360,7 @@ async function buildBundle(options) {
|
|
|
3322
3360
|
native: nativeModules.length > 0,
|
|
3323
3361
|
nativeModules: nativeModules.length > 0 ? nativeModules : void 0
|
|
3324
3362
|
},
|
|
3363
|
+
storage: { authorize: declaresStorageAuthorize },
|
|
3325
3364
|
deps: { declared },
|
|
3326
3365
|
build: {
|
|
3327
3366
|
cli: resolveCliVersion(),
|
|
@@ -3356,6 +3395,59 @@ async function buildBundle(options) {
|
|
|
3356
3395
|
* manifest — no compilation, no dependency closure (a static bundle installs
|
|
3357
3396
|
* nothing at boot).
|
|
3358
3397
|
*/
|
|
3398
|
+
/**
|
|
3399
|
+
* Fold a built static app into a backend bundle, so one runtime serves both.
|
|
3400
|
+
*
|
|
3401
|
+
* ## Why this exists
|
|
3402
|
+
*
|
|
3403
|
+
* A managed tenant runs one pod, and `bootFromBundle` on the backend path already
|
|
3404
|
+
* knows how to serve a SPA — it looks for `entry.static` and mounts `serveSPA`
|
|
3405
|
+
* last, behind `REBASE_SERVE_STATIC`. What was missing was anything putting the
|
|
3406
|
+
* assets there.
|
|
3407
|
+
*
|
|
3408
|
+
* The consequence was not subtle. A project whose custom image served its website
|
|
3409
|
+
* at `/` and its API at `/api` — the shape the scaffolded template produces — lost
|
|
3410
|
+
* the website the moment it moved to the managed runtime: the API answered
|
|
3411
|
+
* perfectly and every page 404'd. Managed could not be a drop-in replacement for
|
|
3412
|
+
* custom while the frontend simply vanished.
|
|
3413
|
+
*
|
|
3414
|
+
* Folding restores parity with the container it replaces, which is the only
|
|
3415
|
+
* honest baseline. It is deliberately the FIRST implementation and not the last:
|
|
3416
|
+
* a static app on its own bucket behind a CDN is better for cache behaviour and
|
|
3417
|
+
* lets the frontend deploy independently. But that needs infrastructure that does
|
|
3418
|
+
* not exist yet, and "your site is gone" is not an acceptable state to leave a
|
|
3419
|
+
* project in while it gets built.
|
|
3420
|
+
*
|
|
3421
|
+
* The trade it makes, stated plainly: frontend and backend now deploy together
|
|
3422
|
+
* and the bundle carries the built assets. For a project that was shipping both
|
|
3423
|
+
* in one image already, that is exactly what it had.
|
|
3424
|
+
*/
|
|
3425
|
+
function foldStaticIntoBundle(options) {
|
|
3426
|
+
const { bundleDir, assetsDir } = options;
|
|
3427
|
+
const manifestPath = path.join(bundleDir, "manifest.json");
|
|
3428
|
+
if (!fs.existsSync(manifestPath)) throw new Error(`No manifest at ${manifestPath} — build the backend bundle first.`);
|
|
3429
|
+
if (!fs.existsSync(assetsDir)) throw new Error(`No built assets at ${assetsDir}.`);
|
|
3430
|
+
const staticOut = path.join(bundleDir, "static");
|
|
3431
|
+
fs.rmSync(staticOut, {
|
|
3432
|
+
recursive: true,
|
|
3433
|
+
force: true
|
|
3434
|
+
});
|
|
3435
|
+
fs.mkdirSync(staticOut, { recursive: true });
|
|
3436
|
+
fs.cpSync(assetsDir, staticOut, { recursive: true });
|
|
3437
|
+
let fileCount = 0;
|
|
3438
|
+
const count = (dir) => {
|
|
3439
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) if (entry.isDirectory()) count(path.join(dir, entry.name));
|
|
3440
|
+
else fileCount++;
|
|
3441
|
+
};
|
|
3442
|
+
count(staticOut);
|
|
3443
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
3444
|
+
manifest.entry = {
|
|
3445
|
+
...manifest.entry,
|
|
3446
|
+
static: "static"
|
|
3447
|
+
};
|
|
3448
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
3449
|
+
return { fileCount };
|
|
3450
|
+
}
|
|
3359
3451
|
function buildStaticBundle(options) {
|
|
3360
3452
|
const { projectRoot, appName, assetsDir, outDir, runtimeRange } = options;
|
|
3361
3453
|
cleanOutDir(projectRoot, outDir);
|
|
@@ -3478,6 +3570,76 @@ function resolveCliVersion() {
|
|
|
3478
3570
|
return "unknown";
|
|
3479
3571
|
}
|
|
3480
3572
|
//#endregion
|
|
3573
|
+
//#region src/fold-static.ts
|
|
3574
|
+
/**
|
|
3575
|
+
* Folding a project's frontend into its backend bundle.
|
|
3576
|
+
*
|
|
3577
|
+
* Shared by `rebase build` and `rebase cloud deploy` deliberately. It lived in
|
|
3578
|
+
* the build *command* first, and `deploy` rebuilds the bundle itself — so a
|
|
3579
|
+
* deploy silently produced a bundle without the frontend, packed 164 KB where
|
|
3580
|
+
* 39 MB was expected, and the site 404'd on the managed runtime exactly as if
|
|
3581
|
+
* folding had never been written. Two callers building the same artefact must
|
|
3582
|
+
* share the step that completes it.
|
|
3583
|
+
*
|
|
3584
|
+
* Why fold at all: `bootFromBundle` already serves a SPA from `entry.static`
|
|
3585
|
+
* behind `REBASE_SERVE_STATIC` (default on). A managed tenant runs one pod, so
|
|
3586
|
+
* putting the built site in the bundle gives it the shape a custom container
|
|
3587
|
+
* already had — site at `/`, API at `/api` — which is the only honest baseline
|
|
3588
|
+
* for calling the managed runtime a drop-in replacement.
|
|
3589
|
+
*/
|
|
3590
|
+
/**
|
|
3591
|
+
* Which static app, if any, should be served by the backend.
|
|
3592
|
+
*
|
|
3593
|
+
* Exactly one `static` app is folded. With several, folding would have to choose,
|
|
3594
|
+
* and silently picking one of two websites is worse than doing nothing — so it
|
|
3595
|
+
* declines and names what it saw. Pure, so the decision is testable without a
|
|
3596
|
+
* filesystem.
|
|
3597
|
+
*/
|
|
3598
|
+
function selectFoldableApp(manifest) {
|
|
3599
|
+
const statics = Object.entries(manifest.apps ?? {}).filter(([, app]) => app?.type === "static").map(([name, app]) => ({
|
|
3600
|
+
name,
|
|
3601
|
+
build: app?.build,
|
|
3602
|
+
output: app?.output
|
|
3603
|
+
}));
|
|
3604
|
+
if (statics.length === 0) return {};
|
|
3605
|
+
if (statics.length > 1) return { reason: `${statics.length} static apps (${statics.map((s) => s.name).join(", ")}) — none folded in. Pick one to serve from the backend, or host them separately.` };
|
|
3606
|
+
const only = statics[0];
|
|
3607
|
+
if (!only.output) return { reason: `"${only.name}" declares no output directory — not folded in.` };
|
|
3608
|
+
return { app: only };
|
|
3609
|
+
}
|
|
3610
|
+
/**
|
|
3611
|
+
* Build the project's frontend and fold it into the backend bundle.
|
|
3612
|
+
*
|
|
3613
|
+
* Throws rather than exiting, so the caller decides whether a missing frontend
|
|
3614
|
+
* should fail its command — a `build` may reasonably want to stop, and so should
|
|
3615
|
+
* a deploy, but that is not this function's call to make.
|
|
3616
|
+
*/
|
|
3617
|
+
async function foldFrontendIntoBundle(options) {
|
|
3618
|
+
const { projectRoot, manifest, bundleDir, skipBuild } = options;
|
|
3619
|
+
const log = options.log ?? ((m) => console.log(m));
|
|
3620
|
+
const { app, reason } = selectFoldableApp(manifest);
|
|
3621
|
+
if (reason) {
|
|
3622
|
+
log(chalk.yellow(` ⚠ ${reason}`));
|
|
3623
|
+
return null;
|
|
3624
|
+
}
|
|
3625
|
+
if (!app) return null;
|
|
3626
|
+
if (app.build && !skipBuild) await execa(app.build, {
|
|
3627
|
+
cwd: projectRoot,
|
|
3628
|
+
stdio: "inherit",
|
|
3629
|
+
shell: true
|
|
3630
|
+
});
|
|
3631
|
+
const assetsDir = path.join(projectRoot, app.output);
|
|
3632
|
+
if (!fs.existsSync(assetsDir)) throw new Error(`"${app.name}" declared output "${app.output}" does not exist after building — the bundle would ship without a frontend.`);
|
|
3633
|
+
const { fileCount } = foldStaticIntoBundle({
|
|
3634
|
+
bundleDir,
|
|
3635
|
+
assetsDir
|
|
3636
|
+
});
|
|
3637
|
+
return {
|
|
3638
|
+
appName: app.name,
|
|
3639
|
+
fileCount
|
|
3640
|
+
};
|
|
3641
|
+
}
|
|
3642
|
+
//#endregion
|
|
3481
3643
|
//#region src/commands/build.ts
|
|
3482
3644
|
/**
|
|
3483
3645
|
* CLI command: rebase build [app...]
|
|
@@ -3518,6 +3680,8 @@ async function buildCommand(rawArgs = []) {
|
|
|
3518
3680
|
"--out": String,
|
|
3519
3681
|
"--skip-type-check": Boolean,
|
|
3520
3682
|
"--skip-schema": Boolean,
|
|
3683
|
+
"--no-static": Boolean,
|
|
3684
|
+
"--skip-static-build": Boolean,
|
|
3521
3685
|
"--legacy": Boolean,
|
|
3522
3686
|
"--help": Boolean,
|
|
3523
3687
|
"-h": "--help"
|
|
@@ -3588,6 +3752,19 @@ async function buildCommand(rawArgs = []) {
|
|
|
3588
3752
|
console.log(chalk.yellow(` ⚠ native dependencies detected: ${names}`));
|
|
3589
3753
|
console.log(chalk.dim(" These cannot run on the managed runtime. See `rebase doctor`."));
|
|
3590
3754
|
}
|
|
3755
|
+
if (!args["--no-static"]) {
|
|
3756
|
+
const folded = await foldFrontendIntoBundle({
|
|
3757
|
+
projectRoot,
|
|
3758
|
+
manifest,
|
|
3759
|
+
bundleDir: result.outDir,
|
|
3760
|
+
skipBuild: args["--skip-static-build"] === true,
|
|
3761
|
+
log: (m) => console.log(m)
|
|
3762
|
+
}).catch((err) => {
|
|
3763
|
+
console.error(chalk.red(` ✗ ${err instanceof Error ? err.message : String(err)}`));
|
|
3764
|
+
process.exit(1);
|
|
3765
|
+
});
|
|
3766
|
+
if (folded) console.log(chalk.green(` ✓ ${folded.appName} folded in`) + chalk.dim(` (${folded.fileCount} file(s) → served at /)`));
|
|
3767
|
+
}
|
|
3591
3768
|
} else if (app.type === "static" || app.type === "admin") await buildAssetApp(projectRoot, name, app, manifest.runtime, args["--out"]);
|
|
3592
3769
|
else if (app.type === "custom") console.log(chalk.dim(" custom container — built at deploy time from its Dockerfile"));
|
|
3593
3770
|
console.log("");
|
|
@@ -4732,7 +4909,7 @@ async function linkCommand(rawArgs) {
|
|
|
4732
4909
|
})).data;
|
|
4733
4910
|
if (projects.length === 0) fail("No projects found for your account.", `Create one with ${chalk.bold("rebase cloud projects create")}.`);
|
|
4734
4911
|
const { picked } = await inquirer.prompt([{
|
|
4735
|
-
type: "
|
|
4912
|
+
type: "select",
|
|
4736
4913
|
name: "picked",
|
|
4737
4914
|
message: "Select a project to link:",
|
|
4738
4915
|
choices: projects.map((p) => ({
|
|
@@ -4776,7 +4953,7 @@ async function selectOrgCommand(rawArgs) {
|
|
|
4776
4953
|
let chosen = target ? orgs.find((o) => String(o.id) === target || o.slug === target) : void 0;
|
|
4777
4954
|
if (!chosen && !target) {
|
|
4778
4955
|
const { picked } = await inquirer.prompt([{
|
|
4779
|
-
type: "
|
|
4956
|
+
type: "select",
|
|
4780
4957
|
name: "picked",
|
|
4781
4958
|
message: "Select the active organization:",
|
|
4782
4959
|
choices: orgs.map((o) => ({
|
|
@@ -5053,6 +5230,7 @@ function packBundle(bundleDir, outPath) {
|
|
|
5053
5230
|
const child = spawn("tar", [
|
|
5054
5231
|
"-czf",
|
|
5055
5232
|
outPath,
|
|
5233
|
+
"--no-xattrs",
|
|
5056
5234
|
"--exclude",
|
|
5057
5235
|
"node_modules",
|
|
5058
5236
|
"-C",
|
|
@@ -5084,9 +5262,28 @@ function bundleDeployBody(input) {
|
|
|
5084
5262
|
app: input.app ?? input.manifest.app ?? "backend",
|
|
5085
5263
|
client: "cli",
|
|
5086
5264
|
frameworkVersion: input.manifest.runtime?.builtAgainst,
|
|
5265
|
+
...input.declaredApps?.length ? { declaredApps: input.declaredApps } : {},
|
|
5087
5266
|
...input.message ? { message: input.message } : {}
|
|
5088
5267
|
};
|
|
5089
5268
|
}
|
|
5269
|
+
/**
|
|
5270
|
+
* The apps a project manifest declares.
|
|
5271
|
+
*
|
|
5272
|
+
* A deploy only ever ships ONE app's bundle, so the trigger alone could never
|
|
5273
|
+
* tell the platform that the repository also contains a web frontend and an
|
|
5274
|
+
* admin panel — and the Apps page, whose whole job is to show the set, listed a
|
|
5275
|
+
* single entry called "backend". Sending the declared set fixes that without
|
|
5276
|
+
* pretending the others are deployed: the platform registers them, and their
|
|
5277
|
+
* status says what is actually true.
|
|
5278
|
+
*/
|
|
5279
|
+
function declaredAppsFrom(manifest) {
|
|
5280
|
+
const apps = manifest?.apps;
|
|
5281
|
+
if (!apps || typeof apps !== "object") return [];
|
|
5282
|
+
return Object.entries(apps).filter(([name]) => name.trim().length > 0).map(([name, value]) => ({
|
|
5283
|
+
name,
|
|
5284
|
+
type: String(value?.type ?? "custom")
|
|
5285
|
+
}));
|
|
5286
|
+
}
|
|
5090
5287
|
/** Upload a bundle archive; returns the control-plane bundle id. */
|
|
5091
5288
|
async function uploadBundle(url, token, projectId, tarPath) {
|
|
5092
5289
|
const bytes = fs.readFileSync(tarPath);
|
|
@@ -5238,6 +5435,17 @@ async function deployBundle(opts) {
|
|
|
5238
5435
|
runtimeRange: loaded.manifest.runtime,
|
|
5239
5436
|
log: (m) => console.log(chalk.gray(m))
|
|
5240
5437
|
})).outDir;
|
|
5438
|
+
try {
|
|
5439
|
+
const folded = await foldFrontendIntoBundle({
|
|
5440
|
+
projectRoot,
|
|
5441
|
+
manifest: loaded.manifest,
|
|
5442
|
+
bundleDir,
|
|
5443
|
+
log: (m) => console.log(m)
|
|
5444
|
+
});
|
|
5445
|
+
if (folded) console.log(chalk.gray(` folded ${folded.appName} in (${folded.fileCount} file(s), served at /)`));
|
|
5446
|
+
} catch (err) {
|
|
5447
|
+
fail(err instanceof Error ? err.message : String(err), "Fix the frontend build, or pass --no-static to deploy the API alone.");
|
|
5448
|
+
}
|
|
5241
5449
|
}
|
|
5242
5450
|
const manifest = readBundleManifest(bundleDir);
|
|
5243
5451
|
if (manifest.hooks?.native) {
|
|
@@ -5261,11 +5469,16 @@ async function deployBundle(opts) {
|
|
|
5261
5469
|
}
|
|
5262
5470
|
console.log("");
|
|
5263
5471
|
console.log(` 🚀 Triggering managed deployment for ${chalk.bold(projectRef)} (schema ${manifest.schemaVersion})...`);
|
|
5472
|
+
let declaredApps = [];
|
|
5473
|
+
try {
|
|
5474
|
+
declaredApps = declaredAppsFrom(loadManifest(process.cwd()).manifest);
|
|
5475
|
+
} catch {}
|
|
5264
5476
|
const body = bundleDeployBody({
|
|
5265
5477
|
projectId,
|
|
5266
5478
|
bundleId,
|
|
5267
5479
|
manifest,
|
|
5268
|
-
message: opts.message
|
|
5480
|
+
message: opts.message,
|
|
5481
|
+
declaredApps
|
|
5269
5482
|
});
|
|
5270
5483
|
try {
|
|
5271
5484
|
const res = await client.functions.invoke("deploy", body);
|
|
@@ -5688,7 +5901,7 @@ async function createDatabase(rawArgs) {
|
|
|
5688
5901
|
let type = args["--type"];
|
|
5689
5902
|
if (!type) {
|
|
5690
5903
|
const { picked } = await inquirer.prompt([{
|
|
5691
|
-
type: "
|
|
5904
|
+
type: "select",
|
|
5692
5905
|
name: "picked",
|
|
5693
5906
|
message: "Database type:",
|
|
5694
5907
|
choices: [{
|
|
@@ -8926,6 +9139,6 @@ ${chalk.gray("Documentation: https://rebase.pro/docs")}
|
|
|
8926
9139
|
`);
|
|
8927
9140
|
}
|
|
8928
9141
|
//#endregion
|
|
8929
|
-
export { CURRENT_RUNTIME_RANGE, DEFAULT_BUNDLE_DIR, DEFAULT_CONFIG_DIR, DEFAULT_CRONS_DIR, DEFAULT_FUNCTIONS_DIR, DEFAULT_SCHEMA_FILE, MANIFEST_FILENAME, ManifestError, appsCommand, assessManagedCompatibility, authCommand, buildBundle, buildCommand, buildInitQuestions, buildStaticBundle, buildableApps, cloudCommand, collectDeclaredDependencies, configureEnvFile, createRebaseApp, dbCommand, detectNativeDependencies, detectPackageManager, devCommand, doctorCommand, entry, findBackendApp, findBackendDir, findEnvFile, findFrontendDir, findProjectRoot, formatCdTarget, generateSdkCommand, getActiveBackendPlugin, getPMCommands, isPnpmAvailable, loadManifest, manifestExists, manifestPath, normalizeEsmSpecifiers, pnpmAvailabilityFromProbe, printInitHelp, requireBackendDir, requireProjectRoot, resetPnpmAvailabilityCache, resolveBackendPaths, resolveLocalBin, resolvePluginCliScript, resolveTsx, schemaCommand, startCommand, synthesizeManifest, validateManifest, validateProjectName, validateTsxInstallation, writeManifest };
|
|
9142
|
+
export { CURRENT_RUNTIME_RANGE, DEFAULT_BUNDLE_DIR, DEFAULT_CONFIG_DIR, DEFAULT_CRONS_DIR, DEFAULT_FUNCTIONS_DIR, DEFAULT_SCHEMA_FILE, MANIFEST_FILENAME, ManifestError, appsCommand, assessManagedCompatibility, authCommand, buildBundle, buildCommand, buildInitQuestions, buildStaticBundle, buildableApps, cloudCommand, collectDeclaredDependencies, configureEnvFile, createRebaseApp, dbCommand, detectNativeDependencies, detectPackageManager, detectStorageAuthorize, devCommand, doctorCommand, entry, findBackendApp, findBackendDir, findEnvFile, findFrontendDir, findProjectRoot, foldStaticIntoBundle, formatCdTarget, generateSdkCommand, getActiveBackendPlugin, getPMCommands, isPnpmAvailable, loadManifest, manifestExists, manifestPath, normalizeEsmSpecifiers, pnpmAvailabilityFromProbe, printInitHelp, requireBackendDir, requireProjectRoot, resetPnpmAvailabilityCache, resolveBackendPaths, resolveLocalBin, resolvePluginCliScript, resolveTsx, schemaCommand, startCommand, synthesizeManifest, validateManifest, validateProjectName, validateTsxInstallation, writeManifest };
|
|
8930
9143
|
|
|
8931
9144
|
//# sourceMappingURL=index.es.js.map
|