clefbase 1.5.3 → 2.0.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/dist/cli.js CHANGED
@@ -33684,8 +33684,7 @@ function writeEnvExample(cfg, cwd = process.cwd()) {
33684
33684
  "# Clefbase \u2014 copy to .env and fill in your values",
33685
33685
  `CLEFBASE_SERVER_URL=${cfg.serverUrl}`,
33686
33686
  `CLEFBASE_PROJECT_ID=${cfg.projectId}`,
33687
- "CLEFBASE_API_KEY=your_api_key_here",
33688
- "CLEFBASE_ADMIN_SECRET=your_admin_secret_here"
33687
+ "CLEFBASE_API_KEY=your_api_key_here"
33689
33688
  ];
33690
33689
  import_fs2.default.writeFileSync(p, lines.join("\n") + "\n");
33691
33690
  }
@@ -33721,49 +33720,46 @@ async function apiFetch(url, opts) {
33721
33720
  function base(cfg) {
33722
33721
  return cfg.serverUrl.replace(/\/+$/, "");
33723
33722
  }
33724
- function adminHeaders(cfg) {
33725
- return { "Content-Type": "application/json", "x-admin-secret": cfg.adminSecret };
33726
- }
33727
33723
  function cfxHeaders(cfg) {
33728
33724
  return { "Content-Type": "application/json", "x-cfx-key": cfg.apiKey };
33729
33725
  }
33730
33726
  async function listSites(cfg) {
33731
33727
  return apiFetch(
33732
- `${base(cfg)}/api/hosting/databases/${cfg.projectId}/sites`,
33733
- { headers: adminHeaders(cfg) }
33728
+ `${base(cfg)}/hosting/sites`,
33729
+ { headers: cfxHeaders(cfg) }
33734
33730
  );
33735
33731
  }
33736
33732
  async function createSite(cfg, name, description) {
33737
33733
  return apiFetch(
33738
- `${base(cfg)}/api/hosting/databases/${cfg.projectId}/sites`,
33739
- { method: "POST", headers: adminHeaders(cfg), body: JSON.stringify({ name, description }) }
33734
+ `${base(cfg)}/hosting/sites`,
33735
+ { method: "POST", headers: cfxHeaders(cfg), body: JSON.stringify({ name, description }) }
33740
33736
  );
33741
33737
  }
33742
33738
  async function getDnsStatus(cfg, siteId) {
33743
33739
  return apiFetch(
33744
- `${base(cfg)}/api/hosting/databases/${cfg.projectId}/sites/${siteId}/dns`,
33745
- { headers: adminHeaders(cfg) }
33740
+ `${base(cfg)}/hosting/sites/${siteId}/dns`,
33741
+ { headers: cfxHeaders(cfg) }
33746
33742
  );
33747
33743
  }
33748
33744
  async function reprovisionDns(cfg, siteId) {
33749
33745
  return apiFetch(
33750
- `${base(cfg)}/api/hosting/databases/${cfg.projectId}/sites/${siteId}/dns/provision`,
33751
- { method: "POST", headers: adminHeaders(cfg), body: JSON.stringify({}) }
33746
+ `${base(cfg)}/hosting/sites/${siteId}/dns/reprovision`,
33747
+ { method: "POST", headers: cfxHeaders(cfg), body: JSON.stringify({}) }
33752
33748
  );
33753
33749
  }
33754
33750
  async function createDeploy(cfg, siteId, entrypoint = "index.html") {
33755
33751
  return apiFetch(
33756
- `${base(cfg)}/api/hosting/databases/${cfg.projectId}/sites/${siteId}/deploys`,
33752
+ `${base(cfg)}/hosting/sites/${siteId}/deploys`,
33757
33753
  {
33758
33754
  method: "POST",
33759
- headers: adminHeaders(cfg),
33755
+ headers: cfxHeaders(cfg),
33760
33756
  body: JSON.stringify({ entrypoint, deployedBy: "clefbase-cli" })
33761
33757
  }
33762
33758
  );
33763
33759
  }
33764
33760
  async function uploadFileBatch(cfg, deployId, files) {
33765
33761
  const fetchFn = await getFetch();
33766
- const url = `${base(cfg)}/api/hosting/databases/${cfg.projectId}/deploys/${deployId}/files/batch`;
33762
+ const url = `${base(cfg)}/hosting/deploys/${deployId}/files/batch`;
33767
33763
  let res;
33768
33764
  if (typeof FormData !== "undefined") {
33769
33765
  const form = new FormData();
@@ -33776,7 +33772,7 @@ async function uploadFileBatch(cfg, deployId, files) {
33776
33772
  }
33777
33773
  res = await fetchFn(url, {
33778
33774
  method: "POST",
33779
- headers: { "x-admin-secret": cfg.adminSecret },
33775
+ headers: { "x-cfx-key": cfg.apiKey },
33780
33776
  body: form
33781
33777
  });
33782
33778
  } else {
@@ -33792,7 +33788,7 @@ async function uploadFileBatch(cfg, deployId, files) {
33792
33788
  }
33793
33789
  res = await fetchFn(url, {
33794
33790
  method: "POST",
33795
- headers: { "x-admin-secret": cfg.adminSecret, ...form.getHeaders() },
33791
+ headers: { "x-cfx-key": cfg.apiKey, ...form.getHeaders() },
33796
33792
  body: form
33797
33793
  });
33798
33794
  }
@@ -33809,10 +33805,10 @@ async function uploadFileBatch(cfg, deployId, files) {
33809
33805
  }
33810
33806
  async function finalizeDeploy(cfg, deployId, message) {
33811
33807
  return apiFetch(
33812
- `${base(cfg)}/api/hosting/databases/${cfg.projectId}/deploys/${deployId}/finalize`,
33808
+ `${base(cfg)}/hosting/deploys/${deployId}/finalize`,
33813
33809
  {
33814
33810
  method: "POST",
33815
- headers: adminHeaders(cfg),
33811
+ headers: cfxHeaders(cfg),
33816
33812
  body: JSON.stringify({ message: message ?? "Deployed via clefbase CLI" })
33817
33813
  }
33818
33814
  );
@@ -33820,8 +33816,8 @@ async function finalizeDeploy(cfg, deployId, message) {
33820
33816
  async function getActiveDeploy(cfg, siteId) {
33821
33817
  try {
33822
33818
  return await apiFetch(
33823
- `${base(cfg)}/api/hosting/databases/${cfg.projectId}/sites/${siteId}/active`,
33824
- { headers: adminHeaders(cfg) }
33819
+ `${base(cfg)}/hosting/sites/${siteId}/active`,
33820
+ { headers: cfxHeaders(cfg) }
33825
33821
  );
33826
33822
  } catch (err) {
33827
33823
  if (err.status === 404) return null;
@@ -33903,18 +33899,10 @@ async function runInit(cwd = process.cwd()) {
33903
33899
  mask: "\u25CF",
33904
33900
  validate: (v) => v.trim().length > 0 || "Required"
33905
33901
  }]);
33906
- const { adminSecret } = await lib_default.prompt([{
33907
- type: "password",
33908
- name: "adminSecret",
33909
- message: "Admin Secret (x-admin-secret, needed for hosting)",
33910
- mask: "\u25CF",
33911
- validate: (v) => v.trim().length > 0 || "Required"
33912
- }]);
33913
33902
  const cfg = {
33914
33903
  serverUrl: serverUrl.replace(/\/+$/, ""),
33915
33904
  projectId: projectId.trim(),
33916
33905
  apiKey: apiKey.trim(),
33917
- adminSecret: adminSecret.trim(),
33918
33906
  services: { database: false, auth: false, storage: false, hosting: false, functions: false }
33919
33907
  };
33920
33908
  const connSpinner = ora2("Testing connection\u2026").start();
@@ -33947,6 +33935,10 @@ async function runInit(cwd = process.cwd()) {
33947
33935
  if (cfg.services.hosting) {
33948
33936
  await setupHosting(cfg, cwd);
33949
33937
  }
33938
+ let functionsRuntime;
33939
+ if (cfg.services.functions) {
33940
+ functionsRuntime = await setupFunctions(cfg, cwd);
33941
+ }
33950
33942
  const configPath = saveConfig(cfg, cwd);
33951
33943
  ensureGitignore(cwd);
33952
33944
  writeEnvExample(cfg, cwd);
@@ -33961,9 +33953,591 @@ async function runInit(cwd = process.cwd()) {
33961
33953
  console.log(source_default.dim(` Lib config: ${libResult.configCopy}`));
33962
33954
  console.log(source_default.dim(` Lib entry: ${libResult.libFile}`));
33963
33955
  }
33956
+ if (cfg.services.functions) {
33957
+ console.log(source_default.dim(` Functions: functions/ (${functionsRuntime ?? "node"})`));
33958
+ console.log(source_default.dim(` run: node functions/deploy.mjs`));
33959
+ }
33964
33960
  console.log();
33965
33961
  printUsageHint(cfg);
33966
33962
  }
33963
+ async function setupFunctions(cfg, cwd) {
33964
+ console.log();
33965
+ console.log(source_default.bold(" Functions"));
33966
+ console.log();
33967
+ const { runtime } = await lib_default.prompt([{
33968
+ type: "list",
33969
+ name: "runtime",
33970
+ message: "Default functions runtime",
33971
+ choices: [
33972
+ { name: "Node.js / TypeScript (recommended)", value: "node" },
33973
+ { name: "Python 3", value: "python" },
33974
+ { name: "Both (create examples for each)", value: "both" }
33975
+ ],
33976
+ default: "node"
33977
+ }]);
33978
+ const scaffoldResult = scaffoldFunctions(cfg, cwd, runtime);
33979
+ const sp = ora2("Creating functions/ folder\u2026").start();
33980
+ sp.succeed(
33981
+ source_default.green(
33982
+ `functions/ created (${scaffoldResult.files.length} file${scaffoldResult.files.length !== 1 ? "s" : ""})`
33983
+ )
33984
+ );
33985
+ return runtime;
33986
+ }
33987
+ function scaffoldFunctions(cfg, cwd = process.cwd(), runtime = "node") {
33988
+ const dir = import_path2.default.join(cwd, "functions");
33989
+ const files = [];
33990
+ import_fs3.default.mkdirSync(dir, { recursive: true });
33991
+ const write = (rel, content) => {
33992
+ const abs = import_path2.default.join(dir, rel);
33993
+ import_fs3.default.mkdirSync(import_path2.default.dirname(abs), { recursive: true });
33994
+ if (!import_fs3.default.existsSync(abs)) {
33995
+ import_fs3.default.writeFileSync(abs, content);
33996
+ files.push(import_path2.default.join("functions", rel));
33997
+ }
33998
+ };
33999
+ const wantNode = runtime === "node" || runtime === "both";
34000
+ const wantPython = runtime === "python" || runtime === "both";
34001
+ write("README.md", buildFunctionsReadme(cfg, runtime));
34002
+ write(".env", buildFunctionsEnv(cfg));
34003
+ write(".env.example", buildFunctionsEnvExample(cfg));
34004
+ write(".gitignore", FUNCTIONS_GITIGNORE);
34005
+ if (wantNode) {
34006
+ write("src/hello.ts", NODE_HELLO_TS);
34007
+ write("src/onUserCreate.ts", NODE_ON_USER_CREATE_TS);
34008
+ write("src/scheduled.ts", NODE_SCHEDULED_TS);
34009
+ write("tsconfig.json", FUNCTIONS_TSCONFIG);
34010
+ }
34011
+ if (wantPython) {
34012
+ write("python/hello.py", PYTHON_HELLO_PY);
34013
+ write("python/on_user_create.py", PYTHON_ON_USER_CREATE_PY);
34014
+ write("python/scheduled.py", PYTHON_SCHEDULED_PY);
34015
+ write("python/requirements.txt", PYTHON_REQUIREMENTS_TXT);
34016
+ }
34017
+ write("package.json", buildFunctionsPackageJson(cfg, runtime));
34018
+ write("deploy.mjs", buildDeployMjs(cfg, runtime));
34019
+ return { dir, files };
34020
+ }
34021
+ function buildFunctionsReadme(cfg, runtime) {
34022
+ const wantNode = runtime === "node" || runtime === "both";
34023
+ const wantPython = runtime === "python" || runtime === "both";
34024
+ const baseUrl = cfg.serverUrl.replace(/\/+$/, "");
34025
+ return `# Clefbase Functions
34026
+
34027
+ Serverless functions for project \`${cfg.projectId}\`.
34028
+
34029
+ Functions run on the Clefbase server and are triggered by HTTP calls,
34030
+ database events, auth events, storage events, or a cron schedule.
34031
+ No infrastructure to manage \u2014 just write code and deploy.
34032
+
34033
+ ---
34034
+
34035
+ ## Quick start
34036
+ ${wantNode ? `
34037
+ ### Node.js / TypeScript
34038
+
34039
+ \`\`\`bash
34040
+ # 1. Install the Clefbase CLI (if not already)
34041
+ npm install -g clefbase
34042
+
34043
+ # 2. Deploy a function
34044
+ clefbase functions:deploy -f src/hello.ts --name hello --trigger http
34045
+
34046
+ # 3. Call it
34047
+ clefbase functions:call hello -d '{"name":"World"}'
34048
+ # or via HTTP
34049
+ curl -X POST ${baseUrl}/functions/call/hello \\
34050
+ -H "x-cfx-key: <YOUR_API_KEY>" \\
34051
+ -H "Content-Type: application/json" \\
34052
+ -d '{"data":{"name":"World"}}'
34053
+ \`\`\`
34054
+ ` : ""}${wantPython ? `
34055
+ ### Python 3
34056
+
34057
+ \`\`\`bash
34058
+ # Deploy a Python function
34059
+ clefbase functions:deploy -f python/hello.py --name hello-py --runtime python --trigger http
34060
+
34061
+ # Call it
34062
+ clefbase functions:call hello-py -d '{"name":"World"}'
34063
+ \`\`\`
34064
+ ` : ""}
34065
+ ---
34066
+
34067
+ ## Writing functions
34068
+
34069
+ Every function must export a single async **\`handler\`** (or the name you
34070
+ pass as \`--entry\`). It receives one argument \u2014 a **\`FunctionContext\`** \u2014 and
34071
+ can return any JSON-serialisable value.
34072
+
34073
+ ### FunctionContext shape
34074
+
34075
+ \`\`\`ts
34076
+ interface FunctionContext {
34077
+ /** Payload supplied by the caller (HTTP) or the event (triggers). */
34078
+ data: unknown;
34079
+
34080
+ /** Populated when the caller includes a valid auth token. */
34081
+ auth?: {
34082
+ uid: string;
34083
+ email?: string;
34084
+ metadata: Record<string, unknown>;
34085
+ };
34086
+
34087
+ /** Describes what fired the function. */
34088
+ trigger: {
34089
+ type: string; // "http" | "schedule" | "onDocumentCreate" | \u2026
34090
+ timestamp: string; // ISO 8601
34091
+ document?: unknown; // populated for document triggers
34092
+ before?: unknown; // populated for onDocumentUpdate / onDocumentWrite
34093
+ after?: unknown; // populated for onDocumentUpdate / onDocumentWrite
34094
+ user?: unknown; // populated for onUserCreate / onUserDelete
34095
+ file?: unknown; // populated for onFileUpload / onFileDelete
34096
+ };
34097
+
34098
+ /** Key-value env vars you passed at deploy time. */
34099
+ env: Record<string, string>;
34100
+ }
34101
+ \`\`\`
34102
+
34103
+ ### Trigger types
34104
+
34105
+ | Type | When it fires |
34106
+ |------|---------------|
34107
+ | \`http\` | \`POST /functions/call/:name\` |
34108
+ | \`schedule\` | Cron timer (e.g. \`0 * * * *\` = every hour) |
34109
+ | \`onDocumentCreate\` | A document is created in the watched collection |
34110
+ | \`onDocumentUpdate\` | A document is updated |
34111
+ | \`onDocumentDelete\` | A document is deleted |
34112
+ | \`onDocumentWrite\` | Any of create / update / delete |
34113
+ | \`onUserCreate\` | A new user account is created |
34114
+ | \`onUserDelete\` | A user account is deleted |
34115
+ | \`onFileUpload\` | A file is uploaded to Storage |
34116
+ | \`onFileDelete\` | A file is deleted from Storage |
34117
+
34118
+ ---
34119
+
34120
+ ## Deployment examples
34121
+
34122
+ \`\`\`bash
34123
+ # HTTP function
34124
+ clefbase functions:deploy -f src/hello.ts --trigger http
34125
+
34126
+ # Scheduled (every day at midnight UTC)
34127
+ clefbase functions:deploy -f src/scheduled.ts --trigger schedule --cron "0 0 * * *"
34128
+
34129
+ # Document trigger (fires on every new "orders" document)
34130
+ clefbase functions:deploy -f src/onOrder.ts --trigger onDocumentCreate --collection orders
34131
+
34132
+ # With env vars and a custom timeout
34133
+ clefbase functions:deploy -f src/sendEmail.ts \\
34134
+ --trigger http \\
34135
+ --env SENDGRID_KEY=SG.xxx \\
34136
+ --timeout 15000
34137
+
34138
+ # Use the deploy script in this folder (deploys everything at once)
34139
+ node deploy.mjs
34140
+ \`\`\`
34141
+
34142
+ ---
34143
+
34144
+ ## Calling HTTP functions
34145
+
34146
+ ### From the Clefbase SDK
34147
+
34148
+ \`\`\`ts
34149
+ import { getFunctions, httpsCallable } from "clefbase";
34150
+ // or: import { fns } from "@lib/clefBase";
34151
+
34152
+ const greet = httpsCallable<{ name: string }, { message: string }>(fns, "hello");
34153
+ const { data, durationMs } = await greet({ name: "Alice" });
34154
+ console.log(data.message); // "Hello, Alice!"
34155
+ console.log(durationMs); // e.g. 42
34156
+ \`\`\`
34157
+
34158
+ ### Auth-aware calls
34159
+
34160
+ \`\`\`ts
34161
+ import { getAuth, setAuthToken } from "clefbase";
34162
+
34163
+ const { token } = await getAuth(app).signIn("alice@example.com", "password");
34164
+ setAuthToken(app, token);
34165
+
34166
+ // ctx.auth.uid and ctx.auth.email are now available inside the function
34167
+ const { data } = await callFunction(fns, "getProfile");
34168
+ \`\`\`
34169
+
34170
+ ### Raw HTTP (no SDK)
34171
+
34172
+ \`\`\`bash
34173
+ curl -X POST ${baseUrl}/functions/call/<functionName> \\
34174
+ -H "x-cfx-key: <YOUR_API_KEY>" \\
34175
+ -H "Content-Type: application/json" \\
34176
+ -H "Authorization: Bearer <USER_JWT>" # optional auth
34177
+ -d '{"data": { "key": "value" }}'
34178
+ \`\`\`
34179
+
34180
+ ---
34181
+
34182
+ ## Viewing logs
34183
+
34184
+ \`\`\`bash
34185
+ # Last 20 executions
34186
+ clefbase functions:logs hello
34187
+
34188
+ # Last 50
34189
+ clefbase functions:logs hello -l 50
34190
+
34191
+ # All functions
34192
+ clefbase functions:list
34193
+ \`\`\`
34194
+
34195
+ ---
34196
+
34197
+ ## Managing functions
34198
+
34199
+ \`\`\`bash
34200
+ clefbase functions:list # see all deployed functions
34201
+ clefbase functions:delete oldFunction # delete (prompts for confirmation)
34202
+ clefbase functions:delete oldFunction -y # delete without confirmation
34203
+ \`\`\`
34204
+
34205
+ ---
34206
+
34207
+ ## Environment variables
34208
+
34209
+ Secrets and config for your functions live in \`functions/.env\`.
34210
+ They are **never** committed to git (already in \`.gitignore\`).
34211
+
34212
+ Pass them at deploy time with \`--env KEY=VALUE\` or edit \`deploy.sh\`
34213
+ to read them automatically from \`.env\`.
34214
+
34215
+ ---
34216
+
34217
+ ## Project info
34218
+
34219
+ | Key | Value |
34220
+ |-----|-------|
34221
+ | Project ID | \`${cfg.projectId}\` |
34222
+ | Server | \`${baseUrl}\` |
34223
+ | Functions base URL | \`${baseUrl}/functions\` |
34224
+ `;
34225
+ }
34226
+ function buildFunctionsEnv(cfg) {
34227
+ return `# Clefbase Functions \u2014 secrets for this project
34228
+ # This file is git-ignored. Do NOT commit it.
34229
+ # Copy values from your Clefbase dashboard.
34230
+
34231
+ CLEFBASE_SERVER_URL=${cfg.serverUrl}
34232
+ CLEFBASE_PROJECT_ID=${cfg.projectId}
34233
+ CLEFBASE_API_KEY=${cfg.apiKey}
34234
+
34235
+ # Add your own secrets below:
34236
+ # SENDGRID_API_KEY=
34237
+ # STRIPE_SECRET_KEY=
34238
+ # DATABASE_URL=
34239
+ `;
34240
+ }
34241
+ function buildFunctionsEnvExample(cfg) {
34242
+ return `# Clefbase Functions \u2014 environment variable template
34243
+ # Copy this to .env and fill in your values.
34244
+
34245
+ CLEFBASE_SERVER_URL=${cfg.serverUrl}
34246
+ CLEFBASE_PROJECT_ID=${cfg.projectId}
34247
+ CLEFBASE_API_KEY=your_api_key_here
34248
+
34249
+ # Add your own secrets below:
34250
+ # SENDGRID_API_KEY=
34251
+ # STRIPE_SECRET_KEY=
34252
+ # DATABASE_URL=
34253
+ `;
34254
+ }
34255
+ var FUNCTIONS_GITIGNORE = `# Dependencies
34256
+ node_modules/
34257
+
34258
+ # Python
34259
+ __pycache__/
34260
+ *.pyc
34261
+ *.pyo
34262
+ .venv/
34263
+ venv/
34264
+
34265
+ # Secrets \u2014 never commit these
34266
+ .env
34267
+
34268
+ # Build artefacts
34269
+ dist/
34270
+ *.js.map
34271
+ `;
34272
+ var FUNCTIONS_TSCONFIG = `{
34273
+ "compilerOptions": {
34274
+ "target": "ES2022",
34275
+ "module": "ESNext",
34276
+ "moduleResolution": "bundler",
34277
+ "lib": ["ES2022"],
34278
+ "strict": true,
34279
+ "esModuleInterop": true,
34280
+ "skipLibCheck": true,
34281
+ "outDir": "dist"
34282
+ },
34283
+ "include": ["src/**/*"],
34284
+ "exclude": ["node_modules", "dist"]
34285
+ }
34286
+ `;
34287
+ var NODE_HELLO_TS = `/**
34288
+ * hello.ts \u2014 HTTP-triggered "hello world" function.
34289
+ *
34290
+ * Deploy:
34291
+ * clefbase functions:deploy -f src/hello.ts --name hello --trigger http
34292
+ *
34293
+ * Call:
34294
+ * clefbase functions:call hello -d '{"name":"World"}'
34295
+ */
34296
+
34297
+ import type { FunctionContext } from "clefbase";
34298
+
34299
+ interface Input { name?: string }
34300
+ interface Output { message: string; timestamp: string }
34301
+
34302
+ export async function handler(ctx: FunctionContext): Promise<Output> {
34303
+ const { name = "World" } = (ctx.data ?? {}) as Input;
34304
+
34305
+ return {
34306
+ message: \`Hello, \${name}!\`,
34307
+ timestamp: ctx.trigger.timestamp,
34308
+ };
34309
+ }
34310
+ `;
34311
+ var NODE_ON_USER_CREATE_TS = `/**
34312
+ * onUserCreate.ts \u2014 fires whenever a new user signs up.
34313
+ *
34314
+ * Deploy:
34315
+ * clefbase functions:deploy -f src/onUserCreate.ts \\
34316
+ * --name onUserCreate --trigger onUserCreate
34317
+ *
34318
+ * ctx.trigger.user contains the new user's profile.
34319
+ */
34320
+
34321
+ import type { FunctionContext } from "clefbase";
34322
+
34323
+ export async function handler(ctx: FunctionContext): Promise<void> {
34324
+ const user = ctx.trigger.user as {
34325
+ uid: string;
34326
+ email?: string;
34327
+ metadata: Record<string, unknown>;
34328
+ } | undefined;
34329
+
34330
+ if (!user) return;
34331
+
34332
+ console.log(\`New user: \${user.uid} email: \${user.email ?? "\u2014"}\`);
34333
+
34334
+ // Example: send a welcome email, add to a mailing list, seed default data\u2026
34335
+ // const emailKey = ctx.env["SENDGRID_API_KEY"];
34336
+ }
34337
+ `;
34338
+ var NODE_SCHEDULED_TS = `/**
34339
+ * scheduled.ts \u2014 runs on a cron schedule.
34340
+ *
34341
+ * Deploy (every day at midnight UTC):
34342
+ * clefbase functions:deploy -f src/scheduled.ts \\
34343
+ * --name dailyCleanup --trigger schedule --cron "0 0 * * *"
34344
+ *
34345
+ * Common cron expressions:
34346
+ * "* * * * *" every minute
34347
+ * "0 * * * *" every hour
34348
+ * "0 9 * * 1" every Monday at 09:00 UTC
34349
+ */
34350
+
34351
+ import type { FunctionContext } from "clefbase";
34352
+
34353
+ export async function handler(ctx: FunctionContext): Promise<{ cleaned: number }> {
34354
+ console.log("Running scheduled cleanup at", ctx.trigger.timestamp);
34355
+
34356
+ // TODO: your scheduled work here
34357
+ const cleaned = 0;
34358
+
34359
+ return { cleaned };
34360
+ }
34361
+ `;
34362
+ var PYTHON_HELLO_PY = `"""
34363
+ hello.py \u2014 HTTP-triggered "hello world" function.
34364
+
34365
+ Deploy:
34366
+ clefbase functions:deploy -f python/hello.py \\
34367
+ --name hello-py --runtime python --trigger http
34368
+
34369
+ Call:
34370
+ clefbase functions:call hello-py -d '{"name":"World"}'
34371
+ """
34372
+
34373
+
34374
+ async def handler(ctx):
34375
+ """
34376
+ ctx.data \u2014 payload from the caller
34377
+ ctx.auth \u2014 populated when a valid auth token is included
34378
+ ctx.trigger \u2014 describes what fired this function
34379
+ ctx.env \u2014 env vars you passed at deploy time
34380
+ """
34381
+ data = ctx.get("data") or {}
34382
+ name = data.get("name", "World")
34383
+
34384
+ return {
34385
+ "message": f"Hello, {name}!",
34386
+ "timestamp": ctx["trigger"]["timestamp"],
34387
+ }
34388
+ `;
34389
+ var PYTHON_ON_USER_CREATE_PY = `"""
34390
+ on_user_create.py \u2014 fires whenever a new user signs up.
34391
+
34392
+ Deploy:
34393
+ clefbase functions:deploy -f python/on_user_create.py \\
34394
+ --name onUserCreate-py --runtime python --trigger onUserCreate
34395
+ """
34396
+
34397
+
34398
+ async def handler(ctx):
34399
+ user = (ctx.get("trigger") or {}).get("user") or {}
34400
+
34401
+ uid = user.get("uid", "unknown")
34402
+ email = user.get("email", "\u2014")
34403
+
34404
+ print(f"New user: {uid} email: {email}")
34405
+
34406
+ # Example: send a welcome email, seed default data, etc.
34407
+ # sendgrid_key = ctx["env"].get("SENDGRID_API_KEY")
34408
+ `;
34409
+ var PYTHON_SCHEDULED_PY = `"""
34410
+ scheduled.py \u2014 runs on a cron schedule.
34411
+
34412
+ Deploy (every day at midnight UTC):
34413
+ clefbase functions:deploy -f python/scheduled.py \\
34414
+ --name dailyCleanup-py --runtime python \\
34415
+ --trigger schedule --cron "0 0 * * *"
34416
+ """
34417
+
34418
+
34419
+ async def handler(ctx):
34420
+ print("Running scheduled cleanup at", ctx["trigger"]["timestamp"])
34421
+
34422
+ # TODO: your scheduled work here
34423
+ cleaned = 0
34424
+
34425
+ return {"cleaned": cleaned}
34426
+ `;
34427
+ var PYTHON_REQUIREMENTS_TXT = `# Add your Python dependencies here.
34428
+ # They are NOT automatically installed on the server \u2014 include only stdlib
34429
+ # and packages already available in the Clefbase Python runtime.
34430
+ #
34431
+ # For heavier workloads, consider the Node.js runtime and npm packages.
34432
+ #
34433
+ # httpx>=0.27
34434
+ # pydantic>=2.0
34435
+ `;
34436
+ function buildDeployMjs(cfg, runtime) {
34437
+ const wantNode = runtime === "node" || runtime === "both";
34438
+ const wantPython = runtime === "python" || runtime === "both";
34439
+ const fns = [];
34440
+ if (wantNode) {
34441
+ fns.push(
34442
+ { name: "hello", file: "src/hello.ts", runtime: "node", trigger: "http" },
34443
+ { name: "onUserCreate", file: "src/onUserCreate.ts", runtime: "node", trigger: "onUserCreate" },
34444
+ { name: "dailyCleanup", file: "src/scheduled.ts", runtime: "node", trigger: "schedule", cron: "0 0 * * *" }
34445
+ );
34446
+ }
34447
+ if (wantPython) {
34448
+ fns.push(
34449
+ { name: "hello-py", file: "python/hello.py", runtime: "python", trigger: "http" },
34450
+ { name: "onUserCreate-py", file: "python/on_user_create.py", runtime: "python", trigger: "onUserCreate" },
34451
+ { name: "dailyCleanup-py", file: "python/scheduled.py", runtime: "python", trigger: "schedule", cron: "0 0 * * *" }
34452
+ );
34453
+ }
34454
+ const fnJson = JSON.stringify(fns, null, 2);
34455
+ return `#!/usr/bin/env node
34456
+ // deploy.mjs \u2014 deploy all functions in this folder to Clefbase
34457
+ // Generated by \`clefbase init\` \u2022 project: ${cfg.projectId}
34458
+ //
34459
+ // Usage (any OS): node deploy.mjs
34460
+ //
34461
+ // Node.js 18+ required (uses built-in fetch + fs).
34462
+ // No extra dependencies \u2014 just the Clefbase CLI on your PATH.
34463
+
34464
+ import { execSync } from "node:child_process";
34465
+ import { existsSync, readFileSync } from "node:fs";
34466
+ import { fileURLToPath } from "node:url";
34467
+ import { dirname, join } from "node:path";
34468
+
34469
+ const __dirname = dirname(fileURLToPath(import.meta.url));
34470
+
34471
+ // \u2500\u2500 Load .env \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
34472
+
34473
+ const envPath = join(__dirname, ".env");
34474
+ if (existsSync(envPath)) {
34475
+ for (const line of readFileSync(envPath, "utf8").split("\\n")) {
34476
+ const match = line.match(/^\\s*([^#][^=]*)=(.*)$/);
34477
+ if (match) process.env[match[1].trim()] = match[2].trim();
34478
+ }
34479
+ }
34480
+
34481
+ // \u2500\u2500 Functions to deploy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
34482
+
34483
+ const functions = ${fnJson};
34484
+
34485
+ // \u2500\u2500 Deploy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
34486
+
34487
+ let passed = 0;
34488
+ let failed = 0;
34489
+
34490
+ for (const fn of functions) {
34491
+ const args = [
34492
+ \`--name \${fn.name}\`,
34493
+ \`--file \${fn.file}\`,
34494
+ \`--runtime \${fn.runtime}\`,
34495
+ \`--trigger \${fn.trigger}\`,
34496
+ fn.cron ? \`--cron "\${fn.cron}"\` : "",
34497
+ fn.collection ? \`--collection "\${fn.collection}"\` : "",
34498
+ ].filter(Boolean).join(" ");
34499
+
34500
+ console.log(\`\\n\u2192 Deploying "\${fn.name}"\u2026\`);
34501
+ try {
34502
+ execSync(\`clefbase functions:deploy \${args}\`, { stdio: "inherit", cwd: __dirname });
34503
+ passed++;
34504
+ } catch {
34505
+ console.error(\` \u2717 "\${fn.name}" failed\`);
34506
+ failed++;
34507
+ }
34508
+ }
34509
+
34510
+ // \u2500\u2500 Summary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
34511
+
34512
+ console.log(\`\\n\${passed} deployed, \${failed} failed \u2014 project: ${cfg.projectId}\\n\`);
34513
+ if (failed > 0) process.exit(1);
34514
+ `;
34515
+ }
34516
+ function buildFunctionsPackageJson(cfg, runtime) {
34517
+ const wantNode = runtime === "node" || runtime === "both";
34518
+ const scripts = {
34519
+ "deploy:all": "node deploy.mjs"
34520
+ };
34521
+ if (wantNode) {
34522
+ scripts["deploy:hello"] = "clefbase functions:deploy -f src/hello.ts --name hello --runtime node --trigger http";
34523
+ scripts["deploy:onUserCreate"] = "clefbase functions:deploy -f src/onUserCreate.ts --name onUserCreate --runtime node --trigger onUserCreate";
34524
+ scripts["deploy:scheduled"] = 'clefbase functions:deploy -f src/scheduled.ts --name dailyCleanup --runtime node --trigger schedule --cron "0 0 * * *"';
34525
+ }
34526
+ if (runtime === "python" || runtime === "both") {
34527
+ scripts["deploy:hello-py"] = "clefbase functions:deploy -f python/hello.py --name hello-py --runtime python --trigger http";
34528
+ scripts["deploy:onUserCreate-py"] = "clefbase functions:deploy -f python/on_user_create.py --name onUserCreate-py --runtime python --trigger onUserCreate";
34529
+ scripts["deploy:scheduled-py"] = 'clefbase functions:deploy -f python/scheduled.py --name dailyCleanup-py --runtime python --trigger schedule --cron "0 0 * * *"';
34530
+ }
34531
+ const pkg = {
34532
+ name: `${cfg.projectId}-functions`,
34533
+ version: "1.0.0",
34534
+ description: `Clefbase Functions for project ${cfg.projectId}`,
34535
+ private: true,
34536
+ type: "module",
34537
+ scripts
34538
+ };
34539
+ return JSON.stringify(pkg, null, 2) + "\n";
34540
+ }
33967
34541
  function scaffoldLib(cfg, cwd = process.cwd()) {
33968
34542
  const libDir = import_path2.default.join(cwd, "src", "lib");
33969
34543
  import_fs3.default.mkdirSync(libDir, { recursive: true });
@@ -34018,10 +34592,9 @@ function buildLibTs(cfg) {
34018
34592
  `// \u2500\u2500\u2500 App \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
34019
34593
  ``,
34020
34594
  `const app = initClefbase({`,
34021
- ` serverUrl: config.serverUrl,`,
34022
- ` projectId: config.projectId,`,
34023
- ` apiKey: config.apiKey,`,
34024
- ` adminSecret: "",`,
34595
+ ` serverUrl: config.serverUrl,`,
34596
+ ` projectId: config.projectId,`,
34597
+ ` apiKey: config.apiKey,`,
34025
34598
  `});`,
34026
34599
  ``
34027
34600
  ];
@@ -34069,7 +34642,6 @@ function buildLibTs(cfg) {
34069
34642
  return lines.join("\n");
34070
34643
  }
34071
34644
  async function setupHosting(cfg, cwd) {
34072
- var _a;
34073
34645
  console.log();
34074
34646
  console.log(source_default.bold(" Hosting"));
34075
34647
  console.log();
@@ -34086,6 +34658,7 @@ async function setupHosting(cfg, cwd) {
34086
34658
  }
34087
34659
  let siteId = "";
34088
34660
  let siteName = "";
34661
+ let previewUrl = "";
34089
34662
  if (existing.length > 0) {
34090
34663
  const choices = [
34091
34664
  ...existing.map((s) => ({ name: `${s.name} ${source_default.dim(s.id)}`, value: s.id })),
@@ -34098,8 +34671,10 @@ async function setupHosting(cfg, cwd) {
34098
34671
  choices
34099
34672
  }]);
34100
34673
  if (chosen !== "__new__") {
34674
+ const found = existing.find((s) => s.id === chosen);
34101
34675
  siteId = chosen;
34102
- siteName = ((_a = existing.find((s) => s.id === chosen)) == null ? void 0 : _a.name) ?? chosen;
34676
+ siteName = (found == null ? void 0 : found.name) ?? chosen;
34677
+ previewUrl = (found == null ? void 0 : found.previewUrl) ?? "";
34103
34678
  }
34104
34679
  }
34105
34680
  if (!siteId) {
@@ -34114,6 +34689,7 @@ async function setupHosting(cfg, cwd) {
34114
34689
  const site = await createSite(cfg, newName.trim());
34115
34690
  siteId = site.id;
34116
34691
  siteName = site.name;
34692
+ previewUrl = site.previewUrl ?? "";
34117
34693
  s.succeed(source_default.green(`Created "${siteName}" ${source_default.dim(siteId)}`));
34118
34694
  } catch (err) {
34119
34695
  s.fail(`Failed: ${err.message}`);
@@ -34137,6 +34713,7 @@ async function setupHosting(cfg, cwd) {
34137
34713
  cfg.hosting = {
34138
34714
  siteId,
34139
34715
  siteName,
34716
+ previewUrl,
34140
34717
  distDir: distDir.trim(),
34141
34718
  entrypoint: entrypoint.trim()
34142
34719
  };
@@ -34175,12 +34752,15 @@ function printUsageHint(cfg) {
34175
34752
  if (cfg.services.functions) {
34176
34753
  console.log();
34177
34754
  console.log(source_default.bold(" Functions:"));
34178
- console.log(source_default.cyan(` // Deploy from a file`));
34179
- console.log(source_default.cyan(` $ clefbase functions:deploy -f ./src/functions/hello.ts`));
34755
+ console.log(source_default.cyan(` # Edit your functions in functions/src/`));
34756
+ console.log(source_default.cyan(` $ clefbase functions:deploy -f functions/src/hello.ts`));
34180
34757
  console.log();
34181
- console.log(source_default.cyan(` // Call from your app`));
34758
+ console.log(source_default.cyan(` # Call from your app`));
34182
34759
  console.log(source_default.cyan(` const greet = httpsCallable(fns, "greetUser");`));
34183
34760
  console.log(source_default.cyan(` const { data } = await greet({ name: "Alice" });`));
34761
+ console.log();
34762
+ console.log(source_default.dim(` Full guide: functions/README.md`));
34763
+ console.log(source_default.dim(` Deploy all: node functions/deploy.mjs`));
34184
34764
  }
34185
34765
  if (cfg.services.hosting && cfg.hosting) {
34186
34766
  console.log();
@@ -34194,6 +34774,7 @@ function printUsageHint(cfg) {
34194
34774
  // src/cli/commands/deploy.ts
34195
34775
  var import_path3 = __toESM(require("path"));
34196
34776
  var import_fs4 = __toESM(require("fs"));
34777
+ var import_child_process2 = require("child_process");
34197
34778
  var SKIP_FILES = /* @__PURE__ */ new Set([".DS_Store", "Thumbs.db", ".gitkeep", ".gitignore"]);
34198
34779
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".svn", ".hg"]);
34199
34780
  function scanDir(dir, base2, out) {
@@ -34218,15 +34799,51 @@ function fmtBytes(n) {
34218
34799
  return `${(n / (1024 * 1024)).toFixed(2)} MB`;
34219
34800
  }
34220
34801
  async function runDeploy(opts) {
34221
- var _a, _b, _c, _d, _e, _f, _g, _h;
34222
34802
  const cwd = opts.cwd ?? process.cwd();
34223
34803
  const cfg = requireConfig(cwd);
34804
+ const deployHosting = !opts.only || opts.only === "hosting";
34805
+ const deployFunctions = !opts.only || opts.only === "functions";
34806
+ if (deployHosting && !cfg.services.hosting && opts.only === "hosting") {
34807
+ console.error(source_default.red("\n Hosting is not enabled for this project.\n Run `clefbase hosting:init` to set it up.\n"));
34808
+ process.exit(1);
34809
+ }
34810
+ if (deployFunctions && !cfg.services.functions && opts.only === "functions") {
34811
+ console.error(source_default.red("\n Functions is not enabled for this project.\n Run `clefbase init` and enable Functions to set it up.\n"));
34812
+ process.exit(1);
34813
+ }
34224
34814
  console.log();
34225
- console.log(source_default.bold.cyan(" Clefbase Deploy"));
34815
+ if (opts.only === "hosting") {
34816
+ console.log(source_default.bold.cyan(" Clefbase Deploy \xB7 Hosting"));
34817
+ } else if (opts.only === "functions") {
34818
+ console.log(source_default.bold.cyan(" Clefbase Deploy \xB7 Functions"));
34819
+ } else {
34820
+ console.log(source_default.bold.cyan(" Clefbase Deploy"));
34821
+ }
34226
34822
  console.log();
34823
+ if (deployFunctions && cfg.services.functions) {
34824
+ await runFunctionsDeploy({ cwd });
34825
+ if (deployHosting && cfg.services.hosting) {
34826
+ console.log();
34827
+ }
34828
+ }
34829
+ if (deployHosting && cfg.services.hosting) {
34830
+ await runHostingDeploy({ dir: opts.dir, message: opts.message, site: opts.site, cwd });
34831
+ }
34832
+ if (!deployHosting && !deployFunctions) {
34833
+ console.log(source_default.yellow(" Nothing to deploy \u2014 no services matched.\n"));
34834
+ }
34835
+ }
34836
+ async function runHostingDeploy(opts) {
34837
+ var _a, _b, _c, _d, _e, _f, _g, _h;
34838
+ const cwd = opts.cwd ?? process.cwd();
34839
+ const cfg = requireConfig(cwd);
34840
+ if (!cfg.services.hosting) {
34841
+ console.error(source_default.red("\n Hosting is not enabled for this project.\n Run `clefbase hosting:init` to set it up.\n"));
34842
+ process.exit(1);
34843
+ }
34227
34844
  let siteId = opts.site ?? ((_a = cfg.hosting) == null ? void 0 : _a.siteId) ?? "";
34228
34845
  let siteName = ((_b = cfg.hosting) == null ? void 0 : _b.siteName) ?? "";
34229
- let previewUrl = "";
34846
+ let previewUrl = ((_c = cfg.hosting) == null ? void 0 : _c.previewUrl) ?? "";
34230
34847
  if (!siteId) {
34231
34848
  const site = await pickOrCreateSite(cfg);
34232
34849
  siteId = site.id;
@@ -34236,12 +34853,10 @@ async function runDeploy(opts) {
34236
34853
  siteId,
34237
34854
  siteName,
34238
34855
  previewUrl,
34239
- distDir: ((_c = cfg.hosting) == null ? void 0 : _c.distDir) ?? "dist",
34240
- entrypoint: ((_d = cfg.hosting) == null ? void 0 : _d.entrypoint) ?? "index.html"
34856
+ distDir: ((_d = cfg.hosting) == null ? void 0 : _d.distDir) ?? "dist",
34857
+ entrypoint: ((_e = cfg.hosting) == null ? void 0 : _e.entrypoint) ?? "index.html"
34241
34858
  };
34242
34859
  saveConfig(cfg, cwd);
34243
- } else {
34244
- previewUrl = ((_e = cfg.hosting) == null ? void 0 : _e.previewUrl) ?? "";
34245
34860
  }
34246
34861
  const distDir = opts.dir ?? ((_f = cfg.hosting) == null ? void 0 : _f.distDir) ?? await promptDistDir(cwd);
34247
34862
  const absDir = import_path3.default.isAbsolute(distDir) ? distDir : import_path3.default.join(cwd, distDir);
@@ -34321,6 +34936,35 @@ async function runDeploy(opts) {
34321
34936
  }
34322
34937
  console.log();
34323
34938
  }
34939
+ async function runFunctionsDeploy(opts = {}) {
34940
+ const cwd = opts.cwd ?? process.cwd();
34941
+ const cfg = requireConfig(cwd);
34942
+ if (!cfg.services.functions) {
34943
+ console.error(source_default.red("\n Functions is not enabled for this project.\n Run `clefbase init` and enable Functions.\n"));
34944
+ process.exit(1);
34945
+ }
34946
+ const deployMjs = import_path3.default.join(cwd, "functions", "deploy.mjs");
34947
+ if (!import_fs4.default.existsSync(deployMjs)) {
34948
+ console.error(source_default.red(`
34949
+ \u2717 functions/deploy.mjs not found.`));
34950
+ console.error(source_default.dim(" Re-run `clefbase init` to regenerate the functions/ folder,"));
34951
+ console.error(source_default.dim(" or deploy individual functions with:"));
34952
+ console.error(source_default.cyan(" clefbase functions:deploy -f functions/src/hello.ts --name hello --trigger http\n"));
34953
+ process.exit(1);
34954
+ }
34955
+ console.log(source_default.bold(" Deploying functions\u2026"));
34956
+ console.log(source_default.dim(` Running: node functions/deploy.mjs
34957
+ `));
34958
+ try {
34959
+ (0, import_child_process2.execSync)("node deploy.mjs", {
34960
+ cwd: import_path3.default.join(cwd, "functions"),
34961
+ stdio: "inherit"
34962
+ });
34963
+ } catch {
34964
+ console.error(source_default.red("\n \u2717 One or more functions failed to deploy.\n"));
34965
+ process.exit(1);
34966
+ }
34967
+ }
34324
34968
  async function runHostingInit(cwd = process.cwd()) {
34325
34969
  var _a, _b;
34326
34970
  const cfg = requireConfig(cwd);
@@ -34647,7 +35291,7 @@ var RUNTIME_BADGE = {
34647
35291
  node: source_default.green("[JS/TS]"),
34648
35292
  python: source_default.blue("[PY] ")
34649
35293
  };
34650
- async function runFunctionsDeploy(opts) {
35294
+ async function runFunctionsDeploy2(opts) {
34651
35295
  const cwd = opts.cwd ?? process.cwd();
34652
35296
  const cfg = requireConfig(cwd);
34653
35297
  console.log();
@@ -34878,7 +35522,7 @@ async function promptRequired(message) {
34878
35522
  }
34879
35523
 
34880
35524
  // package.json
34881
- var version = "1.5.3";
35525
+ var version = "2.0.1";
34882
35526
 
34883
35527
  // src/cli/index.ts
34884
35528
  var program2 = new Command();
@@ -34890,9 +35534,30 @@ program2.command("init").description("Initialise a Clefbase project in the curre
34890
35534
  fatal(err);
34891
35535
  }
34892
35536
  });
34893
- program2.command("deploy").description("Deploy your built site to Clefbase Hosting").option("-d, --dir <path>", "Build output directory (overrides clefbase.json)").option("-s, --site <siteId>", "Site ID to deploy to (overrides clefbase.json)").option("-m, --message <text>", "Deploy message / release note").action(async (opts) => {
35537
+ program2.command("deploy").description("Deploy hosting and/or functions. Deploys both by default if both are enabled.").option("-d, --dir <path>", "Build output directory (overrides clefbase.json)").option("-s, --site <siteId>", "Site ID to deploy to (overrides clefbase.json)").option("-m, --message <text>", "Deploy message / release note").option("--only <target>", "Deploy only one target: hosting | functions").action(async (opts) => {
35538
+ const only = opts.only;
35539
+ if (only && only !== "hosting" && only !== "functions") {
35540
+ console.error(source_default.red(`
35541
+ --only must be "hosting" or "functions" (got: "${only}")
35542
+ `));
35543
+ process.exit(1);
35544
+ }
35545
+ try {
35546
+ await runDeploy({ ...opts, only });
35547
+ } catch (err) {
35548
+ fatal(err);
35549
+ }
35550
+ });
35551
+ program2.command("deploy:hosting").description("Deploy your built site to Clefbase Hosting").option("-d, --dir <path>", "Build output directory (overrides clefbase.json)").option("-s, --site <siteId>", "Site ID to deploy to (overrides clefbase.json)").option("-m, --message <text>", "Deploy message / release note").action(async (opts) => {
35552
+ try {
35553
+ await runHostingDeploy(opts);
35554
+ } catch (err) {
35555
+ fatal(err);
35556
+ }
35557
+ });
35558
+ program2.command("deploy:functions").description("Deploy all functions via functions/deploy.mjs").action(async () => {
34894
35559
  try {
34895
- await runDeploy(opts);
35560
+ await runFunctionsDeploy();
34896
35561
  } catch (err) {
34897
35562
  fatal(err);
34898
35563
  }
@@ -34939,9 +35604,9 @@ program2.command("functions:list").alias("fn:list").description("List all deploy
34939
35604
  fatal(err);
34940
35605
  }
34941
35606
  });
34942
- program2.command("functions:deploy").alias("fn:deploy").description("Deploy (or redeploy) a function from a source file or interactively").option("-n, --name <name>", "Function name").option("-f, --file <path>", "Path to source file (.js, .ts, .py)").option("-r, --runtime <runtime>", "Runtime: node | python (auto-detected from file ext)").option("-t, --trigger <type>", "Trigger type (http, schedule, onDocumentCreate, \u2026)").option("-c, --cron <expr>", "Cron expression for schedule triggers").option("-C, --collection <path>", "Collection path for document triggers").option("-T, --timeout <ms>", "Execution timeout in milliseconds (default: 30000)").option("-e, --entry <name>", "Exported function name to call (default: handler)").option("--env <KEY=VALUE...>", "Environment variable(s) \u2014 repeatable").action(async (opts) => {
35607
+ program2.command("functions:deploy").alias("fn:deploy").description("Deploy (or redeploy) a single function from a source file or interactively").option("-n, --name <name>", "Function name").option("-f, --file <path>", "Path to source file (.js, .ts, .py)").option("-r, --runtime <runtime>", "Runtime: node | python (auto-detected from file ext)").option("-t, --trigger <type>", "Trigger type (http, schedule, onDocumentCreate, \u2026)").option("-c, --cron <expr>", "Cron expression for schedule triggers").option("-C, --collection <path>", "Collection path for document triggers").option("-T, --timeout <ms>", "Execution timeout in milliseconds (default: 30000)").option("-e, --entry <name>", "Exported function name to call (default: handler)").option("--env <KEY=VALUE...>", "Environment variable(s) \u2014 repeatable").action(async (opts) => {
34943
35608
  try {
34944
- await runFunctionsDeploy(opts);
35609
+ await runFunctionsDeploy2(opts);
34945
35610
  } catch (err) {
34946
35611
  fatal(err);
34947
35612
  }
@@ -34980,9 +35645,15 @@ ${source_default.bold("Examples:")}
34980
35645
  ${source_default.cyan("clefbase init")} Set up a new project
34981
35646
  ${source_default.cyan("clefbase info")} Show config & connection status
34982
35647
 
35648
+ ${source_default.bold("Deploy:")}
35649
+ ${source_default.cyan("clefbase deploy")} Deploy functions + hosting (both if enabled)
35650
+ ${source_default.cyan("clefbase deploy --only hosting")} Deploy hosting only
35651
+ ${source_default.cyan("clefbase deploy --only functions")} Deploy all functions only
35652
+ ${source_default.cyan('clefbase deploy -d ./dist -m "v2"')} Deploy hosting from a dir with a note
35653
+ ${source_default.cyan("clefbase deploy:hosting")} Deploy hosting (alias)
35654
+ ${source_default.cyan("clefbase deploy:functions")} Deploy all functions via deploy.mjs (alias)
35655
+
34983
35656
  ${source_default.bold("Hosting:")}
34984
- ${source_default.cyan("clefbase deploy")} Deploy your built site
34985
- ${source_default.cyan('clefbase deploy -d ./dist -m "v2"')} Deploy from a dir with a note
34986
35657
  ${source_default.cyan("clefbase hosting:init")} Link or create a hosted site
34987
35658
  ${source_default.cyan("clefbase hosting:status")} Show current live deploy
34988
35659
  ${source_default.cyan("clefbase hosting:sites")} List all sites
@@ -34991,9 +35662,9 @@ ${source_default.bold("Examples:")}
34991
35662
 
34992
35663
  ${source_default.bold("Functions:")}
34993
35664
  ${source_default.cyan("clefbase functions:list")} List all deployed functions
34994
- ${source_default.cyan("clefbase functions:deploy")} Interactive deploy wizard
34995
- ${source_default.cyan("clefbase functions:deploy -f ./fn.ts")} Deploy from a file (auto-detects runtime)
34996
- ${source_default.cyan("clefbase functions:deploy -n greet -f fn.ts --trigger http")}
35665
+ ${source_default.cyan("clefbase functions:deploy")} Interactive deploy wizard (single function)
35666
+ ${source_default.cyan("clefbase functions:deploy -f functions/src/hello.ts --name hello --trigger http")}
35667
+ ${source_default.cyan('clefbase functions:deploy -f functions/src/scheduled.ts --name daily --trigger schedule --cron "0 0 * * *"')}
34997
35668
  ${source_default.cyan("clefbase functions:call greetUser")} Call an HTTP function
34998
35669
  ${source_default.cyan(`clefbase functions:call greetUser -d '{"name":"Alice"}'}`)}
34999
35670
  ${source_default.cyan("clefbase functions:logs greetUser")} View execution history