@rebasepro/cli 0.25.0-canary.ge06257e → 0.25.1
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/bin/rebase.js +9 -5
- package/dist/{agent-setup-C83DXnlx.js → agent-setup-BYb2Tbdz.js} +2 -2
- package/dist/{agent-setup-C83DXnlx.js.map → agent-setup-BYb2Tbdz.js.map} +1 -1
- package/dist/{api-keys-Bc4FJKYp.js → api-keys-CSMbsmAn.js} +28 -22
- package/dist/api-keys-CSMbsmAn.js.map +1 -0
- package/dist/{apps-wG-ZTryA.js → apps-iBXNZhVN.js} +6 -7
- package/dist/{apps-wG-ZTryA.js.map → apps-iBXNZhVN.js.map} +1 -1
- package/dist/{atlas-dev-database-DeK3Sa4h.js → atlas-dev-database-BusbHP6n.js} +4 -4
- package/dist/{atlas-dev-database-DeK3Sa4h.js.map → atlas-dev-database-BusbHP6n.js.map} +1 -1
- package/dist/{auth-BL7bNQVu.js → auth-DRsTWPky.js} +77 -11
- package/dist/auth-DRsTWPky.js.map +1 -0
- package/dist/{branch-pointer-DTQNLFtb.js → branch-pointer-Bn_FjNld.js} +1 -1
- package/dist/{build-h_7Re-67.js → build-et7Rccxi.js} +6 -7
- package/dist/{build-h_7Re-67.js.map → build-et7Rccxi.js.map} +1 -1
- package/dist/{bundle-CqURVN-K.js → bundle-DlqkDYSe.js} +6 -21
- package/dist/bundle-DlqkDYSe.js.map +1 -0
- package/dist/{cloud-DVV_5GPG.js → cloud-B-ZIjHrA.js} +10 -11
- package/dist/{cloud-DVV_5GPG.js.map → cloud-B-ZIjHrA.js.map} +1 -1
- package/dist/commands/api-keys.d.ts +4 -1
- package/dist/commands/auth.d.ts +3 -2
- package/dist/commands/dev.d.ts +12 -0
- package/dist/commands/generate_sdk.d.ts +4 -2
- package/dist/commands/init.d.ts +20 -0
- package/dist/commands/start.d.ts +10 -0
- package/dist/commands/upgrade.d.ts +12 -0
- package/dist/connection-banner-DbLJzWf4.js +128 -0
- package/dist/connection-banner-DbLJzWf4.js.map +1 -0
- package/dist/{consent-ClRHmcxf.js → consent-BaB-eQKG.js} +2 -2
- package/dist/{consent-ClRHmcxf.js.map → consent-BaB-eQKG.js.map} +1 -1
- package/dist/{console-Dw3HLdUs.js → console-BlD9kjFW.js} +4 -4
- package/dist/{console-Dw3HLdUs.js.map → console-BlD9kjFW.js.map} +1 -1
- package/dist/{context-BM-oOMNX.js → context-DHiiiJ5C.js} +3 -4
- package/dist/context-DHiiiJ5C.js.map +1 -0
- package/dist/{daemon-CknkDjd6.js → daemon-DDMRbz4T.js} +160 -5
- package/dist/daemon-DDMRbz4T.js.map +1 -0
- package/dist/{daemon-entry-C6pxdily.js → daemon-entry-zOC1revO.js} +204 -5
- package/dist/daemon-entry-zOC1revO.js.map +1 -0
- package/dist/{db-Dkn9XhT-.js → db-9P6MnmdC.js} +237 -86
- package/dist/db-9P6MnmdC.js.map +1 -0
- package/dist/{derive-Dl0z2OWi.js → derive-MTdClnkc.js} +29 -14
- package/dist/{derive-Dl0z2OWi.js.map → derive-MTdClnkc.js.map} +1 -1
- package/dist/{dev-DAcZR4Pp.js → dev-BDN_OKXA.js} +183 -23
- package/dist/dev-BDN_OKXA.js.map +1 -0
- package/dist/dev-db/connection-banner.d.ts +47 -0
- package/dist/dev-db/daemon.d.ts +41 -0
- package/dist/dev-db/notification-proxy.d.ts +82 -1
- package/dist/dev-db/pull.d.ts +33 -5
- package/dist/dev-db/state.d.ts +9 -0
- package/dist/{dev-preflight-CorMg7Ub.js → dev-preflight-FT9GbJVj.js} +73 -4
- package/dist/dev-preflight-FT9GbJVj.js.map +1 -0
- package/dist/{dev-server-B1UYifZJ.js → dev-server-CIZwAfZa.js} +14 -3
- package/dist/dev-server-CIZwAfZa.js.map +1 -0
- package/dist/{doctor-Ds_lzc9p.js → doctor-D8YXz7df.js} +6 -6
- package/dist/doctor-D8YXz7df.js.map +1 -0
- package/dist/{eject-CJHkfnat.js → eject-izG2KjMo.js} +3 -4
- package/dist/{eject-CJHkfnat.js.map → eject-izG2KjMo.js.map} +1 -1
- package/dist/{fold-static-BC9CYicw.js → fold-static-CybSay_W.js} +3 -3
- package/dist/{fold-static-BC9CYicw.js.map → fold-static-CybSay_W.js.map} +1 -1
- package/dist/{generate_sdk-kfkZmG-d.js → generate_sdk-B4R65oUV.js} +22 -12
- package/dist/generate_sdk-B4R65oUV.js.map +1 -0
- package/dist/index.es.js +36 -37
- package/dist/index.es.js.map +1 -1
- package/dist/{init-CjoJoThg.js → init-DYaeppEN.js} +52 -63
- package/dist/init-DYaeppEN.js.map +1 -0
- package/dist/invoked-command-CG_rIa_o.js +55 -0
- package/dist/invoked-command-CG_rIa_o.js.map +1 -0
- package/dist/last-dev-database-B_75fxZC.js +2 -0
- package/dist/{manifest-DRp0UVTj.js → manifest-nvPdLlJY.js} +2 -2
- package/dist/{manifest-DRp0UVTj.js.map → manifest-nvPdLlJY.js.map} +1 -1
- package/dist/{normalize-imports-BHxL2d05.js → normalize-imports-BYw8ad87.js} +3 -3
- package/dist/{normalize-imports-BHxL2d05.js.map → normalize-imports-BYw8ad87.js.map} +1 -1
- package/dist/precompress.d.ts +0 -7
- package/dist/{prepare-C1iaiTC4.js → prepare-1rVNd2PF.js} +7 -6
- package/dist/prepare-1rVNd2PF.js.map +1 -0
- package/dist/project-g4K6kV1t.js +701 -0
- package/dist/project-g4K6kV1t.js.map +1 -0
- package/dist/{pull-COpNJFzY.js → pull-DDMpsZ5r.js} +143 -63
- package/dist/pull-DDMpsZ5r.js.map +1 -0
- package/dist/refusal-ReeeGP8a.js +26 -0
- package/dist/refusal-ReeeGP8a.js.map +1 -0
- package/dist/resolve-BQUAC3Wi.js +2 -0
- package/dist/resources/derive.d.ts +9 -0
- package/dist/{resources-CLm6g0br.js → resources-CovP8AZ-.js} +4 -5
- package/dist/{resources-CLm6g0br.js.map → resources-CovP8AZ-.js.map} +1 -1
- package/dist/{schema-BKdh6UoE.js → schema-nuxvvc50.js} +5 -6
- package/dist/{schema-BKdh6UoE.js.map → schema-nuxvvc50.js.map} +1 -1
- package/dist/{skills-DEhYiPeO.js → skills-DeUIsZcT.js} +3 -4
- package/dist/{skills-DEhYiPeO.js.map → skills-DeUIsZcT.js.map} +1 -1
- package/dist/{start-BsPJedPQ.js → start-RzZqDroW.js} +20 -7
- package/dist/start-RzZqDroW.js.map +1 -0
- package/dist/{state-CCB-FAQ9.js → state-CYdBvnwA.js} +3 -2
- package/dist/state-CYdBvnwA.js.map +1 -0
- package/dist/{status-D_B3tlDy.js → status-D9Zm88NZ.js} +11 -12
- package/dist/{status-D_B3tlDy.js.map → status-D9Zm88NZ.js.map} +1 -1
- package/dist/{storage-CtB8polS.js → storage-BB3umRfw.js} +3 -4
- package/dist/{storage-CtB8polS.js.map → storage-BB3umRfw.js.map} +1 -1
- package/dist/{telemetry-EMFRVRQi.js → telemetry-HfU5uAh1.js} +2 -2
- package/dist/{telemetry-EMFRVRQi.js.map → telemetry-HfU5uAh1.js.map} +1 -1
- package/dist/{telemetry-BYmPU-Ji.js → telemetry-TseNLk6X.js} +4 -4
- package/dist/{telemetry-BYmPU-Ji.js.map → telemetry-TseNLk6X.js.map} +1 -1
- package/dist/{upgrade-DbuKX9lv.js → upgrade-apiPKKE_.js} +29 -6
- package/dist/{upgrade-DbuKX9lv.js.map → upgrade-apiPKKE_.js.map} +1 -1
- package/dist/utils/args.d.ts +45 -3
- package/dist/utils/boot-retry.d.ts +41 -0
- package/dist/utils/dev-preflight.d.ts +34 -1
- package/dist/utils/dev-server.d.ts +11 -0
- package/dist/utils/project.d.ts +5 -1
- package/dist/utils/redact.d.ts +10 -0
- package/dist/utils/refusal.d.ts +18 -0
- package/dist/utils/unknown-command.d.ts +4 -0
- package/package.json +7 -7
- package/templates/overlays/baas/README.md +2 -1
- package/templates/resource-graphs/blank.json +25 -0
- package/templates/resource-graphs/blog.json +28 -0
- package/templates/resource-graphs/ecommerce.json +28 -0
- package/templates/resource-graphs/headless.json +22 -0
- package/templates/template/.env.example +11 -4
- package/templates/template/frontend/src/App.tsx +10 -9
- package/templates/template/frontend/vite.config.ts +7 -73
- package/dist/api-keys-Bc4FJKYp.js.map +0 -1
- package/dist/args-CcBphFdj.js +0 -237
- package/dist/args-CcBphFdj.js.map +0 -1
- package/dist/auth-BL7bNQVu.js.map +0 -1
- package/dist/bundle-CqURVN-K.js.map +0 -1
- package/dist/context-BM-oOMNX.js.map +0 -1
- package/dist/daemon-CknkDjd6.js.map +0 -1
- package/dist/daemon-entry-C6pxdily.js.map +0 -1
- package/dist/db-Dkn9XhT-.js.map +0 -1
- package/dist/dev-DAcZR4Pp.js.map +0 -1
- package/dist/dev-preflight-CorMg7Ub.js.map +0 -1
- package/dist/dev-server-B1UYifZJ.js.map +0 -1
- package/dist/doctor-Ds_lzc9p.js.map +0 -1
- package/dist/generate_sdk-kfkZmG-d.js.map +0 -1
- package/dist/init-CjoJoThg.js.map +0 -1
- package/dist/last-dev-database-BEBPfAMY.js +0 -2
- package/dist/prepare-C1iaiTC4.js.map +0 -1
- package/dist/project-Wp2zoyeN.js +0 -349
- package/dist/project-Wp2zoyeN.js.map +0 -1
- package/dist/pull-COpNJFzY.js.map +0 -1
- package/dist/resolve-b_p9bmdH.js +0 -2
- package/dist/start-BsPJedPQ.js.map +0 -1
- package/dist/state-CCB-FAQ9.js.map +0 -1
|
@@ -101,13 +101,33 @@ export declare function probeTcp(host: string, port: number, timeoutMs?: number)
|
|
|
101
101
|
* forward. {@link waitForQueryable} is the check that a query will work.
|
|
102
102
|
*/
|
|
103
103
|
export declare function waitForPort(host: string, port: number, timeoutMs?: number, intervalMs?: number): Promise<boolean>;
|
|
104
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Whether a database answered a query, and if not, the last reason it gave —
|
|
106
|
+
* and, for a database closed to new connections, its name.
|
|
107
|
+
*/
|
|
105
108
|
export type QueryReadiness = {
|
|
106
109
|
ready: true;
|
|
107
110
|
} | {
|
|
108
111
|
ready: false;
|
|
109
112
|
error: string;
|
|
113
|
+
closedDatabase?: string;
|
|
110
114
|
};
|
|
115
|
+
/** What opening a closed database again did, and what to say about it. */
|
|
116
|
+
export interface ClosedDatabaseHeal {
|
|
117
|
+
/** It accepts connections again. */
|
|
118
|
+
reopened: boolean;
|
|
119
|
+
/** The driver's own lines about it (`reopenedDatabasesNotice`). */
|
|
120
|
+
notice: string[];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The database a connection was refused by because it is closed to new
|
|
124
|
+
* connections (`ALLOW_CONNECTIONS false`), or null.
|
|
125
|
+
*
|
|
126
|
+
* The driver's `closedDatabaseName`, for the error a bare `pg` connect gives:
|
|
127
|
+
* the sentence is matched, not SQLSTATE 55000, which also covers failures that
|
|
128
|
+
* have nothing to do with connecting.
|
|
129
|
+
*/
|
|
130
|
+
export declare function closedDatabaseName(error: unknown): string | null;
|
|
111
131
|
/**
|
|
112
132
|
* Poll until `SELECT 1` succeeds on this connection string, or the bound runs out.
|
|
113
133
|
*
|
|
@@ -118,10 +138,21 @@ export type QueryReadiness = {
|
|
|
118
138
|
* three. Every error is retried: a reset, "the database system is starting up"
|
|
119
139
|
* and a refused connection all mean "not yet", and the bound is what turns
|
|
120
140
|
* "not yet" into an answer.
|
|
141
|
+
*
|
|
142
|
+
* Except one, which is not "not yet": a database closed to new connections
|
|
143
|
+
* (`closedDatabaseName`), which is what a `rebase db branch create --force`
|
|
144
|
+
* killed mid-copy leaves its source as. Waiting cannot open it, and it was
|
|
145
|
+
* waited on for the whole minute. It is opened again with the driver's
|
|
146
|
+
* `reopenClosedDatabases`, once, and then either answers or the wait ends at
|
|
147
|
+
* once, naming it.
|
|
121
148
|
*/
|
|
122
149
|
export declare function waitForQueryable(databaseUrl: string, options?: {
|
|
123
150
|
timeoutMs?: number;
|
|
124
151
|
intervalMs?: number;
|
|
152
|
+
/** Opens a closed database again. The driver's `reopenClosedDatabases` by default; injected for tests. */
|
|
153
|
+
reopenClosed?: (databaseUrl: string) => Promise<ClosedDatabaseHeal>;
|
|
154
|
+
/** Where the driver's lines about a database it opened go. */
|
|
155
|
+
onNotice?: (line: string) => void;
|
|
125
156
|
}): Promise<QueryReadiness>;
|
|
126
157
|
/**
|
|
127
158
|
* What a failed schema push printed, as lines to show.
|
|
@@ -204,6 +235,8 @@ export interface EnsureDevDatabaseOptions {
|
|
|
204
235
|
startDatabase?: (projectRoot: string) => Promise<void>;
|
|
205
236
|
/** How long a started database has to answer `SELECT 1`. Defaults to 60s. */
|
|
206
237
|
readyTimeoutMs?: number;
|
|
238
|
+
/** Opens a database closed to new connections again; see `waitForQueryable`. Injected for tests. */
|
|
239
|
+
reopenClosed?: (databaseUrl: string) => Promise<ClosedDatabaseHeal>;
|
|
207
240
|
/**
|
|
208
241
|
* Who answers on the compose port. Asks Docker by default; injected for the
|
|
209
242
|
* same reason `startDatabase` is.
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The address `rebase dev` is starting its backend on, handed to the
|
|
3
|
+
* `generate-sdk` child it runs.
|
|
4
|
+
*
|
|
5
|
+
* Dev regenerates the SDK at boot, beside the database start and before the
|
|
6
|
+
* backend binds — so before the backend writes `.rebase/state.json`, which is
|
|
7
|
+
* where `generate-sdk` otherwise reads the address for the README's example.
|
|
8
|
+
* Dev knows the port it hands the backend, and the child's environment is the
|
|
9
|
+
* only channel between them.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEV_BACKEND_URL_ENV = "REBASE_DEV_BACKEND_URL";
|
|
1
12
|
/**
|
|
2
13
|
* The port `rebase dev` derives from the project root path.
|
|
3
14
|
* Range: 3001–3999 (avoids privileged ports and common services).
|
package/dist/utils/project.d.ts
CHANGED
|
@@ -158,7 +158,11 @@ export interface JsonFailureIssue {
|
|
|
158
158
|
* failure mode of not doing it is an unparseable stream.
|
|
159
159
|
*/
|
|
160
160
|
export declare function failAsJson(message: string, code: string, hint?: string, issues?: JsonFailureIssue[]): never;
|
|
161
|
-
/**
|
|
161
|
+
/**
|
|
162
|
+
* Did the command line ask for machine-readable output? Read as the parser
|
|
163
|
+
* reads a switch: `--json`, `--json=true` and `--json=1` ask for it,
|
|
164
|
+
* `--json=false` and `--json=0` do not.
|
|
165
|
+
*/
|
|
162
166
|
export declare function wantsJsonOutput(argv?: readonly string[]): boolean;
|
|
163
167
|
/**
|
|
164
168
|
* Require the project root or exit with a helpful error.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection-string passwords, replaced before a line is printed.
|
|
3
|
+
*
|
|
4
|
+
* A refusal that repeats what was typed repeats a password with it, and these
|
|
5
|
+
* lines land in terminals, CI logs and issues. `user:***@` is the form every
|
|
6
|
+
* other message uses: the driver's `redactConnectionPasswords` in
|
|
7
|
+
* `@rebasepro/server-postgres`, which its entry does not export, and this
|
|
8
|
+
* mirrors. Covers the user-info password and a `password=` parameter.
|
|
9
|
+
*/
|
|
10
|
+
export declare function redactConnectionPasswords(text: string): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A refusal the CLI raised itself, having said why: nothing broke.
|
|
3
|
+
*
|
|
4
|
+
* `bin/rebase.js` ends every failure with "Re-run with --debug for the stack
|
|
5
|
+
* trace." That is right when something broke inside a command, and noise after
|
|
6
|
+
* a check that decided not to run — the compose-port refusal prints the
|
|
7
|
+
* container holding the port and how to move, and its stack points at the line
|
|
8
|
+
* that decided to refuse. A `UsageError` (`utils/args.ts`) is the same case for
|
|
9
|
+
* a command line that was wrong; this is the one for everything else.
|
|
10
|
+
*
|
|
11
|
+
* Marked with a property rather than left to `instanceof`, for `UsageError`'s
|
|
12
|
+
* reason: the reader is `bin/rebase.js`, which imports the bundle, so the class
|
|
13
|
+
* it would test against is not the one this file compiled to.
|
|
14
|
+
*/
|
|
15
|
+
export declare class Refusal extends Error {
|
|
16
|
+
readonly isRefusal = true;
|
|
17
|
+
constructor(message: string);
|
|
18
|
+
}
|
|
@@ -17,6 +17,10 @@ export declare function suggestCommand(typed: string, known: readonly string[]):
|
|
|
17
17
|
* `family` is empty for the top level: `rebase --help`, not `rebase --help`.
|
|
18
18
|
* `families` are the command families below this level, by name, whose
|
|
19
19
|
* commands a word may be one of.
|
|
20
|
+
*
|
|
21
|
+
* The typed word is repeated with any connection-string password masked: a URL
|
|
22
|
+
* pasted where the command goes (`rebase db "postgres://u:pw@h/db"`) is a
|
|
23
|
+
* word like any other here, and this line lands in terminals and CI logs.
|
|
20
24
|
*/
|
|
21
25
|
export declare function unknownCommandMessage(typed: string | undefined, known: readonly string[], family?: string, families?: Readonly<Record<string, readonly string[]>>): string;
|
|
22
26
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/cli",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.1",
|
|
4
4
|
"description": "Developer tools for Rebase projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
"inquirer": "14.0.2",
|
|
44
44
|
"jiti": "^2.7.0",
|
|
45
45
|
"pg": "^8.22.0",
|
|
46
|
-
"@rebasepro/
|
|
47
|
-
"@rebasepro/
|
|
48
|
-
"@rebasepro/server
|
|
49
|
-
"@rebasepro/
|
|
50
|
-
"@rebasepro/
|
|
51
|
-
"@rebasepro/
|
|
46
|
+
"@rebasepro/client": "0.25.1",
|
|
47
|
+
"@rebasepro/codegen": "0.25.1",
|
|
48
|
+
"@rebasepro/server": "0.25.1",
|
|
49
|
+
"@rebasepro/types": "0.25.1",
|
|
50
|
+
"@rebasepro/agent-skills": "0.25.1",
|
|
51
|
+
"@rebasepro/server-postgres": "0.25.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^26.1.2",
|
|
@@ -17,8 +17,9 @@ log names it); a policy is what opens it:
|
|
|
17
17
|
|
|
18
18
|
```sql
|
|
19
19
|
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
|
|
20
|
+
-- rebase.uid() is text: the cast makes this work for a uuid or integer column too.
|
|
20
21
|
CREATE POLICY your_table_owner ON your_table
|
|
21
|
-
FOR ALL USING (user_id = rebase.uid());
|
|
22
|
+
FOR ALL USING (user_id::text = rebase.uid());
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
`rebase.uid()`, `rebase.roles()` and `rebase.jwt()` are provided by Rebase and read the
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$generated": "Generated from config by `rebase resources`. Edit the declarations, not this file.",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"resources": [
|
|
5
|
+
{
|
|
6
|
+
"kind": "database",
|
|
7
|
+
"key": "(default)",
|
|
8
|
+
"engine": "postgres",
|
|
9
|
+
"transport": "server",
|
|
10
|
+
"usedBy": [
|
|
11
|
+
"collection:users"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"kind": "function",
|
|
16
|
+
"key": "hello",
|
|
17
|
+
"engine": "http",
|
|
18
|
+
"transport": "server",
|
|
19
|
+
"options": {
|
|
20
|
+
"portable": true,
|
|
21
|
+
"file": "backend/functions/hello.ts"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$generated": "Generated from config by `rebase resources`. Edit the declarations, not this file.",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"resources": [
|
|
5
|
+
{
|
|
6
|
+
"kind": "database",
|
|
7
|
+
"key": "(default)",
|
|
8
|
+
"engine": "postgres",
|
|
9
|
+
"transport": "server",
|
|
10
|
+
"usedBy": [
|
|
11
|
+
"collection:authors",
|
|
12
|
+
"collection:posts",
|
|
13
|
+
"collection:tags",
|
|
14
|
+
"collection:users"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"kind": "function",
|
|
19
|
+
"key": "hello",
|
|
20
|
+
"engine": "http",
|
|
21
|
+
"transport": "server",
|
|
22
|
+
"options": {
|
|
23
|
+
"portable": true,
|
|
24
|
+
"file": "backend/functions/hello.ts"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$generated": "Generated from config by `rebase resources`. Edit the declarations, not this file.",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"resources": [
|
|
5
|
+
{
|
|
6
|
+
"kind": "database",
|
|
7
|
+
"key": "(default)",
|
|
8
|
+
"engine": "postgres",
|
|
9
|
+
"transport": "server",
|
|
10
|
+
"usedBy": [
|
|
11
|
+
"collection:categories",
|
|
12
|
+
"collection:orders",
|
|
13
|
+
"collection:products",
|
|
14
|
+
"collection:users"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"kind": "function",
|
|
19
|
+
"key": "hello",
|
|
20
|
+
"engine": "http",
|
|
21
|
+
"transport": "server",
|
|
22
|
+
"options": {
|
|
23
|
+
"portable": true,
|
|
24
|
+
"file": "backend/functions/hello.ts"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$generated": "Generated from config by `rebase resources`. Edit the declarations, not this file.",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"resources": [
|
|
5
|
+
{
|
|
6
|
+
"kind": "database",
|
|
7
|
+
"key": "(default)",
|
|
8
|
+
"engine": "postgres",
|
|
9
|
+
"transport": "server"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"kind": "function",
|
|
13
|
+
"key": "hello",
|
|
14
|
+
"engine": "http",
|
|
15
|
+
"transport": "server",
|
|
16
|
+
"options": {
|
|
17
|
+
"portable": true,
|
|
18
|
+
"file": "backend/functions/hello.ts"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -169,10 +169,11 @@ VITE_API_URL=
|
|
|
169
169
|
# STORAGE_TYPE=local
|
|
170
170
|
|
|
171
171
|
# --- Local filesystem (default — great for dev and single-server deployments) --
|
|
172
|
-
# Unset, files land in <project>/uploads, under `rebase dev`
|
|
173
|
-
# `rebase eject` writes alike. A relative path is read from
|
|
174
|
-
# server runs in: the project root under `rebase dev`,
|
|
175
|
-
# `rebase eject`. So ./uploads names two places, and an absolute
|
|
172
|
+
# Unset, files land in <project>/uploads, under `rebase dev`, `rebase start`
|
|
173
|
+
# and in the server `rebase eject` writes alike. A relative path is read from
|
|
174
|
+
# the directory the server runs in: the project root under `rebase dev`,
|
|
175
|
+
# backend/ after `rebase eject`. So ./uploads names two places, and an absolute
|
|
176
|
+
# path one.
|
|
176
177
|
# STORAGE_PATH=/absolute/path/to/uploads
|
|
177
178
|
# FORCE_LOCAL_STORAGE=true # Only with a durable volume holding STORAGE_PATH
|
|
178
179
|
|
|
@@ -238,6 +239,12 @@ VITE_API_URL=
|
|
|
238
239
|
# Generate with: node -e "console.log(require('crypto').randomBytes(48).toString('base64'))"
|
|
239
240
|
# REBASE_SERVICE_KEY=
|
|
240
241
|
|
|
242
|
+
# Where `rebase api-keys`, `rebase storage` and `rebase auth reset-password`
|
|
243
|
+
# send that key. Leave it unset under `rebase dev`: they find the server it
|
|
244
|
+
# started, and send nothing while it is stopped. Set it when the server runs
|
|
245
|
+
# some other way — `rebase start`, a container, another host.
|
|
246
|
+
# REBASE_BASE_URL=http://localhost:3001
|
|
247
|
+
|
|
241
248
|
# Disable switching to database-level roles (e.g. 'admin') for Studio SQL execution (optional)
|
|
242
249
|
# Falls back to false. Useful for databases with application-only roles or managed setups.
|
|
243
250
|
# DISABLE_DB_ROLE_SWITCHING=true
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
|
|
3
1
|
import "@fontsource/jetbrains-mono";
|
|
4
2
|
import "@fontsource-variable/inter";
|
|
5
3
|
import "@fontsource-variable/instrument-sans";
|
|
@@ -19,14 +17,17 @@ import { collections } from "virtual:rebase-collections";
|
|
|
19
17
|
const API_URL = import.meta.env.VITE_API_URL;
|
|
20
18
|
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID;
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
// One client for the page, created when this module loads. Inside a component,
|
|
21
|
+
// even in a `useMemo`, `<React.StrictMode>` builds a second one in development,
|
|
22
|
+
// and both restore the session and open their own connections.
|
|
23
|
+
const rebaseClient = createRebaseClient({
|
|
24
|
+
baseUrl: API_URL,
|
|
25
|
+
// Store the refresh token in an httpOnly cookie (XSS-safe) rather than
|
|
26
|
+
// localStorage. The backend issues it via `auth.cookieAuth`.
|
|
27
|
+
auth: { authFlowMode: "cookie" }
|
|
28
|
+
});
|
|
29
29
|
|
|
30
|
+
export function App() {
|
|
30
31
|
const authController = useRebaseAuthController({
|
|
31
32
|
client: rebaseClient,
|
|
32
33
|
googleClientId: GOOGLE_CLIENT_ID
|
|
@@ -3,7 +3,7 @@ import { defineConfig } from "vite";
|
|
|
3
3
|
import react from "@vitejs/plugin-react";
|
|
4
4
|
import svgr from "vite-plugin-svgr";
|
|
5
5
|
import tailwindcss from "@tailwindcss/vite";
|
|
6
|
-
import { rebaseCollectionsPlugin } from "@rebasepro/app/vitePlugin";
|
|
6
|
+
import { rebaseCollectionsPlugin, rebaseManualChunks } from "@rebasepro/app/vitePlugin";
|
|
7
7
|
|
|
8
8
|
export default defineConfig({
|
|
9
9
|
// `import.meta.dirname`, not `__dirname`: vite 8 warns that the latter is
|
|
@@ -46,78 +46,12 @@ export default defineConfig({
|
|
|
46
46
|
chunkSizeWarningLimit: Infinity,
|
|
47
47
|
rollupOptions: {
|
|
48
48
|
output: {
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
// that. Switch to the import in the first release that
|
|
56
|
-
// publishes it.
|
|
57
|
-
manualChunks(id) {
|
|
58
|
-
// Heavy vendor libraries — split into individually cached chunks.
|
|
59
|
-
//
|
|
60
|
-
// A name here says these modules travel TOGETHER. It does not
|
|
61
|
-
// say they travel late: a chunk becomes a static dependency of
|
|
62
|
-
// the entry — and so a `modulepreload` in index.html — the
|
|
63
|
-
// moment any one module in it is statically reachable. Naming
|
|
64
|
-
// a library that is only partly lazy therefore drags the lazy
|
|
65
|
-
// part onto the critical path. Read the two exceptions below
|
|
66
|
-
// before adding a line.
|
|
67
|
-
|
|
68
|
-
// @rollup/plugin-commonjs emits its shared helpers as two
|
|
69
|
-
// virtual modules ("\0commonjsHelpers.js" and
|
|
70
|
-
// "\0commonjs-dynamic-modules") that every CommonJS
|
|
71
|
-
// dependency reaches, the entry's included. They match no
|
|
72
|
-
// rule below, so Rollup parks them in one of the chunks
|
|
73
|
-
// that use them — and it chose `vendor-exceljs`, which
|
|
74
|
-
// meant the entry statically imported 940 kB of
|
|
75
|
-
// spreadsheet reader to get a ten-line `require` shim.
|
|
76
|
-
// Give the helpers a chunk of their own so they can never
|
|
77
|
-
// anchor a heavy one to the critical path.
|
|
78
|
-
if (id.includes("commonjsHelpers") || id.includes("commonjs-dynamic-modules")) return "vendor-commonjs-helpers";
|
|
79
|
-
|
|
80
|
-
// Small libraries the entry needs, named before the heavy groups. A name
|
|
81
|
-
// takes the unassigned dependencies of its modules with it, so
|
|
82
|
-
// `prism-react-renderer` took `clsx` — which the kit imports statically — and
|
|
83
|
-
// the entry imported 85 kB of syntax highlighter for a 633-byte helper;
|
|
84
|
-
// React core landed in `vendor-dnd` the same way.
|
|
85
|
-
if (/[\\/]node_modules[\\/](react|scheduler|use-sync-external-store)[\\/]/.test(id)) return "vendor-react";
|
|
86
|
-
if (/[\\/]node_modules[\\/]clsx[\\/]/.test(id)) return "vendor-clsx";
|
|
87
|
-
|
|
88
|
-
if (id.includes("exceljs")) return "vendor-exceljs";
|
|
89
|
-
if (id.includes("prosemirror")) return "vendor-prosemirror";
|
|
90
|
-
if (id.includes("monaco-editor") || id.includes("@monaco-editor")) return "vendor-monaco";
|
|
91
|
-
if (id.includes("@xyflow") || id.includes("dagre")) return "vendor-xyflow";
|
|
92
|
-
if (id.includes("@dnd-kit")) return "vendor-dnd";
|
|
93
|
-
if (id.includes("prism-react-renderer")) return "vendor-prism";
|
|
94
|
-
if (id.includes("markdown-it")) return "vendor-markdown";
|
|
95
|
-
if (id.includes("react-dropzone")) return "vendor-dropzone";
|
|
96
|
-
// date-fns core only. The ~77 locales are imported one at a
|
|
97
|
-
// time by the admin's date preview; sharing a chunk name with
|
|
98
|
-
// the core would make all of them eager again.
|
|
99
|
-
if (id.includes("date-fns/locale")) return undefined;
|
|
100
|
-
if (id.includes("date-fns")) return "vendor-datefns";
|
|
101
|
-
if (id.includes("fuse.js")) return "vendor-fuse";
|
|
102
|
-
if (id.includes("node_modules/react-dom/")) return "vendor-react-dom";
|
|
103
|
-
if (id.includes("node_modules/react-router") || id.includes("node_modules/@remix-run")) return "vendor-react-router";
|
|
104
|
-
if (id.includes("node_modules/@radix-ui/")) return "vendor-radix";
|
|
105
|
-
if (id.includes("node_modules/framer-motion/")) return "vendor-framer-motion";
|
|
106
|
-
if (id.includes("node_modules/zod/")) return "vendor-zod";
|
|
107
|
-
if (id.includes("node_modules/i18next") || id.includes("node_modules/react-i18next")) return "vendor-i18next";
|
|
108
|
-
if (id.includes("node_modules/@floating-ui/")) return "vendor-floating-ui";
|
|
109
|
-
if (id.includes("node_modules/tailwind-merge/")) return "vendor-tailwind-merge";
|
|
110
|
-
if (id.includes("node_modules/notistack/")) return "vendor-notistack";
|
|
111
|
-
|
|
112
|
-
// lucide-react has no line on purpose. The ~130 icons the
|
|
113
|
-
// chrome imports by name are static; the by-name lookup map
|
|
114
|
-
// is fetched on demand. One chunk name cannot hold both apart,
|
|
115
|
-
// and naming it welded 822 kB of icons into the preload set.
|
|
116
|
-
// Left to Rollup, the named icons land in the entry and the
|
|
117
|
-
// map gets its own async chunk.
|
|
118
|
-
|
|
119
|
-
return undefined;
|
|
120
|
-
}
|
|
49
|
+
// How the admin's dependencies are split into cached chunks,
|
|
50
|
+
// from the package rather than copied in, so an upgrade of
|
|
51
|
+
// `@rebasepro/app` brings its improvements with it. Write your
|
|
52
|
+
// own function here if you need to split this app's code too,
|
|
53
|
+
// and return `rebaseManualChunks(id)` for the rest.
|
|
54
|
+
manualChunks: rebaseManualChunks
|
|
121
55
|
}
|
|
122
56
|
}
|
|
123
57
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api-keys-Bc4FJKYp.js","names":[],"sources":["../src/commands/api-keys.ts"],"sourcesContent":["/**\n * CLI command: rebase api-keys <action>\n *\n * The project's service keys, through `/api/admin/api-keys`, authenticated with\n * the service key from `.env`.\n *\n * Subcommands:\n * list — List the service keys (masked): kind, scopes, roles\n * get — Show one key\n * create — Create a service key holding the scopes named on the line\n * revoke — Revoke a key\n * scopes — List the scopes this backend knows, and which the caller holds\n */\nimport chalk from \"chalk\";\nimport type arg from \"arg\";\nimport { createRebaseClient, RebaseApiError } from \"@rebasepro/client\";\nimport {\n ADMIN_SCOPES,\n DATA_PLANE_SCOPES,\n KEY_MANAGEMENT_SCOPES,\n parseScope,\n type ApiKeyMasked,\n type ApiKeyWithSecret,\n type CreateApiKeyRequest,\n type ScopeSummary\n} from \"@rebasepro/types\";\nimport {\n requireProjectRoot,\n readEnvFile\n} from \"../utils/project\";\nimport { parseCommandArgs, UsageError, wantsHelp } from \"../utils/args\";\nimport { unknownCommand } from \"../utils/unknown-command\";\nimport { cliUserAgent } from \"../utils/version\";\nimport { serverUnreachableHint } from \"../utils/dev-server\";\nimport fs from \"fs\";\nimport path from \"path\";\n\n/** Everything the switch below dispatches, for the did-you-mean. */\nexport const API_KEYS_SUBCOMMANDS = [\"list\", \"get\", \"create\", \"revoke\", \"scopes\"] as const;\n\n/* ═══════════════════════════════════════════════════════════════\n Env helper — reads SERVICE_KEY and PORT from .env\n ═══════════════════════════════════════════════════════════════ */\n\n/**\n * Was a hand-rolled `indexOf(\"=\")` loop. It keyed `export KEY=value` as\n * `export KEY` and carried a trailing `# comment` into the value — so a key\n * that was present read as absent, or reached an `Authorization` header with a\n * comment attached and came back 401. See `readEnvFile`.\n */\nconst loadEnv = readEnvFile;\n\n/**\n * Where this project's backend is — or why the CLI will not say.\n *\n * `configured` is an address the developer named; `running` is the one this\n * project's backend recorded when it bound, and is still serving on. There is\n * no third, guessed, answer: the request carries the project's service key —\n * the `admin` role, every scope — and an address the CLI only guessed is one it\n * would hand that key to without knowing whose server answers.\n */\nexport type BackendAddress =\n | { url: string; source: \"configured\" | \"running\" }\n | { url: null; reason: string };\n\n/** Whether a process with this pid is alive and ours to signal. */\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n // ESRCH: gone. EPERM: alive, but another user's — not this project's backend.\n return false;\n }\n}\n\n/**\n * The address the service key may be sent to. Shared with `rebase storage`.\n *\n * In order:\n *\n * 1. `REBASE_BASE_URL`, in the shell or in `.env` — the developer said where.\n * 2. `.rebase/state.json`, which this project's backend writes when it binds\n * and removes when it stops — and only while the pid it names is alive,\n * because a backend that was killed leaves the file behind.\n *\n * Nothing else. `.env`'s `PORT` was the fallback, and every scaffold writes\n * `PORT=3001`: with `rebase dev` stopped, `rebase api-keys list` sent this\n * project's service key to whatever listened on 3001 — on the tester's machine,\n * another project's server. `.rebase-dev-url` is not enough on its own either:\n * a `rebase dev` that was killed leaves it behind, pointing at a port some\n * other server may have taken since.\n */\nexport function resolveBackendAddress(env: Record<string, string>, projectRoot: string): BackendAddress {\n const configured = process.env.REBASE_BASE_URL?.trim() || env.REBASE_BASE_URL?.trim();\n if (configured) return { url: configured, source: \"configured\" };\n\n const state = readBackendState(projectRoot);\n if (state && isAlive(state.pid)) return { url: state.url, source: \"running\" };\n\n const last = state ? ` The last one recorded here, at ${state.url} (pid ${state.pid}), has stopped.` : \"\";\n return {\n url: null,\n reason: `This project's server is not running, so the service key was not sent anywhere.${last} ` +\n \"Start it with `rebase dev`. If it runs some other way — `rebase start`, a container — \" +\n \"set REBASE_BASE_URL in .env to its address.\"\n };\n}\n\n/** What `.rebase/state.json` says about the backend that wrote it, or null. */\nfunction readBackendState(projectRoot: string): { url: string; pid: number } | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(path.join(projectRoot, \".rebase\", \"state.json\"), \"utf-8\"));\n } catch {\n return null;\n }\n if (!isRecord(parsed) || typeof parsed.pid !== \"number\" || !Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;\n const url = typeof parsed.baseUrl === \"string\" && parsed.baseUrl.trim()\n ? parsed.baseUrl.trim()\n : typeof parsed.port === \"number\" ? `http://localhost:${parsed.port}` : null;\n\n return url ? { url, pid: parsed.pid } : null;\n}\n\n/**\n * The address to send the service key to, or the end of the command.\n *\n * Exits before any request is made: the refusal is the point.\n */\nexport function requireBackendUrl(env: Record<string, string>, projectRoot: string): string {\n const address = resolveBackendAddress(env, projectRoot);\n if (address.url !== null) return address.url;\n\n console.error(chalk.red(\"✗ No server to send the request to.\"));\n console.error(chalk.gray(` ${address.reason}`));\n process.exit(1);\n}\n\n/* ═══════════════════════════════════════════════════════════════\n The backend, through the SDK\n ═══════════════════════════════════════════════════════════════ */\n\ntype ProjectClient = ReturnType<typeof createRebaseClient>;\n\ninterface Connection {\n client: ProjectClient;\n baseUrl: string;\n}\n\n/**\n * An SDK client for this project's backend, holding the service key.\n *\n * The service key is the project's own machine identity — the `admin` role,\n * every scope — so it is what may list, mint and revoke service keys. It is a\n * static bearer token: no session to restore or refresh, and no socket.\n */\nfunction connect(): Connection {\n const projectRoot = requireProjectRoot();\n const env = loadEnv(projectRoot);\n const serviceKey = env.SERVICE_KEY || env.REBASE_SERVICE_KEY;\n\n if (!serviceKey) {\n console.error(chalk.red(\"✗ SERVICE_KEY not found in .env — required to manage API keys.\"));\n process.exit(1);\n }\n const baseUrl = requireBackendUrl(env, projectRoot);\n\n const client = createRebaseClient({\n baseUrl,\n token: serviceKey,\n realtime: false,\n headers: { \"User-Agent\": cliUserAgent() },\n auth: { persistSession: false, autoRefresh: false }\n });\n return { client, baseUrl };\n}\n\n/**\n * What to print when a request fails: the server's error code and message,\n * which is what says why — `SCOPE_EXCEEDS_CREATOR`, `INVALID_SCOPES`,\n * `ROLE_EXCEEDS_CREATOR`, `KEY_MANAGEMENT_SCOPE` — and a hint where one helps.\n *\n * Exported so its tests can read the lines without a server.\n */\nexport function describeFailure(action: string, error: unknown, baseUrl: string): string[] {\n if (!(error instanceof RebaseApiError)) {\n return [`✗ ${action}: ${error instanceof Error ? error.message : String(error)}`];\n }\n if (error.status === 0 || error.code === \"NETWORK_ERROR\") {\n return [`✗ ${action}: ${error.message}`, ...serverUnreachableHint(baseUrl)];\n }\n\n const label = [error.code, error.status ? `(${error.status})` : undefined].filter(Boolean).join(\" \");\n const lines = [`✗ ${action}${label ? ` — ${label}` : \"\"}: ${error.message}`];\n\n if (error.status === 401) {\n lines.push(` The server at ${baseUrl} did not accept SERVICE_KEY from .env — is it this project's server?`);\n }\n // The message names the target that is not there; what *is* there rides\n // in `details`, and is the thing to pick from.\n if (error.code === \"UNKNOWN_SCOPE_TARGET\" && isRecord(error.details)) {\n for (const [kind, title] of [[\"collections\", \"Collections\"], [\"buckets\", \"Buckets\"], [\"functions\", \"Functions\"]] as const) {\n const names = error.details[kind];\n if (Array.isArray(names) && names.length > 0) {\n lines.push(` ${title}: ${names.map(String).join(\", \")}`);\n }\n }\n }\n return lines;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction fail(action: string, error: unknown, baseUrl: string): never {\n for (const line of describeFailure(action, error, baseUrl)) {\n console.error(line.startsWith(\"✗\") ? chalk.red(line) : chalk.gray(line));\n }\n process.exit(1);\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Entry\n ═══════════════════════════════════════════════════════════════ */\n\nexport async function apiKeysCommand(subcommand: string | undefined, rawArgs: string[]): Promise<void> {\n // `--help` is answered before dispatch, never by a handler. `cli.ts` only\n // rewrites the subcommand to `\"--help\"` when none was named, so\n // `rebase api-keys list --help` used to *list the keys* and\n // `rebase api-keys revoke --help` sent `DELETE /api/admin/api-keys/--help`.\n if (!subcommand || subcommand === \"--help\" || wantsHelp(rawArgs)) {\n printApiKeysHelp();\n return;\n }\n\n switch (subcommand) {\n case \"list\":\n await listKeys(rawArgs);\n break;\n case \"get\":\n await getKey(rawArgs);\n break;\n case \"create\":\n await createKey(rawArgs);\n break;\n case \"revoke\":\n await revokeKey(rawArgs);\n break;\n case \"scopes\":\n await listScopes(rawArgs);\n break;\n default:\n // The shape every other family uses. This one printed a bare line\n // to stderr *and* the whole help page to stdout, so a typo wrote\n // twenty lines to a stream a `--json` caller reads, with no `✗`, no\n // pointer at `--help`, and no did-you-mean — `craete` is one\n // transposition from `create`.\n unknownCommand(subcommand, API_KEYS_SUBCOMMANDS, \"api-keys\");\n }\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Showing a key\n ═══════════════════════════════════════════════════════════════ */\n\nfunction keyStatus(key: ApiKeyMasked, now: Date): string {\n if (key.revoked_at) return chalk.red(\"revoked\");\n if (key.expires_at && new Date(key.expires_at) < now) return chalk.yellow(\"expired\");\n return chalk.green(\"active\");\n}\n\n/**\n * The lines that describe one key: what it is, what it may do and whom it\n * runs as.\n *\n * A service key runs as the RLS role `service` plus its own `roles`; a\n * personal key runs as its owner's roles as they are when it is used, so it\n * has none of its own to show.\n *\n * Exported so its tests can read the lines without a server.\n */\nexport function describeKey(key: ApiKeyMasked, now: Date = new Date()): string[] {\n const field = (label: string, value: string) => ` ${chalk.gray(`${label}:`.padEnd(12))}${value}`;\n const roles = key.kind === \"personal\"\n ? `its owner's (${key.owner_uid ?? \"unknown\"})`\n : [\"service\", ...key.roles].join(\", \");\n\n const lines = [\n ` ${chalk.bold(key.name)} ${chalk.gray(`[${key.key_prefix}•••]`)} ${keyStatus(key, now)}`,\n field(\"ID\", key.id),\n field(\"Kind\", key.kind),\n field(\"Scopes\", key.scopes.length > 0 ? key.scopes.join(\", \") : chalk.gray(\"none\")),\n field(\"Roles\", roles)\n ];\n if (key.rate_limit !== null) lines.push(field(\"Rate limit\", `${key.rate_limit} requests / 15 min`));\n if (key.expires_at) lines.push(field(\"Expires\", new Date(key.expires_at).toLocaleDateString()));\n lines.push(field(\"Created\", new Date(key.created_at).toLocaleDateString()));\n if (key.last_used_at) lines.push(field(\"Last used\", new Date(key.last_used_at).toLocaleDateString()));\n return lines;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n list\n ═══════════════════════════════════════════════════════════════ */\n\n/** The flags `rebase api-keys list` takes: none of its own. */\nexport const LIST_KEYS_FLAGS = {} as const;\n\nasync function listKeys(rawArgs: string[]): Promise<void> {\n parseCommandArgs({\n spec: LIST_KEYS_FLAGS,\n rawArgs,\n commandWords: 2,\n command: \"api-keys list\",\n maxPositionals: 0\n });\n\n const { client, baseUrl } = connect();\n let keys: ApiKeyMasked[];\n try {\n ({ keys } = await client.apiKeys.listKeys());\n } catch (e: unknown) {\n fail(\"Failed to list API keys\", e, baseUrl);\n }\n\n console.log(\"\");\n console.log(chalk.bold(\" 🔑 API Keys\"));\n console.log(\"\");\n\n if (keys.length === 0) {\n console.log(chalk.gray(\" No API keys found.\"));\n console.log(\"\");\n return;\n }\n\n const now = new Date();\n for (const key of keys) {\n for (const line of describeKey(key, now)) console.log(line);\n console.log(\"\");\n }\n}\n\n/* ═══════════════════════════════════════════════════════════════\n get\n ═══════════════════════════════════════════════════════════════ */\n\n/** The flags `rebase api-keys get` takes. */\nexport const GET_KEY_FLAGS = {\n \"--id\": String\n} as const;\n\n/**\n * Which key this invocation names, as a positional or as `--id`.\n *\n * Exported so its tests can drive the real parser rather than a copy of it.\n */\nexport function resolveGetKeyArgs(rawArgs: string[]): { id?: string } {\n const { flags, positionals } = parseCommandArgs({\n spec: GET_KEY_FLAGS,\n rawArgs,\n commandWords: 2,\n command: \"api-keys get\",\n maxPositionals: 1\n });\n\n return { id: flags[\"--id\"] || positionals[0] };\n}\n\nasync function getKey(rawArgs: string[]): Promise<void> {\n const { id } = resolveGetKeyArgs(rawArgs);\n if (!id) {\n throw new UsageError(\"Key ID is required: rebase api-keys get <key-id> (`rebase api-keys list` shows them).\");\n }\n\n const { client, baseUrl } = connect();\n let key: ApiKeyMasked;\n try {\n ({ key } = await client.apiKeys.getKey(id));\n } catch (e: unknown) {\n fail(\"Failed to read API key\", e, baseUrl);\n }\n\n console.log(\"\");\n for (const line of describeKey(key)) console.log(line);\n console.log(\"\");\n}\n\n/* ═══════════════════════════════════════════════════════════════\n create\n ═══════════════════════════════════════════════════════════════ */\n\n/** The flags `rebase api-keys create` takes. */\nexport const CREATE_KEY_FLAGS = {\n \"--name\": String,\n \"--scopes\": [String],\n \"--full-access\": Boolean,\n \"--roles\": [String],\n \"--rate-limit\": Number,\n \"--expires-in\": String,\n \"--expires-at\": String,\n \"-n\": \"--name\"\n} satisfies arg.Spec;\n\n/**\n * Options `create` refuses by name, each with the flag that does that job.\n *\n * Checked before the line is parsed: the strict parser would answer them\n * \"unknown option\", which does not say what to type instead.\n */\nexport const RETIRED_CREATE_OPTIONS: Readonly<Record<string, string>> = {\n \"--permissions\":\n \"--permissions is gone: name what the key may do with --scopes, e.g. \" +\n \"--scopes data:read:posts,data:write:posts (`rebase api-keys scopes` lists them).\",\n \"--admin\":\n \"--admin is gone: give the key the RLS role with --roles admin, and the admin-plane scopes it needs \" +\n \"with --scopes, e.g. --scopes users:read,schema:read.\",\n \"--expires\":\n \"--expires is gone: use --expires-in <days> (e.g. --expires-in 90) or --expires-at <ISO date>.\"\n};\n\n/** Refuse a retired option, wherever on the line and however it is spelled. */\nfunction refuseRetiredOptions(rawArgs: string[]): void {\n for (const token of rawArgs.slice(2)) {\n // Past `--` everything is a value, never an option.\n if (token === \"--\") return;\n const name = token.split(\"=\", 1)[0];\n const replacement = Object.prototype.hasOwnProperty.call(RETIRED_CREATE_OPTIONS, name)\n ? RETIRED_CREATE_OPTIONS[name]\n : undefined;\n if (replacement) throw new UsageError(replacement);\n }\n}\n\n/**\n * A list flag's values, whether repeated (`--scopes a --scopes b`) or\n * comma-separated (`--scopes a,b`) — or both — trimmed and deduplicated.\n * Whitespace separates too: no scope or role name contains any.\n */\nfunction splitList(values: readonly string[] | undefined): string[] {\n const out = new Set<string>();\n for (const value of values ?? []) {\n for (const entry of value.split(/[\\s,]+/)) {\n if (entry) out.add(entry);\n }\n }\n return [...out];\n}\n\nconst DAY_MS = 86_400_000;\n\nfunction resolveExpiry(expiresIn: string | undefined, expiresAt: string | undefined, now: Date): string | null {\n if (expiresIn !== undefined && expiresAt !== undefined) {\n throw new UsageError(\"Pass --expires-in or --expires-at, not both.\");\n }\n if (expiresIn !== undefined) {\n const days = /^\\d+$/.test(expiresIn.trim()) ? Number(expiresIn.trim()) : NaN;\n if (!Number.isSafeInteger(days) || days < 1) {\n throw new UsageError(`--expires-in takes a whole number of days, e.g. --expires-in 90 (got \"${expiresIn}\").`);\n }\n return new Date(now.getTime() + days * DAY_MS).toISOString();\n }\n if (expiresAt !== undefined) {\n const parsed = new Date(expiresAt);\n if (isNaN(parsed.getTime())) {\n throw new UsageError(`--expires-at takes an ISO date, e.g. --expires-at 2027-01-31 (got \"${expiresAt}\").`);\n }\n if (parsed.getTime() <= now.getTime()) {\n throw new UsageError(`--expires-at must be in the future (got ${parsed.toISOString()}).`);\n }\n return parsed.toISOString();\n }\n return null;\n}\n\n/** What `rebase api-keys create` was asked to mint. */\nexport interface CreateKeyPlan {\n name: string;\n /** The scopes named with `--scopes`, or `\"held\"` for `--full-access`. */\n scopes: string[] | \"held\";\n /** RLS roles beside `service`. */\n roles: string[];\n rate_limit: number | null;\n expires_at: string | null;\n}\n\n/**\n * What this invocation asks to be created.\n *\n * The name may be given either way — `--name \"My Key\"` or as the single\n * positional — and under the old permissive parse an undeclared flag became\n * that positional: `rebase api-keys create --debug --full-access` created a\n * key called `--debug` with access to everything, and `--debug` is what the\n * CLI prints after every failure as the thing to re-run with. Strict parsing\n * makes the flag an error instead of a name.\n *\n * What the key may do has to be asked for by name — `--scopes` or\n * `--full-access` — never defaulted: a forgotten flag must not mint a key that\n * reaches everything.\n *\n * Exported so its tests can drive the real parser rather than a copy of it.\n */\nexport function resolveCreateKeyArgs(rawArgs: string[], now: Date = new Date()): CreateKeyPlan {\n refuseRetiredOptions(rawArgs);\n\n const { flags, positionals } = parseCommandArgs({\n spec: CREATE_KEY_FLAGS,\n rawArgs,\n commandWords: 2,\n command: \"api-keys create\",\n maxPositionals: 1\n });\n\n const name = (flags[\"--name\"] || positionals[0] || \"\").trim();\n if (!name) {\n throw new UsageError('A key needs a name: rebase api-keys create \"CI\" --scopes data:read');\n }\n\n const named = splitList(flags[\"--scopes\"]);\n const fullAccess = flags[\"--full-access\"] === true;\n if (fullAccess && named.length > 0) {\n throw new UsageError(\"Pass --scopes or --full-access, not both: --full-access already means every scope you hold.\");\n }\n if (!fullAccess && named.length === 0) {\n throw new UsageError(\n \"Name what the key may do: --scopes data:read:posts,data:write:posts, or --full-access for every \" +\n \"scope you hold. `rebase api-keys scopes` lists them.\"\n );\n }\n\n const rateLimit = flags[\"--rate-limit\"];\n if (rateLimit !== undefined && (!Number.isSafeInteger(rateLimit) || rateLimit < 1)) {\n throw new UsageError(\"--rate-limit takes a whole number of requests per 15-minute window, e.g. --rate-limit 500.\");\n }\n\n return {\n name,\n scopes: fullAccess ? \"held\" : named,\n roles: splitList(flags[\"--roles\"]),\n rate_limit: rateLimit ?? null,\n expires_at: resolveExpiry(flags[\"--expires-in\"], flags[\"--expires-at\"], now)\n };\n}\n\n/**\n * The scopes `--full-access` puts on a key: every one the caller holds, less\n * key management, which no key may hold.\n *\n * Exported so its tests can check the subtraction without a server.\n */\nexport function grantableScopes(held: readonly string[]): string[] {\n const management: readonly string[] = KEY_MANAGEMENT_SCOPES;\n return held.filter(entry => !management.includes(parseScope(entry)?.scope ?? entry));\n}\n\nasync function createKey(rawArgs: string[]): Promise<void> {\n const plan = resolveCreateKeyArgs(rawArgs);\n const { client, baseUrl } = connect();\n\n let scopes: string[];\n if (plan.scopes === \"held\") {\n let held: string[];\n try {\n ({ held } = await client.personalKeys.listScopes());\n } catch (e: unknown) {\n fail(\"Failed to read the scopes you hold\", e, baseUrl);\n }\n scopes = grantableScopes(held);\n if (scopes.length === 0) {\n console.error(chalk.red(\"✗ You hold no scope a key may carry, so --full-access has nothing to grant.\"));\n process.exit(1);\n }\n } else {\n scopes = plan.scopes;\n }\n\n const request: CreateApiKeyRequest = {\n name: plan.name,\n scopes,\n ...(plan.roles.length > 0 ? { roles: plan.roles } : {}),\n ...(plan.rate_limit !== null ? { rate_limit: plan.rate_limit } : {}),\n ...(plan.expires_at !== null ? { expires_at: plan.expires_at } : {})\n };\n\n let created: ApiKeyWithSecret;\n try {\n ({ key: created } = await client.apiKeys.createKey(request));\n } catch (e: unknown) {\n fail(\"Failed to create API key\", e, baseUrl);\n }\n\n console.log(\"\");\n console.log(chalk.bold.green(\" ✓ API key created\"));\n console.log(\"\");\n for (const line of describeKey(created)) console.log(line);\n console.log(\"\");\n console.log(chalk.bold.yellow(\" ⚠ Copy your key now — it won't be shown again:\"));\n console.log(\"\");\n console.log(` ${chalk.cyan(created.key)}`);\n console.log(\"\");\n}\n\n/* ═══════════════════════════════════════════════════════════════\n revoke\n ═══════════════════════════════════════════════════════════════ */\n\n/** The flags `rebase api-keys revoke` takes. */\nexport const REVOKE_KEY_FLAGS = {\n \"--id\": String\n} as const;\n\n/**\n * Which key this invocation names.\n *\n * The id is a positional, so the permissive parse handed one straight to the\n * DELETE: `rebase api-keys revoke --foo` sent\n * `DELETE /api/admin/api-keys/--foo`, and `rebase --debug api-keys revoke <id>`\n * shifted the words along and revoked the key named `revoke`.\n *\n * Exported so its tests can drive the real parser rather than a copy of it.\n */\nexport function resolveRevokeKeyArgs(rawArgs: string[]): { id?: string } {\n const { flags, positionals } = parseCommandArgs({\n spec: REVOKE_KEY_FLAGS,\n rawArgs,\n commandWords: 2,\n command: \"api-keys revoke\",\n maxPositionals: 1\n });\n\n return { id: flags[\"--id\"] || positionals[0] };\n}\n\nasync function revokeKey(rawArgs: string[]): Promise<void> {\n const { id } = resolveRevokeKeyArgs(rawArgs);\n\n if (!id) {\n throw new UsageError(\"Key ID is required: rebase api-keys revoke <key-id> (`rebase api-keys list` shows them).\");\n }\n\n const { client, baseUrl } = connect();\n try {\n await client.apiKeys.revokeKey(id);\n } catch (e: unknown) {\n fail(\"Failed to revoke API key\", e, baseUrl);\n }\n\n console.log(\"\");\n console.log(chalk.bold.green(\" ✓ API key revoked\"));\n console.log(` ${chalk.gray(\"ID:\")} ${id}`);\n console.log(\"\");\n}\n\n/* ═══════════════════════════════════════════════════════════════\n scopes\n ═══════════════════════════════════════════════════════════════ */\n\n/** The flags `rebase api-keys scopes` takes: none of its own. */\nexport const SCOPES_LIST_FLAGS = {} as const;\n\nconst PLANES: ReadonlyArray<{ plane: ScopeSummary[\"plane\"]; title: string; note: string }> = [\n { plane: \"data\", title: \"Data\", note: \"narrow what a key reaches; row-level security still decides the rows\" },\n { plane: \"admin\", title: \"Admin\", note: \"nobody holds these unless a role grants them; the admin role holds all\" },\n { plane: \"app\", title: \"App\", note: \"declared by this app under auth.scopes on the users collection\" }\n];\n\n/**\n * The scope catalogue as printed lines: one section per plane, each scope with\n * its label and what its target names, marked when the caller holds it.\n *\n * Exported so its tests can read the lines without a server.\n */\nexport function describeScopes(scopes: readonly ScopeSummary[], held: readonly string[]): string[] {\n const management: readonly string[] = KEY_MANAGEMENT_SCOPES;\n const width = Math.max(0, ...scopes.map(s => s.scope.length)) + 2;\n const labelWidth = Math.max(0, ...scopes.map(s => s.label.length)) + 2;\n const lines: string[] = [];\n for (const { plane, title, note } of PLANES) {\n const inPlane = scopes.filter(s => s.plane === plane);\n if (inPlane.length === 0) continue;\n lines.push(` ${chalk.bold(title)} ${chalk.gray(`— ${note}`)}`);\n for (const s of inPlane) {\n const mark = held.includes(s.scope) ? chalk.green(\"✓\") : \" \";\n const extra = management.includes(s.scope)\n ? chalk.gray(\"never on a key\")\n : s.target ? chalk.gray(`:<${s.target}>`) : \"\";\n lines.push(` ${mark} ${s.scope.padEnd(width)}${extra ? s.label.padEnd(labelWidth) : s.label}${extra}`);\n }\n lines.push(\"\");\n }\n return lines;\n}\n\nasync function listScopes(rawArgs: string[]): Promise<void> {\n parseCommandArgs({\n spec: SCOPES_LIST_FLAGS,\n rawArgs,\n commandWords: 2,\n command: \"api-keys scopes\",\n maxPositionals: 0\n });\n\n const { client, baseUrl } = connect();\n let catalogue: { scopes: ScopeSummary[]; held: string[] };\n try {\n catalogue = await client.personalKeys.listScopes();\n } catch (e: unknown) {\n fail(\"Failed to read the scopes\", e, baseUrl);\n }\n\n console.log(\"\");\n console.log(chalk.bold(\" Scopes\") + chalk.gray(\" (✓ = you hold it; :<target> narrows it to one)\"));\n console.log(\"\");\n for (const line of describeScopes(catalogue.scopes, catalogue.held)) console.log(line);\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Help\n ═══════════════════════════════════════════════════════════════ */\n\nfunction printApiKeysHelp() {\n const keyable = ADMIN_SCOPES.filter(scope => !(KEY_MANAGEMENT_SCOPES as readonly string[]).includes(scope));\n console.log(`\n${chalk.bold(\"rebase api-keys\")} — Manage service API keys\n\n${chalk.green.bold(\"Usage\")}\n rebase api-keys ${chalk.blue(\"<command>\")} [options]\n\n${chalk.green.bold(\"Commands\")}\n ${chalk.blue.bold(\"list\")} List the service keys: kind, scopes, roles\n ${chalk.blue.bold(\"get\")} ${chalk.gray(\"<id>\")} Show one key\n ${chalk.blue.bold(\"create\")} Create a service key\n ${chalk.blue.bold(\"revoke\")} ${chalk.gray(\"<id>\")} Revoke a key\n ${chalk.blue.bold(\"scopes\")} List the scopes this backend knows, and which you hold\n\n${chalk.green.bold(\"create Options\")}\n ${chalk.blue(\"--name, -n\")} Key name ${chalk.gray(\"(required; or the first argument)\")}\n ${chalk.blue(\"--scopes\")} What the key may do, comma-separated or repeated\n ${chalk.gray(\"(required unless --full-access)\")}\n ${chalk.blue(\"--full-access\")} Every scope you hold, less keys:read and keys:write\n ${chalk.blue(\"--roles\")} RLS roles the key runs as beside ${chalk.gray(\"service\")}, e.g. admin\n ${chalk.blue(\"--rate-limit\")} Requests per 15-min window ${chalk.gray(\"(default: the server's API-key limit)\")}\n ${chalk.blue(\"--expires-in\")} Days until the key expires\n ${chalk.blue(\"--expires-at\")} Expiry as an ISO date, e.g. 2027-01-31\n\n${chalk.green.bold(\"get / revoke Options\")}\n ${chalk.blue(\"--id\")} API key ID ${chalk.gray(\"(or the first argument)\")}\n\n${chalk.green.bold(\"Scopes\")}\n A scope is ${chalk.blue(\"resource:action\")}, optionally narrowed to one target:\n ${chalk.blue(\"data:read:posts\")} reads the posts collection and nothing else.\n Data ${DATA_PLANE_SCOPES.join(\", \")}\n ${chalk.gray(\"target: a collection slug, a storage source id (default), a function name\")}\n Admin ${keyable.join(\", \")}\n App whatever the app declares under auth.scopes ${chalk.gray(\"(rebase api-keys scopes)\")}\n\n${chalk.green.bold(\"Examples\")}\n rebase api-keys list\n rebase api-keys create \"Analytics\" --scopes data:read:events\n rebase api-keys create -n \"Blog CI\" --scopes data:read:posts,data:write:posts --expires-in 90\n rebase api-keys create -n \"Mailer\" --scopes \"functions:invoke:send-email,storage:read:(default)\"\n rebase api-keys create -n \"Ops\" --full-access --roles admin --expires-at 2027-01-31\n rebase api-keys get abc123-def456\n rebase api-keys revoke abc123-def456\n`);\n}\n"],"mappings":"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"}
|