canopycms 0.6.0 → 0.7.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +188 -47
  2. package/dist/api/branch-review.d.ts +11 -1
  3. package/dist/api/branch-review.js +96 -18
  4. package/dist/api/branch-status.d.ts +26 -2
  5. package/dist/api/branch-status.js +38 -2
  6. package/dist/api/branch-withdraw.js +4 -7
  7. package/dist/api/branch.d.ts +2 -0
  8. package/dist/api/client.d.ts +3 -3
  9. package/dist/api/client.js +4 -4
  10. package/dist/api/comments.d.ts +2 -2
  11. package/dist/api/comments.js +4 -0
  12. package/dist/api/content.d.ts +2 -2
  13. package/dist/api/github-sync.d.ts +4 -0
  14. package/dist/api/github-sync.js +38 -6
  15. package/dist/api/pr-comment-sync.d.ts +18 -0
  16. package/dist/api/pr-comment-sync.js +77 -0
  17. package/dist/api/request-deadline.d.ts +15 -7
  18. package/dist/api/request-deadline.js +15 -7
  19. package/dist/api/schema.d.ts +16 -16
  20. package/dist/authorization/review-role.d.ts +33 -0
  21. package/dist/authorization/review-role.js +17 -0
  22. package/dist/branch-metadata-file.d.ts +3 -0
  23. package/dist/branch-metadata-file.js +1 -0
  24. package/dist/cli/cli.js +699 -130
  25. package/dist/cli/generate-ai-content.js +524 -59
  26. package/dist/cli/github-app-output.d.ts +34 -0
  27. package/dist/cli/github-app-output.js +85 -0
  28. package/dist/cli/init-github-app.d.ts +18 -0
  29. package/dist/cli/init-github-app.js +66 -58
  30. package/dist/cli/init.js +10 -1
  31. package/dist/comment-store.d.ts +5 -0
  32. package/dist/comment-store.js +24 -0
  33. package/dist/config/helpers.js +1 -3
  34. package/dist/config/schemas/collection.d.ts +2 -2
  35. package/dist/config/schemas/config.d.ts +3 -0
  36. package/dist/config/schemas/config.js +2 -1
  37. package/dist/config/schemas/url.d.ts +6 -0
  38. package/dist/config/schemas/url.js +16 -0
  39. package/dist/config/types.d.ts +12 -1
  40. package/dist/content-id-index.d.ts +9 -0
  41. package/dist/content-id-index.js +31 -0
  42. package/dist/editor/BranchManager.d.ts +3 -0
  43. package/dist/editor/BranchManager.js +75 -44
  44. package/dist/editor/CanopyEditor.js +2 -2
  45. package/dist/editor/CanopyEditorPage.js +3 -4
  46. package/dist/editor/Editor.js +47 -8
  47. package/dist/editor/branch-lock.d.ts +7 -0
  48. package/dist/editor/branch-lock.js +9 -2
  49. package/dist/editor/client-reference-resolver.d.ts +5 -3
  50. package/dist/editor/client-reference-resolver.js +10 -5
  51. package/dist/editor/components/BranchesLoadFailedNotice.d.ts +13 -0
  52. package/dist/editor/components/BranchesLoadFailedNotice.js +12 -0
  53. package/dist/editor/components/EditorHeader.d.ts +10 -4
  54. package/dist/editor/components/EditorHeader.js +108 -28
  55. package/dist/editor/components/EntryHeader.d.ts +5 -1
  56. package/dist/editor/components/EntryHeader.js +3 -1
  57. package/dist/editor/components/ReasonedMenuItem.d.ts +8 -0
  58. package/dist/editor/components/ReasonedMenuItem.js +2 -2
  59. package/dist/editor/components/ReviewDialogs.d.ts +19 -0
  60. package/dist/editor/components/ReviewDialogs.js +41 -0
  61. package/dist/editor/copy.d.ts +4 -0
  62. package/dist/editor/copy.js +4 -0
  63. package/dist/editor/fields/MarkdownField.js +1 -1
  64. package/dist/editor/hooks/create-branch-request.d.ts +6 -2
  65. package/dist/editor/hooks/create-branch-request.js +7 -2
  66. package/dist/editor/hooks/useBranchManager.d.ts +7 -2
  67. package/dist/editor/hooks/useBranchManager.js +252 -37
  68. package/dist/editor-url.d.ts +20 -0
  69. package/dist/editor-url.js +71 -0
  70. package/dist/git-manager.js +10 -6
  71. package/dist/github-service.d.ts +2 -2
  72. package/dist/github-service.js +4 -2
  73. package/dist/pr-comments.d.ts +33 -0
  74. package/dist/pr-comments.js +41 -0
  75. package/dist/schema/schema-store.d.ts +8 -8
  76. package/dist/services.d.ts +2 -17
  77. package/dist/services.js +38 -1
  78. package/dist/settings-workspace.d.ts +21 -7
  79. package/dist/settings-workspace.js +91 -16
  80. package/dist/submission-attribution.d.ts +38 -1
  81. package/dist/submission-attribution.js +197 -18
  82. package/dist/task-queue/cms-task-queue.d.ts +1 -1
  83. package/dist/types.d.ts +6 -0
  84. package/dist/utils/content-serialize.js +7 -6
  85. package/dist/utils/markdown-body-splice.d.ts +15 -10
  86. package/dist/utils/markdown-body-splice.js +71 -28
  87. package/dist/utils/markdown-export-options.d.ts +13 -0
  88. package/dist/utils/markdown-export-options.js +13 -0
  89. package/dist/utils/markdown-restyle.d.ts +58 -0
  90. package/dist/utils/markdown-restyle.js +482 -0
  91. package/dist/version.d.ts +1 -1
  92. package/dist/version.js +1 -1
  93. package/dist/worker/bundle-header.d.ts +13 -9
  94. package/dist/worker/bundle-header.js +17 -11
  95. package/dist/worker/git-process.d.ts +2 -2
  96. package/dist/worker/git-sync.js +5 -0
  97. package/dist/worker/github-gateway-http.js +3 -2
  98. package/dist/worker/github-gateway-wire.d.ts +3 -0
  99. package/dist/worker/github-gateway-wire.js +1 -0
  100. package/dist/worker/github-gateway.d.ts +1 -1
  101. package/dist/worker/github-transfer.js +72 -2
  102. package/dist/worker/task-runner.js +53 -3
  103. package/package.json +2 -2
  104. package/dist/editor/fields/markdown-export-options.d.ts +0 -2
  105. package/dist/editor/fields/markdown-export-options.js +0 -8
package/dist/cli/cli.js CHANGED
@@ -1227,7 +1227,7 @@ CanopyCMS worker run-once (mode: ${mode}, auth: ${authMode})
1227
1227
  }
1228
1228
  console.log("\nDone");
1229
1229
  }
