clefbase 1.1.6 → 1.2.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 +169 -103
- package/bin/clefbase.js +15 -0
- package/dist/app.d.ts +16 -32
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +29 -44
- package/dist/app.js.map +1 -1
- package/dist/auth/index.d.ts +16 -44
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +40 -95
- package/dist/auth/index.js.map +1 -1
- package/dist/cli-src/cli/api.js +170 -0
- package/dist/cli-src/cli/commands/deploy.js +285 -0
- package/dist/cli-src/cli/commands/info.js +67 -0
- package/dist/cli-src/cli/commands/init.js +226 -0
- package/dist/cli-src/cli/config.js +82 -0
- package/dist/cli-src/cli/index.js +110 -0
- package/dist/cli-src/types.js +17 -0
- package/dist/cli.js +34375 -0
- package/dist/db/index.d.ts +26 -62
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +34 -70
- package/dist/db/index.js.map +1 -1
- package/dist/hosting/index.d.ts +123 -0
- package/dist/hosting/index.d.ts.map +1 -0
- package/dist/hosting/index.js +202 -0
- package/dist/hosting/index.js.map +1 -0
- package/dist/http.d.ts +2 -4
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +8 -22
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +11 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -6
- package/dist/index.js.map +1 -1
- package/dist/storage/index.d.ts +8 -50
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +9 -59
- package/dist/storage/index.js.map +1 -1
- package/dist/types.d.ts +10 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -1
- package/dist/types.js.map +1 -1
- package/package.json +21 -5
package/dist/auth/index.d.ts
CHANGED
|
@@ -5,17 +5,10 @@ type AuthStateCallback = (user: AuthUser | null) => void;
|
|
|
5
5
|
* Authentication service. Obtain via `getAuth(app)`.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
|
-
* import { initClefbase, getAuth } from "clefbase";
|
|
9
|
-
* const app = initClefbase({ serverUrl, projectId, apiKey });
|
|
10
8
|
* const auth = getAuth(app);
|
|
11
|
-
*
|
|
12
|
-
* const
|
|
13
|
-
*
|
|
14
|
-
* console.log(auth.currentUser);
|
|
15
|
-
* await auth.signOut();
|
|
16
|
-
*
|
|
17
|
-
* const unsub = auth.onAuthStateChanged((u) => console.log(u));
|
|
18
|
-
* unsub(); // stop listening
|
|
9
|
+
* const { user } = await auth.signIn("alice@example.com", "pass");
|
|
10
|
+
* const unsub = auth.onAuthStateChanged(u => console.log(u));
|
|
11
|
+
* unsub();
|
|
19
12
|
*/
|
|
20
13
|
export declare class Auth {
|
|
21
14
|
private readonly http;
|
|
@@ -24,14 +17,14 @@ export declare class Auth {
|
|
|
24
17
|
constructor(http: HttpClient);
|
|
25
18
|
private notify;
|
|
26
19
|
private handleResult;
|
|
27
|
-
/** @internal
|
|
20
|
+
/** @internal — used by Storage to attach the bearer token to upload requests */
|
|
28
21
|
getAuthHeaders(): Record<string, string>;
|
|
29
|
-
/** The currently signed-in user, or null
|
|
22
|
+
/** The currently signed-in user, or null. */
|
|
30
23
|
get currentUser(): AuthUser | null;
|
|
31
24
|
/** The raw bearer token for the active session, or null. */
|
|
32
25
|
get currentToken(): string | null;
|
|
33
26
|
/**
|
|
34
|
-
* Create a new account
|
|
27
|
+
* Create a new account.
|
|
35
28
|
*
|
|
36
29
|
* @example
|
|
37
30
|
* const { user } = await auth.signUp("alice@example.com", "pass123", {
|
|
@@ -48,17 +41,12 @@ export declare class Auth {
|
|
|
48
41
|
* Sign in with email + password.
|
|
49
42
|
*
|
|
50
43
|
* @example
|
|
51
|
-
* const { user, token } = await auth.signIn("alice@example.com", "
|
|
44
|
+
* const { user, token } = await auth.signIn("alice@example.com", "pass");
|
|
52
45
|
*/
|
|
53
46
|
signIn(email: string, password: string): Promise<AuthResult>;
|
|
54
|
-
/**
|
|
55
|
-
* Sign out the current user and clear the local session.
|
|
56
|
-
*/
|
|
47
|
+
/** Sign out and clear the local session. */
|
|
57
48
|
signOut(): Promise<void>;
|
|
58
|
-
/**
|
|
59
|
-
* Re-fetch the current user's profile from the server and update the cache.
|
|
60
|
-
* Useful after an admin changes the user's data server-side.
|
|
61
|
-
*/
|
|
49
|
+
/** Re-fetch the current user's profile from the server and update the cache. */
|
|
62
50
|
refreshCurrentUser(): Promise<AuthUser | null>;
|
|
63
51
|
/**
|
|
64
52
|
* Update the signed-in user's profile.
|
|
@@ -71,42 +59,26 @@ export declare class Auth {
|
|
|
71
59
|
photoUrl?: string;
|
|
72
60
|
metadata?: Record<string, unknown>;
|
|
73
61
|
}): Promise<AuthUser>;
|
|
74
|
-
/**
|
|
75
|
-
* Change the signed-in user's password.
|
|
76
|
-
* All other active sessions are invalidated on the server.
|
|
77
|
-
*/
|
|
62
|
+
/** Change password. All other sessions are invalidated server-side. */
|
|
78
63
|
changePassword(currentPassword: string, newPassword: string): Promise<void>;
|
|
79
|
-
/**
|
|
80
|
-
* Request a password-reset email. Safe to call even if the address
|
|
81
|
-
* is not registered (server returns the same response either way).
|
|
82
|
-
*/
|
|
64
|
+
/** Request a password-reset email. Safe to call even for unregistered addresses. */
|
|
83
65
|
sendPasswordResetEmail(email: string): Promise<void>;
|
|
84
|
-
/**
|
|
85
|
-
* Complete a password reset with the token from the reset email.
|
|
86
|
-
*/
|
|
66
|
+
/** Complete a password reset with the token from the reset email. */
|
|
87
67
|
confirmPasswordReset(resetToken: string, newPassword: string): Promise<void>;
|
|
88
|
-
/**
|
|
89
|
-
* Send an email verification code to the currently signed-in user.
|
|
90
|
-
*/
|
|
68
|
+
/** Send an email verification code to the currently signed-in user. */
|
|
91
69
|
sendEmailVerification(): Promise<void>;
|
|
92
|
-
/**
|
|
93
|
-
* Verify the current user's email with the code they received.
|
|
94
|
-
*
|
|
95
|
-
* @example
|
|
96
|
-
* const user = await auth.verifyEmail("ABC123");
|
|
97
|
-
*/
|
|
70
|
+
/** Verify email with the code the user received. */
|
|
98
71
|
verifyEmail(code: string): Promise<AuthUser>;
|
|
99
72
|
/**
|
|
100
73
|
* Subscribe to auth state changes.
|
|
101
|
-
*
|
|
74
|
+
* Fires immediately with the current user, then on every change.
|
|
102
75
|
* Returns an unsubscribe function.
|
|
103
76
|
*
|
|
104
77
|
* @example
|
|
105
|
-
* const unsubscribe = auth.onAuthStateChanged(
|
|
78
|
+
* const unsubscribe = auth.onAuthStateChanged(user => {
|
|
106
79
|
* if (user) console.log("signed in:", user.email);
|
|
107
80
|
* else console.log("signed out");
|
|
108
81
|
* });
|
|
109
|
-
* // Later:
|
|
110
82
|
* unsubscribe();
|
|
111
83
|
*/
|
|
112
84
|
onAuthStateChanged(callback: AuthStateCallback): () => void;
|
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAiDrD,KAAK,iBAAiB,GAAG,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;AAEzD;;;;;;;;GAQG;AACH,qBAAa,IAAI;IAIH,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,SAAS,CAA2B;gBAEf,IAAI,EAAE,UAAU;IAO7C,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,YAAY;IAUpB,gFAAgF;IAChF,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAOxC,6CAA6C;IAC7C,IAAI,WAAW,IAAI,QAAQ,GAAG,IAAI,CAA4C;IAE9E,4DAA4D;IAC5D,IAAI,YAAY,IAAI,MAAM,GAAG,IAAI,CAA6C;IAE9E;;;;;;;;OAQG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,GACA,OAAO,CAAC,UAAU,CAAC;IAOtB;;;;;OAKG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAKlE,4CAA4C;IACtC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAW9B,gFAAgF;IAC1E,kBAAkB,IAAI,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAYpD;;;;;OAKG;IACG,aAAa,CAAC,OAAO,EAAE;QAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,GAAG,OAAO,CAAC,QAAQ,CAAC;IAYrB,uEAAuE;IACjE,cAAc,CAAC,eAAe,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjF,oFAAoF;IAC9E,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,qEAAqE;IAC/D,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlF,uEAAuE;IACjE,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5C,oDAAoD;IAC9C,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIlD;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,IAAI;CAO5D"}
|
package/dist/auth/index.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Auth = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Stores the session in memory and optionally in localStorage (browser).
|
|
6
|
-
* On Node.js localStorage is not available so only in-memory is used.
|
|
7
|
-
*/
|
|
8
4
|
class SessionStore {
|
|
9
5
|
constructor() {
|
|
10
6
|
this.session = null;
|
|
11
|
-
this.
|
|
7
|
+
this.KEY = "cfb_session";
|
|
12
8
|
}
|
|
13
9
|
get ls() {
|
|
14
10
|
try {
|
|
@@ -21,32 +17,31 @@ class SessionStore {
|
|
|
21
17
|
save(s) {
|
|
22
18
|
this.session = s;
|
|
23
19
|
try {
|
|
24
|
-
this.ls?.setItem(this.
|
|
20
|
+
this.ls?.setItem(this.KEY, JSON.stringify(s));
|
|
25
21
|
}
|
|
26
|
-
catch { /* quota
|
|
22
|
+
catch { /* quota / private mode */ }
|
|
27
23
|
}
|
|
28
24
|
load() {
|
|
29
25
|
if (this.session)
|
|
30
26
|
return this.session;
|
|
31
27
|
try {
|
|
32
|
-
const raw = this.ls?.getItem(this.
|
|
28
|
+
const raw = this.ls?.getItem(this.KEY);
|
|
33
29
|
if (raw) {
|
|
34
30
|
const parsed = JSON.parse(raw);
|
|
35
31
|
if (new Date(parsed.expiresAt) > new Date()) {
|
|
36
32
|
this.session = parsed;
|
|
37
33
|
return this.session;
|
|
38
34
|
}
|
|
39
|
-
|
|
40
|
-
this.ls?.removeItem(this.STORAGE_KEY);
|
|
35
|
+
this.ls?.removeItem(this.KEY);
|
|
41
36
|
}
|
|
42
37
|
}
|
|
43
|
-
catch { /* corrupted
|
|
38
|
+
catch { /* corrupted */ }
|
|
44
39
|
return null;
|
|
45
40
|
}
|
|
46
41
|
clear() {
|
|
47
42
|
this.session = null;
|
|
48
43
|
try {
|
|
49
|
-
this.ls?.removeItem(this.
|
|
44
|
+
this.ls?.removeItem(this.KEY);
|
|
50
45
|
}
|
|
51
46
|
catch { /* ignore */ }
|
|
52
47
|
}
|
|
@@ -55,33 +50,26 @@ class SessionStore {
|
|
|
55
50
|
* Authentication service. Obtain via `getAuth(app)`.
|
|
56
51
|
*
|
|
57
52
|
* @example
|
|
58
|
-
* import { initClefbase, getAuth } from "clefbase";
|
|
59
|
-
* const app = initClefbase({ serverUrl, projectId, apiKey });
|
|
60
53
|
* const auth = getAuth(app);
|
|
61
|
-
*
|
|
62
|
-
* const
|
|
63
|
-
*
|
|
64
|
-
* console.log(auth.currentUser);
|
|
65
|
-
* await auth.signOut();
|
|
66
|
-
*
|
|
67
|
-
* const unsub = auth.onAuthStateChanged((u) => console.log(u));
|
|
68
|
-
* unsub(); // stop listening
|
|
54
|
+
* const { user } = await auth.signIn("alice@example.com", "pass");
|
|
55
|
+
* const unsub = auth.onAuthStateChanged(u => console.log(u));
|
|
56
|
+
* unsub();
|
|
69
57
|
*/
|
|
70
58
|
class Auth {
|
|
71
59
|
constructor(http) {
|
|
72
60
|
this.http = http;
|
|
73
61
|
this.store = new SessionStore();
|
|
74
62
|
this.listeners = [];
|
|
75
|
-
//
|
|
63
|
+
// Restore any persisted session on startup
|
|
76
64
|
this.store.load();
|
|
77
65
|
}
|
|
78
|
-
//
|
|
66
|
+
// ── Internals ─────────────────────────────────────────────────────────────
|
|
79
67
|
notify(user) {
|
|
80
68
|
for (const cb of this.listeners) {
|
|
81
69
|
try {
|
|
82
70
|
cb(user);
|
|
83
71
|
}
|
|
84
|
-
catch { /*
|
|
72
|
+
catch { /* never let a listener crash the SDK */ }
|
|
85
73
|
}
|
|
86
74
|
}
|
|
87
75
|
handleResult(result) {
|
|
@@ -93,22 +81,18 @@ class Auth {
|
|
|
93
81
|
this.notify(result.user);
|
|
94
82
|
return result;
|
|
95
83
|
}
|
|
96
|
-
/** @internal
|
|
84
|
+
/** @internal — used by Storage to attach the bearer token to upload requests */
|
|
97
85
|
getAuthHeaders() {
|
|
98
|
-
const
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
102
|
-
/** The currently signed-in user, or null if no session exists. */
|
|
103
|
-
get currentUser() {
|
|
104
|
-
return this.store.load()?.user ?? null;
|
|
86
|
+
const s = this.store.load();
|
|
87
|
+
return s ? { Authorization: `Bearer ${s.token}` } : {};
|
|
105
88
|
}
|
|
89
|
+
// ── Public API ────────────────────────────────────────────────────────────
|
|
90
|
+
/** The currently signed-in user, or null. */
|
|
91
|
+
get currentUser() { return this.store.load()?.user ?? null; }
|
|
106
92
|
/** The raw bearer token for the active session, or null. */
|
|
107
|
-
get currentToken() {
|
|
108
|
-
return this.store.load()?.token ?? null;
|
|
109
|
-
}
|
|
93
|
+
get currentToken() { return this.store.load()?.token ?? null; }
|
|
110
94
|
/**
|
|
111
|
-
* Create a new account
|
|
95
|
+
* Create a new account.
|
|
112
96
|
*
|
|
113
97
|
* @example
|
|
114
98
|
* const { user } = await auth.signUp("alice@example.com", "pass123", {
|
|
@@ -117,11 +101,8 @@ class Auth {
|
|
|
117
101
|
* });
|
|
118
102
|
*/
|
|
119
103
|
async signUp(email, password, profile) {
|
|
120
|
-
// base is serverUrl/auth → path is /signup → full: serverUrl/auth/signup
|
|
121
104
|
const result = await this.http.post("/signup", {
|
|
122
|
-
email,
|
|
123
|
-
password,
|
|
124
|
-
...profile,
|
|
105
|
+
email, password, ...profile,
|
|
125
106
|
});
|
|
126
107
|
return this.handleResult(result);
|
|
127
108
|
}
|
|
@@ -129,45 +110,31 @@ class Auth {
|
|
|
129
110
|
* Sign in with email + password.
|
|
130
111
|
*
|
|
131
112
|
* @example
|
|
132
|
-
* const { user, token } = await auth.signIn("alice@example.com", "
|
|
113
|
+
* const { user, token } = await auth.signIn("alice@example.com", "pass");
|
|
133
114
|
*/
|
|
134
115
|
async signIn(email, password) {
|
|
135
|
-
const result = await this.http.post("/signin", {
|
|
136
|
-
email,
|
|
137
|
-
password,
|
|
138
|
-
});
|
|
116
|
+
const result = await this.http.post("/signin", { email, password });
|
|
139
117
|
return this.handleResult(result);
|
|
140
118
|
}
|
|
141
|
-
/**
|
|
142
|
-
* Sign out the current user and clear the local session.
|
|
143
|
-
*/
|
|
119
|
+
/** Sign out and clear the local session. */
|
|
144
120
|
async signOut() {
|
|
145
121
|
const token = this.currentToken;
|
|
146
122
|
if (token) {
|
|
147
123
|
try {
|
|
148
|
-
await this.http.post("/signout", undefined, {
|
|
149
|
-
Authorization: `Bearer ${token}`,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
// Always clear locally even if the server call fails
|
|
124
|
+
await this.http.post("/signout", undefined, { Authorization: `Bearer ${token}` });
|
|
154
125
|
}
|
|
126
|
+
catch { /* always clear locally */ }
|
|
155
127
|
}
|
|
156
128
|
this.store.clear();
|
|
157
129
|
this.notify(null);
|
|
158
130
|
}
|
|
159
|
-
/**
|
|
160
|
-
* Re-fetch the current user's profile from the server and update the cache.
|
|
161
|
-
* Useful after an admin changes the user's data server-side.
|
|
162
|
-
*/
|
|
131
|
+
/** Re-fetch the current user's profile from the server and update the cache. */
|
|
163
132
|
async refreshCurrentUser() {
|
|
164
133
|
const token = this.currentToken;
|
|
165
134
|
if (!token)
|
|
166
135
|
return null;
|
|
167
136
|
try {
|
|
168
|
-
const user = await this.http.get("/me", {
|
|
169
|
-
Authorization: `Bearer ${token}`,
|
|
170
|
-
});
|
|
137
|
+
const user = await this.http.get("/me", { Authorization: `Bearer ${token}` });
|
|
171
138
|
const session = this.store.load();
|
|
172
139
|
if (session)
|
|
173
140
|
this.store.save({ ...session, user });
|
|
@@ -197,74 +164,52 @@ class Auth {
|
|
|
197
164
|
this.notify(user);
|
|
198
165
|
return user;
|
|
199
166
|
}
|
|
200
|
-
/**
|
|
201
|
-
* Change the signed-in user's password.
|
|
202
|
-
* All other active sessions are invalidated on the server.
|
|
203
|
-
*/
|
|
167
|
+
/** Change password. All other sessions are invalidated server-side. */
|
|
204
168
|
async changePassword(currentPassword, newPassword) {
|
|
205
169
|
const token = this.currentToken;
|
|
206
170
|
if (!token)
|
|
207
171
|
throw new Error("No user is currently signed in.");
|
|
208
172
|
await this.http.post("/change-password", { currentPassword, newPassword }, { Authorization: `Bearer ${token}` });
|
|
209
173
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Request a password-reset email. Safe to call even if the address
|
|
212
|
-
* is not registered (server returns the same response either way).
|
|
213
|
-
*/
|
|
174
|
+
/** Request a password-reset email. Safe to call even for unregistered addresses. */
|
|
214
175
|
async sendPasswordResetEmail(email) {
|
|
215
176
|
await this.http.post("/forgot-password", { email });
|
|
216
177
|
}
|
|
217
|
-
/**
|
|
218
|
-
* Complete a password reset with the token from the reset email.
|
|
219
|
-
*/
|
|
178
|
+
/** Complete a password reset with the token from the reset email. */
|
|
220
179
|
async confirmPasswordReset(resetToken, newPassword) {
|
|
221
|
-
await this.http.post("/reset-password", {
|
|
222
|
-
token: resetToken,
|
|
223
|
-
newPassword,
|
|
224
|
-
});
|
|
180
|
+
await this.http.post("/reset-password", { token: resetToken, newPassword });
|
|
225
181
|
}
|
|
226
|
-
/**
|
|
227
|
-
* Send an email verification code to the currently signed-in user.
|
|
228
|
-
*/
|
|
182
|
+
/** Send an email verification code to the currently signed-in user. */
|
|
229
183
|
async sendEmailVerification() {
|
|
230
184
|
const token = this.currentToken;
|
|
231
185
|
if (!token)
|
|
232
186
|
throw new Error("No user is currently signed in.");
|
|
233
|
-
await this.http.post("/send-verification", undefined, {
|
|
234
|
-
Authorization: `Bearer ${token}`,
|
|
235
|
-
});
|
|
187
|
+
await this.http.post("/send-verification", undefined, { Authorization: `Bearer ${token}` });
|
|
236
188
|
}
|
|
237
|
-
/**
|
|
238
|
-
* Verify the current user's email with the code they received.
|
|
239
|
-
*
|
|
240
|
-
* @example
|
|
241
|
-
* const user = await auth.verifyEmail("ABC123");
|
|
242
|
-
*/
|
|
189
|
+
/** Verify email with the code the user received. */
|
|
243
190
|
async verifyEmail(code) {
|
|
244
191
|
return this.http.post("/verify-email", { code });
|
|
245
192
|
}
|
|
246
193
|
/**
|
|
247
194
|
* Subscribe to auth state changes.
|
|
248
|
-
*
|
|
195
|
+
* Fires immediately with the current user, then on every change.
|
|
249
196
|
* Returns an unsubscribe function.
|
|
250
197
|
*
|
|
251
198
|
* @example
|
|
252
|
-
* const unsubscribe = auth.onAuthStateChanged(
|
|
199
|
+
* const unsubscribe = auth.onAuthStateChanged(user => {
|
|
253
200
|
* if (user) console.log("signed in:", user.email);
|
|
254
201
|
* else console.log("signed out");
|
|
255
202
|
* });
|
|
256
|
-
* // Later:
|
|
257
203
|
* unsubscribe();
|
|
258
204
|
*/
|
|
259
205
|
onAuthStateChanged(callback) {
|
|
260
206
|
this.listeners.push(callback);
|
|
261
|
-
// Emit the current state immediately
|
|
262
207
|
try {
|
|
263
208
|
callback(this.currentUser);
|
|
264
209
|
}
|
|
265
|
-
catch { /*
|
|
210
|
+
catch { /* ignore */ }
|
|
266
211
|
return () => {
|
|
267
|
-
this.listeners = this.listeners.filter(
|
|
212
|
+
this.listeners = this.listeners.filter(cb => cb !== callback);
|
|
268
213
|
};
|
|
269
214
|
}
|
|
270
215
|
}
|
package/dist/auth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":";;;AAWA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Raw API calls used by the CLI.
|
|
4
|
+
* Written as plain fetch calls (not using HttpClient) so the CLI can be
|
|
5
|
+
* bundled independently and doesn't pull in the full SDK at runtime.
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.CliApiError = void 0;
|
|
42
|
+
exports.listSites = listSites;
|
|
43
|
+
exports.createSite = createSite;
|
|
44
|
+
exports.createDeploy = createDeploy;
|
|
45
|
+
exports.uploadFileBatch = uploadFileBatch;
|
|
46
|
+
exports.finalizeDeploy = finalizeDeploy;
|
|
47
|
+
exports.getActiveDeploy = getActiveDeploy;
|
|
48
|
+
exports.testConnection = testConnection;
|
|
49
|
+
// ─── Error ────────────────────────────────────────────────────────────────────
|
|
50
|
+
class CliApiError extends Error {
|
|
51
|
+
constructor(message, status) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.status = status;
|
|
54
|
+
this.name = "CliApiError";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.CliApiError = CliApiError;
|
|
58
|
+
// ─── Fetch helper ─────────────────────────────────────────────────────────────
|
|
59
|
+
async function getFetch() {
|
|
60
|
+
if (typeof globalThis.fetch === "function")
|
|
61
|
+
return globalThis.fetch.bind(globalThis);
|
|
62
|
+
const mod = await Promise.resolve().then(() => __importStar(require("node-fetch")));
|
|
63
|
+
return mod.default;
|
|
64
|
+
}
|
|
65
|
+
async function apiFetch(url, opts) {
|
|
66
|
+
const fetchFn = await getFetch();
|
|
67
|
+
const res = await fetchFn(url, { method: opts.method ?? "GET", headers: opts.headers, body: opts.body });
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
let msg = `${res.status} ${res.statusText}`;
|
|
70
|
+
try {
|
|
71
|
+
const b = (await res.json());
|
|
72
|
+
if (b.error)
|
|
73
|
+
msg = b.error;
|
|
74
|
+
}
|
|
75
|
+
catch { /* keep default */ }
|
|
76
|
+
throw new CliApiError(msg, res.status);
|
|
77
|
+
}
|
|
78
|
+
if (res.status === 204)
|
|
79
|
+
return undefined;
|
|
80
|
+
return res.json();
|
|
81
|
+
}
|
|
82
|
+
function base(cfg) {
|
|
83
|
+
return cfg.serverUrl.replace(/\/+$/, "");
|
|
84
|
+
}
|
|
85
|
+
function adminHeaders(cfg) {
|
|
86
|
+
return { "Content-Type": "application/json", "x-admin-secret": cfg.adminSecret };
|
|
87
|
+
}
|
|
88
|
+
// ─── Hosting API ──────────────────────────────────────────────────────────────
|
|
89
|
+
async function listSites(cfg) {
|
|
90
|
+
return apiFetch(`${base(cfg)}/api/hosting/databases/${cfg.projectId}/sites`, { headers: adminHeaders(cfg) });
|
|
91
|
+
}
|
|
92
|
+
async function createSite(cfg, name, description) {
|
|
93
|
+
return apiFetch(`${base(cfg)}/api/hosting/databases/${cfg.projectId}/sites`, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers: adminHeaders(cfg),
|
|
96
|
+
body: JSON.stringify({ name, description }),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async function createDeploy(cfg, siteId, entrypoint = "index.html") {
|
|
100
|
+
return apiFetch(`${base(cfg)}/api/hosting/databases/${cfg.projectId}/sites/${siteId}/deploys`, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: adminHeaders(cfg),
|
|
103
|
+
body: JSON.stringify({ entrypoint, deployedBy: "clefbase-cli" }),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async function uploadFileBatch(cfg, deployId, files) {
|
|
107
|
+
const fetchFn = await getFetch();
|
|
108
|
+
// Use native FormData (Node 18+) or form-data package
|
|
109
|
+
let FormDataImpl;
|
|
110
|
+
if (typeof FormData !== "undefined") {
|
|
111
|
+
FormDataImpl = FormData;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const mod = await Promise.resolve().then(() => __importStar(require("form-data")));
|
|
115
|
+
FormDataImpl = mod.default;
|
|
116
|
+
}
|
|
117
|
+
const form = new FormDataImpl();
|
|
118
|
+
for (const f of files) {
|
|
119
|
+
form.append("files[]", f.buffer, {
|
|
120
|
+
filename: f.path.split("/").pop() ?? f.path,
|
|
121
|
+
contentType: "application/octet-stream",
|
|
122
|
+
});
|
|
123
|
+
form.append("filePaths", f.path);
|
|
124
|
+
}
|
|
125
|
+
const url = `${base(cfg)}/api/hosting/databases/${cfg.projectId}/deploys/${deployId}/files/batch`;
|
|
126
|
+
const res = await fetchFn(url, {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: { "x-admin-secret": cfg.adminSecret },
|
|
129
|
+
body: form,
|
|
130
|
+
});
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
let msg = `${res.status} ${res.statusText}`;
|
|
133
|
+
try {
|
|
134
|
+
const b = (await res.json());
|
|
135
|
+
if (b.error)
|
|
136
|
+
msg = b.error;
|
|
137
|
+
}
|
|
138
|
+
catch { /* keep */ }
|
|
139
|
+
throw new CliApiError(msg, res.status);
|
|
140
|
+
}
|
|
141
|
+
return res.json();
|
|
142
|
+
}
|
|
143
|
+
async function finalizeDeploy(cfg, deployId, message) {
|
|
144
|
+
return apiFetch(`${base(cfg)}/api/hosting/databases/${cfg.projectId}/deploys/${deployId}/finalize`, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: adminHeaders(cfg),
|
|
147
|
+
body: JSON.stringify({ message: message ?? "Deployed via clefbase CLI" }),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
async function getActiveDeploy(cfg, siteId) {
|
|
151
|
+
try {
|
|
152
|
+
return await apiFetch(`${base(cfg)}/api/hosting/databases/${cfg.projectId}/sites/${siteId}/active`, { headers: adminHeaders(cfg) });
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
if (err.status === 404)
|
|
156
|
+
return null;
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// ─── Connectivity ─────────────────────────────────────────────────────────────
|
|
161
|
+
async function testConnection(cfg) {
|
|
162
|
+
try {
|
|
163
|
+
const fetchFn = await getFetch();
|
|
164
|
+
const res = await fetchFn(`${base(cfg)}/health`, {});
|
|
165
|
+
return res.ok;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|