canopycms 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +326 -48
- package/dist/api/admin.d.ts +4 -4
- package/dist/api/branch-review.d.ts +20 -2
- package/dist/api/branch-review.js +153 -32
- package/dist/api/branch-status.d.ts +23 -2
- package/dist/api/branch-status.js +16 -2
- package/dist/api/branch-update-from-base.d.ts +23 -0
- package/dist/api/branch-update-from-base.js +227 -0
- package/dist/api/branch.d.ts +10 -0
- package/dist/api/branch.js +28 -2
- package/dist/api/client.d.ts +15 -2
- package/dist/api/client.js +24 -2
- package/dist/api/comments.d.ts +2 -2
- package/dist/api/content.d.ts +38 -1
- package/dist/api/content.js +41 -5
- package/dist/api/merge-conflicts.d.ts +235 -0
- package/dist/api/merge-conflicts.js +709 -0
- package/dist/api/routes.js +2 -0
- package/dist/api/types.d.ts +8 -3
- package/dist/base-update.d.ts +32 -0
- package/dist/base-update.js +31 -0
- package/dist/branch-metadata-file.d.ts +66 -0
- package/dist/branch-metadata-file.js +16 -1
- package/dist/branch-schema-cache.d.ts +12 -0
- package/dist/branch-schema-cache.js +27 -2
- package/dist/cli/backfill-item-keys.d.ts +77 -0
- package/dist/cli/backfill-item-keys.js +320 -0
- package/dist/cli/cli.d.ts +1 -1
- package/dist/cli/cli.js +9798 -1509
- package/dist/cli/generate-ai-content.js +373 -249
- package/dist/cli/github-app-manifest.d.ts +4 -1
- package/dist/cli/github-app-manifest.js +5 -1
- package/dist/cli/init.js +12 -0
- package/dist/cli/site-config.d.ts +14 -2
- package/dist/cli/site-config.js +20 -4
- package/dist/comment-store.d.ts +8 -0
- package/dist/comment-store.js +28 -0
- package/dist/content-index-generation.d.ts +1 -4
- package/dist/content-index-generation.js +1 -4
- package/dist/content-store.d.ts +39 -3
- package/dist/content-store.js +48 -15
- package/dist/editor/BranchManager.d.ts +4 -1
- package/dist/editor/BranchManager.js +9 -5
- package/dist/editor/Editor.js +462 -62
- package/dist/editor/EntryNavigator.d.ts +10 -2
- package/dist/editor/EntryNavigator.js +12 -4
- package/dist/editor/FormRenderer.d.ts +8 -2
- package/dist/editor/FormRenderer.js +5 -6
- package/dist/editor/admin/ConflictsStatusAlert.d.ts +8 -0
- package/dist/editor/admin/ConflictsStatusAlert.js +22 -0
- package/dist/editor/admin/SystemHealthPanel.js +4 -3
- package/dist/editor/base-update-wait.d.ts +78 -0
- package/dist/editor/base-update-wait.js +98 -0
- package/dist/editor/branch-status.js +1 -1
- package/dist/editor/components/BaseUpdateNotice.d.ts +12 -0
- package/dist/editor/components/BaseUpdateNotice.js +24 -0
- package/dist/editor/components/BranchPill.d.ts +4 -1
- package/dist/editor/components/BranchPill.js +5 -2
- package/dist/editor/components/ConflictsGroup.d.ts +112 -0
- package/dist/editor/components/ConflictsGroup.js +404 -0
- package/dist/editor/components/EditorHeader.d.ts +18 -1
- package/dist/editor/components/EditorHeader.js +99 -33
- package/dist/editor/components/EntryChangesDrawer.d.ts +16 -2
- package/dist/editor/components/EntryChangesDrawer.js +85 -167
- package/dist/editor/components/EntryHeader.d.ts +3 -1
- package/dist/editor/components/EntryHeader.js +2 -2
- package/dist/editor/components/GetLatestChangesButton.d.ts +15 -0
- package/dist/editor/components/GetLatestChangesButton.js +29 -0
- package/dist/editor/components/ReleaseHoldButton.d.ts +20 -0
- package/dist/editor/components/ReleaseHoldButton.js +36 -0
- package/dist/editor/components/RetryToastMessage.js +1 -1
- package/dist/editor/components/change-parts.d.ts +51 -0
- package/dist/editor/components/change-parts.js +171 -0
- package/dist/editor/conflict-cards.d.ts +93 -0
- package/dist/editor/conflict-cards.js +284 -0
- package/dist/editor/conflict-choices.d.ts +60 -0
- package/dist/editor/conflict-choices.js +108 -0
- package/dist/editor/conflict-notice.d.ts +25 -0
- package/dist/editor/conflict-notice.js +39 -0
- package/dist/editor/copy.d.ts +12 -0
- package/dist/editor/copy.js +12 -0
- package/dist/editor/entry-diff.d.ts +2 -1
- package/dist/editor/entry-diff.js +5 -21
- package/dist/editor/hooks/useBaseUpdate.d.ts +38 -0
- package/dist/editor/hooks/useBaseUpdate.js +279 -0
- package/dist/editor/hooks/useBranchChangesData.d.ts +7 -1
- package/dist/editor/hooks/useBranchChangesData.js +13 -0
- package/dist/editor/hooks/useBranchManager.d.ts +8 -1
- package/dist/editor/hooks/useBranchManager.js +108 -13
- package/dist/editor/hooks/useBranchesData.js +3 -0
- package/dist/editor/hooks/useConflictChoices.d.ts +24 -0
- package/dist/editor/hooks/useConflictChoices.js +75 -0
- package/dist/editor/hooks/useConflictsData.d.ts +26 -0
- package/dist/editor/hooks/useConflictsData.js +89 -0
- package/dist/editor/hooks/useDraftManager.d.ts +13 -1
- package/dist/editor/hooks/useDraftManager.js +40 -9
- package/dist/editor/hooks/useEntryManager.d.ts +17 -3
- package/dist/editor/hooks/useEntryManager.js +11 -5
- package/dist/editor/release-hold.d.ts +12 -0
- package/dist/editor/release-hold.js +39 -0
- package/dist/editor/server-clock.d.ts +4 -0
- package/dist/editor/server-clock.js +15 -0
- package/dist/editor/undo-change.d.ts +7 -0
- package/dist/editor/undo-change.js +19 -4
- package/dist/git-manager.d.ts +6 -0
- package/dist/git-manager.js +38 -3
- package/dist/item-keys.d.ts +13 -0
- package/dist/item-keys.js +36 -14
- package/dist/merge/conflict-path.d.ts +40 -0
- package/dist/merge/conflict-path.js +128 -0
- package/dist/merge/diff3.d.ts +27 -0
- package/dist/merge/diff3.js +113 -0
- package/dist/merge/entry-merge.d.ts +94 -0
- package/dist/merge/entry-merge.js +876 -0
- package/dist/merge/list-alignment.d.ts +55 -0
- package/dist/merge/list-alignment.js +490 -0
- package/dist/merge/rekey.d.ts +11 -0
- package/dist/merge/rekey.js +75 -0
- package/dist/merge/text-merge.d.ts +29 -0
- package/dist/merge/text-merge.js +59 -0
- package/dist/merge/value-equality.d.ts +18 -0
- package/dist/merge/value-equality.js +50 -0
- package/dist/merge-conflicts.d.ts +232 -0
- package/dist/merge-conflicts.js +451 -0
- package/dist/operating-mode/client-safe-strategy.d.ts +2 -0
- package/dist/operating-mode/client-safe-strategy.js +6 -0
- package/dist/operating-mode/client-unsafe-strategy.js +6 -0
- package/dist/operating-mode/types.d.ts +13 -0
- package/dist/services.d.ts +24 -1
- package/dist/services.js +138 -22
- package/dist/task-queue/cms-task-queue.d.ts +3 -1
- package/dist/types.d.ts +63 -5
- package/dist/utils/content-serialize.d.ts +18 -1
- package/dist/utils/content-serialize.js +12 -0
- package/dist/utils/content-write-lock.d.ts +3 -3
- package/dist/utils/content-write-lock.js +3 -3
- package/dist/utils/git.d.ts +7 -0
- package/dist/utils/git.js +35 -0
- package/dist/utils/markdown-body-splice.d.ts +1 -1
- package/dist/utils/markdown-body-splice.js +1 -19
- package/dist/utils/markdown-parse.d.ts +9 -0
- package/dist/utils/markdown-parse.js +27 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/branch-merge.d.ts +80 -40
- package/dist/worker/branch-merge.js +244 -58
- package/dist/worker/branch-policy.d.ts +18 -2
- package/dist/worker/branch-policy.js +36 -0
- package/dist/worker/cms-worker.d.ts +37 -3
- package/dist/worker/cms-worker.js +88 -16
- package/dist/worker/conflicts-status.d.ts +20 -0
- package/dist/worker/conflicts-status.js +204 -0
- package/dist/worker/field-merge.d.ts +80 -0
- package/dist/worker/field-merge.js +729 -0
- package/dist/worker/git-sync.d.ts +3 -2
- package/dist/worker/git-sync.js +16 -6
- package/dist/worker/github-commits.d.ts +42 -10
- package/dist/worker/github-commits.js +444 -52
- package/dist/worker/github-errors.d.ts +15 -0
- package/dist/worker/github-errors.js +47 -0
- package/dist/worker/github-gateway-http.js +24 -9
- package/dist/worker/github-gateway-wire.d.ts +49 -20
- package/dist/worker/github-gateway-wire.js +19 -3
- package/dist/worker/github-gateway.d.ts +27 -1
- package/dist/worker/github-gateway.js +55 -7
- package/dist/worker/github-transfer.d.ts +5 -3
- package/dist/worker/github-transfer.js +8 -5
- package/dist/worker/history-rewrite.d.ts +40 -8
- package/dist/worker/history-rewrite.js +51 -23
- package/dist/worker/publish-decision.d.ts +37 -0
- package/dist/worker/publish-decision.js +16 -0
- package/dist/worker/rebase.d.ts +29 -8
- package/dist/worker/rebase.js +492 -211
- package/dist/worker/task-runner.d.ts +1 -1
- package/dist/worker/task-runner.js +30 -41
- package/dist/worker/worker-context.d.ts +24 -2
- package/package.json +1 -1
package/dist/api/routes.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { BRANCH_ROUTES } from './branch.js';
|
|
8
8
|
import { BRANCH_CHANGES_ROUTES } from './branch-changes.js';
|
|
9
9
|
import { BRANCH_ENTRY_CHANGE_ROUTES } from './branch-entry-changes.js';
|
|
10
|
+
import { MERGE_CONFLICT_ROUTES } from './merge-conflicts.js';
|
|
10
11
|
import { WORKFLOW_ROUTES } from './branch-status.js';
|
|
11
12
|
import { COMMENT_ROUTES } from './comments.js';
|
|
12
13
|
import { CONTENT_ROUTES } from './content.js';
|
|
@@ -28,6 +29,7 @@ export function buildCanopyRoutes() {
|
|
|
28
29
|
...Object.values(BRANCH_ROUTES),
|
|
29
30
|
...Object.values(BRANCH_CHANGES_ROUTES),
|
|
30
31
|
...Object.values(BRANCH_ENTRY_CHANGE_ROUTES),
|
|
32
|
+
...Object.values(MERGE_CONFLICT_ROUTES),
|
|
31
33
|
...Object.values(WORKFLOW_ROUTES),
|
|
32
34
|
...Object.values(COMMENT_ROUTES),
|
|
33
35
|
...Object.values(CONTENT_ROUTES),
|
package/dist/api/types.d.ts
CHANGED
|
@@ -58,7 +58,8 @@ export interface ApiResponse<TData = unknown> {
|
|
|
58
58
|
* `WRITE_OUTCOME_UNKNOWN`: an entry save ran but lost its branch lock mid-write, so it may have
|
|
59
59
|
* landed (a 409); the version the caller holds may be stale, so it must re-read before retrying.
|
|
60
60
|
* `ENTRY_GONE`: an entry update found no file, because the entry was deleted or moved since the
|
|
61
|
-
* caller read it (a 409); it was not saved, and only a create can bring the entry back.
|
|
61
|
+
* caller read it (a 409); it was not saved, and only a create can bring the entry back. A
|
|
62
|
+
* resolve-entry answers it too, for an entry no longer where its conflict was shown.
|
|
62
63
|
* `EDITOR_MODE_MISMATCH`: the editor bundle was built for the other operating mode than the
|
|
63
64
|
* server runs (a 412, see operating-mode/editor-mode-check.ts); only a rebuild fixes it.
|
|
64
65
|
* `SETTINGS_CHANGED`: a groups or permissions save named a version the file has moved on from (a
|
|
@@ -66,6 +67,10 @@ export interface ApiResponse<TData = unknown> {
|
|
|
66
67
|
* The other settings 409, a busy file lock, carries no code. `BRANCH_SYNCING`: an entry save was
|
|
67
68
|
* refused because the branch is mid-rebase (a 409); nothing was saved, and a retry soon can pass.
|
|
68
69
|
* `BRANCH_NAME_REFUSED`: a branch create was refused for the name it gave (a 400 or 409), so only
|
|
69
|
-
* another name can pass; no other create failure carries it.
|
|
70
|
+
* another name can pass; no other create failure carries it. `OPEN_CONFLICTS`: a submit or approval
|
|
71
|
+
* was refused while a merge's conflicts are open on the branch (a 409, merge-conflicts.ts).
|
|
72
|
+
* `CONFLICT_CHANGED`: a resolve-entry named a conflict the record no longer holds as shown (a 409);
|
|
73
|
+
* re-read the branch's conflicts. `BRANCH_MOVED`: an approval or a request for changes named a commit
|
|
74
|
+
* other than the branch's head, or none (a 409, api/branch-review.ts); reload the branch first.
|
|
70
75
|
*/
|
|
71
|
-
export type ApiErrorCode = 'SCHEMA_UNAVAILABLE' | 'WORKER_FAILED' | 'WRITE_OUTCOME_UNKNOWN' | 'ENTRY_GONE' | 'EDITOR_MODE_MISMATCH' | 'SETTINGS_CHANGED' | 'BRANCH_SYNCING' | 'BRANCH_NAME_REFUSED';
|
|
76
|
+
export type ApiErrorCode = 'SCHEMA_UNAVAILABLE' | 'WORKER_FAILED' | 'WRITE_OUTCOME_UNKNOWN' | 'ENTRY_GONE' | 'EDITOR_MODE_MISMATCH' | 'SETTINGS_CHANGED' | 'BRANCH_SYNCING' | 'BRANCH_NAME_REFUSED' | 'OPEN_CONFLICTS' | 'CONFLICT_CHANGED' | 'BRANCH_MOVED';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { BranchMetadata } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Asking for the latest changes: the API records the request on branch.json and queues this action
|
|
4
|
+
* (api/branch-update-from-base.ts), the worker's sync answers it in `lastBaseUpdate`
|
|
5
|
+
* (worker/rebase.ts), and the editor waits while it stands. Dependency-free, so all three share it.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SYNC_BRANCH_ACTION = "sync-branch";
|
|
8
|
+
/**
|
|
9
|
+
* How long a request, or an answer that didn't fail, stands for the next request on the branch,
|
|
10
|
+
* which joins it instead (api/branch-update-from-base.ts).
|
|
11
|
+
*/
|
|
12
|
+
export declare const BASE_UPDATE_REQUEST_INTERVAL_MS = 30000;
|
|
13
|
+
/** How long the editor waits on a request, polling for its answer. */
|
|
14
|
+
export declare const BASE_UPDATE_GIVE_UP_MS: number;
|
|
15
|
+
/**
|
|
16
|
+
* How long the worker asks itself for early cycles while a request goes unanswered; its regular
|
|
17
|
+
* cycles answer it after that. Each cycle reads every branch in review from GitHub, so it is short.
|
|
18
|
+
*/
|
|
19
|
+
export declare const BASE_UPDATE_EARLY_RETRY_MS: number;
|
|
20
|
+
/**
|
|
21
|
+
* How far in the future a request time may lie and still count: the Lambdas' clocks keep NTP time,
|
|
22
|
+
* so a later one is corrupt and is never waited on.
|
|
23
|
+
*/
|
|
24
|
+
export declare const BASE_UPDATE_CLOCK_SKEW_MS = 60000;
|
|
25
|
+
/**
|
|
26
|
+
* How a run in the request (operating-mode `baseUpdateRunsInRequest`) went: it answered the
|
|
27
|
+
* request; a lock held it back; or the branch clones from a remote other than the workspace's
|
|
28
|
+
* `remote.git`, the only one such a run fetches.
|
|
29
|
+
*/
|
|
30
|
+
export type BaseUpdateRun = 'answered' | 'busy' | 'other-remote';
|
|
31
|
+
/** Whether the branch's latest request for the latest changes is still unanswered. */
|
|
32
|
+
export declare function isBaseUpdatePending(branch: Pick<BranchMetadata, 'baseUpdateRequestedAt' | 'lastBaseUpdate'>): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asking for the latest changes: the API records the request on branch.json and queues this action
|
|
3
|
+
* (api/branch-update-from-base.ts), the worker's sync answers it in `lastBaseUpdate`
|
|
4
|
+
* (worker/rebase.ts), and the editor waits while it stands. Dependency-free, so all three share it.
|
|
5
|
+
*/
|
|
6
|
+
export const SYNC_BRANCH_ACTION = 'sync-branch';
|
|
7
|
+
/**
|
|
8
|
+
* How long a request, or an answer that didn't fail, stands for the next request on the branch,
|
|
9
|
+
* which joins it instead (api/branch-update-from-base.ts).
|
|
10
|
+
*/
|
|
11
|
+
export const BASE_UPDATE_REQUEST_INTERVAL_MS = 30_000;
|
|
12
|
+
/** How long the editor waits on a request, polling for its answer. */
|
|
13
|
+
export const BASE_UPDATE_GIVE_UP_MS = 10 * 60_000;
|
|
14
|
+
/**
|
|
15
|
+
* How long the worker asks itself for early cycles while a request goes unanswered; its regular
|
|
16
|
+
* cycles answer it after that. Each cycle reads every branch in review from GitHub, so it is short.
|
|
17
|
+
*/
|
|
18
|
+
export const BASE_UPDATE_EARLY_RETRY_MS = 2 * 60_000;
|
|
19
|
+
/**
|
|
20
|
+
* How far in the future a request time may lie and still count: the Lambdas' clocks keep NTP time,
|
|
21
|
+
* so a later one is corrupt and is never waited on.
|
|
22
|
+
*/
|
|
23
|
+
export const BASE_UPDATE_CLOCK_SKEW_MS = 60_000;
|
|
24
|
+
/** Whether the branch's latest request for the latest changes is still unanswered. */
|
|
25
|
+
export function isBaseUpdatePending(branch) {
|
|
26
|
+
const requestedAt = branch.baseUpdateRequestedAt;
|
|
27
|
+
if (requestedAt === undefined)
|
|
28
|
+
return false;
|
|
29
|
+
const answeredAt = branch.lastBaseUpdate?.at;
|
|
30
|
+
return answeredAt === undefined || Date.parse(answeredAt) < Date.parse(requestedAt);
|
|
31
|
+
}
|
|
@@ -83,6 +83,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
83
83
|
lastAt: string;
|
|
84
84
|
}>>;
|
|
85
85
|
historyRewrittenFrom: z.ZodOptional<z.ZodString>;
|
|
86
|
+
historyRewrittenTo: z.ZodOptional<z.ZodString>;
|
|
86
87
|
lastGitHubTip: z.ZodOptional<z.ZodString>;
|
|
87
88
|
keptOverGitHub: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
88
89
|
rewrittenOnGitHub: z.ZodOptional<z.ZodString>;
|
|
@@ -121,6 +122,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
121
122
|
name?: string | undefined;
|
|
122
123
|
}>;
|
|
123
124
|
at: z.ZodString;
|
|
125
|
+
commit: z.ZodOptional<z.ZodString>;
|
|
124
126
|
}, "strip", z.ZodTypeAny, {
|
|
125
127
|
at: string;
|
|
126
128
|
state: "approved" | "changes-requested";
|
|
@@ -128,6 +130,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
128
130
|
userId: string;
|
|
129
131
|
name?: string | undefined;
|
|
130
132
|
};
|
|
133
|
+
commit?: string | undefined;
|
|
131
134
|
}, {
|
|
132
135
|
at: string;
|
|
133
136
|
state: "approved" | "changes-requested";
|
|
@@ -135,6 +138,25 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
135
138
|
userId: string;
|
|
136
139
|
name?: string | undefined;
|
|
137
140
|
};
|
|
141
|
+
commit?: string | undefined;
|
|
142
|
+
}>>>;
|
|
143
|
+
baseUpdateRequestedAt: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
144
|
+
latestChangesWaiting: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
145
|
+
lastBaseUpdate: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
146
|
+
at: z.ZodString;
|
|
147
|
+
outcome: z.ZodEnum<["merged", "up-to-date", "conflicts", "failed"]>;
|
|
148
|
+
baseSha: z.ZodOptional<z.ZodString>;
|
|
149
|
+
keptEntries: z.ZodOptional<z.ZodNumber>;
|
|
150
|
+
}, "strip", z.ZodTypeAny, {
|
|
151
|
+
at: string;
|
|
152
|
+
outcome: "merged" | "conflicts" | "up-to-date" | "failed";
|
|
153
|
+
baseSha?: string | undefined;
|
|
154
|
+
keptEntries?: number | undefined;
|
|
155
|
+
}, {
|
|
156
|
+
at: string;
|
|
157
|
+
outcome: "merged" | "conflicts" | "up-to-date" | "failed";
|
|
158
|
+
baseSha?: string | undefined;
|
|
159
|
+
keptEntries?: number | undefined;
|
|
138
160
|
}>>>;
|
|
139
161
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
140
162
|
name: z.ZodString;
|
|
@@ -199,6 +221,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
199
221
|
lastAt: string;
|
|
200
222
|
}>>;
|
|
201
223
|
historyRewrittenFrom: z.ZodOptional<z.ZodString>;
|
|
224
|
+
historyRewrittenTo: z.ZodOptional<z.ZodString>;
|
|
202
225
|
lastGitHubTip: z.ZodOptional<z.ZodString>;
|
|
203
226
|
keptOverGitHub: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
204
227
|
rewrittenOnGitHub: z.ZodOptional<z.ZodString>;
|
|
@@ -237,6 +260,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
237
260
|
name?: string | undefined;
|
|
238
261
|
}>;
|
|
239
262
|
at: z.ZodString;
|
|
263
|
+
commit: z.ZodOptional<z.ZodString>;
|
|
240
264
|
}, "strip", z.ZodTypeAny, {
|
|
241
265
|
at: string;
|
|
242
266
|
state: "approved" | "changes-requested";
|
|
@@ -244,6 +268,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
244
268
|
userId: string;
|
|
245
269
|
name?: string | undefined;
|
|
246
270
|
};
|
|
271
|
+
commit?: string | undefined;
|
|
247
272
|
}, {
|
|
248
273
|
at: string;
|
|
249
274
|
state: "approved" | "changes-requested";
|
|
@@ -251,6 +276,25 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
251
276
|
userId: string;
|
|
252
277
|
name?: string | undefined;
|
|
253
278
|
};
|
|
279
|
+
commit?: string | undefined;
|
|
280
|
+
}>>>;
|
|
281
|
+
baseUpdateRequestedAt: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
282
|
+
latestChangesWaiting: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
283
|
+
lastBaseUpdate: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
284
|
+
at: z.ZodString;
|
|
285
|
+
outcome: z.ZodEnum<["merged", "up-to-date", "conflicts", "failed"]>;
|
|
286
|
+
baseSha: z.ZodOptional<z.ZodString>;
|
|
287
|
+
keptEntries: z.ZodOptional<z.ZodNumber>;
|
|
288
|
+
}, "strip", z.ZodTypeAny, {
|
|
289
|
+
at: string;
|
|
290
|
+
outcome: "merged" | "conflicts" | "up-to-date" | "failed";
|
|
291
|
+
baseSha?: string | undefined;
|
|
292
|
+
keptEntries?: number | undefined;
|
|
293
|
+
}, {
|
|
294
|
+
at: string;
|
|
295
|
+
outcome: "merged" | "conflicts" | "up-to-date" | "failed";
|
|
296
|
+
baseSha?: string | undefined;
|
|
297
|
+
keptEntries?: number | undefined;
|
|
254
298
|
}>>>;
|
|
255
299
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
256
300
|
name: z.ZodString;
|
|
@@ -315,6 +359,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
315
359
|
lastAt: string;
|
|
316
360
|
}>>;
|
|
317
361
|
historyRewrittenFrom: z.ZodOptional<z.ZodString>;
|
|
362
|
+
historyRewrittenTo: z.ZodOptional<z.ZodString>;
|
|
318
363
|
lastGitHubTip: z.ZodOptional<z.ZodString>;
|
|
319
364
|
keptOverGitHub: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
320
365
|
rewrittenOnGitHub: z.ZodOptional<z.ZodString>;
|
|
@@ -353,6 +398,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
353
398
|
name?: string | undefined;
|
|
354
399
|
}>;
|
|
355
400
|
at: z.ZodString;
|
|
401
|
+
commit: z.ZodOptional<z.ZodString>;
|
|
356
402
|
}, "strip", z.ZodTypeAny, {
|
|
357
403
|
at: string;
|
|
358
404
|
state: "approved" | "changes-requested";
|
|
@@ -360,6 +406,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
360
406
|
userId: string;
|
|
361
407
|
name?: string | undefined;
|
|
362
408
|
};
|
|
409
|
+
commit?: string | undefined;
|
|
363
410
|
}, {
|
|
364
411
|
at: string;
|
|
365
412
|
state: "approved" | "changes-requested";
|
|
@@ -367,6 +414,25 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
367
414
|
userId: string;
|
|
368
415
|
name?: string | undefined;
|
|
369
416
|
};
|
|
417
|
+
commit?: string | undefined;
|
|
418
|
+
}>>>;
|
|
419
|
+
baseUpdateRequestedAt: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
420
|
+
latestChangesWaiting: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
421
|
+
lastBaseUpdate: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
422
|
+
at: z.ZodString;
|
|
423
|
+
outcome: z.ZodEnum<["merged", "up-to-date", "conflicts", "failed"]>;
|
|
424
|
+
baseSha: z.ZodOptional<z.ZodString>;
|
|
425
|
+
keptEntries: z.ZodOptional<z.ZodNumber>;
|
|
426
|
+
}, "strip", z.ZodTypeAny, {
|
|
427
|
+
at: string;
|
|
428
|
+
outcome: "merged" | "conflicts" | "up-to-date" | "failed";
|
|
429
|
+
baseSha?: string | undefined;
|
|
430
|
+
keptEntries?: number | undefined;
|
|
431
|
+
}, {
|
|
432
|
+
at: string;
|
|
433
|
+
outcome: "merged" | "conflicts" | "up-to-date" | "failed";
|
|
434
|
+
baseSha?: string | undefined;
|
|
435
|
+
keptEntries?: number | undefined;
|
|
370
436
|
}>>>;
|
|
371
437
|
}, z.ZodTypeAny, "passthrough">>;
|
|
372
438
|
/**
|
|
@@ -82,6 +82,7 @@ const branchMetadataShape = {
|
|
|
82
82
|
.object({ message: z.string(), firstAt: z.string(), lastAt: z.string() })
|
|
83
83
|
.optional(),
|
|
84
84
|
historyRewrittenFrom: z.string().optional(),
|
|
85
|
+
historyRewrittenTo: z.string().optional(),
|
|
85
86
|
lastGitHubTip: z.string().optional(),
|
|
86
87
|
keptOverGitHub: z.array(z.string()).optional(),
|
|
87
88
|
rewrittenOnGitHub: z.string().optional(),
|
|
@@ -99,12 +100,26 @@ const branchMetadataShape = {
|
|
|
99
100
|
])
|
|
100
101
|
.optional()
|
|
101
102
|
.catch(undefined),
|
|
102
|
-
// Dropped when malformed: it
|
|
103
|
+
// Dropped when malformed, never failing a read: it decorates the PR body, and without `commit`
|
|
104
|
+
// the worker judges a move against the clone's head before it (worker/github-commits.ts).
|
|
103
105
|
review: z
|
|
104
106
|
.object({
|
|
105
107
|
state: z.enum(['approved', 'changes-requested']),
|
|
106
108
|
by: z.object({ userId: z.string(), name: z.string().optional() }),
|
|
107
109
|
at: z.string(),
|
|
110
|
+
commit: z.string().optional(),
|
|
111
|
+
})
|
|
112
|
+
.optional()
|
|
113
|
+
.catch(undefined),
|
|
114
|
+
baseUpdateRequestedAt: z.string().optional().catch(undefined),
|
|
115
|
+
latestChangesWaiting: z.string().optional().catch(undefined),
|
|
116
|
+
// Dropped when malformed: it only ends the editor's wait, which then times out instead.
|
|
117
|
+
lastBaseUpdate: z
|
|
118
|
+
.object({
|
|
119
|
+
at: z.string(),
|
|
120
|
+
outcome: z.enum(['merged', 'up-to-date', 'conflicts', 'failed']),
|
|
121
|
+
baseSha: z.string().optional(),
|
|
122
|
+
keptEntries: z.number().int().nonnegative().optional(),
|
|
108
123
|
})
|
|
109
124
|
.optional()
|
|
110
125
|
.catch(undefined),
|
|
@@ -2,6 +2,11 @@ import type { RootCollectionConfig } from './config/index.js';
|
|
|
2
2
|
import type { FlatSchemaItem } from './config/types.js';
|
|
3
3
|
import type { OperatingMode } from './operating-mode/index.js';
|
|
4
4
|
import type { EntrySchemaRegistry, SchemaIssue, SchemaResolutionResult, UnknownSchemaPolicy } from './schema/types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Bump when BranchSchemaCacheEntry shape changes to auto-invalidate stale caches.
|
|
7
|
+
* @internal Exported for tests.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SCHEMA_CACHE_VERSION = 4;
|
|
5
10
|
/** File name of the schema cache inside {@link schemaCacheDir}. */
|
|
6
11
|
export declare const SCHEMA_CACHE_FILE = "schema-cache.json";
|
|
7
12
|
/** resource-generation.ts resource key for the schema cache's marker. */
|
|
@@ -32,6 +37,13 @@ export interface ResolvedBranchSchema {
|
|
|
32
37
|
flatSchema: FlatSchemaItem[];
|
|
33
38
|
issues: SchemaIssue[];
|
|
34
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* The entry types and collections of a branch's last persisted schema snapshot, for a reader with
|
|
42
|
+
* no entry schema registry to resolve one: the worker's merge, which names conflicts by them.
|
|
43
|
+
* Undefined when there is no snapshot of this version. Its freshness is not checked, so it can lag
|
|
44
|
+
* the tree.
|
|
45
|
+
*/
|
|
46
|
+
export declare function readSchemaSnapshot(branchRoot: string): Promise<readonly FlatSchemaItem[] | undefined>;
|
|
35
47
|
/**
|
|
36
48
|
* Per-branch schema cache: one file in {@link schemaCacheDir}, the only state
|
|
37
49
|
* shared across processes. Each process keeps the entries it parsed or wrote,
|
|
@@ -10,8 +10,11 @@ import { bumpResourceGeneration, readResourceGeneration, isGenerationCurrent, }
|
|
|
10
10
|
import { timeRequestPhase } from './utils/request-timing.js';
|
|
11
11
|
import { CANOPY_META_DIR } from './utils/git.js';
|
|
12
12
|
import { canopyLogWarn } from './utils/logger.js';
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Bump when BranchSchemaCacheEntry shape changes to auto-invalidate stale caches.
|
|
15
|
+
* @internal Exported for tests.
|
|
16
|
+
*/
|
|
17
|
+
export const SCHEMA_CACHE_VERSION = 4;
|
|
15
18
|
/** Minimum interval between mtime staleness checks (ms) */
|
|
16
19
|
const MTIME_CHECK_DEBOUNCE_MS = 1000;
|
|
17
20
|
/** Cap on the parsed snapshots one process keeps, counted in bytes of their files. */
|
|
@@ -89,6 +92,28 @@ async function schemaCacheDir(branchRoot) {
|
|
|
89
92
|
const isClone = await fs.stat(gitDir).then((stat) => stat.isDirectory(), () => false);
|
|
90
93
|
return isClone ? path.join(gitDir, 'canopycms') : path.join(branchRoot, CANOPY_META_DIR);
|
|
91
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* The entry types and collections of a branch's last persisted schema snapshot, for a reader with
|
|
97
|
+
* no entry schema registry to resolve one: the worker's merge, which names conflicts by them.
|
|
98
|
+
* Undefined when there is no snapshot of this version. Its freshness is not checked, so it can lag
|
|
99
|
+
* the tree.
|
|
100
|
+
*/
|
|
101
|
+
export async function readSchemaSnapshot(branchRoot) {
|
|
102
|
+
try {
|
|
103
|
+
const cachePath = path.join(await schemaCacheDir(branchRoot), SCHEMA_CACHE_FILE);
|
|
104
|
+
const entry = JSON.parse(await fs.readFile(cachePath, 'utf-8'));
|
|
105
|
+
if (typeof entry !== 'object' || entry === null)
|
|
106
|
+
return undefined;
|
|
107
|
+
if (!('version' in entry) || entry.version !== SCHEMA_CACHE_VERSION)
|
|
108
|
+
return undefined;
|
|
109
|
+
if (!('flatSchema' in entry) || !Array.isArray(entry.flatSchema))
|
|
110
|
+
return undefined;
|
|
111
|
+
return entry.flatSchema;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
92
117
|
/**
|
|
93
118
|
* Per-branch schema cache: one file in {@link schemaCacheDir}, the only state
|
|
94
119
|
* shared across processes. Each process keeps the entries it parsed or wrote,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: npx canopycms backfill-item-keys [--check] [--force] [--app-dir <dir>]
|
|
3
|
+
*
|
|
4
|
+
* Writes the `_key` of every object-list item and block (`item-keys.ts`) into the site's content,
|
|
5
|
+
* so an editor's first save of an entry adds no key lines and a merge sees only real edits.
|
|
6
|
+
*
|
|
7
|
+
* Each entry goes through the editor's own save path, with nothing re-derived:
|
|
8
|
+
* `ContentStore.read` keys the items exactly as the editor's form holds them, and
|
|
9
|
+
* `ContentStore.write` writes that data back with the read's version, through the serialiser,
|
|
10
|
+
* content-write lock and version check a save uses, after the content API's own
|
|
11
|
+
* `entryDataAsSaved`. So a written file is byte for byte what an editor save of the unedited entry
|
|
12
|
+
* leaves, a second run finds every item keyed and writes nothing, and the next editor save adds no
|
|
13
|
+
* lines of its own, a CRLF markdown file aside (safeinsights/canopycms#1234).
|
|
14
|
+
*
|
|
15
|
+
* A file is written only when its items lack keys. The write then also carries whatever else an
|
|
16
|
+
* unedited save normalises (a one-line JSON file comes out pretty-printed, a stray frontmatter key
|
|
17
|
+
* named as the body goes), and those files are listed apart for review. Out of scope, as for the editor: list fields the schema does not
|
|
18
|
+
* describe, `.collection.json`, and entries of a type this code's registry lacks.
|
|
19
|
+
*/
|
|
20
|
+
import type { FlatSchemaItem } from '../config/index.js';
|
|
21
|
+
/** One entry file the backfill keys. */
|
|
22
|
+
interface BackfillFileResult {
|
|
23
|
+
/** Path relative to the project root. */
|
|
24
|
+
path: string;
|
|
25
|
+
/** Items and blocks whose file text gives them no usable key, or one an earlier item holds. */
|
|
26
|
+
itemsKeyed: number;
|
|
27
|
+
/** Lines the write changes besides adding `_key` lines: what else an unedited save normalises. */
|
|
28
|
+
otherLines: number;
|
|
29
|
+
}
|
|
30
|
+
interface BackfillResult {
|
|
31
|
+
/** Files written, or with `check`, files a run would write; by path. */
|
|
32
|
+
changed: BackfillFileResult[];
|
|
33
|
+
/** Entries that could not be read or written, and collections that could not be listed. */
|
|
34
|
+
failed: {
|
|
35
|
+
path: string;
|
|
36
|
+
error: string;
|
|
37
|
+
}[];
|
|
38
|
+
/** Entries left alone because the registry has no schema for their entry type. */
|
|
39
|
+
unavailable: string[];
|
|
40
|
+
/** Files in a collection that look like entries but whose names don't parse as one. */
|
|
41
|
+
malformed: string[];
|
|
42
|
+
}
|
|
43
|
+
interface BackfillOptions {
|
|
44
|
+
/** The project root: the directory holding the content root. */
|
|
45
|
+
root: string;
|
|
46
|
+
flatSchema: FlatSchemaItem[];
|
|
47
|
+
contentRootName: string;
|
|
48
|
+
/** Report what a run would write, and write nothing. */
|
|
49
|
+
check: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Keys every entry's items under `root`, or with `check`, reports which files a run would write.
|
|
53
|
+
* @internal Exported for tests.
|
|
54
|
+
*/
|
|
55
|
+
export declare function backfillItemKeys(options: BackfillOptions): Promise<BackfillResult>;
|
|
56
|
+
export interface BackfillItemKeysCLIOptions {
|
|
57
|
+
projectDir: string;
|
|
58
|
+
/** Directory holding `schemas.ts`, relative to the project. Default `app`. */
|
|
59
|
+
appDir?: string;
|
|
60
|
+
check?: boolean;
|
|
61
|
+
/** Write even when the content tree has uncommitted changes. */
|
|
62
|
+
force?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Exit codes. `--check`: 0 when no file needs keys, 1 when some would change. A run: 0 when done,
|
|
66
|
+
* 1 when it would not write: uncommitted content changes, or no git to ask. Both: 2 when the
|
|
67
|
+
* site's config or schema could not be loaded, a collection listed, or an entry read or written.
|
|
68
|
+
* @internal Exported for tests.
|
|
69
|
+
*/
|
|
70
|
+
export declare const BACKFILL_EXIT_CODES: {
|
|
71
|
+
readonly ok: 0;
|
|
72
|
+
readonly changesOrRefused: 1;
|
|
73
|
+
readonly loadError: 2;
|
|
74
|
+
};
|
|
75
|
+
/** Runs the command and returns its exit code. */
|
|
76
|
+
export declare function backfillItemKeysCLI(options: BackfillItemKeysCLIOptions): Promise<number>;
|
|
77
|
+
export {};
|