1230
- var __defProp2, __getOwnPropNames2, __esm2, __export2, active, init_logger, scalarFieldTypes, markdownFieldTypes, primitiveFieldTypes, fieldTypes, init_types, SAFE_HTML_TAGS, COMPONENT_NAME, MDX_BINDINGS, FORBIDDEN_ATTRIBUTES, init_mdx_allowlist, fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, mdxPropAllowSchema, mdxAllowlistSchema, markdownFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceSortFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema, init_field, relativePathSchema, entryTypeSchema, collectionSchema, init_collection, SENTINEL_BASE, DOT_SEGMENT, init_sanitize_href, uploadTargetUrlSchema, previewPrefixSchema, init_url, mediaSchema, init_media, defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, seoFieldNameSchema, seoSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE, init_config, init_config2, init_error2, init_fs2, templates_exports, __dirname, TEMPLATES_DIR, SCAFFOLD_STACK_NAME, init_templates, project_detect_exports, COMMANDS, MAX_WORKSPACE_DEPTH, PLACEHOLDER_GITHUB_REPO, CDK_DEPENDENCIES, init_project_detect, task_queue_config_exports, init_task_queue_config, envFlag, LOG_LEVELS, DebugLogger, testLogger, init_debug, init_atomic_write, DEFAULT_MAX_RETRIES, nullLogger, init_task_queue, cms_task_queue_exports, debugLogger, cmsTaskQueueLogger, init_cms_task_queue, ProdClientSafeStrategy, DevClientSafeStrategy, warned, VALID_DEPLOYMENT_NAME, isValidDeploymentName, ProdStrategy, DevStrategy, strategyCache;
1230
+ var __defProp2, __getOwnPropNames2, __esm2, __export2, active, init_logger, scalarFieldTypes, markdownFieldTypes, primitiveFieldTypes, fieldTypes, init_types, SAFE_HTML_TAGS, COMPONENT_NAME, MDX_BINDINGS, FORBIDDEN_ATTRIBUTES, init_mdx_allowlist, fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, mdxPropAllowSchema, mdxAllowlistSchema, markdownFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceSortFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema, init_field, relativePathSchema, entryTypeSchema, collectionSchema, init_collection, SENTINEL_BASE, DOT_SEGMENT, init_sanitize_href, uploadTargetUrlSchema, previewPrefixSchema, editorUrlSchema, init_url, mediaSchema, init_media, defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, seoFieldNameSchema, seoSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE, init_config, init_config2, init_error2, init_fs2, templates_exports, __dirname, TEMPLATES_DIR, SCAFFOLD_STACK_NAME, init_templates, project_detect_exports, COMMANDS, MAX_WORKSPACE_DEPTH, PLACEHOLDER_GITHUB_REPO, CDK_DEPENDENCIES, init_project_detect, task_queue_config_exports, init_task_queue_config, envFlag, LOG_LEVELS, DebugLogger, testLogger, init_debug, init_atomic_write, DEFAULT_MAX_RETRIES, nullLogger, init_task_queue, cms_task_queue_exports, debugLogger, cmsTaskQueueLogger, init_cms_task_queue, ProdClientSafeStrategy, DevClientSafeStrategy, warned, VALID_DEPLOYMENT_NAME, isValidDeploymentName, ProdStrategy, DevStrategy, strategyCache;
1231
1231
  var init_init = __esm({
1232
1232
  "dist/cli/init.js"() {
1233
1233
  "use strict";
@@ -1542,6 +1542,14 @@ var init_init = __esm({
1542
1542
  previewPrefixSchema = z3.string().trim().refine((value) => isHttpUrlOrSameOriginPath(value), {
1543
1543
  message: 'editor.previewPrefix must be an absolute http(s) URL or a site-relative path beginning with a single "/", with no query or fragment'
1544
1544
  });
1545
+ editorUrlSchema = z3.string().trim().refine((value) => {
1546
+ if (!isHttpUrlOrSameOriginPath(value) || !/^https?:\/\//i.test(value))
1547
+ return false;
1548
+ const url = new URL(value);
1549
+ return !url.username && !url.password;
1550
+ }, {
1551
+ message: "editorUrl must be an absolute http(s) URL with no credentials, query or fragment"
1552
+ });
1545
1553
  }
1546
1554
  });
1547
1555
  init_media = __esm2({
@@ -1684,6 +1692,7 @@ var init_init = __esm({
1684
1692
  contentRoot: contentRootSchema.default("content"),
1685
1693
  sourceRoot: sourceRootSchema.optional(),
1686
1694
  basePath: basePathSchema,
1695
+ editorUrl: editorUrlSchema.optional(),
1687
1696
  unauthenticatedStatus: z5.union([z5.literal(401), z5.literal(419)]).optional(),
1688
1697
  danglingReferences: z5.enum(["error", "warn"]).optional(),
1689
1698
  editor: editorConfigSchema.optional(),
@@ -2237,6 +2246,84 @@ var init_github_app_manifest = __esm({
2237
2246
  }
2238
2247
  });
2239
2248
 
2249
+ // dist/cli/github-app-output.js
2250
+ import pc from "picocolors";
2251
+ function colorsFor(stream) {
2252
+ const { NO_COLOR, FORCE_COLOR, TERM } = process.env;
2253
+ if (NO_COLOR)
2254
+ return pc.createColors(false);
2255
+ if (FORCE_COLOR)
2256
+ return pc.createColors(FORCE_COLOR !== "0" && FORCE_COLOR !== "false");
2257
+ return pc.createColors(Boolean(stream.isTTY) && TERM !== "dumb");
2258
+ }
2259
+ function recoveryPreamble() {
2260
+ return [
2261
+ outColors.bold(outColors.yellow("If this goes wrong after you click Create")),
2262
+ " The App will exist, but this command will not have its private key. To recover, either:",
2263
+ " \u2022 delete the App in its settings and re-run this command, or",
2264
+ ' \u2022 open the App, scroll to "Private keys" and click "Generate a private key". The',
2265
+ " .pem it downloads is your only chance to save it.",
2266
+ outColors.dim(' (Not the "client secret" a few sections above: that is for OAuth user flows, is unused\n by this App, and fails confusingly when used to sign.)'),
2267
+ ""
2268
+ ].join("\n");
2269
+ }
2270
+ function registrationMessage(input) {
2271
+ const width = Math.max(...Object.keys(CANOPY_APP_PERMISSIONS).map((name) => name.length));
2272
+ const permissions = Object.entries(CANOPY_APP_PERMISSIONS).map(([name, level]) => ` ${name.padEnd(width)} ${outColors.bold(level)}`);
2273
+ return [
2274
+ outColors.bold(`Registering "${input.name}" for ${input.owner}/${input.repo}`),
2275
+ "",
2276
+ `${outColors.bold("Permissions")} ${outColors.dim("(the part that matters)")}`,
2277
+ ...permissions,
2278
+ "",
2279
+ `${outColors.bold("Private key")} goes to ${input.destination}`,
2280
+ outColors.dim(" If that fails you are asked for somewhere else before the key is discarded."),
2281
+ "",
2282
+ step(1, "Open this file in a browser:"),
2283
+ "",
2284
+ ` ${link(input.formFile)}`,
2285
+ "",
2286
+ " The browser matters, twice over. It must be:",
2287
+ ` \u2022 ${outColors.bold("logged in to GitHub")} as an owner of ${input.owner}`,
2288
+ ` \u2022 running ${outColors.bold("on this machine")}: the redirect returns to 127.0.0.1:${input.callbackPort}, which only a local browser can reach`,
2289
+ outColors.dim(" An embedded or app-internal browser bounces off the GitHub login wall and appears\n to do nothing when you click Create."),
2290
+ "",
2291
+ " The page posts the manifest to GitHub, which shows the App pre-filled with exactly",
2292
+ " those permissions. Review them and click Create.",
2293
+ "",
2294
+ outColors.dim(`Waiting for GitHub to redirect back (up to ${input.timeoutMinutes} minutes)...`)
2295
+ ].join("\n");
2296
+ }
2297
+ function deploymentSettingsMessage(appId, installationId) {
2298
+ return [
2299
+ "",
2300
+ outColors.bold("Set these on your deployment"),
2301
+ "",
2302
+ ` GITHUB_APP_ID=${appId}`,
2303
+ ` GITHUB_APP_INSTALLATION_ID=${installationId ?? "<run `verify` to read it>"}`,
2304
+ ` GITHUB_APP_PRIVATE_KEY_SECRET_ARN=<wherever you just put the key>`,
2305
+ "",
2306
+ outColors.dim(' Both ids are numeric. The App ID is NOT the "Client ID" (`Iv1.\u2026`) shown beside it,\n and the installation id is NOT the App ID: an App installed twice has one App ID\n and two installation ids.'),
2307
+ "",
2308
+ outColors.bold("Re-check this any time, without creating anything"),
2309
+ ` canopycms init-github-app verify --app-id ${appId} --key-file <path>`
2310
+ ].join("\n");
2311
+ }
2312
+ var outColors, errColors, tick, failMark, cross, link, step;
2313
+ var init_github_app_output = __esm({
2314
+ "dist/cli/github-app-output.js"() {
2315
+ "use strict";
2316
+ init_github_app_manifest();
2317
+ outColors = colorsFor(process.stdout);
2318
+ errColors = colorsFor(process.stderr);
2319
+ tick = (text3) => `${outColors.green("\u2713")} ${text3}`;
2320
+ failMark = errColors.red("\u2717");
2321
+ cross = (text3) => `${failMark} ${text3}`;
2322
+ link = (url, palette = outColors) => palette.cyan(palette.underline(url));
2323
+ step = (n, text3) => outColors.bold(outColors.cyan(`${n}.`) + ` ${text3}`);
2324
+ }
2325
+ });
2326
+
2240
2327
  // dist/cli/prompt.js
2241
2328
  import { createInterface } from "node:readline";
2242
2329
  function readLineOnce(prompt) {
@@ -2464,10 +2551,13 @@ var init_github_app_exports = {};
2464
2551
  __export(init_github_app_exports, {
2465
2552
  appJwt: () => appJwt,
2466
2553
  appManifest: () => appManifest,
2554
+ appSettingsUrl: () => appSettingsUrl,
2467
2555
  creationForm: () => creationForm,
2468
2556
  handOffKey: () => handOffKey,
2469
2557
  handOffWithRetry: () => handOffWithRetry,
2470
2558
  initGitHubApp: () => initGitHubApp,
2559
+ installStepMessage: () => installStepMessage,
2560
+ keyNotStoredMessage: () => keyNotStoredMessage,
2471
2561
  manifestPostUrl: () => manifestPostUrl,
2472
2562
  parseKeyRetryAnswer: () => parseKeyRetryAnswer,
2473
2563
  startCallbackServer: () => startCallbackServer
@@ -2479,8 +2569,33 @@ import { mkdtemp, rm, writeFile as writeFile2 } from "node:fs/promises";
2479
2569
  import { tmpdir } from "node:os";
2480
2570
  import path10 from "node:path";
2481
2571
  import { Readable } from "node:stream";
2572
+ function appsBaseUrl(target) {
2573
+ return target.isOrganization ? `https://github.com/organizations/${encodeURIComponent(target.owner)}/settings/apps` : "https://github.com/settings/apps";
2574
+ }
2575
+ function appSettingsUrl(target, slug) {
2576
+ return `${appsBaseUrl(target)}/${encodeURIComponent(slug)}`;
2577
+ }
2578
+ function keyNotStoredMessage(target, created) {
2579
+ return `
2580
+ ${errColors.bold(errColors.red("Stopping here:"))} the App exists but its key was not stored, so installing it now
2581
+ would achieve nothing. To recover, open the App and generate a private key from
2582
+ its "Private keys" section, then run \`verify\`; or delete the App and start again.
2583
+
2584
+ GITHUB_APP_ID=${created.id}
2585
+ App settings: ${link(appSettingsUrl(target, created.slug), errColors)}`;
2586
+ }
2587
+ function installStepMessage(target, slug) {
2588
+ return `
2589
+ ${step(2, `Install it on ${target.owner}/${target.repo}:`)}
2590
+
2591
+ ${link(`${appSettingsUrl(target, slug)}/installations`)}
2592
+
2593
+ That page shows an Install button first \u2014 the repository picker only appears after
2594
+ you click it. Choose "Only select repositories" and pick that one repository.
2595
+ `;
2596
+ }
2482
2597
  function manifestPostUrl(target, state) {
2483
- const base = target.isOrganization ? `https://github.com/organizations/${encodeURIComponent(target.owner)}/settings/apps/new` : "https://github.com/settings/apps/new";
2598
+ const base = `${appsBaseUrl(target)}/new`;
2484
2599
  return `${base}?state=${encodeURIComponent(state)}`;
2485
2600
  }
2486
2601
  function appManifest(target, redirectUrl, name = appName(target.repo)) {
@@ -2784,7 +2899,7 @@ async function handOffWithRetry(pem, destination) {
2784
2899
  const result = await handOffKey(pem, attempt);
2785
2900
  if (result.stored) {
2786
2901
  console.log(`
2787
- private key: ${result.detail}`);
2902
+ ${tick(`Private key: ${result.detail}`)}`);
2788
2903
  return true;
2789
2904
  }
2790
2905
  console.error(`
@@ -2884,41 +2999,41 @@ No installation of this App found on ${target.owner}/${target.repo} (HTTP ${foun
2884
2999
  }
2885
3000
  const installation = found.body;
2886
3001
  console.log(`
2887
- installation ${installation.id} on ${target.owner}/${target.repo}`);
3002
+ ${outColors.bold(`Installation ${installation.id}`)} on ${target.owner}/${target.repo}`);
2888
3003
  const findings = readbackVerdict(installation);
2889
3004
  if (findings.length === 0) {
2890
- console.log(" grant matches exactly what this package needs:");
3005
+ console.log(" The grant matches exactly what this package needs:");
2891
3006
  for (const [name, level] of Object.entries(CANOPY_APP_PERMISSIONS)) {
2892
- console.log(` \u2713 ${name}: ${level}`);
3007
+ console.log(` ${tick(`${name}: ${level}`)}`);
2893
3008
  }
2894
3009
  } else {
2895
3010
  for (const finding of findings) {
2896
- console.error(` \u2717 ${finding.message}`);
3011
+ console.error(` ${cross(finding.message)}`);
2897
3012
  }
2898
3013
  }
2899
3014
  let installationsOk = false;
2900
3015
  const installations = await installationCount(jwt);
2901
3016
  if (installations === null) {
2902
- console.error(` \u2717 could not list this App's installations, so "installed once" is UNCHECKED.
3017
+ console.error(` ${failMark} could not list this App's installations, so "installed once" is UNCHECKED.
2903
3018
  Reported as a failure rather than passed over: this is the check that observes
2904
3019
  the one-App-per-site rule, and an unread check is not a satisfied one.`);
2905
3020
  } else if (installations === 1) {
2906
3021
  installationsOk = true;
2907
- console.log(" \u2713 installed once, so this key reaches no other account");
3022
+ console.log(` ${tick("installed once, so this key reaches no other account")}`);
2908
3023
  } else if (installations > 1) {
2909
- console.error(` \u2717 this App is installed ${installations} times, and should be installed ONCE.
3024
+ console.error(` ${failMark} this App is installed ${installations} times, and should be installed ONCE.
2910
3025
  Its private key is App-level: whoever holds it can mint a token for any of
2911
3026
  those installations. Register a separate App per site instead, and remove
2912
3027
  the installations that do not belong to this one.`);
2913
3028
  } else {
2914
- console.error(` \u2717 GitHub reports ${installations} installations of this App, yet one was just read
3029
+ console.error(` ${failMark} GitHub reports ${installations} installations of this App, yet one was just read
2915
3030
  back for this repository. Something is inconsistent; re-run before trusting it.`);
2916
3031
  }
2917
3032
  const mint = await proveTokenMint(jwt, installation.id, target.repo);
2918
3033
  if (mint.ok) {
2919
- console.log(` \u2713 installation token ${mint.detail}`);
3034
+ console.log(` ${tick(`installation token ${mint.detail}`)}`);
2920
3035
  } else {
2921
- console.error(` \u2717 could not mint an installation token \u2014 ${mint.detail}
3036
+ console.error(` ${failMark} could not mint an installation token \u2014 ${mint.detail}
2922
3037
  If that message says the requested permissions are not granted to this
2923
3038
  installation, that is a GitHub behaviour rather than a bug here: adding a
2924
3039
  permission to an App does NOT apply to existing installations until an account
@@ -2994,7 +3109,7 @@ async function createCommand(options) {
2994
3109
  console.log(`! Could not check whether the name "${name}" is already taken, so this is unverified.
2995
3110
  `);
2996
3111
  }
2997
- console.log('IF THIS GOES WRONG AFTER YOU CLICK CREATE, read this first:\n The App will exist and this command will not have its private key. That is\n recoverable. Either:\n - delete the App in its settings and re-run this command, or\n - open the App, scroll to "Private keys", and click "Generate a private key".\n It downloads a .pem and that is the only chance to save it. This is NOT the\n "client secret" listed a few sections above: that is for OAuth user flows, is\n unused by this App, and fails confusingly when used to sign.\n');
3112
+ console.log(recoveryPreamble());
2998
3113
  const state = randomUUID();
2999
3114
  let formDir;
3000
3115
  let callback;
@@ -3017,29 +3132,15 @@ async function createCommand(options) {
3017
3132
  formDir = await mkdtemp(path10.join(tmpdir(), "canopycms-app-"));
3018
3133
  const formFile = path10.join(formDir, `create-${slug}.html`);
3019
3134
  await writeFile2(formFile, creationForm(postUrl, manifest), { mode: 384, flag: "wx" });
3020
- console.log(`Registering "${name}" for ${target.owner}/${target.repo}.
3021
-
3022
- Its permissions, which is the part that matters:
3023
-
3024
- ${JSON.stringify(CANOPY_APP_PERMISSIONS, null, 2)}
3025
-
3026
- The key will go to ${describeDestination(destination)} \u2014 and if that fails you will
3027
- be asked for somewhere else before it is discarded.
3028
-
3029
- 1. Open this file in a browser:
3030
-
3031
- ${formFile}
3032
-
3033
- The browser matters, twice over. It must be LOGGED IN TO GITHUB as an owner of
3034
- ${target.owner}, and it must be running on THIS machine \u2014 the redirect comes back to
3035
- 127.0.0.1:${callback.port}, which only a local browser can reach. An embedded or
3036
- app-internal browser bounces off the GitHub login wall and simply appears to do
3037
- nothing when you click Create.
3038
-
3039
- The page posts the manifest to GitHub, which shows you the App pre-filled with
3040
- exactly those permissions. Review them and click Create.
3041
-
3042
- Waiting for GitHub to redirect back (up to ${CALLBACK_TIMEOUT_MS / 6e4} minutes)...`);
3135
+ console.log(registrationMessage({
3136
+ name,
3137
+ owner: target.owner,
3138
+ repo: target.repo,
3139
+ destination: describeDestination(destination),
3140
+ formFile,
3141
+ callbackPort: callback.port,
3142
+ timeoutMinutes: CALLBACK_TIMEOUT_MS / 6e4
3143
+ }));
3043
3144
  let code;
3044
3145
  try {
3045
3146
  code = await callback.code;
@@ -3054,32 +3155,20 @@ ${getErrorMessage(err2)}.
3054
3155
  See the recovery note printed above if the App was in fact created.`);
3055
3156
  return 1;
3056
3157
  }
3057
- console.log(" got the manifest code; exchanging it for the App credentials");
3158
+ console.log(`
3159
+ ${tick("Got the manifest code; exchanging it for the App credentials")}`);
3058
3160
  const created = await convertManifest(code);
3059
3161
  if (!created) {
3060
3162
  console.error(' the exchange failed, so this command never received the private key.\n\n Do NOT simply re-run `create`: it submits a fresh manifest and would register a\n SECOND App. The App you just created almost certainly exists \u2014 open it, generate\n a key from its "Private keys" section (see the recovery note above), and then run\n `canopycms init-github-app verify`. Delete the App instead if you would rather\n start over.');
3061
3163
  return 1;
3062
3164
  }
3063
- console.log(` created App ${created.slug} (id ${created.id}) \u2014 private key captured; sending it to ${describeDestination(destination)}`);
3165
+ console.log(tick(`Created App ${outColors.bold(created.slug)} (id ${created.id}); private key captured, sending it to ${describeDestination(destination)}`));
3064
3166
  const stored = await handOffWithRetry(created.pem, destination);
3065
3167
  if (!stored) {
3066
- console.error(`
3067
- Stopping here: the App exists but its key was not stored, so installing it now
3068
- would achieve nothing. To recover, open the App and generate a private key from
3069
- its "Private keys" section, then run \`verify\`; or delete the App and start again.
3070
-
3071
- GITHUB_APP_ID=${created.id}
3072
- App settings: https://github.com/settings/apps/${created.slug}`);
3168
+ console.error(keyNotStoredMessage(target, created));
3073
3169
  return 1;
3074
3170
  }
3075
- console.log(`
3076
- 2. Install it on ${target.owner}/${target.repo}:
3077
-
3078
- https://github.com/settings/apps/${created.slug}/installations
3079
-
3080
- That page shows an Install button first \u2014 the repository picker only appears after
3081
- you click it. Choose "Only select repositories" and pick that one repository.
3082
- `);
3171
+ console.log(installStepMessage(target, created.slug));
3083
3172
  await pressEnter(" Press Enter once it is installed.");
3084
3173
  let readback;
3085
3174
  try {
@@ -3090,19 +3179,7 @@ Stopping here: the App exists but its key was not stored, so installing it now
3090
3179
  This says nothing about the App or the key \u2014 re-run \`canopycms init-github-app verify\`.`);
3091
3180
  readback = { ok: false, installationId: null };
3092
3181
  }
3093
- console.log(`
3094
- Set these on your deployment:
3095
-
3096
- GITHUB_APP_ID=${created.id}
3097
- GITHUB_APP_INSTALLATION_ID=${readback.installationId ?? "<run `verify` to read it>"}
3098
- GITHUB_APP_PRIVATE_KEY_SECRET_ARN=<wherever you just put the key>
3099
-
3100
- Both ids are numeric. The App ID is NOT the "Client ID" (\`Iv1.\u2026\`) shown beside it,
3101
- and the installation id is NOT the App ID \u2014 an App installed twice has one App ID
3102
- and two installation ids.
3103
-
3104
- Re-check this any time, without creating anything:
3105
- canopycms init-github-app verify --app-id ${created.id} --key-file <path>`);
3182
+ console.log(deploymentSettingsMessage(created.id, readback.installationId));
3106
3183
  return stored && readback.ok ? 0 : 1;
3107
3184
  } finally {
3108
3185
  callback.close();
@@ -3147,6 +3224,7 @@ var init_init_github_app = __esm({
3147
3224
  init_error();
3148
3225
  init_github_auth();
3149
3226
  init_github_app_manifest();
3227
+ init_github_app_output();
3150
3228
  init_prompt();
3151
3229
  CALLBACK_TIMEOUT_MS = 10 * 60 * 1e3;
3152
3230
  REQUEST_TIMEOUT_MS = 3e4;
@@ -3710,6 +3788,70 @@ function isBlockStructuralKey(key) {
3710
3788
  function createDebugLogger2(options) {
3711
3789
  return new DebugLogger2(options);
3712
3790
  }
3791
+ function startOf(node) {
3792
+ return node?.position?.start.offset;
3793
+ }
3794
+ function endOf(node) {
3795
+ return node?.position?.end.offset;
3796
+ }
3797
+ function apply(source, edits, from = 0, to = source.length) {
3798
+ let out = "";
3799
+ let at = from;
3800
+ for (const edit of [...edits].sort((a, b) => a.start - b.start)) {
3801
+ if (edit.start < at || edit.end > to)
3802
+ continue;
3803
+ out += source.slice(at, edit.start) + edit.text;
3804
+ at = edit.end;
3805
+ }
3806
+ return out + source.slice(at, to);
3807
+ }
3808
+ function underscoreStarts(node) {
3809
+ const bare = startOf(node) === startOf(node.children?.[0]);
3810
+ if (node.type === "link" && !bare || node.type === "linkReference")
3811
+ return [];
3812
+ if (node.type === "text" || node.type === "mdxTextExpression") {
3813
+ const start = startOf(node);
3814
+ return start !== void 0 && (node.value ?? "").includes("_") ? [start] : [];
3815
+ }
3816
+ return (node.children ?? []).flatMap(underscoreStarts);
3817
+ }
3818
+ function orderedNumber(numbers, index) {
3819
+ const [first = 1, second, third] = numbers;
3820
+ if (index === 0)
3821
+ return first;
3822
+ const ones = numbers.length > 1 && second === 1 && (first !== 0 || numbers.length < 3 || third === 1);
3823
+ return ones ? 1 : first + index;
3824
+ }
3825
+ function restyleMarkdown(source, tree, fixed, readsBack) {
3826
+ if (source.startsWith("\uFEFF"))
3827
+ return void 0;
3828
+ const blocks = tree.children ?? [];
3829
+ const groups = blocks.map(() => []);
3830
+ new Restyler(source).children(tree, [], [], fixed, (index) => groups[index] ?? []);
3831
+ const changed = groups.filter((group) => group.length > 0);
3832
+ if (changed.length === 0)
3833
+ return void 0;
3834
+ let kept = [];
3835
+ let best;
3836
+ let budget = 8 * Math.ceil(Math.log2(changed.length + 1)) + 8;
3837
+ const attempt = (tried) => {
3838
+ if (budget-- <= 0)
3839
+ return;
3840
+ const trial = [...kept, ...tried.flat()];
3841
+ const text3 = apply(source, trial);
3842
+ const parsed = readsBack(text3);
3843
+ if (parsed !== void 0) {
3844
+ kept = trial;
3845
+ best = { text: text3, tree: parsed };
3846
+ } else if (tried.length > 1) {
3847
+ const half = Math.ceil(tried.length / 2);
3848
+ attempt(tried.slice(0, half));
3849
+ attempt(tried.slice(half));
3850
+ }
3851
+ };
3852
+ attempt(changed);
3853
+ return best;
3854
+ }
3713
3855
  function snapshotDocument(doc) {
3714
3856
  const items = /* @__PURE__ */ new Map();
3715
3857
  const visitNode = (node) => {
@@ -3823,15 +3965,15 @@ function canonical(node) {
3823
3965
  function kindOf(node) {
3824
3966
  return `${node.type}:${String(node.ordered ?? "")}:${String(node.checked ?? "")}`;
3825
3967
  }
3826
- function startOf(node) {
3968
+ function startOf2(node) {
3827
3969
  return node?.position?.start.offset;
3828
3970
  }
3829
- function endOf(node) {
3971
+ function endOf2(node) {
3830
3972
  return node?.position?.end.offset;
3831
3973
  }
3832
3974
  function sliceOf(text3, node) {
3833
- const start = startOf(node);
3834
- const end = endOf(node);
3975
+ const start = startOf2(node);
3976
+ const end = endOf2(node);
3835
3977
  return start === void 0 || end === void 0 ? void 0 : text3.slice(start, end);
3836
3978
  }
3837
3979
  function reindent(text3, from, to) {
@@ -3895,12 +4037,12 @@ function leadWidth(text3, offset) {
3895
4037
  return /^(?: |[-*+](?= )|\d{1,9}[.)](?= )|\[[ xX]\](?= ))*$/.test(lead) ? lead.length : void 0;
3896
4038
  }
3897
4039
  function gapBetween(text3, a, b) {
3898
- const end = endOf(a);
3899
- const start = startOf(b);
4040
+ const end = endOf2(a);
4041
+ const start = startOf2(b);
3900
4042
  return end === void 0 || start === void 0 ? void 0 : text3.slice(end, start);
3901
4043
  }
3902
4044
  function listMarkers(text3, items, ordered) {
3903
- const firstStart = startOf(items[0]);
4045
+ const firstStart = startOf2(items[0]);
3904
4046
  if (firstStart === void 0)
3905
4047
  return void 0;
3906
4048
  if (!ordered) {
@@ -3913,16 +4055,14 @@ function listMarkers(text3, items, ordered) {
3913
4055
  const first = numberAt(firstStart);
3914
4056
  if (first === null)
3915
4057
  return void 0;
3916
- const start = Number(first[1]);
3917
4058
  const delimiter = first[2] ?? ".";
3918
4059
  const padTo = first[1]?.startsWith("0") ? first[1].length : 0;
3919
- const second = items.length > 1 ? numberAt(startOf(items[1])) : null;
3920
- const step = second !== null && Number(second[1]) === start ? 0 : 1;
4060
+ const typed = items.map((item) => Number(numberAt(startOf2(item))?.[1]));
3921
4061
  return (itemText, index) => {
3922
4062
  const current = /^\d{1,9}[.)]/.exec(itemText);
3923
4063
  if (current === null)
3924
4064
  return void 0;
3925
- const marker = `${String(start + step * index).padStart(padTo, "0")}${delimiter}`;
4065
+ const marker = `${String(orderedNumber(typed, index)).padStart(padTo, "0")}${delimiter}`;
3926
4066
  if (marker.length !== current[0].length && itemText.includes("\n"))
3927
4067
  return void 0;
3928
4068
  return marker + itemText.slice(current[0].length);
@@ -3938,22 +4078,56 @@ function preserveMarkdownSource(original, updated, format) {
3938
4078
  return updated;
3939
4079
  }
3940
4080
  }
4081
+ function diskBlocks(oNodes, nNodes) {
4082
+ const oKeys = oNodes.map(canonical);
4083
+ const nKeys = nNodes.map(canonical);
4084
+ const pairs = matchSequences(oKeys, nKeys);
4085
+ const fromDisk = new Set(pairs.map(([, ni]) => ni));
4086
+ const matched = new Set(pairs.map(([oi]) => oi));
4087
+ const unmatched = /* @__PURE__ */ new Map();
4088
+ oKeys.forEach((key, index) => {
4089
+ if (!matched.has(index))
4090
+ unmatched.set(key, (unmatched.get(key) ?? 0) + 1);
4091
+ });
4092
+ nKeys.forEach((key, index) => {
4093
+ const left = unmatched.get(key) ?? 0;
4094
+ if (fromDisk.has(index) || left === 0)
4095
+ return;
4096
+ unmatched.set(key, left - 1);
4097
+ fromDisk.add(index);
4098
+ });
4099
+ return fromDisk;
4100
+ }
3941
4101
  function splice(original, updated, format) {
3942
- const before = parse(original, format);
4102
+ const before = original === void 0 ? void 0 : parse(original, format);
3943
4103
  const after = parse(updated, format);
3944
- if (before === void 0 || after === void 0)
4104
+ if (after === void 0)
3945
4105
  return void 0;
3946
4106
  const target = canonical(after);
3947
- for (const deep of [true, false]) {
3948
- const candidate = new Splicer(original, updated, deep).body(before, after);
3949
- if (candidate === void 0)
3950
- continue;
3951
- const reparsed = parse(candidate, format);
3952
- if (reparsed !== void 0 && canonical(reparsed) === target)
3953
- return candidate;
3954
- log2.debug("markdown-body-splice", "splice does not read back as the edit", { deep });
4107
+ const fromDisk = diskBlocks(before?.children ?? [], after.children);
4108
+ const restyled = restyleMarkdown(updated, after, (index) => fromDisk.has(index), (text3) => {
4109
+ const tree = parse(text3, format);
4110
+ return tree !== void 0 && canonical(tree) === target ? tree : void 0;
4111
+ });
4112
+ if (original === void 0 || before === void 0)
4113
+ return restyled?.text;
4114
+ const asSent = { text: updated, tree: after };
4115
+ const edits = restyled === void 0 || restyled.text === updated ? [asSent] : [restyled, asSent];
4116
+ for (const edit of edits) {
4117
+ for (const deep of [true, false]) {
4118
+ const candidate = new Splicer(original, edit.text, deep).body(before, edit.tree);
4119
+ if (candidate === void 0)
4120
+ continue;
4121
+ const reparsed = parse(candidate, format);
4122
+ if (reparsed !== void 0 && canonical(reparsed) === target)
4123
+ return candidate;
4124
+ log2.debug("markdown-body-splice", "splice does not read back as the edit", {
4125
+ deep,
4126
+ restyled: edit !== asSent
4127
+ });
4128
+ }
3955
4129
  }
3956
- return void 0;
4130
+ return restyled?.text;
3957
4131
  }
3958
4132
  function createNode(ctx, value, asKey = false) {
3959
4133
  const node = ctx.doc.createNode(value);
@@ -4238,7 +4412,7 @@ function writesFrontmatter(data) {
4238
4412
  }
4239
4413
  function bodyToWrite(body, data, format, existingRaw) {
4240
4414
  const priorBody = existingRaw === void 0 ? void 0 : extractBody(existingRaw);
4241
- const spliced = priorBody === void 0 ? body : preserveMarkdownSource(priorBody, body, format);
4415
+ const spliced = preserveMarkdownSource(priorBody, body, format);
4242
4416
  if (!writesFrontmatter(data)) {
4243
4417
  const flush = spliced.replace(/^(?:\r?\n)+/, "");
4244
4418
  return /^\uFEFF?---/.test(flush) ? `
@@ -7832,7 +8006,7 @@ async function generateAIContentCLI(options) {
7832
8006
  console.log(` Output: ${result.outputDir}
7833
8007
  `);
7834
8008
  }
7835
- var __defProp3, __getOwnPropNames3, __esm3, __export3, init_error3, init_atomic_write2, init_field_traversal, ITEM_KEY, ITEM_KEY_PATTERN, isItemKey, BASE58, isListField, init_item_keys, active2, init_logger2, SENTINEL_BASE2, DOT_SEGMENT2, init_sanitize_href2, init_normalize, ROOT_COLLECTION_ID, init_types2, normalizePathValue, flattenSchema, init_flatten, scalarFieldTypes2, markdownFieldTypes2, primitiveFieldTypes2, fieldTypes2, init_types22, SAFE_HTML_TAGS2, COMPONENT_NAME2, MDX_BINDINGS2, FORBIDDEN_ATTRIBUTES2, init_mdx_allowlist2, fieldBaseSchema2, selectOptionSchema2, referenceOptionSchema2, primitiveFieldSchema2, mdxPropAllowSchema2, mdxAllowlistSchema2, markdownFieldSchema2, ASPECT_RATIO_RE2, imageFieldSchema2, selectFieldSchema2, referenceSortFieldSchema2, referenceFieldSchema2, fieldHolder2, blockSchema2, blockFieldSchema2, objectFieldSchema2, inlineGroupFieldSchema2, customFieldSchema2, knownFieldSchema2, fieldSchema2, init_field2, relativePathSchema2, entryTypeSchema2, collectionSchema2, init_collection2, uploadTargetUrlSchema2, previewPrefixSchema2, init_url2, mediaSchema2, init_media2, defaultBranchAccessSchema2, defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2, defaultPathAccessSchema2, defaultBaseBranchSchema2, defaultRemoteNameSchema2, defaultRemoteUrlSchema2, gitBotAuthorNameSchema2, gitBotAuthorEmailSchema2, githubTokenEnvVarSchema2, operatingModeSchema2, deployedAsSchema2, contentRootSchema2, sourceRootSchema2, basePathSchema2, deploymentNameSchema2, devContentSyncSchema2, devConfigSchema2, editorConfigSchema2, seoFieldNameSchema2, seoSchema2, CanopyConfigSchema2, DEFAULT_PROD_WORKSPACE2, init_config3, SERVER_MODE_ENV_VAR, BROWSER_MODE_ENV_VAR, warned2, init_mode_env, forEachReferenceField, init_validation, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned3, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, init_config22, ProdStrategy2, DevStrategy2, strategyCache2, init_client_unsafe_strategy, operating_mode_exports, init_operating_mode, task_queue_config_exports2, init_task_queue_config2, worker_status_exports, WORKER_STATUS_FILE, init_worker_status, BLOCK_DISCRIMINATOR_KEYS, BLOCK_STRUCTURAL_KEYS, envFlag2, LOG_LEVELS2, DebugLogger2, testLogger2, END, FRESH, LINE_WIDTH, MIN_LINE_WIDTH, SourceSplicer, log2, MAX_LCS_CELLS, Splicer, log22, EVIDENCE_MAX_DEPTH, log3, locks, PROVISIONING_LOCK_STALE_MS, sleep, META_DIR, CONTENT_WRITE_LOCK_NAME, DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, ContentWriteLockBusyError, sleep2, CONTENT_WRITE_LOCK_STALE_MS, heldRoots, getFormatExtension, isDataOnlyFormat, asRecord, DEFAULT_SEO_FIELD_NAMES, asRecord2, asText, asFlag, asImageUrl, asOgType, asTwitterCard, RESTRICTED_REFERENCE_MARKER, SEO_GROUP_FIELDS, MAX_WARNED, warned4, SchemaUnavailableError, BASE58_PATTERN, CONTENT_ID_PATTERN, PHYSICAL_SEGMENT_PATTERN, isValidId, EMPTY_LOGICAL_PATH, ContentIdIndex, parseTypedFilename, CONTENT_EXTENSIONS, registry, finalization, log4, META_DIR2, entryTypeMetaSchema, collectionMetaSchema, rootCollectionMetaSchema, fingerprints, editDistance, closestName, forEachEntryType, collectEntryTypeNames, validateReferenceEntryTypes, isDeployedStatic, isBuildMode, readsFromCheckout, STATIC_DEPLOY_USER, storage, log5, BaseBranchUnresolvedError, NETWORK_SCHEME_PATTERN, TRANSPORT_HELPER_PATTERN, LEADING_OPTION_PATTERN, WINDOWS_DRIVE_PATTERN, COLON_BEFORE_SLASH_PATTERN, MISSING_REMOTE_REF_REASONS, CANOPY_META_DIR, SCHEMA_CACHE_VERSION, MTIME_CHECK_DEBOUNCE_MS, SCHEMA_CACHE_FILE, SCHEMA_GENERATION_RESOURCE, reportedIssues, BranchSchemaCache, RESOURCE, RESERVED_SETTINGS_BRANCH_PREFIX, BranchPathError, resolveContentBranchesRoot, ContentStoreError, ContentConflictError, EntryGoneError, BranchSyncingError, DuplicateContentIdError, UrlPathConflictError, isEmptyFieldValue, DEFAULT_INDEX_FRESHNESS_INTERVAL_MS, FORCED_REFRESH_MIN_INTERVAL_MS, STALE_LOOKUP, isMissingReference, createReferenceResolveCache, ContentStore, ATTR_CONTENT, BLOCK_PREFIX, BLOCK_SUFFIX, BLOCK_RESTORE_RE, INLINE_PREFIX, INLINE_SUFFIX, INLINE_RESTORE_RE, DEFAULT_REFERENCE_RENDERING, FLAT_CELL_TYPES, log6, BASE58_CHAR, ENTRY_LINK_PATTERN, INLINE_LINK_TARGET, BranchMetadataCorruptError, BRANCH_META_DIR, BRANCH_META_FILE, branchMetadataFilePath, UNKNOWN_TIME, enumOf, branchMetadataShape, branchMetadataSchema, branchMetadataFileSchema, log7, REGISTRY_FILE, REGISTRY_STALE_FILE, REGISTRY_TEMP_FILE, REGISTRY_VERSION, RESOURCE2, GET_MISS_REFRESH_MIN_INTERVAL_MS, snapshotSchema, reportedUnreadable, BranchRegistry, log8, OccWriteConflictError, CURRENT_SCHEMA_VERSION, BranchMetadataConflictError, BranchMetadataFileManager, loadBranchContext, log9, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, GIT_NETWORK_ENV_PASSTHROUGH, NO_AUTO_GC_CONFIG, REMOTE_GIT_CONFIG, REMOTE_GIT_MAX_LOOSE_OBJECTS, REMOTE_GIT_MAX_PACKS, PARALLEL_CHECKOUT_ARGS, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, GitConflictError, GitRemoteRefMissingError, RemoteNotReadyError, SettingsRemoteUnreadableError, SettingsBranchDivergedError, SETTINGS_BRANCH_FILES, SettingsBranchHasContentHistoryError, GitManager, PUBLISH_LOCK_WAIT_MS, CREATE_RESIDUE_MIN_QUIET_MS, STAGING_MAX_AGE_MS, REPAIR_MAX_AGE_MS, DELETING_MAX_AGE_MS, SETTINGS_TRASH_MAX_AGE_MS, BranchProvisioningBusyError, BranchDirOccupiedError, STAGING_PREFIX, REPAIR_PREFIX, TRASH_PREFIX, stagingDirName, NO_GIT, RENAME_BLOCKED, SPARSE_CONE_RECORD, defaultSink, sink, ProvisionLog, testHooks, provisionsInFlight, BranchWorkspaceManager, log10, normalizePath, readEntryData, createReferenceResolver, withoutUnavailableEntries, resolveCollectionItemReferences, looksLikeMalformedEntry, listCollectionEntries, MAX_INPUT_PIXELS, CROP_PRECISION, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, GENERATED_RECORD_FILENAME, SAFE_BUILD_ID, BUILD_ID_RULE, jiti;
8009
+ var __defProp3, __getOwnPropNames3, __esm3, __export3, init_error3, init_atomic_write2, init_field_traversal, ITEM_KEY, ITEM_KEY_PATTERN, isItemKey, BASE58, isListField, init_item_keys, active2, init_logger2, SENTINEL_BASE2, DOT_SEGMENT2, init_sanitize_href2, init_normalize, ROOT_COLLECTION_ID, init_types2, normalizePathValue, flattenSchema, init_flatten, scalarFieldTypes2, markdownFieldTypes2, primitiveFieldTypes2, fieldTypes2, init_types22, SAFE_HTML_TAGS2, COMPONENT_NAME2, MDX_BINDINGS2, FORBIDDEN_ATTRIBUTES2, init_mdx_allowlist2, fieldBaseSchema2, selectOptionSchema2, referenceOptionSchema2, primitiveFieldSchema2, mdxPropAllowSchema2, mdxAllowlistSchema2, markdownFieldSchema2, ASPECT_RATIO_RE2, imageFieldSchema2, selectFieldSchema2, referenceSortFieldSchema2, referenceFieldSchema2, fieldHolder2, blockSchema2, blockFieldSchema2, objectFieldSchema2, inlineGroupFieldSchema2, customFieldSchema2, knownFieldSchema2, fieldSchema2, init_field2, relativePathSchema2, entryTypeSchema2, collectionSchema2, init_collection2, uploadTargetUrlSchema2, previewPrefixSchema2, editorUrlSchema2, init_url2, mediaSchema2, init_media2, defaultBranchAccessSchema2, defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2, defaultPathAccessSchema2, defaultBaseBranchSchema2, defaultRemoteNameSchema2, defaultRemoteUrlSchema2, gitBotAuthorNameSchema2, gitBotAuthorEmailSchema2, githubTokenEnvVarSchema2, operatingModeSchema2, deployedAsSchema2, contentRootSchema2, sourceRootSchema2, basePathSchema2, deploymentNameSchema2, devContentSyncSchema2, devConfigSchema2, editorConfigSchema2, seoFieldNameSchema2, seoSchema2, CanopyConfigSchema2, DEFAULT_PROD_WORKSPACE2, init_config3, SERVER_MODE_ENV_VAR, BROWSER_MODE_ENV_VAR, warned2, init_mode_env, forEachReferenceField, init_validation, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned3, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, init_config22, ProdStrategy2, DevStrategy2, strategyCache2, init_client_unsafe_strategy, operating_mode_exports, init_operating_mode, task_queue_config_exports2, init_task_queue_config2, worker_status_exports, WORKER_STATUS_FILE, init_worker_status, BLOCK_DISCRIMINATOR_KEYS, BLOCK_STRUCTURAL_KEYS, envFlag2, LOG_LEVELS2, DebugLogger2, testLogger2, MARKDOWN_EXPORT_OPTIONS, BULLETS, DELIMITERS, EMPHASIS, STRONG, RULE, WORD_CHAR, GAP, Restyler, END, FRESH, LINE_WIDTH, MIN_LINE_WIDTH, SourceSplicer, log2, MAX_LCS_CELLS, Splicer, log22, EVIDENCE_MAX_DEPTH, log3, locks, PROVISIONING_LOCK_STALE_MS, sleep, META_DIR, CONTENT_WRITE_LOCK_NAME, DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, ContentWriteLockBusyError, sleep2, CONTENT_WRITE_LOCK_STALE_MS, heldRoots, getFormatExtension, isDataOnlyFormat, asRecord, DEFAULT_SEO_FIELD_NAMES, asRecord2, asText, asFlag, asImageUrl, asOgType, asTwitterCard, RESTRICTED_REFERENCE_MARKER, SEO_GROUP_FIELDS, MAX_WARNED, warned4, SchemaUnavailableError, BASE58_PATTERN, CONTENT_ID_PATTERN, PHYSICAL_SEGMENT_PATTERN, isValidId, parseTypedFilename, EMPTY_LOGICAL_PATH, ENTRY_EXTENSIONS, ContentIdIndex, CONTENT_EXTENSIONS, registry, finalization, log4, META_DIR2, entryTypeMetaSchema, collectionMetaSchema, rootCollectionMetaSchema, fingerprints, editDistance, closestName, forEachEntryType, collectEntryTypeNames, validateReferenceEntryTypes, isDeployedStatic, isBuildMode, readsFromCheckout, STATIC_DEPLOY_USER, storage, log5, BaseBranchUnresolvedError, NETWORK_SCHEME_PATTERN, TRANSPORT_HELPER_PATTERN, LEADING_OPTION_PATTERN, WINDOWS_DRIVE_PATTERN, COLON_BEFORE_SLASH_PATTERN, MISSING_REMOTE_REF_REASONS, CANOPY_META_DIR, SCHEMA_CACHE_VERSION, MTIME_CHECK_DEBOUNCE_MS, SCHEMA_CACHE_FILE, SCHEMA_GENERATION_RESOURCE, reportedIssues, BranchSchemaCache, RESOURCE, RESERVED_SETTINGS_BRANCH_PREFIX, BranchPathError, resolveContentBranchesRoot, ContentStoreError, ContentConflictError, EntryGoneError, BranchSyncingError, DuplicateContentIdError, UrlPathConflictError, isEmptyFieldValue, DEFAULT_INDEX_FRESHNESS_INTERVAL_MS, FORCED_REFRESH_MIN_INTERVAL_MS, STALE_LOOKUP, isMissingReference, createReferenceResolveCache, ContentStore, ATTR_CONTENT, BLOCK_PREFIX, BLOCK_SUFFIX, BLOCK_RESTORE_RE, INLINE_PREFIX, INLINE_SUFFIX, INLINE_RESTORE_RE, DEFAULT_REFERENCE_RENDERING, FLAT_CELL_TYPES, log6, BASE58_CHAR, ENTRY_LINK_PATTERN, INLINE_LINK_TARGET, BranchMetadataCorruptError, BRANCH_META_DIR, BRANCH_META_FILE, branchMetadataFilePath, UNKNOWN_TIME, enumOf, branchMetadataShape, branchMetadataSchema, branchMetadataFileSchema, log7, REGISTRY_FILE, REGISTRY_STALE_FILE, REGISTRY_TEMP_FILE, REGISTRY_VERSION, RESOURCE2, GET_MISS_REFRESH_MIN_INTERVAL_MS, snapshotSchema, reportedUnreadable, BranchRegistry, log8, OccWriteConflictError, CURRENT_SCHEMA_VERSION, BranchMetadataConflictError, BranchMetadataFileManager, loadBranchContext, log9, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, GIT_NETWORK_ENV_PASSTHROUGH, NO_AUTO_GC_CONFIG, REMOTE_GIT_CONFIG, REMOTE_GIT_MAX_LOOSE_OBJECTS, REMOTE_GIT_MAX_PACKS, PARALLEL_CHECKOUT_ARGS, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, GitConflictError, GitRemoteRefMissingError, RemoteNotReadyError, SettingsRemoteUnreadableError, SettingsBranchDivergedError, SETTINGS_BRANCH_FILES, SettingsBranchHasContentHistoryError, GitManager, PUBLISH_LOCK_WAIT_MS, CREATE_RESIDUE_MIN_QUIET_MS, STAGING_MAX_AGE_MS, REPAIR_MAX_AGE_MS, DELETING_MAX_AGE_MS, SETTINGS_TRASH_MAX_AGE_MS, BranchProvisioningBusyError, BranchDirOccupiedError, STAGING_PREFIX, REPAIR_PREFIX, TRASH_PREFIX, stagingDirName, NO_GIT, RENAME_BLOCKED, SPARSE_CONE_RECORD, defaultSink, sink, ProvisionLog, testHooks, provisionsInFlight, BranchWorkspaceManager, log10, normalizePath, readEntryData, createReferenceResolver, withoutUnavailableEntries, resolveCollectionItemReferences, looksLikeMalformedEntry, listCollectionEntries, MAX_INPUT_PIXELS, CROP_PRECISION, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, GENERATED_RECORD_FILENAME, SAFE_BUILD_ID, BUILD_ID_RULE, jiti;
7836
8010
  var init_generate_ai_content = __esm({
7837
8011
  "dist/cli/generate-ai-content.js"() {
7838
8012
  "use strict";
@@ -8285,6 +8459,14 @@ var init_generate_ai_content = __esm({
8285
8459
  previewPrefixSchema2 = z42.string().trim().refine((value) => isHttpUrlOrSameOriginPath2(value), {
8286
8460
  message: 'editor.previewPrefix must be an absolute http(s) URL or a site-relative path beginning with a single "/", with no query or fragment'
8287
8461
  });
8462
+ editorUrlSchema2 = z42.string().trim().refine((value) => {
8463
+ if (!isHttpUrlOrSameOriginPath2(value) || !/^https?:\/\//i.test(value))
8464
+ return false;
8465
+ const url = new URL(value);
8466
+ return !url.username && !url.password;
8467
+ }, {
8468
+ message: "editorUrl must be an absolute http(s) URL with no credentials, query or fragment"
8469
+ });
8288
8470
  }
8289
8471
  });
8290
8472
  init_media2 = __esm3({
@@ -8427,6 +8609,7 @@ var init_generate_ai_content = __esm({
8427
8609
  contentRoot: contentRootSchema2.default("content"),
8428
8610
  sourceRoot: sourceRootSchema2.optional(),
8429
8611
  basePath: basePathSchema2,
8612
+ editorUrl: editorUrlSchema2.optional(),
8430
8613
  unauthenticatedStatus: z6.union([z6.literal(401), z6.literal(419)]).optional(),
8431
8614
  danglingReferences: z6.enum(["error", "warn"]).optional(),
8432
8615
  editor: editorConfigSchema2.optional(),
@@ -8796,6 +8979,360 @@ var init_generate_ai_content = __esm({
8796
8979
  });
8797
8980
  init_error3();
8798
8981
  init_error3();
8982
+ MARKDOWN_EXPORT_OPTIONS = {
8983
+ listItemIndent: "one",
8984
+ bullet: "-",
8985
+ emphasis: "_",
8986
+ strong: "*",
8987
+ rule: "-"
8988
+ };
8989
+ BULLETS = [MARKDOWN_EXPORT_OPTIONS.bullet, "*"];
8990
+ DELIMITERS = [".", ")"];
8991
+ EMPHASIS = MARKDOWN_EXPORT_OPTIONS.emphasis;
8992
+ STRONG = MARKDOWN_EXPORT_OPTIONS.strong.repeat(2);
8993
+ RULE = MARKDOWN_EXPORT_OPTIONS.rule.repeat(3);
8994
+ WORD_CHAR = /[^\s\p{P}\p{S}]/u;
8995
+ GAP = /^[ \t>]*(?:\r?\n[ \t>]*)+$/;
8996
+ Restyler = class {
8997
+ constructor(source) {
8998
+ this.source = source;
8999
+ this.listMarker = /* @__PURE__ */ new Map();
9000
+ this.underscores = /* @__PURE__ */ new Map();
9001
+ }
9002
+ /** Edits for `node` and everything under it, given its ancestors (nearest last). */
9003
+ collect(node, ancestors, out) {
9004
+ const inside = [...ancestors, node];
9005
+ switch (node.type) {
9006
+ case "emphasis":
9007
+ this.emphasis(node, ancestors, out);
9008
+ break;
9009
+ case "strong":
9010
+ this.strong(node, out);
9011
+ break;
9012
+ case "text":
9013
+ this.text(node, ancestors, out);
9014
+ return;
9015
+ case "thematicBreak":
9016
+ this.replaceWhole(node, this.ruleIn(ancestors), out);
9017
+ return;
9018
+ case "code":
9019
+ this.fence(node, out);
9020
+ return;
9021
+ case "heading":
9022
+ this.heading(node, inside, out);
9023
+ return;
9024
+ case "table":
9025
+ this.table(node, inside, out);
9026
+ return;
9027
+ default:
9028
+ break;
9029
+ }
9030
+ this.children(node, inside, out);
9031
+ }
9032
+ /**
9033
+ * Edits for `parent`'s children and the gaps between them. `fixed` names children whose text
9034
+ * must stay as it is (untouched top-level blocks); their lists still count for alternation.
9035
+ */
9036
+ children(parent, inside, out, fixed = () => false, outFor = () => out) {
9037
+ const children = parent.children ?? [];
9038
+ const markers = this.listMarkers(children, fixed);
9039
+ children.forEach((child, index) => {
9040
+ if (fixed(index))
9041
+ return;
9042
+ const edits = outFor(index);
9043
+ const marker = markers.get(child);
9044
+ if (marker !== void 0)
9045
+ this.list(child, marker, edits);
9046
+ this.collect(child, inside, edits);
9047
+ });
9048
+ const always = parent.type === "root" || parent.type === "blockquote";
9049
+ const spaced = always || parent.type === "list" || parent.type === "listItem";
9050
+ if (!spaced)
9051
+ return;
9052
+ for (let index = 1; index < children.length; index++) {
9053
+ if (fixed(index) && fixed(index - 1))
9054
+ continue;
9055
+ this.gap(children[index - 1], children[index], always, outFor(fixed(index) ? index - 1 : index));
9056
+ }
9057
+ }
9058
+ /** The marker each list among `children` takes: its own, then alternating with the one before. */
9059
+ listMarkers(children, fixed) {
9060
+ const markers = /* @__PURE__ */ new Map();
9061
+ let previous;
9062
+ children.forEach((child, index) => {
9063
+ if (child.type !== "list") {
9064
+ previous = void 0;
9065
+ return;
9066
+ }
9067
+ const ordered = Boolean(child.ordered);
9068
+ const current = this.markerOf(child);
9069
+ if (current === void 0) {
9070
+ previous = void 0;
9071
+ return;
9072
+ }
9073
+ let marker = current;
9074
+ if (!fixed(index)) {
9075
+ const choices = ordered ? DELIMITERS : BULLETS;
9076
+ const next = children[index + 1];
9077
+ const nextFixed = next?.type === "list" && Boolean(next.ordered) === ordered && fixed(index + 1) ? this.markerOf(next) : void 0;
9078
+ const before = previous?.ordered === ordered ? previous.marker : void 0;
9079
+ marker = choices.find((choice) => choice !== before && choice !== nextFixed) ?? current;
9080
+ markers.set(child, marker);
9081
+ }
9082
+ this.listMarker.set(child, marker);
9083
+ previous = { ordered, marker };
9084
+ });
9085
+ return markers;
9086
+ }
9087
+ /**
9088
+ * A rule's text: `---`, but inside a list Prettier writes `***`, or `---` when the list takes
9089
+ * the alternate marker.
9090
+ */
9091
+ ruleIn(ancestors) {
9092
+ const list = [...ancestors].reverse().find((a) => a.type === "list");
9093
+ if (list === void 0)
9094
+ return RULE;
9095
+ const marker = this.listMarker.get(list);
9096
+ return marker === BULLETS[0] || marker === DELIMITERS[0] ? "***" : RULE;
9097
+ }
9098
+ /** A list's bullet, or its ordered delimiter, read from its first item. */
9099
+ markerOf(list) {
9100
+ const at = startOf(list.children?.[0]);
9101
+ if (at === void 0)
9102
+ return void 0;
9103
+ if (!list.ordered)
9104
+ return this.source[at];
9105
+ return /^\d{1,9}([.)])/.exec(this.source.slice(at, at + 10))?.[1];
9106
+ }
9107
+ /** Item markers: the list's bullet or delimiter, and Prettier's numbering where widths allow. */
9108
+ list(list, marker, out) {
9109
+ const items = list.children ?? [];
9110
+ const starts = items.map(startOf);
9111
+ if (!list.ordered) {
9112
+ for (const at of starts) {
9113
+ if (at !== void 0 && this.source[at] !== marker) {
9114
+ out.push({ start: at, end: at + 1, text: marker });
9115
+ }
9116
+ }
9117
+ return;
9118
+ }
9119
+ const numbers = starts.map((at) => at === void 0 ? null : /^(\d{1,9})[.)]/.exec(this.source.slice(at, at + 11)));
9120
+ const typed = numbers.map((match) => Number(match?.[1]));
9121
+ const renumber = numbers.every((match, i) => match !== null && String(orderedNumber(typed, i)).length === match[1]?.length);
9122
+ numbers.forEach((match, i) => {
9123
+ const at = starts[i];
9124
+ if (match === null || at === void 0)
9125
+ return;
9126
+ const digits = match[1] ?? "";
9127
+ const wanted = renumber ? String(orderedNumber(typed, i)) : digits;
9128
+ if (wanted !== digits || match[0].slice(-1) !== marker) {
9129
+ out.push({ start: at, end: at + match[0].length, text: wanted + marker });
9130
+ }
9131
+ });
9132
+ }
9133
+ /**
9134
+ * The gap between two siblings with exactly one blank line, kept in the container's own
9135
+ * prefix and line ending; where `always` is false, only a gap that had a blank line.
9136
+ */
9137
+ gap(a, b, always, out) {
9138
+ const end = this.contentEnd(a);
9139
+ const start = startOf(b);
9140
+ if (end === void 0 || start === void 0)
9141
+ return;
9142
+ const gap = this.source.slice(end, start);
9143
+ if (!GAP.test(gap))
9144
+ return;
9145
+ const lines = gap.split(/\r?\n/);
9146
+ if (!always && lines.length < 3)
9147
+ return;
9148
+ const eol = gap.includes("\r\n") ? "\r\n" : "\n";
9149
+ const prefix = lines[lines.length - 1] ?? "";
9150
+ const together = a?.type === "definition" && b?.type === "definition";
9151
+ const text3 = together ? eol + prefix : eol + prefix.trimEnd() + eol + prefix;
9152
+ if (text3 !== gap)
9153
+ out.push({ start: end, end: start, text: text3 });
9154
+ }
9155
+ /**
9156
+ * Where `node`'s last line of content ends: a list's position runs on over the blank lines
9157
+ * after it, which in a block quote are lines of `>`.
9158
+ */
9159
+ contentEnd(node) {
9160
+ const start = startOf(node);
9161
+ let end = endOf(node);
9162
+ if (start === void 0 || end === void 0)
9163
+ return end;
9164
+ for (; ; ) {
9165
+ const lineStart = this.source.lastIndexOf("\n", end - 1);
9166
+ if (lineStart <= start || !/^[ \t>]*$/.test(this.source.slice(lineStart + 1, end)))
9167
+ break;
9168
+ end = this.source[lineStart - 1] === "\r" ? lineStart - 1 : lineStart;
9169
+ }
9170
+ return end;
9171
+ }
9172
+ emphasis(node, ancestors, out) {
9173
+ const start = startOf(node);
9174
+ const end = endOf(node);
9175
+ if (start === void 0 || end === void 0)
9176
+ return;
9177
+ const open = this.source[start];
9178
+ if (open !== this.source[end - 1] || open !== "*" && open !== "_")
9179
+ return;
9180
+ const marker = ancestors.some((a) => a.type === "emphasis") || this.besideWord(start, end) || this.afterUnderscore(start, ancestors) ? "*" : EMPHASIS;
9181
+ if (open === marker)
9182
+ return;
9183
+ out.push({ start, end: start + 1, text: marker }, { start: end - 1, end, text: marker });
9184
+ }
9185
+ strong(node, out) {
9186
+ const start = startOf(node);
9187
+ const end = endOf(node);
9188
+ if (start === void 0 || end === void 0)
9189
+ return;
9190
+ if (this.source.slice(start, start + 2) !== "__" || this.source.slice(end - 2, end) !== "__") {
9191
+ return;
9192
+ }
9193
+ out.push({ start, end: start + 2, text: STRONG }, { start: end - 2, end, text: STRONG });
9194
+ }
9195
+ /** Whether the characters past the markers around `[start, end)` include a word character. */
9196
+ besideWord(start, end) {
9197
+ const markers = "*_";
9198
+ let before = start - 1;
9199
+ while (before >= 0 && markers.includes(this.source[before] ?? ""))
9200
+ before--;
9201
+ let after = end;
9202
+ while (after < this.source.length && markers.includes(this.source[after] ?? ""))
9203
+ after++;
9204
+ return WORD_CHAR.test(this.source[before] ?? " ") || WORD_CHAR.test(this.source[after] ?? " ");
9205
+ }
9206
+ afterUnderscore(start, ancestors) {
9207
+ const block = [...ancestors].reverse().find((a) => a.type === "paragraph" || a.type === "heading" || a.type === "tableCell");
9208
+ if (block === void 0)
9209
+ return false;
9210
+ let starts = this.underscores.get(block);
9211
+ if (starts === void 0) {
9212
+ starts = underscoreStarts(block);
9213
+ this.underscores.set(block, starts);
9214
+ }
9215
+ const link2 = [...ancestors].reverse().find((a) => a.type === "link" || a.type === "linkReference");
9216
+ const inLink = (link2?.children ?? []).flatMap(underscoreStarts);
9217
+ return [...starts, ...inLink].some((at) => at < start);
9218
+ }
9219
+ text(node, ancestors, out) {
9220
+ const start = startOf(node);
9221
+ const end = endOf(node);
9222
+ if (start === void 0 || end === void 0)
9223
+ return;
9224
+ const raw = this.source.slice(start, end);
9225
+ for (const match of raw.matchAll(/[ \t]+(?=\r?\n)/g)) {
9226
+ out.push({ start: start + match.index, end: start + match.index + match[0].length, text: "" });
9227
+ }
9228
+ if (ancestors.some((a) => a.type === "link" || a.type === "linkReference"))
9229
+ return;
9230
+ const block = [...ancestors].reverse().find((a) => a.type === "paragraph" || a.type === "heading" || a.type === "tableCell");
9231
+ const blockStart = startOf(block);
9232
+ const blockEnd = endOf(block);
9233
+ if (blockStart === void 0 || blockEnd === void 0)
9234
+ return;
9235
+ if (/\$|\[\[/.test(this.source.slice(blockStart, blockEnd)))
9236
+ return;
9237
+ for (const match of raw.matchAll(/(?<=[\p{L}\p{N}])\\_(?=[\p{L}\p{N}])/gu)) {
9238
+ out.push({ start: start + match.index, end: start + match.index + 1, text: "" });
9239
+ }
9240
+ }
9241
+ replaceWhole(node, text3, out) {
9242
+ const start = startOf(node);
9243
+ const end = endOf(node);
9244
+ if (start === void 0 || end === void 0)
9245
+ return;
9246
+ if (this.source.slice(start, end) !== text3)
9247
+ out.push({ start, end, text: text3 });
9248
+ }
9249
+ /** A `~~~` fence as backticks, when nothing inside it would end a backtick fence. */
9250
+ fence(node, out) {
9251
+ const start = startOf(node);
9252
+ const end = endOf(node);
9253
+ if (start === void 0 || end === void 0)
9254
+ return;
9255
+ const raw = this.source.slice(start, end);
9256
+ const close = /(?:\r?\n)[ \t>]*(~{3,})[ \t]*$/.exec(raw);
9257
+ if (!/^~~~(?!~)[^`\n]*\n/.test(raw) || close?.[1] === void 0 || raw.includes("`"))
9258
+ return;
9259
+ const closeAt = start + close.index + close[0].indexOf("~");
9260
+ out.push({ start, end: start + 3, text: "```" }, { start: closeAt, end: closeAt + close[1].length, text: "```" });
9261
+ }
9262
+ /** A one-line heading as `# x`, its content with its own edits. */
9263
+ heading(node, inside, out) {
9264
+ const inner = [];
9265
+ this.children(node, inside, inner);
9266
+ const children = node.children ?? [];
9267
+ const start = startOf(node);
9268
+ const end = endOf(node);
9269
+ const contentStart = startOf(children[0]);
9270
+ const contentEnd = endOf(children[children.length - 1]);
9271
+ if (start === void 0 || end === void 0 || contentStart === void 0 || contentEnd === void 0 || /[\r\n]/.test(this.source.slice(contentStart, contentEnd))) {
9272
+ out.push(...inner);
9273
+ return;
9274
+ }
9275
+ const content = apply(this.source, inner, contentStart, contentEnd);
9276
+ const text3 = `${"#".repeat(node.depth ?? 1)} ${content}`;
9277
+ if (text3 !== this.source.slice(start, end))
9278
+ out.push({ start, end, text: text3 });
9279
+ else
9280
+ out.push(...inner);
9281
+ }
9282
+ /** A table padded to its columns, as Prettier pads it; its cells keep their own text. */
9283
+ table(node, inside, out) {
9284
+ const start = startOf(node);
9285
+ const end = endOf(node);
9286
+ if (start === void 0 || end === void 0)
9287
+ return;
9288
+ const prefix = this.source.slice(this.source.lastIndexOf("\n", start - 1) + 1, start);
9289
+ const inner = [];
9290
+ const rows = (node.children ?? []).map((row) => (row.children ?? []).map((cell) => {
9291
+ const edits = [];
9292
+ this.children(cell, [...inside, row, cell], edits);
9293
+ inner.push(...edits);
9294
+ const content = cell.children ?? [];
9295
+ const from = startOf(content[0]);
9296
+ const to = endOf(content[content.length - 1]);
9297
+ return from === void 0 || to === void 0 ? "" : apply(this.source, edits, from, to);
9298
+ }));
9299
+ const plain = rows.every((row) => row.every((cell) => /^[\x20-\x7e]*$/.test(cell)));
9300
+ if (!plain || !/^[ \t>]*$/.test(prefix)) {
9301
+ out.push(...inner);
9302
+ return;
9303
+ }
9304
+ const align = node.align ?? [];
9305
+ const columns = Math.max(...rows.map((row) => row.length), align.length);
9306
+ const widths = Array.from({ length: columns }, (_, c) => Math.max(3, ...rows.map((row) => row[c]?.length ?? 0)));
9307
+ const pad = (text22, c) => {
9308
+ const room = (widths[c] ?? 3) - text22.length;
9309
+ if (align[c] === "right")
9310
+ return " ".repeat(room) + text22;
9311
+ if (align[c] === "center") {
9312
+ const left = Math.floor(room / 2);
9313
+ return " ".repeat(left) + text22 + " ".repeat(room - left);
9314
+ }
9315
+ return text22 + " ".repeat(room);
9316
+ };
9317
+ const delimiter = widths.map((width, c) => {
9318
+ const a = align[c];
9319
+ if (a === "left")
9320
+ return `:${"-".repeat(width - 1)}`;
9321
+ if (a === "right")
9322
+ return `${"-".repeat(width - 1)}:`;
9323
+ if (a === "center")
9324
+ return `:${"-".repeat(width - 2)}:`;
9325
+ return "-".repeat(width);
9326
+ });
9327
+ const line = (cells) => `| ${cells.join(" | ")} |`;
9328
+ const lines = rows.map((row) => line(row.map(pad)));
9329
+ lines.splice(1, 0, line(delimiter));
9330
+ const eol = this.source.slice(start, end).includes("\r\n") ? "\r\n" : "\n";
9331
+ const text3 = lines.join(eol + prefix);
9332
+ if (text3 !== this.source.slice(start, end))
9333
+ out.push({ start, end, text: text3 });
9334
+ }
9335
+ };
8799
9336
  END = Symbol("end");
8800
9337
  FRESH = Symbol("fresh");
8801
9338
  LINE_WIDTH = 80;
@@ -9127,8 +9664,8 @@ var init_generate_ai_content = __esm({
9127
9664
  body(before, after) {
9128
9665
  const oNodes = before.children ?? [];
9129
9666
  const nNodes = after.children ?? [];
9130
- const first = startOf(oNodes[0]);
9131
- const last = endOf(oNodes[oNodes.length - 1]);
9667
+ const first = startOf2(oNodes[0]);
9668
+ const last = endOf2(oNodes[oNodes.length - 1]);
9132
9669
  if (first === void 0 || last === void 0 || nNodes.length === 0)
9133
9670
  return void 0;
9134
9671
  const o = { text: this.original, indent: "" };
@@ -9236,9 +9773,9 @@ var init_generate_ai_content = __esm({
9236
9773
  item(o, oItem, n, nItem) {
9237
9774
  const oChildren = oItem.children ?? [];
9238
9775
  const nChildren = nItem.children ?? [];
9239
- const oStart = startOf(oItem);
9240
- const oContent = startOf(oChildren[0]);
9241
- const nContent = startOf(nChildren[0]);
9776
+ const oStart = startOf2(oItem);
9777
+ const oContent = startOf2(oChildren[0]);
9778
+ const nContent = startOf2(nChildren[0]);
9242
9779
  if (oStart === void 0 || oContent === void 0 || nContent === void 0)
9243
9780
  return void 0;
9244
9781
  const prefix = o.text.slice(oStart, oContent);
@@ -9417,7 +9954,32 @@ var init_generate_ai_content = __esm({
9417
9954
  isValidId = isValidContentId;
9418
9955
  init_error3();
9419
9956
  init_logger2();
9957
+ parseTypedFilename = (filename, entryTypes) => {
9958
+ if (filename.startsWith("."))
9959
+ return null;
9960
+ const lastDot = filename.lastIndexOf(".");
9961
+ if (lastDot === -1)
9962
+ return null;
9963
+ const nameWithoutExt = filename.slice(0, lastDot);
9964
+ const parts = nameWithoutExt.split(".");
9965
+ if (parts.length < 3)
9966
+ return null;
9967
+ const potentialType = parts[0];
9968
+ if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
9969
+ return null;
9970
+ }
9971
+ const id = parts[parts.length - 1];
9972
+ if (!isValidId(id))
9973
+ return null;
9974
+ const slug = parts.slice(1, -1).join(".").toLowerCase();
9975
+ return {
9976
+ type: potentialType,
9977
+ slug,
9978
+ id
9979
+ };
9980
+ };
9420
9981
  EMPTY_LOGICAL_PATH = "";
9982
+ ENTRY_EXTENSIONS = new Set(Object.keys({ md: true, mdx: true, json: true, yaml: true }).map((format) => getFormatExtension(format)));
9421
9983
  ContentIdIndex = class {
9422
9984
  constructor(root) {
9423
9985
  this.idToLocation = /* @__PURE__ */ new Map();
@@ -9705,30 +10267,6 @@ var init_generate_ai_content = __esm({
9705
10267
  }
9706
10268
  };
9707
10269
  init_error3();
9708
- parseTypedFilename = (filename, entryTypes) => {
9709
- if (filename.startsWith("."))
9710
- return null;
9711
- const lastDot = filename.lastIndexOf(".");
9712
- if (lastDot === -1)
9713
- return null;
9714
- const nameWithoutExt = filename.slice(0, lastDot);
9715
- const parts = nameWithoutExt.split(".");
9716
- if (parts.length < 3)
9717
- return null;
9718
- const potentialType = parts[0];
9719
- if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
9720
- return null;
9721
- }
9722
- const id = parts[parts.length - 1];
9723
- if (!isValidId(id))
9724
- return null;
9725
- const slug = parts.slice(1, -1).join(".").toLowerCase();
9726
- return {
9727
- type: potentialType,
9728
- slug,
9729
- id
9730
- };
9731
- };
9732
10270
  CONTENT_EXTENSIONS = [".md", ".mdx", ".json", ".yaml"];
9733
10271
  registry = /* @__PURE__ */ new Map();
9734
10272
  finalization = new FinalizationRegistry(({ rootKey, ref }) => {
@@ -11322,6 +11860,7 @@ var init_generate_ai_content = __esm({
11322
11860
  pullRequestNumber: z72.number().int().optional(),
11323
11861
  submittedAt: z72.string().optional(),
11324
11862
  pushedToGitHubAt: z72.string().optional(),
11863
+ editorUrl: z72.string().optional(),
11325
11864
  syncStatus: enumOf({
11326
11865
  synced: "synced",
11327
11866
  "pending-sync": "pending-sync",
@@ -12375,7 +12914,7 @@ var init_generate_ai_content = __esm({
12375
12914
  throw cleanupErr;
12376
12915
  }
12377
12916
  }
12378
- const step = (name, run) => options.provisionLog ? options.provisionLog.step(name, run) : run();
12917
+ const step2 = (name, run) => options.provisionLog ? options.provisionLog.step(name, run) : run();
12379
12918
  const identity = managedWorkspaceConfig(options.gitBotAuthorName, options.gitBotAuthorEmail);
12380
12919
  let justCloned = false;
12381
12920
  let resolvedRemoteUrl;
@@ -12388,9 +12927,9 @@ var init_generate_ai_content = __esm({
12388
12927
  sourceRoot: options.sourceRoot,
12389
12928
  allowNetworkRemoteInProd: options.allowNetworkRemoteInProd
12390
12929
  });
12391
- await step("clone", () => _GitManager.cloneWorkspace(remoteUrl, options.workspacePath, baseBranch, {
12930
+ await step2("clone", () => _GitManager.cloneWorkspace(remoteUrl, options.workspacePath, baseBranch, {
12392
12931
  remoteName,
12393
- noCheckout: options.branchType === "content",
12932
+ noCheckout: true,
12394
12933
  config: identity
12395
12934
  }));
12396
12935
  justCloned = true;
@@ -12403,7 +12942,7 @@ var init_generate_ai_content = __esm({
12403
12942
  skipIndexMarker: options.branchType === "orphan"
12404
12943
  });
12405
12944
  if (!justCloned) {
12406
- await step("config", async () => {
12945
+ await step2("config", async () => {
12407
12946
  await git.ensureLocalConfig(identity);
12408
12947
  const remoteUrl = await _GitManager.resolveRemoteUrl({
12409
12948
  mode: options.mode,
@@ -12421,19 +12960,19 @@ var init_generate_ai_content = __esm({
12421
12960
  }
12422
12961
  if (options.branchType === "orphan") {
12423
12962
  try {
12424
- await step("checkout", () => git.createOrphanSettingsBranch(options.branchName, {}));
12963
+ await step2("checkout", () => git.createOrphanSettingsBranch(options.branchName, {}));
12425
12964
  } catch (err2) {
12426
12965
  if (err2 instanceof SettingsRemoteUnreadableError && !resolvedRemoteUrl) {
12427
12966
  throw await _GitManager.remoteNotReadyError(options.mode) ?? err2;
12428
12967
  }
12429
12968
  throw err2;
12430
12969
  }
12431
- await step("exclude", () => git.ensureGitExclude("*.lock"));
12970
+ await step2("exclude", () => git.ensureGitExclude("*.lock"));
12432
12971
  } else {
12433
- await step("checkout", () => justCloned ? git.checkoutFreshClone(options.branchName) : git.checkoutBranch(options.branchName));
12972
+ await step2("checkout", () => justCloned ? git.checkoutFreshClone(options.branchName) : git.checkoutBranch(options.branchName));
12434
12973
  const pattern = options.gitExcludePattern;
12435
12974
  if (pattern) {
12436
- await step("exclude", () => git.ensureGitExclude(pattern));
12975
+ await step2("exclude", () => git.ensureGitExclude(pattern));
12437
12976
  }
12438
12977
  }
12439
12978
  return git;
@@ -12858,7 +13397,7 @@ var init_generate_ai_content = __esm({
12858
13397
  log9.debug("git", "Checked out the remote settings branch", { branchName });
12859
13398
  return;
12860
13399
  }
12861
- await this.git.raw(["checkout", "--orphan", branchName]);
13400
+ await this.git.raw(["switch", "--orphan", branchName]);
12862
13401
  await this.git.raw(["rm", "-r", "-f", "-q", "--sparse", "--ignore-unmatch", "--", "."]);
12863
13402
  const leftover = (await this.git.raw(["ls-files"])).trim();
12864
13403
  if (leftover) {
@@ -15575,7 +16114,7 @@ var init_collection3 = __esm({
15575
16114
 
15576
16115
  // dist/config/schemas/url.js
15577
16116
  import { z as z12 } from "zod";
15578
- var uploadTargetUrlSchema3, previewPrefixSchema3;
16117
+ var uploadTargetUrlSchema3, previewPrefixSchema3, editorUrlSchema3;
15579
16118
  var init_url3 = __esm({
15580
16119
  "dist/config/schemas/url.js"() {
15581
16120
  "use strict";
@@ -15586,6 +16125,14 @@ var init_url3 = __esm({
15586
16125
  previewPrefixSchema3 = z12.string().trim().refine((value) => isHttpUrlOrSameOriginPath3(value), {
15587
16126
  message: 'editor.previewPrefix must be an absolute http(s) URL or a site-relative path beginning with a single "/", with no query or fragment'
15588
16127
  });
16128
+ editorUrlSchema3 = z12.string().trim().refine((value) => {
16129
+ if (!isHttpUrlOrSameOriginPath3(value) || !/^https?:\/\//i.test(value))
16130
+ return false;
16131
+ const url = new URL(value);
16132
+ return !url.username && !url.password;
16133
+ }, {
16134
+ message: "editorUrl must be an absolute http(s) URL with no credentials, query or fragment"
16135
+ });
15589
16136
  }
15590
16137
  });
15591
16138
 
@@ -15822,6 +16369,7 @@ var init_config4 = __esm({
15822
16369
  contentRoot: contentRootSchema3.default("content"),
15823
16370
  sourceRoot: sourceRootSchema3.optional(),
15824
16371
  basePath: basePathSchema3,
16372
+ editorUrl: editorUrlSchema3.optional(),
15825
16373
  unauthenticatedStatus: z14.union([z14.literal(401), z14.literal(419)]).optional(),
15826
16374
  danglingReferences: z14.enum(["error", "warn"]).optional(),
15827
16375
  editor: editorConfigSchema3.optional(),
@@ -16463,6 +17011,23 @@ var init_id = __esm({
16463
17011
  }
16464
17012
  });
16465
17013
 
17014
+ // dist/utils/format.js
17015
+ var getFormatExtension2;
17016
+ var init_format = __esm({
17017
+ "dist/utils/format.js"() {
17018
+ "use strict";
17019
+ getFormatExtension2 = (format) => {
17020
+ if (format === "md")
17021
+ return ".md";
17022
+ if (format === "mdx")
17023
+ return ".mdx";
17024
+ if (format === "yaml")
17025
+ return ".yaml";
17026
+ return ".json";
17027
+ };
17028
+ }
17029
+ });
17030
+
16466
17031
  // dist/content-id-index.js
16467
17032
  function extractIdFromFilename2(filename) {
16468
17033
  if (filename.startsWith(".")) {
@@ -16506,10 +17071,13 @@ function extractSlugFromFilename2(filename, entryTypeName) {
16506
17071
  }
16507
17072
  return filename.toLowerCase();
16508
17073
  }
17074
+ var ENTRY_EXTENSIONS2;
16509
17075
  var init_content_id_index = __esm({
16510
17076
  "dist/content-id-index.js"() {
16511
17077
  "use strict";
16512
17078
  init_id();
17079
+ init_format();
17080
+ ENTRY_EXTENSIONS2 = new Set(Object.keys({ md: true, mdx: true, json: true, yaml: true }).map((format) => getFormatExtension2(format)));
16513
17081
  }
16514
17082
  });
16515
17083
 
@@ -17693,6 +18261,7 @@ var init_branch_metadata_file = __esm({
17693
18261
  pullRequestNumber: z18.number().int().optional(),
17694
18262
  submittedAt: z18.string().optional(),
17695
18263
  pushedToGitHubAt: z18.string().optional(),
18264
+ editorUrl: z18.string().optional(),
17696
18265
  syncStatus: enumOf2({
17697
18266
  synced: "synced",
17698
18267
  "pending-sync": "pending-sync",