canopycms 0.0.65 → 0.0.66-int.82

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 (60) hide show
  1. package/README.md +1 -3
  2. package/dist/ai/generate.d.ts +13 -0
  3. package/dist/ai/generate.js +9 -3
  4. package/dist/ai/to-plain-text.js +4 -0
  5. package/dist/ai/types.d.ts +20 -1
  6. package/dist/api/admin-branch-health.js +13 -12
  7. package/dist/api/branch.js +2 -2
  8. package/dist/branch-health.js +1 -1
  9. package/dist/branch-metadata-file.d.ts +58 -0
  10. package/dist/branch-metadata-file.js +65 -0
  11. package/dist/branch-metadata.d.ts +2 -26
  12. package/dist/branch-metadata.js +6 -33
  13. package/dist/branch-registry.js +4 -2
  14. package/dist/build/generate-ai-content.js +103 -0
  15. package/dist/cli/cli.js +432 -317
  16. package/dist/cli/generate-ai-content.js +264 -160
  17. package/dist/content-id-index.js +22 -0
  18. package/dist/content-listing.js +3 -0
  19. package/dist/content-reader.d.ts +25 -1
  20. package/dist/content-reader.js +15 -0
  21. package/dist/content-store.d.ts +50 -1
  22. package/dist/content-store.js +102 -4
  23. package/dist/context.d.ts +36 -4
  24. package/dist/context.js +15 -4
  25. package/dist/editor/admin/SystemHealthPanel.js +2 -2
  26. package/dist/entry-schema.d.ts +22 -0
  27. package/dist/git-manager.d.ts +9 -8
  28. package/dist/git-manager.js +35 -9
  29. package/dist/github-service.d.ts +1 -1
  30. package/dist/github-service.js +1 -1
  31. package/dist/paths/branch-name.d.ts +1 -1
  32. package/dist/paths/branch-name.js +1 -1
  33. package/dist/schema/index.d.ts +1 -1
  34. package/dist/schema/index.js +1 -1
  35. package/dist/schema/meta-loader.js +1 -1
  36. package/dist/services.d.ts +22 -0
  37. package/dist/types.d.ts +1 -1
  38. package/dist/url-exclusivity-fixtures.d.ts +76 -0
  39. package/dist/url-exclusivity-fixtures.js +119 -0
  40. package/dist/url-path-resolver.js +8 -4
  41. package/dist/utils/content-write-lock.d.ts +2 -1
  42. package/dist/utils/content-write-lock.js +2 -1
  43. package/dist/utils/error.d.ts +17 -0
  44. package/dist/utils/error.js +17 -0
  45. package/dist/utils/git.d.ts +1 -1
  46. package/dist/utils/git.js +1 -1
  47. package/dist/utils/occ-json-write.js +1 -1
  48. package/dist/worker/cms-worker.d.ts +22 -288
  49. package/dist/worker/cms-worker.js +118 -1995
  50. package/dist/worker/git-sync.d.ts +166 -0
  51. package/dist/worker/git-sync.js +554 -0
  52. package/dist/worker/history-rewrite.d.ts +129 -0
  53. package/dist/worker/history-rewrite.js +216 -0
  54. package/dist/worker/rebase.d.ts +171 -0
  55. package/dist/worker/rebase.js +859 -0
  56. package/dist/worker/task-runner.d.ts +93 -0
  57. package/dist/worker/task-runner.js +529 -0
  58. package/dist/worker/worker-context.d.ts +133 -0
  59. package/dist/worker/worker-context.js +1 -0
  60. package/package.json +1 -1
