@theholocron/cli 5.0.0-alpha.190 → 5.0.0-alpha.192

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.mjs CHANGED
@@ -2898,11 +2898,6 @@ async function runNotificationsChannelAdd(input) {
2898
2898
  */
2899
2899
  /** Features not built yet, in the order of the table in holocron#1098. Move an entry into a real check when it lands. */
2900
2900
  const PLANNED_CHECKS = [
2901
- {
2902
- step: 10,
2903
- id: "server-settings",
2904
- title: "server verification, notifications and system channels set"
2905
- },
2906
2901
  {
2907
2902
  step: 11,
2908
2903
  id: "welcome",
@@ -2939,6 +2934,7 @@ const same = (a, b) => a.toLowerCase() === b.toLowerCase();
2939
2934
  const accessKey = (v) => v === "everyone" ? "everyone" : JSON.stringify([...new Set(v.map((r) => r.toLowerCase()))].sort());
2940
2935
  const accessText = (v) => v === "everyone" ? "everyone" : v.length === 0 ? "nobody (bots and webhooks only)" : v.join(", ");
2941
2936
  const shown = (s) => sanitizeDetail(s);
2937
+ const SERVER_TITLE = "server verification, notifications and community set";
2942
2938
  const ACCESS_TITLE = "channel and category access set";
2943
2939
  const ROLE_APPEARANCE_TITLE = "role colour, hoist and permissions set";
2944
2940
  const SECRETS_TITLE = "webhook URLs and role id stored as secrets";
@@ -3058,7 +3054,8 @@ async function runDoctorNotifications(input) {
3058
3054
  ["webhooks", "webhooks on their channels"],
3059
3055
  ["roles", "roles exist and are mentionable"],
3060
3056
  ["role-appearance", ROLE_APPEARANCE_TITLE],
3061
- ["access", ACCESS_TITLE]
3057
+ ["access", ACCESS_TITLE],
3058
+ ["server-settings", SERVER_TITLE]
3062
3059
  ]) failed(id, title, "not checked: could not read the server");
3063
3060
  } else {
3064
3061
  const inspected = found;
@@ -3162,6 +3159,33 @@ async function runDoctorNotifications(input) {
3162
3159
  }
3163
3160
  }
3164
3161
  add("access", ACCESS_TITLE, inspected.rolesError && accessDeclared > 0 ? ["not checked: could not read the roles"] : accessProblems, accessDeclared === 0 ? "none declared" : `${accessDeclared} of ${accessDeclared} as declared`);
3162
+ const wantServer = layout.server;
3163
+ const serverProblems = [];
3164
+ if (wantServer) {
3165
+ if (inspected.serverError) serverProblems.push(`not checked: could not read the server settings (${shown(inspected.serverError)})`);
3166
+ else if (!inspected.server) serverProblems.push("the provider did not report the server's settings");
3167
+ else {
3168
+ const have = inspected.server;
3169
+ for (const field of [
3170
+ "verification",
3171
+ "defaultNotifications",
3172
+ "contentFilter",
3173
+ "systemChannel",
3174
+ "rulesChannel",
3175
+ "updatesChannel",
3176
+ "community",
3177
+ "description",
3178
+ "locale"
3179
+ ]) {
3180
+ const want = wantServer[field];
3181
+ if (want === void 0) continue;
3182
+ const actual = have[field];
3183
+ if (!((field === "systemChannel" || field === "rulesChannel" || field === "updatesChannel") && typeof want === "string" && typeof actual === "string" ? same(want, actual) : want === actual)) serverProblems.push(`${field} is ${shown(String(actual ?? "not set"))} but the layout says ${shown(String(want))}`);
3184
+ }
3185
+ }
3186
+ }
3187
+ const serverFields = wantServer ? Object.values(wantServer).filter((v) => v !== void 0).length : 0;
3188
+ add("server-settings", SERVER_TITLE, serverProblems, serverFields === 0 ? "none declared" : `${serverFields} of ${serverFields} as declared`);
3165
3189
  }
