canopycms 0.0.68-int.111 → 0.0.68-int.113

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 (41) hide show
  1. package/dist/branch-health.js +14 -9
  2. package/dist/cli/cli.js +4 -0
  3. package/dist/cli/generate-ai-content.js +2 -0
  4. package/dist/editor/Editor.js +2 -2
  5. package/dist/editor/components/StagedChangesDrawer.d.ts +37 -0
  6. package/dist/editor/components/StagedChangesDrawer.js +75 -0
  7. package/dist/editor/group-manager/GroupCard.d.ts +2 -0
  8. package/dist/editor/group-manager/GroupCard.js +2 -2
  9. package/dist/editor/group-manager/GroupForm.js +1 -1
  10. package/dist/editor/group-manager/InternalGroupsTab.d.ts +1 -0
  11. package/dist/editor/group-manager/InternalGroupsTab.js +2 -2
  12. package/dist/editor/group-manager/hooks/useGroupState.d.ts +2 -0
  13. package/dist/editor/group-manager/hooks/useGroupState.js +18 -1
  14. package/dist/editor/group-manager/index.js +9 -4
  15. package/dist/editor/group-manager/types.d.ts +3 -1
  16. package/dist/editor/hooks/usePermissionManager.js +4 -3
  17. package/dist/editor/permission-manager/index.js +9 -3
  18. package/dist/editor/permission-manager/types.d.ts +3 -1
  19. package/dist/git-manager.d.ts +10 -0
  20. package/dist/git-manager.js +6 -5
  21. package/dist/utils/error.js +6 -0
  22. package/dist/version.d.ts +1 -1
  23. package/dist/version.js +1 -1
  24. package/dist/worker/cms-worker.d.ts +39 -124
  25. package/dist/worker/cms-worker.js +232 -259
  26. package/dist/worker/git-sync.d.ts +7 -5
  27. package/dist/worker/git-sync.js +32 -29
  28. package/dist/worker/github-auth.d.ts +7 -7
  29. package/dist/worker/github-auth.js +12 -14
  30. package/dist/worker/github-gateway.d.ts +144 -0
  31. package/dist/worker/github-gateway.js +370 -0
  32. package/dist/worker/github-mirror.d.ts +84 -10
  33. package/dist/worker/github-mirror.js +237 -21
  34. package/dist/worker/rebase.d.ts +1 -1
  35. package/dist/worker/rebase.js +1 -6
  36. package/dist/worker/shared-repo-git.d.ts +6 -0
  37. package/dist/worker/shared-repo-git.js +8 -0
  38. package/dist/worker/task-runner.d.ts +2 -2
  39. package/dist/worker/task-runner.js +77 -127
  40. package/dist/worker/worker-context.d.ts +12 -40
  41. package/package.json +1 -1
@@ -118,11 +118,10 @@ export declare function recordWorkerStartupFailure(options: {
118
118
  }): Promise<void>;
