naro-cli 0.25.0 → 0.26.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/deploy.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  readProjectEnv,
12
12
  slugify,
13
13
  workingTreeNotices
14
- } from "./chunk-X5TUBMEU.js";
14
+ } from "./chunk-RQJTCRKY.js";
15
15
  import "./chunk-K3NQKI34.js";
16
16
  export {
17
17
  assertFunctionsNode,
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  BACKEND_STEPS,
5
5
  BOOKMARK_FORMAT_MESSAGE,
6
6
  BUNDLE_MESSAGES,
7
+ CAPABILITIES,
7
8
  DATA_ENV_MESSAGES,
8
9
  FUNCTION_INVOKE_MAX_RESPONSE_BYTES,
9
10
  FUNCTION_INVOKE_METHODS,
@@ -36,6 +37,9 @@ import {
36
37
  controlPlaneUrl,
37
38
  createAuthClient,
38
39
  createPrompter,
40
+ dashboardCapability,
41
+ dashboardLink,
42
+ dashboardNeeds,
39
43
  dataClient,
40
44
  dataEnvironment,
41
45
  dataError,
@@ -125,7 +129,7 @@ import {
125
129
  warningLines,
126
130
  withFunctionRetries,
127
131
  writeProjectLink
128
- } from "./chunk-X5TUBMEU.js";
132
+ } from "./chunk-RQJTCRKY.js";
129
133
  import "./chunk-K3NQKI34.js";
130
134
 
131
135
  // src/args.ts
@@ -4754,7 +4758,7 @@ async function zoneNamed(context, usage) {
4754
4758
  if (!zone)
4755
4759
  throw new NaroError(
4756
4760
  "NOT_FOUND",
4757
- `${domain} is not on naro DNS for team ${team}. \`naro dns\` lists the zones.`
4761
+ `${domain} is not on Naro DNS for team ${team}. \`naro dns\` lists the zones.`
4758
4762
  );
4759
4763
  return { client, team, zone };
4760
4764
  }
@@ -4769,7 +4773,7 @@ async function runDns(context) {
4769
4773
  const { client, team } = await teamAndClient(context);
4770
4774
  const state = await client.dns.state(team);
4771
4775
  context.output.result({ team, ...state }, [
4772
- `naro DNS for ${team}${state.enabled ? "" : " (not enabled on this server)"}:`,
4776
+ `Naro DNS for ${team}${state.enabled ? "" : " (not enabled on this server)"}:`,
4773
4777
  ...state.zones.length === 0 ? [" No zones. `naro dns add <domain>` starts a move."] : state.zones.flatMap((zone) => zoneLines(zone).map((l) => ` ${l}`))
4774
4778
  ]);
4775
4779
  }
@@ -4778,7 +4782,7 @@ async function runDnsAdd(context) {
4778
4782
  const { client, team } = await teamAndClient(context);
4779
4783
  const zone = await client.dns.addZone(team, domain);
4780
4784
  context.output.result({ team, zone }, [
4781
- `\u2705 Started moving ${zone.name} to naro DNS`,
4785
+ `\u2705 Started moving ${zone.name} to Naro DNS`,
4782
4786
  ` Set these name servers at your registrar: ${zone.nameServers.join(", ")}`,
4783
4787
  " `naro dns mail` shows the mail records to keep, `naro dns check` asks whether the move is done."
4784
4788
  ]);
@@ -4790,11 +4794,11 @@ async function runDnsRemove(context) {
4790
4794
  );
4791
4795
  await confirmOrAbort(
4792
4796
  context,
4793
- `Remove ${zone.name} from naro DNS (${zone.status})?`
4797
+ `Remove ${zone.name} from Naro DNS (${zone.status})?`
4794
4798
  );
4795
4799
  await client.dns.removeZone(team, zone.id);
4796
4800
  context.output.result({ team, removed: zone.name }, [
4797
- `\u2705 Removed ${zone.name} from naro DNS`
4801
+ `\u2705 Removed ${zone.name} from Naro DNS`
4798
4802
  ]);
4799
4803
  }
4800
4804
  async function runDnsCheck(context) {
@@ -5269,6 +5273,15 @@ async function accessJwtFor(config, jwtStore) {
5269
5273
  }
5270
5274
  return { error: invalidSession() };
5271
5275
  }
5276
+ async function freshAccessJwt(config) {
5277
+ const jwtStore = config.accessJwt;
5278
+ if (jwtStore === void 0 || config.service) return;
5279
+ const jwt = await jwtStore.get();
5280
+ if (jwt === null) return;
5281
+ const expiry = localExpiry(config, jwt);
5282
+ if (expiry === null || expiry - ACCESS_JWT_MARGIN_MS > Date.now()) return;
5283
+ await accessJwtFor(config, jwtStore);
5284
+ }
5272
5285
  async function bearerFor(config, path, withToken) {
5273
5286
  if (!withToken) return { token: null, session: null };
5274
5287
  const store = storeFor(config, path);
@@ -6205,6 +6218,7 @@ var AuthClient = class {
6205
6218
  const renewed = tokenFrom(result.answer);
6206
6219
  if (renewed !== null && token !== null && renewed !== token)
6207
6220
  await tokens.replaceIf(token, renewed);
6221
+ await freshAccessJwt(this.#http);
6208
6222
  return { data: { session: body.session, user }, error: null };
6209
6223
  }
6210
6224
  /** `{ data: { user } }`; `user` is null when signed out. */
@@ -9002,7 +9016,7 @@ var RealtimeChannel = class {
9002
9016
  payload: isRecord(payload.payload) ? payload.payload : {}
9003
9017
  };
9004
9018
  for (const binding of this.#bindings)
9005
- if (binding.type === "broadcast" && binding.event === event)
9019
+ if (binding.type === "broadcast" && (binding.event === event || binding.event === "*"))
9006
9020
  safely(() => binding.callback(message));
9007
9021
  }
9008
9022
  #system(payload) {
@@ -11823,6 +11837,47 @@ async function runFunctionsInvoke(context) {
11823
11837
  );
11824
11838
  }
11825
11839
 
11840
+ // src/commands/dashboard.ts
11841
+ var WITH_PAGE = CAPABILITIES.filter(
11842
+ (capability) => typeof capability.dashboard === "string"
11843
+ );
11844
+ async function runDashboard(context) {
11845
+ const [id] = context.positionals;
11846
+ if (id === void 0) {
11847
+ const rows = WITH_PAGE.map((capability2) => ({
11848
+ capability: capability2.id,
11849
+ title: capability2.title,
11850
+ cli: capability2.cli.kind === "built" ? capability2.cli.commands ?? [] : []
11851
+ }));
11852
+ const width = Math.max(...rows.map((row) => row.capability.length));
11853
+ context.output.result({ capabilities: rows }, [
11854
+ "Pages in the dashboard (naro dashboard <capability> prints the URL):",
11855
+ ...rows.map(
11856
+ (row) => ` ${row.capability.padEnd(width)} ${row.title}${row.cli.length === 0 ? " (dashboard only)" : ""}`
11857
+ )
11858
+ ]);
11859
+ return;
11860
+ }
11861
+ const capability = dashboardCapability(id);
11862
+ const needs = dashboardNeeds(capability.dashboard);
11863
+ const client = await context.client();
11864
+ const target = needs.project ? { projectId: await context.resolveProjectId() } : needs.team ? {
11865
+ teamSlug: (await resolveTeam(context, await client.teams.list(), {
11866
+ intent: "read"
11867
+ })).slug
11868
+ } : {};
11869
+ const link = await dashboardLink(client, capability, target, {
11870
+ baseUrl: controlPlaneUrl(),
11871
+ client: "cli"
11872
+ });
11873
+ context.output.result({ link }, [
11874
+ link.url,
11875
+ ` ${link.title}`,
11876
+ ...link.reason ? [` Not in the CLI: ${link.reason.why}`] : [],
11877
+ ...link.cli.length > 0 ? [` Or: ${link.cli.join(", ")}`] : []
11878
+ ]);
11879
+ }
11880
+
11826
11881
  // src/commands/readouts.ts
11827
11882
  function money(cents) {
11828
11883
  return `$${(cents / 100).toFixed(2)}`;
@@ -11841,7 +11896,9 @@ function billingLines(billing) {
11841
11896
  ...overage ? [
11842
11897
  ` overage ${overage.month}: ${money(overage.totalCents)} (requests ${money(overage.requestsCents)}, builds ${money(overage.buildsCents)}, domains ${money(overage.domainsCents)})`
11843
11898
  ] : [],
11844
- ...billing.canManage ? [" Change the plan in the dashboard: Settings \u2192 Billing."] : []
11899
+ ...billing.canManage ? [
11900
+ " Change the plan in the dashboard: naro dashboard billing-checkout prints the page."
11901
+ ] : []
11845
11902
  ];
11846
11903
  }
11847
11904
  async function runBilling(context) {
@@ -12176,61 +12233,6 @@ async function runFlagRemove(context) {
12176
12233
  `\u2705 Removed flag ${flag.name}`
12177
12234
  ]);
12178
12235
  }
12179
- function bindingLine(binding) {
12180
- return `${binding.bindingName.padEnd(16)} ${binding.type.padEnd(3)} ${binding.resourceName}${binding.provisioned ? "" : " (not provisioned)"}${binding.managed ? " (managed by naro)" : ""}`;
12181
- }
12182
- async function runBindings(context) {
12183
- const { client, projectId } = await projectAndClient(context);
12184
- const bindings = await client.bindings.list(projectId);
12185
- context.output.result({ projectId, bindings }, [
12186
- `Bindings (${String(bindings.length)}):`,
12187
- ...bindings.length === 0 ? [
12188
- " None. `naro bindings add --type <r2|kv|d1> --name <BINDING>` creates one."
12189
- ] : bindings.map((binding) => ` ${bindingLine(binding)}`)
12190
- ]);
12191
- }
12192
- async function runBindingAdd(context) {
12193
- const { type, name } = context.flags;
12194
- if (!type || !name)
12195
- throw new NaroError(
12196
- "VALIDATION_FAILED",
12197
- "Usage: naro bindings add --type <r2|kv|d1> --name <BINDING>"
12198
- );
12199
- const { client, projectId } = await projectAndClient(context);
12200
- const binding = await client.bindings.add(projectId, type, name);
12201
- context.output.result({ projectId, binding }, [
12202
- `\u2705 Added ${bindingLine(binding)}`,
12203
- " The worker sees it from the next deploy."
12204
- ]);
12205
- }
12206
- async function runBindingRemove(context) {
12207
- const [name] = context.positionalOrThrow(
12208
- 1,
12209
- "Usage: naro bindings remove <BINDING>"
12210
- );
12211
- const { client, projectId } = await projectAndClient(context);
12212
- const binding = (await client.bindings.list(projectId)).find(
12213
- (candidate) => candidate.bindingName === name
12214
- );
12215
- if (!binding)
12216
- throw new NaroError(
12217
- "NOT_FOUND",
12218
- `No binding named ${name}. \`naro bindings\` lists them.`
12219
- );
12220
- if (binding.managed)
12221
- throw new NaroError(
12222
- "STORAGE_MANAGED",
12223
- "The Naro database is removed when the project is deleted. Set the project's database to None to unbind it."
12224
- );
12225
- await confirmOrAbort(
12226
- context,
12227
- `Remove the binding ${binding.bindingName}? This deletes the ${binding.type} resource ${binding.resourceName} and everything in it.`
12228
- );
12229
- await client.bindings.remove(projectId, binding.id);
12230
- context.output.result({ projectId, removed: binding.bindingName }, [
12231
- `\u2705 Removed ${binding.bindingName} and its ${binding.type} resource`
12232
- ]);
12233
- }
12234
12236
 
12235
12237
  // src/commands/teams.ts
12236
12238
  async function teamScope(context) {
@@ -14152,6 +14154,7 @@ Usage:
14152
14154
  naro usage [--month YYYY-MM] [--team <slug>]
14153
14155
  Team usage: requests, builds and custom domains against the plan, then by project
14154
14156
  naro billing [--team <slug>] The team's plan, subscription and this month's overage; the plan changes in the dashboard
14157
+ naro dashboard [<capability>] The dashboard URL for what the CLI leaves there (payments, GitHub App, \u2026); no argument lists them
14155
14158
 
14156
14159
  naro deploy [--prod] Build locally and deploy (preview by default)
14157
14160
  [--root <dir>] [--name <name>] --root points at the app in a monorepo
@@ -14211,9 +14214,9 @@ Usage:
14211
14214
  naro domains verify [host] [--wait] Re-check DNS and print what is missing
14212
14215
  exits non-zero while a domain is not live yet
14213
14216
 
14214
- naro dns [list] [--team <slug>] naro DNS: the team's zones, their status and name servers
14215
- naro dns add <domain> Start moving a domain onto naro DNS; prints the name servers to set
14216
- naro dns remove <domain> Cancel the move, or take the domain off naro DNS (asks first; --yes in CI)
14217
+ naro dns [list] [--team <slug>] Naro DNS: the team's zones, their status and name servers
14218
+ naro dns add <domain> Start moving a domain onto Naro DNS; prints the name servers to set
14219
+ naro dns remove <domain> Cancel the move, or take the domain off Naro DNS (asks first; --yes in CI)
14217
14220
  naro dns check <domain> Read the zone's status and ask Cloudflare whether the move is done
14218
14221
  naro dns mail <domain> The mail records (MX, SPF, DMARC, DKIM) to keep before the move
14219
14222
  naro dns scan <domain> The records Cloudflare found in the current DNS
@@ -14238,10 +14241,6 @@ Usage:
14238
14241
  naro flags on <name> Turn a flag on; the live app follows within about a minute
14239
14242
  naro flags off <name> Turn a flag off
14240
14243
  naro flags remove <name> Remove a flag; code still reading it sees it off (asks first)
14241
- naro bindings [list] The R2, KV and D1 resources bound to the worker
14242
- naro bindings add --type <r2|kv|d1> --name <BINDING>
14243
- Create a resource and bind it as env.<BINDING> from the next deploy
14244
- naro bindings remove <BINDING> Delete the resource and everything in it (asks first; --yes in CI)
14245
14244
 
14246
14245
  The data commands below (db, migration, gen, backend, policy, storage, realtime) take --env <name>:
14247
14246
  a named environment's data instead of production's, which preview shares
@@ -14833,6 +14832,8 @@ function routeCommand(parsed) {
14833
14832
  if (sub === void 0 || sub === "list")
14834
14833
  return positionals.length <= 1 ? { handler: runSupabase, positionals: [] } : null;
14835
14834
  return sub === "disconnect" && positionals.length <= 2 ? { handler: runSupabaseDisconnect, positionals: positionals.slice(1) } : null;
14835
+ case "dashboard":
14836
+ return positionals.length <= 1 ? { handler: runDashboard, positionals } : null;
14836
14837
  case "billing":
14837
14838
  return positionals.length === 0 ? { handler: runBilling, positionals: [] } : null;
14838
14839
  case "observability":
@@ -15029,16 +15030,6 @@ function routeCommand(parsed) {
15029
15030
  const command2 = commands[sub];
15030
15031
  return command2 ? { handler: command2, positionals: positionals.slice(1) } : null;
15031
15032
  }
15032
- case "bindings": {
15033
- if (sub === void 0 || sub === "list")
15034
- return positionals.length <= 1 ? { handler: runBindings, positionals: [] } : null;
15035
- const commands = {
15036
- add: runBindingAdd,
15037
- remove: runBindingRemove
15038
- };
15039
- const command2 = commands[sub];
15040
- return command2 ? { handler: command2, positionals: positionals.slice(1) } : null;
15041
- }
15042
15033
  case "dns": {
15043
15034
  if (sub === void 0 || sub === "list")
15044
15035
  return positionals.length <= 1 ? { handler: runDns, positionals: [] } : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naro-cli",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "naro": "./dist/index.js"
@@ -37,14 +37,14 @@
37
37
  "tsx": "4.23.12",
38
38
  "typescript": "7.0.2",
39
39
  "vitest": "3.2.7",
40
- "naro-js": "0.7.1",
41
40
  "@next-deploy/build": "0.1.0",
41
+ "@next-deploy/data-core": "0.1.0",
42
42
  "@next-deploy/config": "0.1.0",
43
- "@next-deploy/functions-bundler": "0.1.0",
44
43
  "@next-deploy/functions-runtime": "0.1.0",
44
+ "naro-js": "0.9.0",
45
45
  "@next-deploy/sdk": "0.1.0",
46
46
  "@next-deploy/core": "0.1.0",
47
- "@next-deploy/data-core": "0.1.0"
47
+ "@next-deploy/functions-bundler": "0.1.0"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "tsup && pnpm build:types",