3166
3190
  const wanted = [...declaredChannels.filter(({ channel }) => channel.webhook).map(({ channel }) => channel.webhook !== true && channel.webhook && channel.webhook.secret ? channel.webhook.secret : defaultSecretName(channel.name)), ...(layout.roles ?? []).flatMap((r) => r.secret ? [r.secret] : [])];
3167
3191
  if (wanted.length === 0) checks.push({
@@ -3860,6 +3884,25 @@ function accessRoleProblem(layout) {
3860
3884
  }
3861
3885
  }
3862
3886
  }
3887
+ /** The first problem with the layout's `server` section that needs no knowledge of the provider, or undefined. */
3888
+ function serverLayoutProblem(layout) {
3889
+ const server = layout.server;
3890
+ if (!server) return void 0;
3891
+ const counts = /* @__PURE__ */ new Map();
3892
+ for (const name of (layout.categories ?? []).flatMap((c) => (c.channels ?? []).map((ch) => ch.name.toLowerCase()))) counts.set(name, (counts.get(name) ?? 0) + 1);
3893
+ for (const field of [
3894
+ "systemChannel",
3895
+ "rulesChannel",
3896
+ "updatesChannel"
3897
+ ]) {
3898
+ const name = server[field];
3899
+ if (name === void 0) continue;
3900
+ const count = counts.get(name.toLowerCase()) ?? 0;
3901
+ if (count === 0) return `server: \`${field}\` names the channel \`${name}\`, which is not in the layout's channels`;
3902
+ if (count > 1) return `server: \`${field}\` names \`${name}\`, but more than one channel in the layout has that name`;
3903
+ }
3904
+ if (server.community === true && (!server.rulesChannel || !server.updatesChannel)) return "server: `community: true` needs both `rulesChannel` and `updatesChannel`";
3905
+ }
3863
3906
  async function runNotificationsSetup(input) {
3864
3907
  const print = input.print ?? ((line) => console.log(line));
3865
3908
  const logger = input.logger ?? getLogger();
@@ -3915,10 +3958,18 @@ async function runNotificationsSetup(input) {
3915
3958
  if (secretProblem) return refuse(secretProblem);
3916
3959
  const accessProblem = accessRoleProblem(layout);
3917
3960
  if (accessProblem) return refuse(accessProblem);
3961
+ const serverProblem = serverLayoutProblem(layout);
3962
+ if (serverProblem) return refuse(serverProblem);
3963
+ if (layout.server && !notifications.ensureServer) return refuse(`the layout declares \`server\` settings, but the \`${notifications.providerName}\` provider cannot manage them`);
3918
3964
  const needsCategories = (layout.categories ?? []).find((c) => c.access);
3919
3965
  if (needsCategories && !notifications.ensureCategory) return refuse(`category \`${needsCategories.name}\` declares \`access\`, but the \`${notifications.providerName}\` provider cannot manage categories`);
3920
3966
  const providerProblem = notifications.validateLayout?.(layout);
3921
3967
  if (providerProblem) return refuse(providerProblem);
3968
+ if (!dryRun && notifications.preflight) try {
3969
+ await notifications.preflight(layout);
3970
+ } catch (err) {
3971
+ return refuse(describeProviderError(err), logReason(err));
3972
+ }
3922
3973
  const tally = {
3923
3974
  ok: 0,
3924
3975
  skip: 0
@@ -4022,6 +4073,20 @@ async function runNotificationsSetup(input) {
4022
4073
  });
4023
4074
  });
4024
4075
  }
4076
+ if (layout.server) {
4077
+ print(`\n${style.header("Server")}`);
4078
+ await attempt("server settings", async () => {
4079
+ if (dryRun) {
4080
+ print(` ${style.skip("would ensure server settings")}`);
4081
+ tally.skip++;
4082
+ return;
4083
+ }
4084
+ const result = await notifications.ensureServer(layout.server);
4085
+ const changed = result.updated.length > 0;
4086
+ print(` ${(changed ? style.success : style.skip)(`${changed ? "updated" : "exists "} server settings${changed ? `: ${result.updated.join(", ")}` : ""}`)}`);
4087
+ made(changed);
4088
+ });
4089
+ }
4025
4090
  if (report.failures.length > 0) report.status = "fail";
4026
4091
  else if (dryRun) report.status = "dry-run";
4027
4092
  print("");