119
119
  export declare class CmsWorker {
120
120
  private config;
121
- private octokit;
121
+ private gateway?;
122
122
  private taskDir;
123
123
  private remoteGitPath;
124
124
  private stateDirectory;
125
- private githubMirror;
126
125
  private contentBranchesPath;
127
126
  private resolvedBaseBranch?;
128
127
  private settingsBranchResolved?;
@@ -142,7 +141,6 @@ export declare class CmsWorker {
142
141
  private contentRoot;
143
142
  private log;
144
143
  private statusReport?;
145
- private githubAuth?;
146
144
  private lockLostMessage?;
147
145
  private statusWriterAtLock?;
148
146
  private settleSelfStopped;
@@ -192,8 +190,8 @@ export declare class CmsWorker {
192
190
  * Built FRESH on every call, with instance-backed members as functions rather
193
191
  * than copied values. See WorkerContext's INVARIANT: a context that captured
194
192
  * any of them at construction time would hand the extracted code the pre-test
195
- * value, which for `buildGitHubUrl` means a test's push going to github.com
196
- * for real instead of its local fixture repo.
193
+ * value, which for the GitHub gateway means a test's push going to
194
+ * github.com for real instead of its local fixture repo.
197
195
  */
198
196
  private ctx;
199
197
  /**
@@ -278,9 +276,19 @@ export declare class CmsWorker {
278
276
  /**
279
277
  * An unconfigured base branch is the one remote.git's HEAD names, which is what the Lambda
280
278
  * reads too (GitManager.detectBaseBranch); before remote.git exists, it is GitHub's default
281
- * branch, which the clone then records as that HEAD. Never assumes 'main'.
279
+ * branch, which the clone then records as that HEAD. So it is too when remote.git has no branch
280
+ * outside the settings namespace: no base branch is there for HEAD to name, and ensureRemoteGit
281
+ * replaces it from GitHub unless something in it is at stake. Never assumes 'main'.
282
282
  */
283
283
  private resolveBaseBranch;
284
+ /**
285
+ * Check remote.git's config at boot. A refusal is never a case for replacement: it reports that
286
+ * something wrote there, and listing the refs a replacement would lose means running git under
287
+ * that config. The message says so, so System health does not read it as a routine failure.
288
+ */
289
+ private assertRemoteGitConfig;
290
+ /** Whether the bare repo at `gitDir` has a branch that is not a settings branch. */
291
+ private hasContentBranch;
284
292
  /**
285
293
  * Point remote.git's HEAD at the base branch this worker uses, so a Lambda left to detect it
286
294
  * (GitManager.detectBaseBranch) reads the same name. Only the worker writes it, at boot under
@@ -337,91 +345,41 @@ export declare class CmsWorker {
337
345
  * pushed) succeeds and leaves a refs-less repo that `fs.stat` cannot tell from a healthy one, so
338
346
  * left unchecked it silently poisons remote.git and the stat short-circuit means it never heals.
339
347
  * The base branch is therefore verified right after seeding AND on the already-exists fast
340
- * path, since a previous run can have left a poisoned remote.git behind.
348
+ * path, where a poisoned remote.git is replaced (`replacePoisonedRemoteGit`).
341
349
  */
342
350
  private ensureRemoteGit;
343
351
  /**
344
- * Refuse a state directory on the shared filesystem: the mirror there would be as writable by
345
- * the Lambda as remote.git is.
352
+ * Replace an existing remote.git that has no base branch with a fresh seed from GitHub, but only
353
+ * when every ref in it names a commit a GitHub branch contains, so replacing it loses no commit;
354
+ * a branch name GitHub lacks, such as a merged branch deleted there, goes. Otherwise refuse,
355
+ * naming the refs at stake: unpushed work such as a settings branch.
356
+ *
357
+ * The refs are compared in the same mirror session that fetches GitHub and seeds the
358
+ * replacement, and listed again just before the swap, since the Lambda can push into remote.git
359
+ * while the seed runs. The swap is two renames, so the name remote.git resolves to the old repo,
360
+ * the new one, or for an instant nothing, never a half-built or half-deleted one.
361
+ */
362
+ private replacePoisonedRemoteGit;
363
+ /**
364
+ * Seed a new bare repo from GitHub at `<remote.git>.cloning`, verified and configured, and
365
+ * return its path for the caller to rename into place. `check` runs in the mirror session right
366
+ * after the GitHub fetch, and its throw stops the seed. Throws with the staging directory gone.
346
367
  */
347
- private ensureStateDirectoryIsPrivate;
368
+ private seedRemoteGitStaging;
348
369
  processTaskQueue(): Promise<void>;
349
370
  private executeTask;
350
371
  private updateBranchMetadata;
351
372
  private pushBranchToGitHub;
352
373
  /**
353
- * Resolve which GitHub credential this worker uses, once, and build the
354
- * Octokit client from it.
355
- *
356
- * DEFERRED out of the constructor deliberately, exactly as
357
- * `ensureSettingsBranch()` is and for the reason that method records:
358
- * `resolveWorkerGitHubAuth` throws for a half-configured credential (both
359
- * set, neither set, an unusable mint timeout or refresh interval), and a
360
- * throw during `new CmsWorker(...)` lands BEFORE start()'s catch, which
361
- * records `lastFatalError` for every entrypoint.
362
- *
363
- * Idempotent, and it does NOT replace an `octokit` a test has already
364
- * assigned onto the instance — see the field's comment.
365
- */
366
- private ensureGitHubAuth;
367
- /**
368
- * The Octokit client, built on first use. Every read goes through here rather
369
- * than touching the field, which the constructor does not populate: a method
370
- * reached without start() would otherwise see `undefined`.
371
- * `rebaseActiveBranches()` is exactly that case — apps/test-app's e2e route
372
- * calls it directly, and its `pollMergeState` dispatch reads `ctx.octokit()`.
374
+ * The worker's GitHub gateway, created on first use: inside start()'s try, or wherever a test
375
+ * entry point first reaches it. Creating it resolves the credential, which throws for a
376
+ * half-configured one (both set, neither set, an unusable mint timeout or refresh interval) --
377
+ * never in the constructor, for the reason `ensureSettingsBranch()` gives. Keeps a gateway a
378
+ * test installed.
373
379
  */
374
- private octokitClient;
375
- /**
376
- * The single seam through which every git-over-HTTPS credential reaches a git
377
- * command. The only other consumer is Octokit, built from the same resolution
378
- * by `ensureGitHubAuth()` above.
379
- *
380
- * Async because under GitHub App auth `resolveGitToken` mints an installation
381
- * token lasting about an hour. NOTHING may cache what this returns — a URL
382
- * built from an installation token goes stale with it — and resolving per use
383
- * is cheap, since `@octokit/auth-app` answers from its own cache until the
384
- * token is near expiry.
385
- *
386
- * A mint failure propagates AS THROWN, carrying the `.status` that
387
- * `isPermanentTaskFailure` classifies on — see github-auth.ts.
388
- *
389
- * Do NOT add a parallel token accessor alongside it. Every instance-backed
390
- * WorkerContext member stays a function precisely so tests can replace it
391
- * through the instance (worker-context.ts's INVARIANT); a second credential
392
- * path would be one nothing stubs.
393
- */
394
- private buildGitHubUrl;
395
- /**
396
- * Prove the GitHub App credential works before anything depends on it.
397
- *
398
- * Without this the first failure comes out of `ensureRemoteGit`'s bare clone
399
- * below, whose catch blames the repository ("may be empty, or the base branch
400
- * may not exist") and sends an operator holding a bad private key looking for
401
- * a problem that does not exist. Called from start()'s try, so the failure is
402
- * also recorded as `lastFatalError` and reaches the admin panel. No-op on the
403
- * token path: a PAT is a literal, so the first real request checks everything
404
- * this could.
405
- *
406
- * FATAL UNLESS THE FAILURE POSITIVELY LOOKS TRANSIENT. Both halves are
407
- * load-bearing. Not always fatal, because the two credential paths must
408
- * degrade alike: on the token path a GitHub 502 during boot is absorbed (a
409
- * warm `remote.git` short-circuits `ensureRemoteGit`, `Promise.allSettled`
410
- * swallows the initial `syncGit`) so the worker starts and its loops retry,
411
- * while rethrowing every error class would make the App path exit and systemd
412
- * crash-loop it until GitHub recovered — each iteration telling the operator
413
- * to check their private key.
414
- *
415
- * But fail CLOSED, via `isTransientAuthFailure` rather than the inverse of
416
- * `isPermanentTaskFailure`: that classifier defaults a status-less error to
417
- * transient, which is right on the task path (bounded by `maxRetries`) and
418
- * wrong here (bounded by nothing). A key that never reaches GitHub at all —
419
- * the wrong type, or too mangled to sign with — fails locally and
420
- * status-lessly, so defaulting to transient would boot a worker with a dead
421
- * credential, record no `lastFatalError`, and show healthy in the admin panel
422
- * while every task and every sync failed.
423
- */
424
- private preflightGitHubAppAuth;
380
+ private github;
381
+ /** What the in-process gateway is built from; test-utils/worker-gateway.ts builds from it too. */
382
+ private localGitHubGatewayOptions;
425
383
  /**
426
384
  * The workspace directory for a branch named by its GIT REF name -- the form
427
385
  * task payloads carry (`context.branch.name`), not the directory form.
@@ -460,49 +418,6 @@ export declare class CmsWorker {
460
418
  protected afterRebaseCompletedForTesting(): Promise<void>;
461
419
  private rebaseActiveBranches;
462
420
  syncGit(): Promise<void>;
463
- /**
464
- * `syncGit`, plus "the credential may have rotated" on the way out.
465
- *
466
- * One of `refreshGitHubCredential`'s two call sites, and the one that works
467
- * when nobody is publishing: it fetches from GitHub every `gitSyncInterval`
468
- * whether or not anyone is editing, so a credential that has stopped working
469
- * surfaces here even with no push queued for days.
470
- *
471
- * The SYNC failure is what propagates to `scheduleLoop`'s catch;
472
- * `refreshGitHubCredential` never throws, so nothing it does can replace it.
473
- */
474
- private syncGitWithCredentialRefresh;
475
- /**
476
- * Re-read the GitHub credential, because an operation using it just failed.
477
- *
478
- * **Two call sites, each covering what the other cannot.** The git-sync loop
479
- * (`syncGitWithCredentialRefresh`) notices a dead credential when nobody is
480
- * publishing. `processTaskQueue`'s per-task catch is what saves a publish: a
481
- * push task spends its retry budget on a 5s/10s/20s backoff, well inside one
482
- * 5-minute sync interval, so with the sync loop as the only trigger a publish
483
- * meeting a rotated token fails permanently while the working one is already
484
- * in the secret store. Every consumer reaches the credential through
485
- * `ensureGitHubAuth()`, which reads it per use, so a refresh from either site
486
- * repairs all of them for their NEXT use — but not an attempt already failed.
487
- *
488
- * NOT gated on the error looking auth-shaped, at either site: a `git
489
- * fetch`/`push` rejected for a dead token throws a plain simple-git error
490
- * (exit 128, no HTTP `.status`) that `isPermanentTaskFailure` reads as
491
- * transient, so a gate keyed on it would never fire. Two floors bound the
492
- * cost instead — core's `refreshGitHubTokenMinIntervalMs` (default 60s,
493
- * enforced by `refreshCredential` in github-auth.ts) and whatever floor the
494
- * provider keeps (the AWS one reads at most once per five minutes) — and both
495
- * call sites share both, so a read issued by one throttles the other. On the
496
- * GitHub App path the refresh is a no-op.
497
- *
498
- * **Never throws**: both callers are already reporting the failure that must
499
- * reach the log. **Bounded by `taskTimeoutMs`**, because the task loop awaits
500
- * it and a read that never settled would stop every publish queued behind it
501
- * (an adopter's provider may have no bound at all; the AWS one can take 87s
502
- * for one `getSecret`). A losing read is not cancelled and may land later, but
503
- * `refreshCredential` discards a result older than one already applied.
504
- */
505
- private refreshGitHubCredential;
506
421
  private pushSettingsBranches;
507
422
  private refreshBaseBranchWorkspace;
508
423
  private cleanupTrashedBranchDirs;