@rebasepro/cli 0.23.0 → 0.24.0
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/README.md +19 -3
- package/bin/rebase.js +1 -1
- package/dist/{branch-pointer-dBiClJ0p.js → branch-pointer-kk9F5FBO.js} +2 -2
- package/dist/{branch-pointer-dBiClJ0p.js.map → branch-pointer-kk9F5FBO.js.map} +1 -1
- package/dist/bundle.d.ts +6 -2
- package/dist/cli.d.ts +21 -0
- package/dist/commands/__test_support__/parser-specs.d.ts +13 -0
- package/dist/commands/agent-setup.d.ts +116 -0
- package/dist/commands/api-keys.d.ts +84 -23
- package/dist/commands/apps.d.ts +17 -0
- package/dist/commands/auth.d.ts +7 -0
- package/dist/commands/build.d.ts +5 -1
- package/dist/commands/cloud/auth.d.ts +4 -3
- package/dist/commands/cloud/bundle-deploy.d.ts +45 -0
- package/dist/commands/cloud/context.d.ts +44 -5
- package/dist/commands/cloud/cron.d.ts +53 -0
- package/dist/commands/cloud/debug.d.ts +134 -0
- package/dist/commands/cloud/index.d.ts +89 -12
- package/dist/commands/cloud/token-capabilities.d.ts +76 -0
- package/dist/commands/cloud/tokens.d.ts +23 -0
- package/dist/commands/db.d.ts +15 -1
- package/dist/commands/dev.d.ts +78 -7
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/generate_sdk.d.ts +3 -2
- package/dist/commands/init.d.ts +57 -14
- package/dist/commands/schema.d.ts +6 -0
- package/dist/commands/skills.d.ts +109 -5
- package/dist/commands/start.d.ts +29 -0
- package/dist/{daemon-DR9Tk0La.js → daemon-C8p32i-n.js} +5 -3
- package/dist/daemon-C8p32i-n.js.map +1 -0
- package/dist/dev-db/daemon.d.ts +10 -0
- package/dist/dev-db/prepare.d.ts +10 -0
- package/dist/fold-static.d.ts +26 -3
- package/dist/index.es.js +12086 -9459
- package/dist/index.es.js.map +1 -1
- package/dist/manifest.d.ts +41 -1
- package/dist/package-manager-CXQevsE1.js +193 -0
- package/dist/package-manager-CXQevsE1.js.map +1 -0
- package/dist/{project-3WCbrrfW.js → project-DmaGLmH2.js} +4 -183
- package/dist/project-DmaGLmH2.js.map +1 -0
- package/dist/upgrade.d.ts +14 -2
- package/dist/utils/path-flags.d.ts +18 -0
- package/dist/version-DsgBmP22.js +58 -0
- package/dist/version-DsgBmP22.js.map +1 -0
- package/package.json +7 -7
- package/templates/eject/backend/src/index.ts +14 -1
- package/templates/overlays/baas/ai-instructions.md +1 -1
- package/templates/overlays/baas/config/storage.ts +5 -3
- package/templates/template/.cursorrules +1 -1
- package/templates/template/.env.example +10 -6
- package/templates/template/.github/copilot-instructions.md +1 -1
- package/templates/template/.mcp.json +2 -2
- package/templates/template/.windsurfrules +1 -1
- package/templates/template/AGENTS.md +1 -1
- package/templates/template/CLAUDE.md +1 -1
- package/templates/template/README.md +10 -11
- package/templates/template/ai-instructions.md +3 -3
- package/templates/template/backend/functions/hello.ts +8 -0
- package/templates/template/backend/package.json +1 -1
- package/templates/template/config/storage.ts +5 -3
- package/templates/template/docker-compose.yml +6 -6
- package/templates/template/gitignore +6 -1
- package/templates/template/package.json +4 -3
- package/templates/template/scripts/example.ts +4 -3
- package/dist/daemon-DR9Tk0La.js.map +0 -1
- package/dist/project-3WCbrrfW.js.map +0 -1
package/dist/commands/init.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PackageManager, PMCommands } from "../utils/package-manager.js";
|
|
2
|
+
import { type AgentKey } from "./skills.js";
|
|
2
3
|
/**
|
|
3
4
|
* Every scaffolded file that carries a `{{PLACEHOLDER}}`.
|
|
4
5
|
*
|
|
@@ -42,6 +43,10 @@ export interface InitOptions {
|
|
|
42
43
|
setupKey?: string;
|
|
43
44
|
/** Control-plane URL the setup key is redeemed against. */
|
|
44
45
|
cloudUrl?: string;
|
|
46
|
+
/** AI coding agents to install skills and the MCP server for. */
|
|
47
|
+
agents: AgentKey[];
|
|
48
|
+
/** Whoever reads the closing steps can answer a prompt (a TTY, not --yes). */
|
|
49
|
+
interactive?: boolean;
|
|
45
50
|
}
|
|
46
51
|
export interface BuildQuestionsParams {
|
|
47
52
|
nameArg?: string;
|
|
@@ -105,21 +110,59 @@ export declare function printInitHelp(): void;
|
|
|
105
110
|
export declare function createRebaseApp(rawArgs: string[]): Promise<void>;
|
|
106
111
|
/** The flags `rebase init` takes. */
|
|
107
112
|
export declare const INIT_FLAGS: {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
113
|
+
"--git": BooleanConstructor;
|
|
114
|
+
"--install": BooleanConstructor;
|
|
115
|
+
"--no-install": BooleanConstructor;
|
|
116
|
+
"--database-url": StringConstructor;
|
|
117
|
+
"--introspect": BooleanConstructor;
|
|
118
|
+
"--template": StringConstructor;
|
|
119
|
+
"--headless": BooleanConstructor;
|
|
120
|
+
"--project": StringConstructor;
|
|
121
|
+
"--setup-key": StringConstructor;
|
|
122
|
+
"--agent": [StringConstructor];
|
|
123
|
+
"--yes": BooleanConstructor;
|
|
124
|
+
"-a": string;
|
|
125
|
+
"-g": string;
|
|
126
|
+
"-i": string;
|
|
127
|
+
"-t": string;
|
|
128
|
+
"-y": string;
|
|
122
129
|
};
|
|
130
|
+
/**
|
|
131
|
+
* The closing "AI Agent Skills" lines, each command one its reader can run.
|
|
132
|
+
*
|
|
133
|
+
* `rebase skills install` with no `--agent` prompts, and refuses without a
|
|
134
|
+
* TTY — so printing it bare at the end of `rebase init --yes`, the agent and
|
|
135
|
+
* CI path, told that reader to run a command that answers "Cannot prompt".
|
|
136
|
+
* Named agents are spelled out (a refresh must not depend on detection, which
|
|
137
|
+
* never finds Copilot); otherwise a TTY reader gets the prompt, anyone else
|
|
138
|
+
* the `--agent` form.
|
|
139
|
+
*/
|
|
140
|
+
export declare function agentNextSteps(agents: readonly AgentKey[], interactive: boolean, pm: PMCommands): string[];
|
|
141
|
+
/**
|
|
142
|
+
* What a directory may already hold for `rebase init` to scaffold into it.
|
|
143
|
+
*
|
|
144
|
+
* `git clone` of an empty repository, or `git init`, leaves exactly `.git` —
|
|
145
|
+
* and `rebase init . --yes --git` is the help's own example. Counting every
|
|
146
|
+
* entry as "not empty" refused the one directory that example is most likely
|
|
147
|
+
* to be typed in. The entries here are a repository's metadata, the files
|
|
148
|
+
* operating systems drop into any folder, and a licence: none of them is
|
|
149
|
+
* something the scaffold writes (`init.test.ts` holds the template to that),
|
|
150
|
+
* so tolerating them can never overwrite anything. Everything else is a
|
|
151
|
+
* conflict and is named, never merged into.
|
|
152
|
+
*/
|
|
153
|
+
export declare const SCAFFOLD_TOLERATED_ENTRIES: ReadonlySet<string>;
|
|
154
|
+
/** Entries in `dir` the scaffold would overwrite or mix with — `[]` when it may proceed. */
|
|
155
|
+
export declare function scaffoldDirectoryConflicts(dir: string): string[];
|
|
156
|
+
/** The refusal for a directory that already holds files, with the two ways forward. */
|
|
157
|
+
export declare function scaffoldConflictMessage(projectName: string, displayPath: string, conflicts: string[]): string[];
|
|
158
|
+
/**
|
|
159
|
+
* `git init`, or nothing when the directory is already a repository.
|
|
160
|
+
*
|
|
161
|
+
* A cloned repository keeps its branch, its remote and its history: the
|
|
162
|
+
* scaffold is committed on top of what is there. Only a repository this
|
|
163
|
+
* function created is pointed at `main`.
|
|
164
|
+
*/
|
|
165
|
+
export declare function initGitRepository(targetDirectory: string): Promise<boolean>;
|
|
123
166
|
/**
|
|
124
167
|
* Whether `port` is free — on the wildcard address *and* on both loopback addresses.
|
|
125
168
|
*
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The line handed to the driver, its paths resolved from where the command was
|
|
3
|
+
* run (the driver runs in `backend/`). `--schema` is left alone: on
|
|
4
|
+
* `introspect` it names a Postgres schema, not a file.
|
|
5
|
+
*/
|
|
6
|
+
export declare function schemaDriverArgs(rawArgs: string[], cwd: string): string[];
|
|
1
7
|
export declare function schemaCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void>;
|
|
2
8
|
/** Every subcommand the family dispatches — the help table's keys. */
|
|
3
9
|
export declare const SCHEMA_SUBCOMMANDS: string[];
|
|
@@ -18,14 +18,32 @@
|
|
|
18
18
|
* A subdirectory layout has no such problem: the rule file already sits beside
|
|
19
19
|
* its own assets, so `references/x.md` resolves as written and nothing is
|
|
20
20
|
* loaded until it is opened.
|
|
21
|
+
*
|
|
22
|
+
* `homeMarkers` are the directories under `~` that say the assistant is
|
|
23
|
+
* installed on this machine. They answer a different question from
|
|
24
|
+
* `detectDir`: a fresh scaffold has no `.claude/` of its own, so `rebase init`
|
|
25
|
+
* asks the machine which assistants the developer has and pre-selects those.
|
|
26
|
+
*
|
|
27
|
+
* `mcp` is where the assistant reads a *project-level* MCP server list, and in
|
|
28
|
+
* which shape — see `agent-setup.ts`. `projectDir` is what `REBASE_PROJECT_DIR`
|
|
29
|
+
* is set to: `"."` where the server is spawned in the project, the editor's own
|
|
30
|
+
* workspace variable where it may not be. `null` means there is no project
|
|
31
|
+
* file to write: Windsurf reads only a user-level config, and one entry there
|
|
32
|
+
* cannot name every project on the machine.
|
|
21
33
|
*/
|
|
22
|
-
declare const AGENTS: {
|
|
34
|
+
export declare const AGENTS: {
|
|
23
35
|
readonly cursor: {
|
|
24
36
|
readonly label: "Cursor";
|
|
25
37
|
readonly detectDir: ".cursor";
|
|
38
|
+
readonly homeMarkers: readonly [".cursor"];
|
|
26
39
|
readonly targetDir: ".cursor/rules";
|
|
27
40
|
readonly flatLayout: true;
|
|
28
41
|
readonly indexFile: "rebase.mdc";
|
|
42
|
+
readonly mcp: {
|
|
43
|
+
readonly file: ".cursor/mcp.json";
|
|
44
|
+
readonly format: "mcpServers";
|
|
45
|
+
readonly projectDir: "${workspaceFolder}";
|
|
46
|
+
};
|
|
29
47
|
/** The body is not a rule file here — `indexFile` is. */
|
|
30
48
|
readonly transformFile: (skillName: string, content: string) => {
|
|
31
49
|
fileName: string;
|
|
@@ -35,8 +53,14 @@ declare const AGENTS: {
|
|
|
35
53
|
readonly claude: {
|
|
36
54
|
readonly label: "Claude Code";
|
|
37
55
|
readonly detectDir: ".claude";
|
|
56
|
+
readonly homeMarkers: readonly [".claude"];
|
|
38
57
|
readonly targetDir: ".claude/skills";
|
|
39
58
|
readonly flatLayout: false;
|
|
59
|
+
readonly mcp: {
|
|
60
|
+
readonly file: ".mcp.json";
|
|
61
|
+
readonly format: "mcpServers";
|
|
62
|
+
readonly projectDir: ".";
|
|
63
|
+
};
|
|
40
64
|
/** Claude Code uses the standard SKILL.md format in subdirectories. */
|
|
41
65
|
readonly transformFile: (skillName: string, content: string) => {
|
|
42
66
|
fileName: string;
|
|
@@ -46,9 +70,11 @@ declare const AGENTS: {
|
|
|
46
70
|
readonly windsurf: {
|
|
47
71
|
readonly label: "Windsurf";
|
|
48
72
|
readonly detectDir: ".windsurf";
|
|
73
|
+
readonly homeMarkers: readonly [".codeium/windsurf", ".windsurf"];
|
|
49
74
|
readonly targetDir: ".windsurf/rules";
|
|
50
75
|
readonly flatLayout: true;
|
|
51
76
|
readonly indexFile: "rebase.md";
|
|
77
|
+
readonly mcp: null;
|
|
52
78
|
/** The body is not a rule file here — `indexFile` is. */
|
|
53
79
|
readonly transformFile: (skillName: string, content: string) => {
|
|
54
80
|
fileName: string;
|
|
@@ -58,8 +84,14 @@ declare const AGENTS: {
|
|
|
58
84
|
readonly gemini: {
|
|
59
85
|
readonly label: "Gemini CLI / Antigravity";
|
|
60
86
|
readonly detectDir: ".agents";
|
|
87
|
+
readonly homeMarkers: readonly [".gemini"];
|
|
61
88
|
readonly targetDir: ".agents/skills";
|
|
62
89
|
readonly flatLayout: false;
|
|
90
|
+
readonly mcp: {
|
|
91
|
+
readonly file: ".gemini/settings.json";
|
|
92
|
+
readonly format: "mcpServers";
|
|
93
|
+
readonly projectDir: ".";
|
|
94
|
+
};
|
|
63
95
|
/** Gemini uses the standard SKILL.md format in subdirectories. */
|
|
64
96
|
readonly transformFile: (skillName: string, content: string) => {
|
|
65
97
|
fileName: string;
|
|
@@ -69,9 +101,15 @@ declare const AGENTS: {
|
|
|
69
101
|
readonly codex: {
|
|
70
102
|
readonly label: "Codex CLI";
|
|
71
103
|
readonly detectDir: ".codex";
|
|
72
|
-
readonly
|
|
104
|
+
readonly homeMarkers: readonly [".codex"];
|
|
105
|
+
readonly targetDir: ".agents/skills";
|
|
73
106
|
readonly flatLayout: false;
|
|
74
|
-
|
|
107
|
+
readonly mcp: {
|
|
108
|
+
readonly file: ".codex/config.toml";
|
|
109
|
+
readonly format: "codexToml";
|
|
110
|
+
readonly projectDir: ".";
|
|
111
|
+
};
|
|
112
|
+
/** Codex uses the standard SKILL.md format in subdirectories. */
|
|
75
113
|
readonly transformFile: (skillName: string, content: string) => {
|
|
76
114
|
fileName: string;
|
|
77
115
|
content: string;
|
|
@@ -80,9 +118,15 @@ declare const AGENTS: {
|
|
|
80
118
|
readonly kiro: {
|
|
81
119
|
readonly label: "Kiro";
|
|
82
120
|
readonly detectDir: ".kiro";
|
|
121
|
+
readonly homeMarkers: readonly [".kiro"];
|
|
83
122
|
readonly targetDir: ".kiro/steering";
|
|
84
123
|
readonly flatLayout: true;
|
|
85
124
|
readonly indexFile: "rebase.md";
|
|
125
|
+
readonly mcp: {
|
|
126
|
+
readonly file: ".kiro/settings/mcp.json";
|
|
127
|
+
readonly format: "mcpServers";
|
|
128
|
+
readonly projectDir: ".";
|
|
129
|
+
};
|
|
86
130
|
/** The body is not a rule file here — `indexFile` is. */
|
|
87
131
|
readonly transformFile: (skillName: string, content: string) => {
|
|
88
132
|
fileName: string;
|
|
@@ -92,9 +136,15 @@ declare const AGENTS: {
|
|
|
92
136
|
readonly copilot: {
|
|
93
137
|
readonly label: "GitHub Copilot";
|
|
94
138
|
readonly detectDir: null;
|
|
139
|
+
readonly homeMarkers: readonly [];
|
|
95
140
|
readonly targetDir: ".github/instructions";
|
|
96
141
|
readonly flatLayout: true;
|
|
97
142
|
readonly indexFile: "rebase.instructions.md";
|
|
143
|
+
readonly mcp: {
|
|
144
|
+
readonly file: ".vscode/mcp.json";
|
|
145
|
+
readonly format: "vscode";
|
|
146
|
+
readonly projectDir: "${workspaceFolder}";
|
|
147
|
+
};
|
|
98
148
|
/** The body is not a rule file here — `indexFile` is. */
|
|
99
149
|
readonly transformFile: (skillName: string, content: string) => {
|
|
100
150
|
fileName: string;
|
|
@@ -102,7 +152,11 @@ declare const AGENTS: {
|
|
|
102
152
|
};
|
|
103
153
|
};
|
|
104
154
|
};
|
|
105
|
-
type AgentKey = keyof typeof AGENTS;
|
|
155
|
+
export type AgentKey = keyof typeof AGENTS;
|
|
156
|
+
/** Narrows a string to an agent name, by the table's own keys. */
|
|
157
|
+
export declare function isAgentKey(value: string): value is AgentKey;
|
|
158
|
+
/** Every agent, in table order. */
|
|
159
|
+
export declare const AGENT_KEYS: AgentKey[];
|
|
106
160
|
export interface LoadedSkill {
|
|
107
161
|
name: string;
|
|
108
162
|
/** Absolute path of the skill's source directory. */
|
|
@@ -113,6 +167,13 @@ export interface LoadedSkill {
|
|
|
113
167
|
}
|
|
114
168
|
/** Read all skill directories and return their names, content and assets. */
|
|
115
169
|
export declare function loadSkills(skillsDir: string): LoadedSkill[];
|
|
170
|
+
/**
|
|
171
|
+
* The skills this CLI ships, from its own `@rebasepro/agent-skills`.
|
|
172
|
+
*
|
|
173
|
+
* Throws rather than exiting, so `rebase init` can report a missing bundle as a
|
|
174
|
+
* warning and still finish the scaffold.
|
|
175
|
+
*/
|
|
176
|
+
export declare function loadBundledSkills(): LoadedSkill[];
|
|
116
177
|
/**
|
|
117
178
|
* The one always-on rule a flat-layout agent gets, in place of every skill.
|
|
118
179
|
*
|
|
@@ -136,11 +197,43 @@ export declare function renderSkillIndex(skills: LoadedSkill[], agentKey: AgentK
|
|
|
136
197
|
* guessing, because the wrong guess installs 21 files and reports success.
|
|
137
198
|
*/
|
|
138
199
|
export declare function detectAgents(projectDir: string): AgentKey[];
|
|
200
|
+
/** What `detectInstalledAgents` found for one assistant. */
|
|
201
|
+
export interface InstalledAgent {
|
|
202
|
+
key: AgentKey;
|
|
203
|
+
label: string;
|
|
204
|
+
/** The home directory checked, as `~/…` — the one found, else the first. */
|
|
205
|
+
marker: string | null;
|
|
206
|
+
found: boolean;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Which assistants are installed on this machine, from their home directories.
|
|
210
|
+
*
|
|
211
|
+
* The question `rebase init` needs answered, and one `detectAgents` cannot: a
|
|
212
|
+
* project that does not exist yet has no `.claude/` in it. What the machine has
|
|
213
|
+
* is only a default — the developer confirms it — so a false positive costs an
|
|
214
|
+
* unticked box, not 21 files nobody asked for.
|
|
215
|
+
*/
|
|
216
|
+
export declare function detectInstalledAgents(home?: string): InstalledAgent[];
|
|
139
217
|
/** Install skills for a specific agent into the project directory. */
|
|
140
218
|
export declare function installForAgent(agentKey: AgentKey, skills: LoadedSkill[], projectDir: string): {
|
|
141
219
|
skills: number;
|
|
142
220
|
assets: number;
|
|
143
221
|
};
|
|
222
|
+
/** What `installSkills` did for one agent. */
|
|
223
|
+
export interface SkillsInstallResult {
|
|
224
|
+
agent: AgentKey;
|
|
225
|
+
skills: number;
|
|
226
|
+
assets: number;
|
|
227
|
+
/** An earlier agent in the same run that already wrote this directory. */
|
|
228
|
+
sharedWith?: AgentKey;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Install for several agents, writing each target directory once.
|
|
232
|
+
*
|
|
233
|
+
* Gemini and Codex both read `.agents/skills`; installing twice would write the
|
|
234
|
+
* same 21 files twice and report two installs of one directory.
|
|
235
|
+
*/
|
|
236
|
+
export declare function installSkills(agents: readonly AgentKey[], skills: LoadedSkill[], projectDir: string): SkillsInstallResult[];
|
|
144
237
|
/** Everything the switch below dispatches, for the did-you-mean. */
|
|
145
238
|
export declare const SKILLS_SUBCOMMANDS: readonly ["install"];
|
|
146
239
|
export declare function skillsCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void>;
|
|
@@ -150,4 +243,15 @@ export declare function skillsCommand(subcommand: string | undefined, rawArgs: s
|
|
|
150
243
|
* given.
|
|
151
244
|
*/
|
|
152
245
|
export declare function parseAgentFlags(rawArgs: string[]): AgentKey[] | null;
|
|
153
|
-
|
|
246
|
+
/** `rebase skills install`'s whole line: the agents it names, and `--mcp`. */
|
|
247
|
+
export declare function parseSkillsInstallLine(rawArgs: string[]): {
|
|
248
|
+
agents: AgentKey[] | null;
|
|
249
|
+
mcp: boolean;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* The agents a list of `--agent` values names, or null when it names none.
|
|
253
|
+
*
|
|
254
|
+
* Shared by `rebase skills install` and `rebase init`, so both accept the same
|
|
255
|
+
* spellings. Throws a UsageError on a name that is not an agent.
|
|
256
|
+
*/
|
|
257
|
+
export declare function resolveAgentNames(values: readonly string[]): AgentKey[] | null;
|
package/dist/commands/start.d.ts
CHANGED
|
@@ -1 +1,30 @@
|
|
|
1
|
+
import type { RebaseBundleStatic } from "@rebasepro/types";
|
|
2
|
+
/** The NODE_ENV the runtime will see, and where it comes from. */
|
|
3
|
+
export interface StartNodeEnv {
|
|
4
|
+
value: string | undefined;
|
|
5
|
+
source: "shell" | ".env" | "unset";
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* What NODE_ENV the server will boot in. The shell wins — dotenv never
|
|
9
|
+
* overrides a variable that is already set — then `.env`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function startNodeEnv(projectRoot: string): StartNodeEnv;
|
|
12
|
+
/**
|
|
13
|
+
* The banner for a `rebase start` that is not a production server, or `null`.
|
|
14
|
+
*
|
|
15
|
+
* Not forced to production: the scaffold's `.env` points at a loopback
|
|
16
|
+
* database, which a production boot refuses, so forcing it would break the
|
|
17
|
+
* documented local run. What must not happen is the silence — a command the
|
|
18
|
+
* help called "(production)" booting with the first-registration window open.
|
|
19
|
+
*/
|
|
20
|
+
export declare function nonProductionWarning(nodeEnv: StartNodeEnv): string[] | null;
|
|
21
|
+
/**
|
|
22
|
+
* What to say about the apps a bundle serves only on their own hostname.
|
|
23
|
+
*
|
|
24
|
+
* The runtime prints "Server running at http://localhost:<port>", and an app
|
|
25
|
+
* whose `path` names a hostname answers only requests carrying that `Host` —
|
|
26
|
+
* so opening the printed URL showed a 404 for an app that was running fine,
|
|
27
|
+
* and nothing said why. One line per such app, with a command that reaches it.
|
|
28
|
+
*/
|
|
29
|
+
export declare function hostRoutedNotice(staticApps: readonly RebaseBundleStatic[], port: string | undefined): string[];
|
|
1
30
|
export declare function startCommand(rawArgs?: string[]): Promise<void>;
|
|
@@ -30,6 +30,7 @@ var daemon_exports = /* @__PURE__ */ __exportAll({
|
|
|
30
30
|
managedUrl: () => managedUrl,
|
|
31
31
|
pgliteLogTail: () => pgliteLogTail,
|
|
32
32
|
resetManagedDatabase: () => resetManagedDatabase,
|
|
33
|
+
resolveCliEntry: () => resolveCliEntry,
|
|
33
34
|
resolveSpawn: () => resolveSpawn,
|
|
34
35
|
stopManagedDatabase: () => stopManagedDatabase
|
|
35
36
|
});
|
|
@@ -130,7 +131,8 @@ async function findRunningDaemon(projectRoot) {
|
|
|
130
131
|
* A hidden subcommand rather than a second build entry: under `tsx` this file
|
|
131
132
|
* is `src/dev-db/daemon.ts` and in a published CLI it is bundled into
|
|
132
133
|
* `dist/index.js`, and in both cases the executable to re-invoke is the one
|
|
133
|
-
* already running.
|
|
134
|
+
* already running. `rebase dev` re-runs itself the same way to regenerate the
|
|
135
|
+
* SDK types.
|
|
134
136
|
*/
|
|
135
137
|
function resolveCliEntry() {
|
|
136
138
|
const argvEntry = process.argv[1];
|
|
@@ -350,6 +352,6 @@ function ensureGitignore(projectRoot) {
|
|
|
350
352
|
fs.writeFileSync(target, "# Generated by `rebase dev` — a local Postgres data directory and its log.\n# Machine-specific and large; never commit it.\n*\n", "utf8");
|
|
351
353
|
}
|
|
352
354
|
//#endregion
|
|
353
|
-
export { managedUrl as i, ensureManagedDatabase as n, findRunningDaemon as r, daemon_exports as t };
|
|
355
|
+
export { resolveCliEntry as a, managedUrl as i, ensureManagedDatabase as n, resolveSpawn as o, findRunningDaemon as r, daemon_exports as t };
|
|
354
356
|
|
|
355
|
-
//# sourceMappingURL=daemon-
|
|
357
|
+
//# sourceMappingURL=daemon-C8p32i-n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-C8p32i-n.js","names":[],"sources":["../src/dev-db/daemon.ts"],"sourcesContent":["/**\n * Starting, finding and stopping the managed database, from the caller's side.\n *\n * Every command that needs Postgres calls {@link ensureManagedDatabase} and\n * gets a connection string back. Whether that started a process or found one\n * already running is not the caller's business, which is the point: `rebase\n * db push`, `rebase dev` and `rebase studio` in three terminals must all reach\n * the same database without coordinating, because two processes opening one\n * PGlite data directory would corrupt it.\n *\n * The hard part is not starting the daemon; it is deciding whether the one the\n * state file describes is still there. A pid can be recycled after a reboot and\n * a port can be taken by a stranger, so believing either on its own would let\n * Rebase send a migration somewhere unintended. {@link isDaemonAlive} asks the\n * daemon to identify itself instead.\n */\n\nimport { randomBytes } from \"crypto\";\nimport { type ChildProcess, spawn } from \"child_process\";\nimport fs from \"fs\";\nimport net from \"net\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\n\nimport {\n acquireStartLock,\n clearState,\n type DaemonState,\n dataDir,\n devDbDir,\n findFreePort,\n pidRunning,\n readState,\n releaseStartLock,\n stateFile\n} from \"./state\";\n\n/** How long to wait for a first boot. PGlite runs initdb on an empty data dir. */\nconst START_TIMEOUT_MS = 60_000;\n/** Poll interval while waiting for the daemon to publish its state file. */\nconst POLL_INTERVAL_MS = 150;\n/** How long the identity handshake may take before we call it dead. */\nconst IDENTITY_TIMEOUT_MS = 1_500;\n\nexport interface ManagedDatabase {\n /** Connection string for this project's managed database. */\n url: string;\n /** Where the data lives, for diagnostics and `--reset`. */\n dataDir: string;\n port: number;\n pid: number;\n /** True when this call started the daemon rather than finding it. */\n started: boolean;\n /**\n * Connection strings for the additional databases the caller asked for,\n * by declared key — `database(\"analytics\")` → `analytics`. Each is its\n * own PGlite instance behind the same daemon.\n */\n additional: Record<string, string>;\n}\n\n/**\n * PGlite's built-in superuser and database.\n *\n * Fixed by PGlite rather than chosen here, and asserted by the socket spike:\n * `session_user` comes back as `postgres`.\n */\nconst MANAGED_USER = \"postgres\";\nconst MANAGED_DATABASE = \"postgres\";\n\n/**\n * The DSN for the managed development database.\n *\n * **`sslmode=disable` is not decoration.** PGlite's socket server speaks no TLS,\n * and libpq-family clients try SSL first — so Atlas, which `rebase db push`\n * shells out to, failed the whole push with\n * `pq: SSL is not enabled on the server`. Its remedy box then told the reader to\n * append `sslmode=disable` to `DATABASE_URL`, which on this path is not set at\n * all: `rebase init` leaves it commented out precisely so the managed database\n * is used. The advice was correct and unfollowable.\n *\n * Putting it in the URL fixes every consumer at once — Atlas, `pg`, and anything\n * a driver shells out to later — rather than teaching each one about PGlite.\n */\n\n/**\n * The end of `pglite.log`, for an error message that has to carry its own reason.\n *\n * The daemon writes its output to a file because it is detached — it has to\n * outlive the command that started it. That is right, and it means every\n * startup failure used to be reported as a path: \"see pglite.log for the\n * reason\". On a laptop that is a fair trade. In CI it is not a trade at all,\n * because the runner discards the workspace: the log named in the message no\n * longer exists by the time anybody reads the message, so a failed run says\n * only that something failed.\n *\n * So the tail comes with the error. Bounded, because a log that has been\n * appended to across many runs is not something to paste in full.\n */\nexport function pgliteLogTail(projectRoot: string, lines = 12): string {\n try {\n const contents = fs.readFileSync(path.join(devDbDir(projectRoot), \"pglite.log\"), \"utf8\").trimEnd();\n if (!contents) return \" (pglite.log is empty — the daemon wrote nothing before exiting)\";\n return contents.split(\"\\n\").slice(-lines).map(line => ` ${line}`).join(\"\\n\");\n } catch {\n return \" (pglite.log could not be read)\";\n }\n}\n\nexport function managedUrl(port: number): string {\n return `postgresql://${MANAGED_USER}@127.0.0.1:${port}/${MANAGED_DATABASE}?sslmode=disable`;\n}\n\n/**\n * Ask the process behind a state record to prove it is the one we wrote down.\n *\n * A pid check alone answers \"is *a* process running\", and a port check alone\n * answers \"is *something* listening\" — after a reboot both say yes about\n * strangers. The daemon answers with the token from its own state file, so a\n * match is the only evidence accepted.\n */\nexport function isDaemonAlive(state: DaemonState): Promise<boolean> {\n if (!pidRunning(state.pid)) return Promise.resolve(false);\n\n return new Promise((resolve) => {\n const socket = new net.Socket();\n let answer = \"\";\n const settle = (alive: boolean) => {\n socket.removeAllListeners();\n socket.destroy();\n resolve(alive);\n };\n socket.setTimeout(IDENTITY_TIMEOUT_MS);\n socket.once(\"timeout\", () => settle(false));\n socket.once(\"error\", () => settle(false));\n socket.on(\"data\", (chunk) => {\n answer += chunk.toString(\"utf8\");\n if (answer.includes(\"\\n\")) settle(answer.trim() === `rebase-dev-db ${state.token}`);\n });\n socket.once(\"close\", () => settle(answer.trim() === `rebase-dev-db ${state.token}`));\n socket.connect(state.identityPort, \"127.0.0.1\");\n });\n}\n\n/** The running daemon for this project, or `null`. Never starts anything. */\nexport async function findRunningDaemon(projectRoot: string): Promise<DaemonState | null> {\n const state = readState(projectRoot);\n if (!state) return null;\n if (await isDaemonAlive(state)) return state;\n\n // The record describes something that is not there. Clearing it is the\n // whole recovery: the next ensure call starts a fresh daemon, and leaving\n // it would make every subsequent check pay the identity timeout.\n clearState(projectRoot);\n\n return null;\n}\n\n/**\n * Resolve the CLI entry point, so the daemon can be spawned as ourselves.\n *\n * A hidden subcommand rather than a second build entry: under `tsx` this file\n * is `src/dev-db/daemon.ts` and in a published CLI it is bundled into\n * `dist/index.js`, and in both cases the executable to re-invoke is the one\n * already running. `rebase dev` re-runs itself the same way to regenerate the\n * SDK types.\n */\nexport function resolveCliEntry(): string {\n // `process.argv[1]` is `bin/rebase.js` for a real invocation, which is\n // exactly what should be re-run. Under a test runner it is the runner, so\n // fall back to this module's own directory.\n const argvEntry = process.argv[1];\n if (argvEntry && fs.existsSync(argvEntry)) return argvEntry;\n\n return fileURLToPath(new URL(\"../index.js\", import.meta.url));\n}\n\n/**\n * How to re-invoke ourselves, which differs between a published CLI and this\n * repository.\n *\n * A published `rebase` is `node bin/rebase.js`, and re-running that is trivial.\n * Inside the monorepo the entry is TypeScript, which plain `node` cannot load —\n * so the daemon has to be started through the same loader that is running now.\n * Getting this wrong fails as a spawn that exits instantly with a syntax error,\n * which is why the caller reads `pglite.log` on failure.\n */\nexport function resolveSpawn(entry: string): { execPath: string; prefixArgs: string[] } {\n if (!/\\.[cm]?ts$/.test(entry)) return { execPath: process.execPath, prefixArgs: [] };\n\n // Node can load TypeScript when tsx is registered as an import hook. tsx is\n // already a dependency of this package, because `rebase dev` runs the\n // backend through it.\n return { execPath: process.execPath, prefixArgs: [\"--import\", \"tsx\"] };\n}\n\nexport interface EnsureOptions {\n /** Silence the \"starting…\" progress line. */\n quiet?: boolean;\n /** Milliseconds of inactivity before the daemon exits. 0 disables. */\n idleTimeoutMs?: number;\n /** Where progress goes. Injected for tests. */\n onProgress?: (message: string) => void;\n /** Override how the daemon process is launched. For tests. */\n spawn?: { execPath: string; prefixArgs: string[] };\n /**\n * Override the CLI entry to re-invoke. For tests.\n *\n * Necessary because under a test runner `process.argv[1]` is the runner\n * itself, which exists and is therefore accepted by {@link resolveCliEntry}\n * — spawning vitest with `__dev-db-daemon` rather than the CLI.\n */\n entry?: string;\n /**\n * Keys of the additional databases the project declares, beyond the\n * default. Each is served by the daemon on request, so a project that\n * adds `database(\"analytics\")` while the daemon is running gets it\n * without a restart.\n */\n additionalKeys?: readonly string[];\n}\n\n/**\n * Ask the running daemon for one additional database, and get its port.\n *\n * The identity socket doubles as the command channel: the daemon answers\n * with its token first, which is how the caller knows it is talking to the\n * daemon the state file describes and not to a stranger on a recycled port.\n */\nfunction ensureAdditionalDatabase(state: DaemonState, key: string): Promise<number> {\n return new Promise((resolve, reject) => {\n const socket = new net.Socket();\n let answer = \"\";\n let identified = false;\n const fail = (message: string) => {\n socket.removeAllListeners();\n socket.destroy();\n reject(new Error(message));\n };\n socket.setTimeout(START_TIMEOUT_MS);\n socket.once(\"timeout\", () => fail(`The development database did not answer for \"${key}\" within ${Math.round(START_TIMEOUT_MS / 1000)}s.`));\n socket.once(\"error\", (err) => fail(`Could not reach the development database for \"${key}\": ${err.message}`));\n socket.on(\"data\", (chunk) => {\n answer += chunk.toString(\"utf8\");\n let newline = answer.indexOf(\"\\n\");\n while (newline !== -1) {\n const line = answer.slice(0, newline).trim();\n answer = answer.slice(newline + 1);\n if (!identified) {\n if (line !== `rebase-dev-db ${state.token}`) return fail(\"The process on the development database's port is not the daemon.\");\n identified = true;\n socket.write(`ensure ${key}\\n`);\n } else if (line.startsWith(\"ok \")) {\n const port = Number(line.slice(3));\n socket.removeAllListeners();\n socket.destroy();\n if (!Number.isInteger(port) || port <= 0) return reject(new Error(`The development database answered with an invalid port for \"${key}\": ${line}`));\n return resolve(port);\n } else {\n return fail(`The development database could not serve \"${key}\": ${line.replace(/^error\\s*/, \"\")}`);\n }\n newline = answer.indexOf(\"\\n\");\n }\n });\n socket.connect(state.identityPort, \"127.0.0.1\");\n });\n}\n\n/** Every additional database the caller asked for, as connection strings. */\nasync function adoptWithAdditional(state: DaemonState, started: boolean, keys: readonly string[]): Promise<ManagedDatabase> {\n const additional: Record<string, string> = {};\n for (const key of keys) {\n const known = state.databases?.[key];\n const port = known ? known.port : await ensureAdditionalDatabase(state, key);\n additional[key] = managedUrl(port);\n }\n return { ...adopt(state, started), additional };\n}\n\n/**\n * The project's managed database, started if it is not already running.\n *\n * Safe to call concurrently from several commands: the loser of the race finds\n * the winner's state file during its poll and adopts it rather than starting a\n * second daemon.\n */\nexport async function ensureManagedDatabase(\n projectRoot: string,\n options: EnsureOptions = {}\n): Promise<ManagedDatabase> {\n const keys = options.additionalKeys ?? [];\n const existing = await findRunningDaemon(projectRoot);\n if (existing) return adoptWithAdditional(existing, false, keys);\n\n fs.mkdirSync(devDbDir(projectRoot), { recursive: true });\n ensureGitignore(projectRoot);\n\n // Exactly one process may spawn a daemon. Without this, `rebase dev` and\n // `rebase db push` started in the same second both see no state file, both\n // spawn, and two processes open one PGlite data directory — the corruption\n // the single-daemon design exists to prevent.\n if (!acquireStartLock(projectRoot, START_TIMEOUT_MS)) {\n const adopted = await waitForDaemon(projectRoot, START_TIMEOUT_MS);\n if (adopted) return adoptWithAdditional(adopted, false, keys);\n\n // The holder gave up or died without publishing. Falling through to\n // start one ourselves is better than failing: the lock is stale by now,\n // so the next acquire will break it.\n if (!acquireStartLock(projectRoot, 0)) {\n throw new Error(\n \"Another process is starting the development database and did not finish.\\n\" +\n ` Remove ${path.join(devDbDir(projectRoot), \"starting.lock\")} if nothing is running.`\n );\n }\n }\n\n const port = await findFreePort();\n const token = randomBytes(16).toString(\"hex\");\n const entry = options.entry ?? resolveCliEntry();\n\n const spawnPlan = options.spawn ?? resolveSpawn(entry);\n const args = [\n ...spawnPlan.prefixArgs,\n entry,\n \"__dev-db-daemon\",\n \"--project\", projectRoot,\n \"--port\", String(port),\n \"--token\", token\n ];\n if (options.idleTimeoutMs !== undefined) {\n args.push(\"--idle-timeout\", String(options.idleTimeoutMs));\n }\n\n const log = fs.openSync(path.join(devDbDir(projectRoot), \"pglite.log\"), \"a\");\n const child: ChildProcess = spawn(spawnPlan.execPath, args, {\n // Detached with no stdin and its output on a file: the daemon must\n // survive the command that started it, and must not hold the terminal\n // open when that command exits.\n detached: true,\n stdio: [\"ignore\", log, log]\n });\n child.unref();\n\n try {\n const deadline = Date.now() + START_TIMEOUT_MS;\n let announced = false;\n while (Date.now() < deadline) {\n const state = readState(projectRoot);\n if (state && (await isDaemonAlive(state))) return adoptWithAdditional(state, true, keys);\n\n if (!announced && !options.quiet) {\n announced = true;\n options.onProgress?.(\"Starting the development database…\");\n }\n\n // A daemon that died on startup will never publish a state file, and\n // waiting the full minute for that is a bad way to learn it. The log\n // is the only place the reason exists.\n if (child.exitCode !== null && child.exitCode !== 0) {\n throw new Error(\n `The development database failed to start (exit ${child.exitCode}).\\n` +\n ` ${path.join(devDbDir(projectRoot), \"pglite.log\")} ends:\\n` +\n pgliteLogTail(projectRoot)\n );\n }\n\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n }\n\n throw new Error(\n `The development database did not start within ${Math.round(START_TIMEOUT_MS / 1000)}s.\\n` +\n ` ${path.join(devDbDir(projectRoot), \"pglite.log\")} ends:\\n` +\n pgliteLogTail(projectRoot) + \"\\n\" +\n \" To use your own Postgres instead, set DATABASE_URL or pass --database-url.\"\n );\n } finally {\n // Released whether we succeeded, timed out or threw. A lock left behind\n // by a crash is broken by age, but only after a full timeout — which a\n // developer should never have to wait out.\n releaseStartLock(projectRoot);\n }\n}\n\n/** Shape a live state record as the caller's result. */\nfunction adopt(state: DaemonState, started: boolean): ManagedDatabase {\n return {\n url: managedUrl(state.port),\n dataDir: state.dataDir,\n port: state.port,\n pid: state.pid,\n started,\n additional: {}\n };\n}\n\n/** Poll until another process publishes a live daemon, or give up. */\nasync function waitForDaemon(projectRoot: string, timeoutMs: number): Promise<DaemonState | null> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n const state = readState(projectRoot);\n if (state && (await isDaemonAlive(state))) return state;\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n }\n\n return null;\n}\n\n/** Stop the daemon. Returns false when there was nothing running. */\nexport async function stopManagedDatabase(projectRoot: string): Promise<boolean> {\n const state = await findRunningDaemon(projectRoot);\n if (!state) return false;\n\n try {\n process.kill(state.pid, \"SIGTERM\");\n } catch {\n clearState(projectRoot);\n\n return false;\n }\n\n // Wait for it to clear its own state file, which is how it says it closed\n // the data directory cleanly. A hard kill here would risk the WAL.\n const deadline = Date.now() + 10_000;\n while (Date.now() < deadline) {\n if (!fs.existsSync(stateFile(projectRoot))) return true;\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n\n clearState(projectRoot);\n\n return true;\n}\n\n/**\n * Stop the daemon and delete the data directory.\n *\n * Destructive and deliberately not clever: it removes the whole directory\n * rather than dropping schemas, because \"give me an empty database\" is the only\n * thing anyone means by it.\n */\nexport async function resetManagedDatabase(projectRoot: string): Promise<void> {\n await stopManagedDatabase(projectRoot);\n fs.rmSync(dataDir(projectRoot), { recursive: true, force: true });\n // Every additional database too: `pgdata__<key>` beside `pgdata`. \"Give\n // me an empty database\" means all of them — a reset that emptied the\n // default and kept the analytics rows would be a surprise on first read.\n const dir = devDbDir(projectRoot);\n if (fs.existsSync(dir)) {\n for (const entry of fs.readdirSync(dir)) {\n if (entry.startsWith(\"pgdata__\")) fs.rmSync(path.join(dir, entry), { recursive: true, force: true });\n }\n }\n clearState(projectRoot);\n}\n\n/**\n * Keep the generated directory out of git.\n *\n * `.rebase/` holds a Postgres data directory and a log. A project that commits\n * it will push hundreds of megabytes and a database that only makes sense on\n * one machine, so the ignore file is written next to the data rather than\n * relying on the project's root `.gitignore` having been updated.\n */\nfunction ensureGitignore(projectRoot: string): void {\n const target = path.join(devDbDir(projectRoot), \".gitignore\");\n if (fs.existsSync(target)) return;\n fs.writeFileSync(\n target,\n \"# Generated by `rebase dev` — a local Postgres data directory and its log.\\n\" +\n \"# Machine-specific and large; never commit it.\\n*\\n\",\n \"utf8\"\n );\n}\n"],"mappings":"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"}
|
package/dist/dev-db/daemon.d.ts
CHANGED
|
@@ -72,6 +72,16 @@ export declare function managedUrl(port: number): string;
|
|
|
72
72
|
export declare function isDaemonAlive(state: DaemonState): Promise<boolean>;
|
|
73
73
|
/** The running daemon for this project, or `null`. Never starts anything. */
|
|
74
74
|
export declare function findRunningDaemon(projectRoot: string): Promise<DaemonState | null>;
|
|
75
|
+
/**
|
|
76
|
+
* Resolve the CLI entry point, so the daemon can be spawned as ourselves.
|
|
77
|
+
*
|
|
78
|
+
* A hidden subcommand rather than a second build entry: under `tsx` this file
|
|
79
|
+
* is `src/dev-db/daemon.ts` and in a published CLI it is bundled into
|
|
80
|
+
* `dist/index.js`, and in both cases the executable to re-invoke is the one
|
|
81
|
+
* already running. `rebase dev` re-runs itself the same way to regenerate the
|
|
82
|
+
* SDK types.
|
|
83
|
+
*/
|
|
84
|
+
export declare function resolveCliEntry(): string;
|
|
75
85
|
/**
|
|
76
86
|
* How to re-invoke ourselves, which differs between a published CLI and this
|
|
77
87
|
* repository.
|
package/dist/dev-db/prepare.d.ts
CHANGED
|
@@ -108,6 +108,16 @@ export declare function devDatabaseKind(projectRoot?: string | null): DevDatabas
|
|
|
108
108
|
* a fourth reads it in another package.
|
|
109
109
|
*/
|
|
110
110
|
export declare const DEV_DATABASE_KIND_ENV = "REBASE_DEV_DATABASE_KIND";
|
|
111
|
+
/**
|
|
112
|
+
* The variable that tells a server started on a branch which database it was
|
|
113
|
+
* branched from.
|
|
114
|
+
*
|
|
115
|
+
* The branch registry lives in that database, and a branch database holds
|
|
116
|
+
* only a snapshot of it taken when it was copied — so a server on a branch
|
|
117
|
+
* keeps its branches, and copies new ones from, the database named here. A
|
|
118
|
+
* branch URL does not say which that is; only this checkout's `.env` does.
|
|
119
|
+
*/
|
|
120
|
+
export declare const BRANCH_PARENT_DATABASE_ENV = "REBASE_BRANCH_PARENT_DATABASE";
|
|
111
121
|
/**
|
|
112
122
|
* Resolve, start if needed, and describe the database for this command.
|
|
113
123
|
*
|
package/dist/fold-static.d.ts
CHANGED
|
@@ -16,6 +16,14 @@ export interface FoldOptions {
|
|
|
16
16
|
bundleDir: string;
|
|
17
17
|
/** Skip running each app's own build command; fold what is already built. */
|
|
18
18
|
skipBuild?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* The static apps already built in this run, shared with `buildAssetApp`.
|
|
21
|
+
* An app named here is not built again; an app built here is added. One
|
|
22
|
+
* `rebase build` used to run the admin's `vite build && tsc` twice — once
|
|
23
|
+
* to fold it, once for its own static bundle — with the same command, the
|
|
24
|
+
* same environment and the same output directory.
|
|
25
|
+
*/
|
|
26
|
+
builtApps?: Set<string>;
|
|
19
27
|
log?: (message: string) => void;
|
|
20
28
|
/** Send each app's build output to stderr: the caller's stdout carries a result. See `toolStdio`. */
|
|
21
29
|
quietStdout?: boolean;
|
|
@@ -25,14 +33,22 @@ export interface FoldOutcome {
|
|
|
25
33
|
fileCount: number;
|
|
26
34
|
/** Public base path this app was folded in at. */
|
|
27
35
|
path: string;
|
|
36
|
+
/** The one hostname it answers on, when its `path` names one. */
|
|
37
|
+
host?: string;
|
|
28
38
|
}
|
|
29
39
|
/** A static app as folding sees it, with the manifest's defaults applied. */
|
|
30
40
|
export interface FoldableApp {
|
|
31
41
|
name: string;
|
|
32
42
|
build?: string;
|
|
33
43
|
output?: string;
|
|
34
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Public base path, defaulted to `/`. Always a path: when the manifest's
|
|
46
|
+
* `path` is a URL, this is the part after the hostname, which is what the
|
|
47
|
+
* app is built for and mounted at.
|
|
48
|
+
*/
|
|
35
49
|
path: string;
|
|
50
|
+
/** The one hostname this app answers on, when its `path` is a URL. */
|
|
51
|
+
host?: string;
|
|
36
52
|
/** SPA fallback, defaulted to `true`. */
|
|
37
53
|
spa: boolean;
|
|
38
54
|
/** Where this app mounts the Rebase CMS, if it does. */
|
|
@@ -42,8 +58,9 @@ export interface FoldableApp {
|
|
|
42
58
|
* Every static app in the manifest, in mount order.
|
|
43
59
|
*
|
|
44
60
|
* Longest path first, so the `/`-rooted app is registered last — its catch-all
|
|
45
|
-
* would otherwise claim its siblings' URLs
|
|
46
|
-
* without
|
|
61
|
+
* would otherwise claim its siblings' URLs — and, at an equal path, the app with
|
|
62
|
+
* a hostname before the one without (see `compareMountOrder`). Pure, so the
|
|
63
|
+
* ordering is testable without a filesystem.
|
|
47
64
|
*/
|
|
48
65
|
export declare function foldableApps(manifest: FoldableManifest): {
|
|
49
66
|
apps: FoldableApp[];
|
|
@@ -82,6 +99,12 @@ export declare function assertBuiltForPath(indexHtml: string, basePath: string,
|
|
|
82
99
|
* reads `base` from REBASE_APP_BASE, and the trailing slash is that field's
|
|
83
100
|
* convention. See `assertBuiltForPath`.
|
|
84
101
|
*
|
|
102
|
+
* For an app declared at a URL these carry the path part only — `/` for
|
|
103
|
+
* `https://admin.example.com`. The hostname is where the platform routes the
|
|
104
|
+
* app, not something its assets should name: an absolute `base` would pin
|
|
105
|
+
* every script to production, so `vite preview` and any other hostname the app
|
|
106
|
+
* is opened on would load it from somewhere else.
|
|
107
|
+
*
|
|
85
108
|
* ## NODE_ENV
|
|
86
109
|
*
|
|
87
110
|
* A built app is a production artifact by construction, so build it as one. Not
|