@@ -0,0 +1,166 @@
1
+ import { simpleGit } from 'simple-git';
2
+ import { type RebaseContext } from './rebase.js';
3
+ import type { WorkerContext } from './worker-context.js';
4
+ /**
5
+ * The git-sync cluster: everything reachable from `CmsWorker.syncGit()`, the
6
+ * slower of the worker's two poll loops (default 5 minutes, against the task
7
+ * queue's 5 seconds).
8
+ *
9
+ * One cycle, in order: fetch every GitHub branch into the tracking namespace,
10
+ * bring `refs/heads/*` toward it non-destructively (`reconcileTrackedBranches`),
11
+ * push this deployment's own settings branch, fast-forward the base branch's
12
+ * workspace, rebase every branch that is behind it (rebase.ts), then sweep old
13
+ * tasks and expired trashed branch directories.
14
+ *
15
+ * Two of those steps are ordered by something the code enforces, and the rest
16
+ * are not ordered at all:
17
+ *
18
+ * - `pushSettingsBranches` consumes `trackedNames` from
19
+ * `reconcileTrackedBranches`, which is a DATA dependency its signature
20
+ * already enforces -- see its own comment, which says as much.
21
+ * - `runRebaseCycle` must follow `reconcileTrackedBranches`, and this is the
22
+ * one dependency nothing enforces. Branch clones fetch the base tip from
23
+ * `remote.git` (`origin`), and `reconcileTrackedBranches` is what advances
24
+ * `remote.git`'s `refs/heads/*` toward what the fetch above put in the
25
+ * tracking namespace. Reorder them and every branch rebases onto the
26
+ * PREVIOUS cycle's base tip -- not corrupting, but silently a cycle behind.
27
+ *
28
+ * `refreshBaseBranchWorkspace` and the two sweeps are order-independent.
29
+ *
30
+ * What matters more than any of that is that the whole cycle is wrapped so
31
+ * both outcomes record a worker-status.json snapshot, since an operator's only
32
+ * view of this loop is the admin panel.
33
+ */
34
+ export type GitSyncContext = Pick<WorkerContext, 'baseBranch' | 'sanitizedBaseBranch' | 'contentBranchesPath' | 'remoteGitPath' | 'taskDir' | 'taskTimeoutMs' | 'log' | 'buildGitHubUrl' | 'ensureSettingsBranch' | 'ensureStatusReport' | 'isRunning'> & RebaseContext;
35
+ /**
36
+ * Per-cycle outcome of `reconcileTrackedBranches()` -- the non-destructive
37
+ * replacement for the old `+refs/heads/*:refs/heads/*` fetch refspec (see
38
+ * `GITHUB_TRACKING_REF_PREFIX`'s doc comment for the bug this fixes). Folded
39
+ * by `syncGit()` into the worker's self-reported status
40
+ * (`WorkerStatusReport.lastGitSync.tracked`, see worker-status.ts).
41
+ */
42
+ export interface TrackedBranchSummary {
43
+ /** GitHub branches with no corresponding local `refs/heads/<name>` yet -- created at GitHub's tip. */
44
+ created: string[];
45
+ /** Local heads that were strict ancestors of GitHub's tip -- fast-forwarded to it. */
46
+ fastForwarded: string[];
47
+ /**
48
+ * Local heads AHEAD of GitHub's tip -- unpushed editor/settings work.
49
+ * Deliberately left untouched; this is exactly what the old refspec used
50
+ * to force-rewind or (with --prune) delete outright.
51
+ */
52
+ ahead: string[];
53
+ /**
54
+ * Local heads that diverged from GitHub's tip (neither side is an
55
+ * ancestor of the other) -- left untouched and logged. A real collision
56
+ * (e.g. another deployment moved the same branch name); the next push
57
+ * attempt will be rejected non-fast-forward, which is the correct,
58
+ * visible outcome.
59
+ */
60
+ diverged: string[];
61
+ /**
62
+ * Local heads that diverged from GitHub's tip because THIS worker's rebase
63
+ * loop rewrote them and published the rewrite into `remote.git`, with the
64
+ * GitHub push still outstanding (`BranchMetadata.historyRewrittenFrom` is
65
+ * set). Structurally identical to `diverged` at the ref level, but a known,
66
+ * self-resolving state rather than a cross-deployment collision -- kept in
67
+ * its own bucket so the collision warning stays meaningful.
68
+ */
69
+ rewritten: string[];
70
+ }
71
+ /**
72
+ * Push THIS deployment's own settings branch (`ensureSettingsBranch()`) from
73
+ * remote.git to GitHub. Non-fatal: a no-op push for an up-to-date branch
74
+ * just succeeds quietly.
75
+ *
76
+ * Deliberately narrowed to one branch — this used to push EVERY local
77
+ * branch matching `canopycms-settings-*`. With the tracking-namespace fetch
78
+ * fix (GITHUB_TRACKING_REF_PREFIX), `reconcileTrackedBranches` creates local
79
+ * heads for branches that exist on GitHub, so ANOTHER deployment's settings
80
+ * branch (sharing this same GitHub repo) can legitimately show up as a
81
+ * local head here too. Pushing it would be this deployment shipping
82
+ * settings state it doesn't own.
83
+ */
84
+ export declare function pushSettingsBranches(ctx: GitSyncContext, git: ReturnType<typeof simpleGit>, trackedNames: ReadonlySet<string>): Promise<void>;
85
+ /**
86
+ * Bring `refs/heads/*` in `remote.git` toward what was just fetched into
87
+ * `GITHUB_TRACKING_REF_PREFIX` -- WITHOUT ever force-rewinding or deleting
88
+ * a local head. This is the non-destructive replacement for what the old
89
+ * `+refs/heads/*:refs/heads/*` fetch refspec used to do implicitly (and
90
+ * destructively) as part of the fetch itself; see
91
+ * `GITHUB_TRACKING_REF_PREFIX`'s doc comment for the two failure modes
92
+ * that refspec caused.
93
+ *
94
+ * Per tracked branch:
95
+ * - no local `refs/heads/<name>` yet -> create it at the tracked commit
96
+ * (a branch created on GitHub, or by another deployment sharing this
97
+ * GitHub repo, becomes visible locally).
98
+ * - local is a strict ancestor of tracked (behind) -> fast-forward it.
99
+ * - local === tracked -> nothing to do.
100
+ * - tracked is a strict ancestor of local (ahead) -> LEAVE IT ALONE. This
101
+ * is unpushed editor/settings work; the queued push task (or
102
+ * pushSettingsBranches) ships it. This is exactly the branch state the
103
+ * old refspec used to destroy.
104
+ * - neither is an ancestor of the other (diverged) -> LEAVE IT ALONE and
105
+ * count/log it. A real collision (e.g. another deployment moved the
106
+ * same branch name on GitHub); the next push attempt will be rejected
107
+ * non-fast-forward, which is the correct, visible outcome -- this
108
+ * method must never silently pick a winner. The one expected, benign
109
+ * form of this -- our own rebase loop having published a rewrite into
110
+ * remote.git with the GitHub push still queued -- is split out into the
111
+ * `rewritten` bucket so the collision warning stays meaningful.
112
+ *
113
+ * Never deletes a local head: a branch removed on GitHub simply stops
114
+ * being tracked here; the local ref persists until removed through its
115
+ * own explicit path (the sync loop must not be one of them).
116
+ *
117
+ * `remote.git` is bare, so there is no worktree to invalidate by moving
118
+ * these refs -- unlike a non-bare repo, updating the ref that happens to
119
+ * be "checked out" is a non-issue here.
120
+ *
121
+ * Concurrency: `remote.git` is bare and on EFS, and the Lambda pushes into
122
+ * it concurrently (`GitManager.push()`'s `target:target` refspec) while
123
+ * this runs. Every `update-ref` below passes the expected old value (the
124
+ * all-zeros OID for "must not exist yet" on creation, the previously-read
125
+ * SHA for the fast-forward case) so a concurrent Lambda write landing in
126
+ * the gap between the read and the write loses the ref update instead of
127
+ * being silently clobbered -- the branch is simply revisited next cycle.
128
+ */
129
+ export declare function reconcileTrackedBranches(ctx: GitSyncContext, git: ReturnType<typeof simpleGit>): Promise<{
130
+ summary: TrackedBranchSummary;
131
+ trackedNames: Set<string>;
132
+ }>;
133
+ export declare function syncGit(ctx: GitSyncContext): Promise<void>;
134
+ /**
135
+ * [C1] Remove `.trash-*` branch directories (created by the admin purge
136
+ * action, api/admin-branch-health.ts) whose name-embedded stamp is older
137
+ * than {@link TRASH_RETENTION_MS}. Names that don't match the expected
138
+ * `.trash-{dirName}-{STAMP}` shape, or whose stamp fails to parse, are
139
+ * left alone (logged once per cycle, not per file, to avoid flooding logs
140
+ * if something odd accumulates) -- purge is the only writer of this
141
+ * naming scheme, so an unparseable name is unexpected and worth a human
142
+ * looking rather than a silent skip.
143
+ */
144
+ export declare function cleanupTrashedBranchDirs(ctx: Pick<GitSyncContext, 'contentBranchesPath'>): Promise<number>;
145
+ /**
146
+ * Fast-forward the base branch's own working-tree clone
147
+ * (content-branches/<baseBranch>) to match origin/<baseBranch>.
148
+ *
149
+ * Previously this clone was refreshed only incidentally, by the generic
150
+ * rebase loop below (rebaseActiveBranches): for a branch with status
151
+ * 'editing', rebasing onto origin/<baseBranch> degenerates to a
152
+ * fast-forward when the clone IS the base branch. But that loop's skip
153
+ * paths -- a dirty tree, a missing .git -- are silent, which is the
154
+ * suspected live failure mode: a wedged base clone with no diagnosable
155
+ * signal in the logs. This dedicated step makes the refresh explicit,
156
+ * ff-only, and loud, so a stuck base view (an editor forking a new branch
157
+ * "from base" that's actually a stale snapshot) is diagnosable from logs.
158
+ * This runs every sync cycle so the drift window is bounded by
159
+ * gitSyncInterval.
160
+ *
161
+ * ff-only on purpose: this clone must stay a linear mirror of
162
+ * origin/<baseBranch>, so a merge that isn't a fast-forward (diverged
163
+ * local history) is treated as a should-never-happen condition and left
164
+ * untouched rather than force-resolved.
165
+ */
166
+ export declare function refreshBaseBranchWorkspace(ctx: GitSyncContext): Promise<void>;