clefbase 1.4.1 → 1.5.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/dist/app.d.ts +19 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +25 -1
- package/dist/app.js.map +1 -1
- package/dist/cli-src/cli/api.js +33 -11
- package/dist/cli-src/cli/commands/functions.js +331 -0
- package/dist/cli-src/cli/commands/init.js +53 -16
- package/dist/cli-src/cli/config.js +7 -2
- package/dist/cli-src/cli/index.js +102 -11
- package/dist/cli.js +469 -66
- package/dist/functions.d.ts +94 -0
- package/dist/functions.d.ts.map +1 -0
- package/dist/functions.js +116 -0
- package/dist/functions.js.map +1 -0
- package/dist/index.d.ts +28 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -19
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +164 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -2
|
@@ -60,7 +60,7 @@ async function runInit(cwd = process.cwd()) {
|
|
|
60
60
|
projectId: projectId.trim(),
|
|
61
61
|
apiKey: apiKey.trim(),
|
|
62
62
|
adminSecret: adminSecret.trim(),
|
|
63
|
-
services: { database: false, auth: false, storage: false, hosting: false },
|
|
63
|
+
services: { database: false, auth: false, storage: false, hosting: false, functions: false },
|
|
64
64
|
};
|
|
65
65
|
// ── Step 2: Connection test ───────────────────────────────────────────────
|
|
66
66
|
const connSpinner = (0, ora_1.default)("Testing connection…").start();
|
|
@@ -78,10 +78,11 @@ async function runInit(cwd = process.cwd()) {
|
|
|
78
78
|
name: "services",
|
|
79
79
|
message: "Which services will you use?",
|
|
80
80
|
choices: [
|
|
81
|
-
{ name: "Database
|
|
82
|
-
{ name: "Auth
|
|
83
|
-
{ name: "Storage
|
|
84
|
-
{ name: "Hosting
|
|
81
|
+
{ name: "Database — store and query documents", value: "database", checked: true },
|
|
82
|
+
{ name: "Auth — user sign-up / sign-in", value: "auth", checked: true },
|
|
83
|
+
{ name: "Storage — file uploads", value: "storage", checked: false },
|
|
84
|
+
{ name: "Hosting — deploy static sites", value: "hosting", checked: false },
|
|
85
|
+
{ name: "Functions — serverless JS/TS & Python functions", value: "functions", checked: false },
|
|
85
86
|
],
|
|
86
87
|
}]);
|
|
87
88
|
cfg.services = {
|
|
@@ -89,6 +90,7 @@ async function runInit(cwd = process.cwd()) {
|
|
|
89
90
|
auth: services.includes("auth"),
|
|
90
91
|
storage: services.includes("storage"),
|
|
91
92
|
hosting: services.includes("hosting"),
|
|
93
|
+
functions: services.includes("functions"),
|
|
92
94
|
};
|
|
93
95
|
// ── Step 4: Hosting setup ─────────────────────────────────────────────────
|
|
94
96
|
if (cfg.services.hosting) {
|
|
@@ -116,21 +118,19 @@ async function runInit(cwd = process.cwd()) {
|
|
|
116
118
|
}
|
|
117
119
|
/**
|
|
118
120
|
* Writes two files into `<cwd>/src/lib/`:
|
|
119
|
-
* • clefbase.json — a copy of the project config (
|
|
121
|
+
* • clefbase.json — a copy of the project config (adminSecret stripped)
|
|
120
122
|
* • clefBase.ts — ready-to-import service exports
|
|
121
123
|
*
|
|
122
|
-
* Safe to call multiple times —
|
|
123
|
-
* stays in sync with the enabled services.
|
|
124
|
+
* Safe to call multiple times — always regenerated in sync with enabled services.
|
|
124
125
|
*/
|
|
125
126
|
function scaffoldLib(cfg, cwd = process.cwd()) {
|
|
126
127
|
const libDir = path_1.default.join(cwd, "src", "lib");
|
|
127
128
|
fs_1.default.mkdirSync(libDir, { recursive: true });
|
|
128
|
-
// ── 1. Config copy (
|
|
129
|
+
// ── 1. Config copy (adminSecret omitted — not needed in client code) ──────
|
|
129
130
|
const publicCfg = {
|
|
130
131
|
serverUrl: cfg.serverUrl,
|
|
131
132
|
projectId: cfg.projectId,
|
|
132
133
|
apiKey: cfg.apiKey,
|
|
133
|
-
// adminSecret intentionally omitted — not needed in client code
|
|
134
134
|
services: cfg.services,
|
|
135
135
|
...(cfg.hosting ? { hosting: cfg.hosting } : {}),
|
|
136
136
|
};
|
|
@@ -146,7 +146,7 @@ function scaffoldLib(cfg, cwd = process.cwd()) {
|
|
|
146
146
|
}
|
|
147
147
|
/** Build the content of src/lib/clefBase.ts based on enabled services. */
|
|
148
148
|
function buildLibTs(cfg) {
|
|
149
|
-
const { database, auth, storage } = cfg.services;
|
|
149
|
+
const { database, auth, storage, functions: fns } = cfg.services;
|
|
150
150
|
// Collect SDK imports
|
|
151
151
|
const sdkImports = ["initClefbase"];
|
|
152
152
|
if (database)
|
|
@@ -155,6 +155,8 @@ function buildLibTs(cfg) {
|
|
|
155
155
|
sdkImports.push("getAuth");
|
|
156
156
|
if (storage)
|
|
157
157
|
sdkImports.push("getStorage");
|
|
158
|
+
if (fns)
|
|
159
|
+
sdkImports.push("getFunctions", "httpsCallable");
|
|
158
160
|
// Collect type imports
|
|
159
161
|
const typeImports = [];
|
|
160
162
|
if (database)
|
|
@@ -163,13 +165,22 @@ function buildLibTs(cfg) {
|
|
|
163
165
|
typeImports.push("Auth");
|
|
164
166
|
if (storage)
|
|
165
167
|
typeImports.push("ClefbaseStorage");
|
|
168
|
+
if (fns)
|
|
169
|
+
typeImports.push("ClefbaseFunctions");
|
|
166
170
|
const lines = [
|
|
167
171
|
`/**`,
|
|
168
172
|
` * Clefbase — pre-initialised service exports`,
|
|
169
173
|
` *`,
|
|
170
174
|
` * Usage:`,
|
|
171
|
-
` * import { db
|
|
172
|
-
` * import
|
|
175
|
+
...(database ? [` * import { db } from "@lib/clefBase";`] : []),
|
|
176
|
+
...(auth ? [` * import { auth } from "@lib/clefBase";`] : []),
|
|
177
|
+
...(storage ? [` * import { storage } from "@lib/clefBase";`] : []),
|
|
178
|
+
...(fns ? [
|
|
179
|
+
` * import { fns, httpsCallable } from "@lib/clefBase";`,
|
|
180
|
+
` *`,
|
|
181
|
+
` * const greet = httpsCallable<{ name: string }, { message: string }>(fns, "greetUser");`,
|
|
182
|
+
` * const { data } = await greet({ name: "Alice" });`,
|
|
183
|
+
] : []),
|
|
173
184
|
` */`,
|
|
174
185
|
``,
|
|
175
186
|
`import { ${sdkImports.join(", ")} } from "clefbase";`,
|
|
@@ -189,7 +200,8 @@ function buildLibTs(cfg) {
|
|
|
189
200
|
``,
|
|
190
201
|
];
|
|
191
202
|
// ── Service instances ──────────────────────────────────────────────────────
|
|
192
|
-
|
|
203
|
+
const hasServices = database || auth || storage || fns;
|
|
204
|
+
if (hasServices) {
|
|
193
205
|
lines.push("// ─── Services ───────────────────────────────────────────────────────────────");
|
|
194
206
|
lines.push("");
|
|
195
207
|
}
|
|
@@ -208,9 +220,22 @@ function buildLibTs(cfg) {
|
|
|
208
220
|
lines.push(`export const storage: ClefbaseStorage = getStorage(app);`);
|
|
209
221
|
lines.push("");
|
|
210
222
|
}
|
|
211
|
-
|
|
223
|
+
if (fns) {
|
|
224
|
+
lines.push(`/** Clefbase Functions — deploy and call serverless functions. */`);
|
|
225
|
+
lines.push(`export const fns: ClefbaseFunctions = getFunctions(app);`);
|
|
226
|
+
lines.push("");
|
|
227
|
+
lines.push(`/**`);
|
|
228
|
+
lines.push(` * Create a typed callable reference to an HTTP-triggered function.`);
|
|
229
|
+
lines.push(` *`);
|
|
230
|
+
lines.push(` * @example`);
|
|
231
|
+
lines.push(` * const greet = httpsCallable<{ name: string }, { message: string }>(fns, "greetUser");`);
|
|
232
|
+
lines.push(` * const { data } = await greet({ name: "Alice" });`);
|
|
233
|
+
lines.push(` */`);
|
|
234
|
+
lines.push(`export { httpsCallable };`);
|
|
235
|
+
lines.push("");
|
|
236
|
+
}
|
|
237
|
+
// ── Re-export app ─────────────────────────────────────────────────────────
|
|
212
238
|
lines.push(`// ─── Advanced ────────────────────────────────────────────────────────────────`, ``, `/** The underlying ClefbaseApp instance (for advanced use). */`, `export { app };`, ``);
|
|
213
|
-
lines.push("");
|
|
214
239
|
return lines.join("\n");
|
|
215
240
|
}
|
|
216
241
|
// ─── Hosting sub-flow ─────────────────────────────────────────────────────────
|
|
@@ -304,6 +329,8 @@ function printUsageHint(cfg) {
|
|
|
304
329
|
namedImports.push("auth");
|
|
305
330
|
if (cfg.services.storage)
|
|
306
331
|
namedImports.push("storage");
|
|
332
|
+
if (cfg.services.functions)
|
|
333
|
+
namedImports.push("fns", "httpsCallable");
|
|
307
334
|
console.log(chalk_1.default.bold(" Quick start:"));
|
|
308
335
|
console.log();
|
|
309
336
|
if (namedImports.length > 0) {
|
|
@@ -324,6 +351,16 @@ function printUsageHint(cfg) {
|
|
|
324
351
|
console.log();
|
|
325
352
|
console.log(chalk_1.default.cyan(` await storage.ref("uploads/photo.jpg").upload(file);`));
|
|
326
353
|
}
|
|
354
|
+
if (cfg.services.functions) {
|
|
355
|
+
console.log();
|
|
356
|
+
console.log(chalk_1.default.bold(" Functions:"));
|
|
357
|
+
console.log(chalk_1.default.cyan(` // Deploy from a file`));
|
|
358
|
+
console.log(chalk_1.default.cyan(` $ clefbase functions:deploy -f ./src/functions/hello.ts`));
|
|
359
|
+
console.log();
|
|
360
|
+
console.log(chalk_1.default.cyan(` // Call from your app`));
|
|
361
|
+
console.log(chalk_1.default.cyan(` const greet = httpsCallable(fns, "greetUser");`));
|
|
362
|
+
console.log(chalk_1.default.cyan(` const { data } = await greet({ name: "Alice" });`));
|
|
363
|
+
}
|
|
327
364
|
if (cfg.services.hosting && cfg.hosting) {
|
|
328
365
|
console.log();
|
|
329
366
|
console.log(chalk_1.default.bold(" Deploy:"));
|
|
@@ -13,7 +13,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
13
13
|
const path_1 = __importDefault(require("path"));
|
|
14
14
|
// ─── File helpers ─────────────────────────────────────────────────────────────
|
|
15
15
|
const CONFIG_FILE = "clefbase.json";
|
|
16
|
-
/** Walk up the directory tree looking for clefbase.json (max
|
|
16
|
+
/** Walk up the directory tree looking for clefbase.json (max 6 levels). */
|
|
17
17
|
function findConfigPath(cwd = process.cwd()) {
|
|
18
18
|
let dir = cwd;
|
|
19
19
|
for (let i = 0; i < 6; i++) {
|
|
@@ -32,7 +32,12 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
32
32
|
if (!p)
|
|
33
33
|
return null;
|
|
34
34
|
try {
|
|
35
|
-
|
|
35
|
+
const raw = JSON.parse(fs_1.default.readFileSync(p, "utf-8"));
|
|
36
|
+
// Back-compat: configs written before functions was added won't have the field
|
|
37
|
+
if (raw.services && raw.services.functions === undefined) {
|
|
38
|
+
raw.services.functions = false;
|
|
39
|
+
}
|
|
40
|
+
return raw;
|
|
36
41
|
}
|
|
37
42
|
catch {
|
|
38
43
|
return null;
|
|
@@ -8,11 +8,12 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
8
8
|
const init_1 = require("./commands/init");
|
|
9
9
|
const deploy_1 = require("./commands/deploy");
|
|
10
10
|
const info_1 = require("./commands/info");
|
|
11
|
+
const functions_1 = require("./commands/functions");
|
|
11
12
|
const package_json_1 = require("../../package.json");
|
|
12
13
|
const program = new commander_1.Command();
|
|
13
14
|
program
|
|
14
15
|
.name("clefbase")
|
|
15
|
-
.description("Clefbase CLI — initialise projects, deploy sites, manage
|
|
16
|
+
.description("Clefbase CLI — initialise projects, deploy sites, manage functions")
|
|
16
17
|
.version(package_json_1.version);
|
|
17
18
|
// ─── init ─────────────────────────────────────────────────────────────────────
|
|
18
19
|
program
|
|
@@ -105,6 +106,83 @@ program
|
|
|
105
106
|
fatal(err);
|
|
106
107
|
}
|
|
107
108
|
});
|
|
109
|
+
// ─── functions:list ───────────────────────────────────────────────────────────
|
|
110
|
+
program
|
|
111
|
+
.command("functions:list")
|
|
112
|
+
.alias("fn:list")
|
|
113
|
+
.description("List all deployed functions for this project")
|
|
114
|
+
.action(async () => {
|
|
115
|
+
try {
|
|
116
|
+
await (0, functions_1.runFunctionsList)();
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
fatal(err);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
// ─── functions:deploy ─────────────────────────────────────────────────────────
|
|
123
|
+
program
|
|
124
|
+
.command("functions:deploy")
|
|
125
|
+
.alias("fn:deploy")
|
|
126
|
+
.description("Deploy (or redeploy) a function from a source file or interactively")
|
|
127
|
+
.option("-n, --name <name>", "Function name")
|
|
128
|
+
.option("-f, --file <path>", "Path to source file (.js, .ts, .py)")
|
|
129
|
+
.option("-r, --runtime <runtime>", "Runtime: node | python (auto-detected from file ext)")
|
|
130
|
+
.option("-t, --trigger <type>", "Trigger type (http, schedule, onDocumentCreate, …)")
|
|
131
|
+
.option("-c, --cron <expr>", "Cron expression for schedule triggers")
|
|
132
|
+
.option("-C, --collection <path>", "Collection path for document triggers")
|
|
133
|
+
.option("-T, --timeout <ms>", "Execution timeout in milliseconds (default: 30000)")
|
|
134
|
+
.option("-e, --entry <name>", "Exported function name to call (default: handler)")
|
|
135
|
+
.option("--env <KEY=VALUE...>", "Environment variable(s) — repeatable")
|
|
136
|
+
.action(async (opts) => {
|
|
137
|
+
try {
|
|
138
|
+
await (0, functions_1.runFunctionsDeploy)(opts);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
fatal(err);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
// ─── functions:call ───────────────────────────────────────────────────────────
|
|
145
|
+
program
|
|
146
|
+
.command("functions:call <name>")
|
|
147
|
+
.alias("fn:call")
|
|
148
|
+
.description("Call an HTTP-triggered function and print its return value")
|
|
149
|
+
.option("-d, --data <json>", "JSON payload to pass as ctx.data")
|
|
150
|
+
.action(async (name, opts) => {
|
|
151
|
+
try {
|
|
152
|
+
await (0, functions_1.runFunctionsCall)(name, opts);
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
fatal(err);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
// ─── functions:delete ─────────────────────────────────────────────────────────
|
|
159
|
+
program
|
|
160
|
+
.command("functions:delete <name>")
|
|
161
|
+
.alias("fn:delete")
|
|
162
|
+
.description("Delete a deployed function")
|
|
163
|
+
.option("-y, --force", "Skip confirmation prompt")
|
|
164
|
+
.action(async (name, opts) => {
|
|
165
|
+
try {
|
|
166
|
+
await (0, functions_1.runFunctionsDelete)(name, opts);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
fatal(err);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
// ─── functions:logs ───────────────────────────────────────────────────────────
|
|
173
|
+
program
|
|
174
|
+
.command("functions:logs <name>")
|
|
175
|
+
.alias("fn:logs")
|
|
176
|
+
.description("Show recent execution history for a function")
|
|
177
|
+
.option("-l, --limit <n>", "Number of executions to show (default: 20, max: 100)")
|
|
178
|
+
.action(async (name, opts) => {
|
|
179
|
+
try {
|
|
180
|
+
await (0, functions_1.runFunctionsLogs)(name, opts);
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
fatal(err);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
108
186
|
// ─── info ─────────────────────────────────────────────────────────────────────
|
|
109
187
|
program
|
|
110
188
|
.command("info")
|
|
@@ -120,16 +198,29 @@ program
|
|
|
120
198
|
// ─── help footer ─────────────────────────────────────────────────────────────
|
|
121
199
|
program.addHelpText("after", `
|
|
122
200
|
${chalk_1.default.bold("Examples:")}
|
|
123
|
-
${chalk_1.default.
|
|
124
|
-
${chalk_1.default.cyan("clefbase
|
|
125
|
-
${chalk_1.default.cyan("clefbase
|
|
126
|
-
|
|
127
|
-
${chalk_1.default.
|
|
128
|
-
${chalk_1.default.cyan("clefbase
|
|
129
|
-
${chalk_1.default.cyan("clefbase
|
|
130
|
-
${chalk_1.default.cyan("clefbase hosting:
|
|
131
|
-
${chalk_1.default.cyan("clefbase hosting:
|
|
132
|
-
${chalk_1.default.cyan("clefbase
|
|
201
|
+
${chalk_1.default.bold("Project:")}
|
|
202
|
+
${chalk_1.default.cyan("clefbase init")} Set up a new project
|
|
203
|
+
${chalk_1.default.cyan("clefbase info")} Show config & connection status
|
|
204
|
+
|
|
205
|
+
${chalk_1.default.bold("Hosting:")}
|
|
206
|
+
${chalk_1.default.cyan("clefbase deploy")} Deploy your built site
|
|
207
|
+
${chalk_1.default.cyan("clefbase deploy -d ./dist -m \"v2\"")} Deploy from a dir with a note
|
|
208
|
+
${chalk_1.default.cyan("clefbase hosting:init")} Link or create a hosted site
|
|
209
|
+
${chalk_1.default.cyan("clefbase hosting:status")} Show current live deploy
|
|
210
|
+
${chalk_1.default.cyan("clefbase hosting:sites")} List all sites
|
|
211
|
+
${chalk_1.default.cyan("clefbase hosting:dns")} Show DNS status
|
|
212
|
+
${chalk_1.default.cyan("clefbase hosting:dns:reprovision")} Fix / create the preview CNAME
|
|
213
|
+
|
|
214
|
+
${chalk_1.default.bold("Functions:")}
|
|
215
|
+
${chalk_1.default.cyan("clefbase functions:list")} List all deployed functions
|
|
216
|
+
${chalk_1.default.cyan("clefbase functions:deploy")} Interactive deploy wizard
|
|
217
|
+
${chalk_1.default.cyan("clefbase functions:deploy -f ./fn.ts")} Deploy from a file (auto-detects runtime)
|
|
218
|
+
${chalk_1.default.cyan("clefbase functions:deploy -n greet -f fn.ts --trigger http")}
|
|
219
|
+
${chalk_1.default.cyan("clefbase functions:call greetUser")} Call an HTTP function
|
|
220
|
+
${chalk_1.default.cyan("clefbase functions:call greetUser -d '{\"name\":\"Alice\"}'}")}
|
|
221
|
+
${chalk_1.default.cyan("clefbase functions:logs greetUser")} View execution history
|
|
222
|
+
${chalk_1.default.cyan("clefbase functions:logs greetUser -l 50")} View last 50 executions
|
|
223
|
+
${chalk_1.default.cyan("clefbase functions:delete oldFunction")} Delete a function
|
|
133
224
|
`);
|
|
134
225
|
program.parse(process.argv);
|
|
135
226
|
// ─── Helper ───────────────────────────────────────────────────────────────────
|