canopycms 0.8.3 → 0.10.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 +73 -47
- package/dist/ai/json-to-markdown.js +2 -0
- package/dist/api/admin.d.ts +11 -0
- package/dist/api/admin.js +2 -0
- package/dist/api/archived-branches.d.ts +70 -0
- package/dist/api/archived-branches.js +146 -0
- package/dist/api/branch-entry-changes.js +2 -4
- package/dist/api/branch-merge.js +16 -1
- package/dist/api/branch-status.d.ts +11 -0
- package/dist/api/branch-status.js +2 -0
- package/dist/api/branch-withdraw.js +1 -1
- package/dist/api/branch.js +46 -108
- package/dist/api/client.d.ts +25 -21
- package/dist/api/client.js +46 -38
- package/dist/branch-archive.d.ts +19 -0
- package/dist/branch-archive.js +27 -0
- package/dist/branch-delete.d.ts +45 -0
- package/dist/branch-delete.js +97 -0
- package/dist/branch-metadata-file.d.ts +3 -0
- package/dist/branch-metadata-file.js +1 -0
- package/dist/branch-metadata.d.ts +6 -1
- package/dist/branch-metadata.js +15 -2
- package/dist/cli/cli.js +85 -16
- package/dist/cli/generate-ai-content.js +83 -15
- package/dist/cli/init.js +1 -1
- package/dist/config/types.d.ts +4 -4
- package/dist/config/types.js +1 -1
- package/dist/config/validation.d.ts +9 -3
- package/dist/config/validation.js +30 -5
- package/dist/content-listing.js +2 -3
- package/dist/content-store.js +5 -5
- package/dist/editor/BranchManager.d.ts +20 -0
- package/dist/editor/BranchManager.js +92 -26
- package/dist/editor/Editor.js +5 -1
- package/dist/editor/FormRenderer.js +5 -0
- package/dist/editor/admin/SystemHealthPanel.js +61 -6
- package/dist/editor/admin/useSystemHealth.d.ts +3 -0
- package/dist/editor/admin/useSystemHealth.js +59 -0
- package/dist/editor/branch-status.js +9 -0
- package/dist/editor/components/EntryChangesDrawer.js +3 -2
- package/dist/editor/copy.d.ts +3 -0
- package/dist/editor/copy.js +3 -0
- package/dist/editor/entry-diff.js +7 -1
- package/dist/editor/fields/DateField.d.ts +19 -0
- package/dist/editor/fields/DateField.js +53 -0
- package/dist/editor/fields/DateTimeField.d.ts +10 -0
- package/dist/editor/fields/DateTimeField.js +5 -15
- package/dist/editor/fields/list-item-title.js +2 -1
- package/dist/editor/hooks/useBranchManager.d.ts +5 -0
- package/dist/editor/hooks/useBranchManager.js +55 -4
- package/dist/editor/hooks/useBranchesData.d.ts +2 -0
- package/dist/editor/hooks/useBranchesData.js +3 -1
- package/dist/editor/theme.js +12 -4
- package/dist/entry-schema-registry.js +2 -1
- package/dist/static/index.js +5 -2
- package/dist/types.d.ts +33 -0
- package/dist/utils/calendar-day.d.ts +23 -0
- package/dist/utils/calendar-day.js +102 -0
- package/dist/utils/content-serialize.d.ts +4 -2
- package/dist/utils/content-serialize.js +8 -6
- package/dist/validation/entry-validator.js +7 -1
- package/dist/validation/field-traversal.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/archived-sweep.d.ts +23 -0
- package/dist/worker/archived-sweep.js +137 -0
- package/dist/worker/cms-worker.d.ts +6 -0
- package/dist/worker/cms-worker.js +7 -0
- package/dist/worker/git-sync.d.ts +4 -4
- package/dist/worker/git-sync.js +3 -0
- package/dist/worker/github-commits.d.ts +2 -2
- package/dist/worker/github-commits.js +5 -2
- package/dist/worker/github-gateway-wire.d.ts +2 -2
- package/dist/worker/rebase.js +6 -2
- package/dist/worker/task-runner.js +7 -4
- package/dist/worker/worker-context.d.ts +2 -0
- package/package.json +1 -1
package/dist/api/client.js
CHANGED
|
@@ -68,6 +68,10 @@ export class CanopyApiClient {
|
|
|
68
68
|
markMerged: (params, options) => {
|
|
69
69
|
return this.request('POST', this.buildPath('/:branch/mark-merged', params), undefined, 'standard', options);
|
|
70
70
|
},
|
|
71
|
+
/** POST /:branch/archive */
|
|
72
|
+
archive: (params, options) => {
|
|
73
|
+
return this.request('POST', this.buildPath('/:branch/archive', params), undefined, 'standard', options);
|
|
74
|
+
},
|
|
71
75
|
/** GET /:branch/status */
|
|
72
76
|
getStatus: (params, options) => {
|
|
73
77
|
return this.request('GET', this.buildPath('/:branch/status', params), undefined, 'standard', options);
|
|
@@ -77,6 +81,48 @@ export class CanopyApiClient {
|
|
|
77
81
|
return this.request('POST', this.buildPath('/:branch/submit', params), body, 'long', options);
|
|
78
82
|
},
|
|
79
83
|
};
|
|
84
|
+
this.admin = {
|
|
85
|
+
/** POST /admin/archived-branches/delete */
|
|
86
|
+
deleteArchivedBranches: (body, options) => {
|
|
87
|
+
return this.request('POST', '/admin/archived-branches/delete', body, 'long', options);
|
|
88
|
+
},
|
|
89
|
+
/** GET /admin/branch-health */
|
|
90
|
+
branchHealth: (params, options) => {
|
|
91
|
+
return this.request('GET', this.buildPath('/admin/branch-health', params), undefined, 'long', options);
|
|
92
|
+
},
|
|
93
|
+
/** POST /admin/branch-dirs/:dirName/purge */
|
|
94
|
+
purgeBranchDir: (params, options) => {
|
|
95
|
+
return this.request('POST', this.buildPath('/admin/branch-dirs/:dirName/purge', params), undefined, 'long', options);
|
|
96
|
+
},
|
|
97
|
+
/** POST /admin/branch-dirs/:dirName/repair-metadata */
|
|
98
|
+
repairBranchDir: (params, options) => {
|
|
99
|
+
return this.request('POST', this.buildPath('/admin/branch-dirs/:dirName/repair-metadata', params), undefined, 'long', options);
|
|
100
|
+
},
|
|
101
|
+
/** POST /admin/branch-dirs/:dirName/dismiss-unpublished */
|
|
102
|
+
dismissUnpublished: (params, options) => {
|
|
103
|
+
return this.request('POST', this.buildPath('/admin/branch-dirs/:dirName/dismiss-unpublished', params), undefined, 'standard', options);
|
|
104
|
+
},
|
|
105
|
+
/** POST /admin/branch-dirs/:dirName/repair-content-duplicates */
|
|
106
|
+
repairContentDuplicates: (params, options) => {
|
|
107
|
+
return this.request('POST', this.buildPath('/admin/branch-dirs/:dirName/repair-content-duplicates', params), undefined, 'long', options);
|
|
108
|
+
},
|
|
109
|
+
/** GET /admin/status */
|
|
110
|
+
status: (options) => {
|
|
111
|
+
return this.request('GET', '/admin/status', undefined, 'standard', options);
|
|
112
|
+
},
|
|
113
|
+
/** GET /admin/tasks/:status */
|
|
114
|
+
listTasks: (params, options) => {
|
|
115
|
+
return this.request('GET', this.buildPath('/admin/tasks/:status', params), undefined, 'standard', options);
|
|
116
|
+
},
|
|
117
|
+
/** POST /admin/tasks/:taskId/retry */
|
|
118
|
+
retryTask: (params, options) => {
|
|
119
|
+
return this.request('POST', this.buildPath('/admin/tasks/:taskId/retry', params), undefined, 'standard', options);
|
|
120
|
+
},
|
|
121
|
+
/** DELETE /admin/tasks/:status/:fileName */
|
|
122
|
+
deleteTask: (params, options) => {
|
|
123
|
+
return this.request('DELETE', this.buildPath('/admin/tasks/:status/:fileName', params), undefined, 'standard', options);
|
|
124
|
+
},
|
|
125
|
+
};
|
|
80
126
|
this.comments = {
|
|
81
127
|
/** GET /:branch/comments */
|
|
82
128
|
list: (params, options) => {
|
|
@@ -246,44 +292,6 @@ export class CanopyApiClient {
|
|
|
246
292
|
return this.request('POST', this.buildPath('/:branch/schema/invalidate-cache', params), undefined, 'standard', options);
|
|
247
293
|
},
|
|
248
294
|
};
|
|
249
|
-
this.admin = {
|
|
250
|
-
/** GET /admin/branch-health */
|
|
251
|
-
branchHealth: (params, options) => {
|
|
252
|
-
return this.request('GET', this.buildPath('/admin/branch-health', params), undefined, 'long', options);
|
|
253
|
-
},
|
|
254
|
-
/** POST /admin/branch-dirs/:dirName/purge */
|
|
255
|
-
purgeBranchDir: (params, options) => {
|
|
256
|
-
return this.request('POST', this.buildPath('/admin/branch-dirs/:dirName/purge', params), undefined, 'long', options);
|
|
257
|
-
},
|
|
258
|
-
/** POST /admin/branch-dirs/:dirName/repair-metadata */
|
|
259
|
-
repairBranchDir: (params, options) => {
|
|
260
|
-
return this.request('POST', this.buildPath('/admin/branch-dirs/:dirName/repair-metadata', params), undefined, 'long', options);
|
|
261
|
-
},
|
|
262
|
-
/** POST /admin/branch-dirs/:dirName/dismiss-unpublished */
|
|
263
|
-
dismissUnpublished: (params, options) => {
|
|
264
|
-
return this.request('POST', this.buildPath('/admin/branch-dirs/:dirName/dismiss-unpublished', params), undefined, 'standard', options);
|
|
265
|
-
},
|
|
266
|
-
/** POST /admin/branch-dirs/:dirName/repair-content-duplicates */
|
|
267
|
-
repairContentDuplicates: (params, options) => {
|
|
268
|
-
return this.request('POST', this.buildPath('/admin/branch-dirs/:dirName/repair-content-duplicates', params), undefined, 'long', options);
|
|
269
|
-
},
|
|
270
|
-
/** GET /admin/status */
|
|
271
|
-
status: (options) => {
|
|
272
|
-
return this.request('GET', '/admin/status', undefined, 'standard', options);
|
|
273
|
-
},
|
|
274
|
-
/** GET /admin/tasks/:status */
|
|
275
|
-
listTasks: (params, options) => {
|
|
276
|
-
return this.request('GET', this.buildPath('/admin/tasks/:status', params), undefined, 'standard', options);
|
|
277
|
-
},
|
|
278
|
-
/** POST /admin/tasks/:taskId/retry */
|
|
279
|
-
retryTask: (params, options) => {
|
|
280
|
-
return this.request('POST', this.buildPath('/admin/tasks/:taskId/retry', params), undefined, 'standard', options);
|
|
281
|
-
},
|
|
282
|
-
/** DELETE /admin/tasks/:status/:fileName */
|
|
283
|
-
deleteTask: (params, options) => {
|
|
284
|
-
return this.request('DELETE', this.buildPath('/admin/tasks/:status/:fileName', params), undefined, 'standard', options);
|
|
285
|
-
},
|
|
286
|
-
};
|
|
287
295
|
this.baseUrl = options.baseUrl ?? '/api/canopycms';
|
|
288
296
|
// An unbound fetch throws "Illegal invocation" in browsers; Node has no window.
|
|
289
297
|
this.fetchFn = options.fetch ?? (typeof window !== 'undefined' ? fetch.bind(window) : fetch);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BranchMetadata } from './types.js';
|
|
2
|
+
type ArchiveFields = Pick<BranchMetadata, 'status' | 'archivedAt' | 'mergedAt' | 'unpublishedAtMerge' | 'rewrittenOnGitHub'>;
|
|
3
|
+
/** When the branch was archived, or undefined for one that records neither time. */
|
|
4
|
+
export declare const archivedTime: (branch: ArchiveFields) => number | undefined;
|
|
5
|
+
/** Archived before `cutoffMs`. `updatedAt` is no clock: any metadata write moves it. */
|
|
6
|
+
export declare const archivedBefore: (branch: ArchiveFields, cutoffMs: number) => boolean;
|
|
7
|
+
/** Why deletion by age leaves a branch for a person to delete. */
|
|
8
|
+
export type AgeDeletionHold = 'not-merged' | 'unpublished';
|
|
9
|
+
/**
|
|
10
|
+
* What keeps a branch from deletion by age, past any age: one archived without merging holds work
|
|
11
|
+
* that reached no base branch, and one merged with an unfinished submit, or rewritten on GitHub,
|
|
12
|
+
* may hold the only copy of commits the merge left out.
|
|
13
|
+
*/
|
|
14
|
+
export declare const ageDeletionHold: (branch: ArchiveFields) => AgeDeletionHold | undefined;
|
|
15
|
+
/** The one rule the retention sweep and the admin bulk delete both select by. */
|
|
16
|
+
export declare const deletableByAge: (branch: ArchiveFields, cutoffMs: number) => boolean;
|
|
17
|
+
/** `now` less `days`, as the cutoff the functions above take. */
|
|
18
|
+
export declare const archivedCutoff: (days: number, now: number) => number;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** When the branch was archived, or undefined for one that records neither time. */
|
|
2
|
+
export const archivedTime = (branch) => {
|
|
3
|
+
const recorded = branch.archivedAt ?? branch.mergedAt;
|
|
4
|
+
if (recorded === undefined)
|
|
5
|
+
return undefined;
|
|
6
|
+
const time = Date.parse(recorded);
|
|
7
|
+
return Number.isNaN(time) ? undefined : time;
|
|
8
|
+
};
|
|
9
|
+
/** Archived before `cutoffMs`. `updatedAt` is no clock: any metadata write moves it. */
|
|
10
|
+
export const archivedBefore = (branch, cutoffMs) => {
|
|
11
|
+
const time = archivedTime(branch);
|
|
12
|
+
return branch.status === 'archived' && time !== undefined && time < cutoffMs;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* What keeps a branch from deletion by age, past any age: one archived without merging holds work
|
|
16
|
+
* that reached no base branch, and one merged with an unfinished submit, or rewritten on GitHub,
|
|
17
|
+
* may hold the only copy of commits the merge left out.
|
|
18
|
+
*/
|
|
19
|
+
export const ageDeletionHold = (branch) => branch.mergedAt === undefined
|
|
20
|
+
? 'not-merged'
|
|
21
|
+
: branch.unpublishedAtMerge === true || branch.rewrittenOnGitHub !== undefined
|
|
22
|
+
? 'unpublished'
|
|
23
|
+
: undefined;
|
|
24
|
+
/** The one rule the retention sweep and the admin bulk delete both select by. */
|
|
25
|
+
export const deletableByAge = (branch, cutoffMs) => archivedBefore(branch, cutoffMs) && ageDeletionHold(branch) === undefined;
|
|
26
|
+
/** `now` less `days`, as the cutoff the functions above take. */
|
|
27
|
+
export const archivedCutoff = (days, now) => now - days * 24 * 60 * 60_000;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { BranchRegistry } from './branch-registry.js';
|
|
2
|
+
import type { BranchContext, BranchMetadata } from './types.js';
|
|
3
|
+
export interface DeleteBranchWorkspaceOptions {
|
|
4
|
+
context: Pick<BranchContext, 'branch' | 'branchRoot' | 'baseRoot'>;
|
|
5
|
+
registry: Pick<BranchRegistry, 'invalidate'>;
|
|
6
|
+
/**
|
|
7
|
+
* For a delete a person asked for. Deletion by age passes none: commits pushed to a GitHub branch
|
|
8
|
+
* after its merge have no other copy, and reconcile prunes the mirror's head once GitHub drops it.
|
|
9
|
+
*/
|
|
10
|
+
remoteCopies?: {
|
|
11
|
+
/** The branch it forked from, or the deployment's base branch. */
|
|
12
|
+
baseBranch: string;
|
|
13
|
+
/** Removes the branch's head from `remote.git`; a failure costs a warning, not the delete. */
|
|
14
|
+
deleteMirrorHead: () => Promise<void>;
|
|
15
|
+
/** Deletes or queues deleting the branch on GitHub, resolving a warning if that failed. */
|
|
16
|
+
deleteOnGitHub: () => Promise<string | undefined>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Re-decides from a fresh read inside the lock every save() takes, so a change before the rename
|
|
20
|
+
* keeps the branch and one after it fails. For a caller that chose the branch earlier.
|
|
21
|
+
*/
|
|
22
|
+
stillWanted?: (current: BranchMetadata) => boolean;
|
|
23
|
+
}
|
|
24
|
+
export type DeleteBranchWorkspaceOutcome = {
|
|
25
|
+
kind: 'deleted';
|
|
26
|
+
cleanupWarning?: string;
|
|
27
|
+
}
|
|
28
|
+
/** `stillWanted` refused it, or it was already gone. */
|
|
29
|
+
| {
|
|
30
|
+
kind: 'kept';
|
|
31
|
+
}
|
|
32
|
+
/** The lock was not taken in time, or branch.json could not be read under it. */
|
|
33
|
+
| {
|
|
34
|
+
kind: 'busy';
|
|
35
|
+
error: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Delete a branch and everything stored with it: workspace, branch.json and comments.
|
|
39
|
+
*
|
|
40
|
+
* The directory leaves its name by one rename, under the server-enforced lockfile branch-metadata
|
|
41
|
+
* saves hold (utils/occ-json-write.ts), so a save() that reaches that lock after the rename fails
|
|
42
|
+
* rather than recreating the tree. The `rm` runs last: a process killed during it leaves only a
|
|
43
|
+
* `.deleting-*` directory the worker sweeps, never residue under a name that may be created again.
|
|
44
|
+
*/
|
|
45
|
+
export declare function deleteBranchWorkspace(options: DeleteBranchWorkspaceOptions): Promise<DeleteBranchWorkspaceOutcome>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { BRANCH_META_DIR, BRANCH_META_FILE, readBranchMetadataFile } from './branch-metadata-file.js';
|
|
4
|
+
import { deletingDirName, removeLeftoverDir } from './branch-provisioning.js';
|
|
5
|
+
import { RESERVED_SETTINGS_BRANCH_PREFIX, sanitizeBranchName } from './paths/branch-name.js';
|
|
6
|
+
import { getErrorMessage, isNotFoundError, sanitizeErrorMessage } from './utils/error.js';
|
|
7
|
+
import { filePathExists } from './utils/fs.js';
|
|
8
|
+
import { canopyLogError, canopyLogWarn } from './utils/logger.js';
|
|
9
|
+
import { withOccFileLock } from './utils/occ-json-write.js';
|
|
10
|
+
/**
|
|
11
|
+
* Delete a branch and everything stored with it: workspace, branch.json and comments.
|
|
12
|
+
*
|
|
13
|
+
* The directory leaves its name by one rename, under the server-enforced lockfile branch-metadata
|
|
14
|
+
* saves hold (utils/occ-json-write.ts), so a save() that reaches that lock after the rename fails
|
|
15
|
+
* rather than recreating the tree. The `rm` runs last: a process killed during it leaves only a
|
|
16
|
+
* `.deleting-*` directory the worker sweeps, never residue under a name that may be created again.
|
|
17
|
+
*/
|
|
18
|
+
export async function deleteBranchWorkspace(options) {
|
|
19
|
+
const { context, registry, remoteCopies, stillWanted } = options;
|
|
20
|
+
const metadataFile = path.join(context.branchRoot, BRANCH_META_DIR, BRANCH_META_FILE);
|
|
21
|
+
let cleanupWarning;
|
|
22
|
+
let deletingPath;
|
|
23
|
+
const warn = (warning) => {
|
|
24
|
+
cleanupWarning = cleanupWarning ? `${cleanupWarning}; ${warning}` : warning;
|
|
25
|
+
};
|
|
26
|
+
let wanted = true;
|
|
27
|
+
try {
|
|
28
|
+
await withOccFileLock(metadataFile, async () => {
|
|
29
|
+
if (stillWanted) {
|
|
30
|
+
const current = await readBranchMetadataFile(context.branchRoot);
|
|
31
|
+
if (current === null || !stillWanted(current.branch)) {
|
|
32
|
+
wanted = false;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (context.branchRoot === context.baseRoot) {
|
|
37
|
+
await fs.unlink(metadataFile).catch((err) => {
|
|
38
|
+
if (!isNotFoundError(err))
|
|
39
|
+
throw err;
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const target = path.join(context.baseRoot, deletingDirName(path.basename(context.branchRoot)));
|
|
44
|
+
try {
|
|
45
|
+
await fs.rename(context.branchRoot, target);
|
|
46
|
+
deletingPath = target;
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (isNotFoundError(err))
|
|
50
|
+
return;
|
|
51
|
+
// Still deleted logically: without branch.json the branch is unlisted,
|
|
52
|
+
// and the worker quarantines what is left.
|
|
53
|
+
await fs.unlink(metadataFile).catch(() => { });
|
|
54
|
+
// [REDACT] Returned to the browser; the console line keeps the path.
|
|
55
|
+
warn(`Failed to remove branch directory: ${sanitizeErrorMessage(getErrorMessage(err))}`);
|
|
56
|
+
canopyLogError(`CanopyCMS: Failed to move branch directory for ${context.branch.name} aside:`, getErrorMessage(err));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
// A lock on a directory another delete took fails like any other.
|
|
62
|
+
if (!(await filePathExists(context.branchRoot)))
|
|
63
|
+
return { kind: 'kept' };
|
|
64
|
+
return { kind: 'busy', error: getErrorMessage(err) };
|
|
65
|
+
}
|
|
66
|
+
if (!wanted)
|
|
67
|
+
return { kind: 'kept' };
|
|
68
|
+
// Defense-in-depth only: callers refuse the base branch, and settings branches never resolve
|
|
69
|
+
// to a context here.
|
|
70
|
+
const sanitizedDeleted = sanitizeBranchName(context.branch.name);
|
|
71
|
+
if (remoteCopies &&
|
|
72
|
+
sanitizedDeleted !== sanitizeBranchName(remoteCopies.baseBranch) &&
|
|
73
|
+
!sanitizedDeleted.startsWith(RESERVED_SETTINGS_BRANCH_PREFIX)) {
|
|
74
|
+
try {
|
|
75
|
+
await remoteCopies.deleteMirrorHead();
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
// Client-facing copy is sanitized (API-H2 — no absolute EFS paths); the console line keeps
|
|
79
|
+
// the raw detail for server logs.
|
|
80
|
+
warn(`Failed to remove the deleted branch's head from the local mirror: ${sanitizeErrorMessage(getErrorMessage(err))}`);
|
|
81
|
+
canopyLogWarn(`CanopyCMS: failed to remove deleted branch's mirror head (branch ${context.branch.name}): ${getErrorMessage(err)}`);
|
|
82
|
+
}
|
|
83
|
+
const warning = await remoteCopies.deleteOnGitHub();
|
|
84
|
+
if (warning)
|
|
85
|
+
warn(warning);
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
await registry.invalidate();
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
// The branch is gone; a listing corrects itself at the next bump.
|
|
92
|
+
warn(`Failed to refresh the branch list: ${sanitizeErrorMessage(getErrorMessage(err))}`);
|
|
93
|
+
}
|
|
94
|
+
if (deletingPath)
|
|
95
|
+
await removeLeftoverDir(deletingPath);
|
|
96
|
+
return { kind: 'deleted', ...(cleanupWarning && { cleanupWarning }) };
|
|
97
|
+
}
|
|
@@ -68,6 +68,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
68
68
|
merged: "merged";
|
|
69
69
|
}>>;
|
|
70
70
|
mergedAt: z.ZodOptional<z.ZodString>;
|
|
71
|
+
archivedAt: z.ZodOptional<z.ZodString>;
|
|
71
72
|
rebaseFailure: z.ZodOptional<z.ZodObject<{
|
|
72
73
|
message: z.ZodString;
|
|
73
74
|
firstAt: z.ZodString;
|
|
@@ -183,6 +184,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
183
184
|
merged: "merged";
|
|
184
185
|
}>>;
|
|
185
186
|
mergedAt: z.ZodOptional<z.ZodString>;
|
|
187
|
+
archivedAt: z.ZodOptional<z.ZodString>;
|
|
186
188
|
rebaseFailure: z.ZodOptional<z.ZodObject<{
|
|
187
189
|
message: z.ZodString;
|
|
188
190
|
firstAt: z.ZodString;
|
|
@@ -298,6 +300,7 @@ export declare const branchMetadataSchema: z.ZodObject<{
|
|
|
298
300
|
merged: "merged";
|
|
299
301
|
}>>;
|
|
300
302
|
mergedAt: z.ZodOptional<z.ZodString>;
|
|
303
|
+
archivedAt: z.ZodOptional<z.ZodString>;
|
|
301
304
|
rebaseFailure: z.ZodOptional<z.ZodObject<{
|
|
302
305
|
message: z.ZodString;
|
|
303
306
|
firstAt: z.ZodString;
|
|
@@ -130,7 +130,12 @@ export declare const awaitsSubmitRetry: ({ submitUnfinished, status }: BranchMet
|
|
|
130
130
|
* existing values for fields the incoming update omits, so the branch's ref and
|
|
131
131
|
* its recorded PR number and URL survive untouched.
|
|
132
132
|
*/
|
|
133
|
-
export declare function buildMergedBranchUpdate(branch: BranchMetadata,
|
|
133
|
+
export declare function buildMergedBranchUpdate(branch: BranchMetadata, mergedAt?: Date): NonNullable<BranchMetadataUpdate['branch']>;
|
|
134
|
+
/**
|
|
135
|
+
* The metadata update for archiving a branch whose PR closed without merging. Records no merge:
|
|
136
|
+
* `mergedAt` is what tells the two apart, and `pullRequestState` stays `closed`.
|
|
137
|
+
*/
|
|
138
|
+
export declare function buildUnmergedArchiveUpdate(): NonNullable<BranchMetadataUpdate['branch']>;
|
|
134
139
|
/** {@link BranchMetadataFileManager.get} as a function. */
|
|
135
140
|
export declare const getBranchMetadataFileManager: (branchRoot: string, baseRoot: string, options?: {
|
|
136
141
|
settleMs?: number;
|
package/dist/branch-metadata.js
CHANGED
|
@@ -282,11 +282,13 @@ const owesSubmit = (branch) => awaitsSubmitRetry(branch) ||
|
|
|
282
282
|
* existing values for fields the incoming update omits, so the branch's ref and
|
|
283
283
|
* its recorded PR number and URL survive untouched.
|
|
284
284
|
*/
|
|
285
|
-
export function buildMergedBranchUpdate(branch,
|
|
285
|
+
export function buildMergedBranchUpdate(branch, mergedAt = new Date()) {
|
|
286
286
|
return {
|
|
287
287
|
status: 'archived',
|
|
288
288
|
pullRequestState: 'merged',
|
|
289
|
-
mergedAt:
|
|
289
|
+
mergedAt: mergedAt.toISOString(),
|
|
290
|
+
// Now, not the merge: retention's clock must not start before a worker outage that hid it.
|
|
291
|
+
archivedAt: new Date().toISOString(),
|
|
290
292
|
// The merge settled every conflict the record named.
|
|
291
293
|
conflictStatus: 'clean',
|
|
292
294
|
conflictFiles: [],
|
|
@@ -294,6 +296,17 @@ export function buildMergedBranchUpdate(branch, now = new Date()) {
|
|
|
294
296
|
...(owesSubmit(branch) ? { unpublishedAtMerge: true } : {}),
|
|
295
297
|
};
|
|
296
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* The metadata update for archiving a branch whose PR closed without merging. Records no merge:
|
|
301
|
+
* `mergedAt` is what tells the two apart, and `pullRequestState` stays `closed`.
|
|
302
|
+
*/
|
|
303
|
+
export function buildUnmergedArchiveUpdate() {
|
|
304
|
+
return {
|
|
305
|
+
status: 'archived',
|
|
306
|
+
archivedAt: new Date().toISOString(),
|
|
307
|
+
submitUnfinished: undefined,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
297
310
|
/** {@link BranchMetadataFileManager.get} as a function. */
|
|
298
311
|
export const getBranchMetadataFileManager = (branchRoot, baseRoot, options) => {
|
|
299
312
|
return BranchMetadataFileManager.get(branchRoot, baseRoot, options);
|
package/dist/cli/cli.js
CHANGED
|
@@ -1349,7 +1349,7 @@ var init_init = __esm({
|
|
|
1349
1349
|
init_types = __esm2({
|
|
1350
1350
|
"dist/config/types.js"() {
|
|
1351
1351
|
"use strict";
|
|
1352
|
-
scalarFieldTypes = ["string", "number", "boolean", "datetime", "code"];
|
|
1352
|
+
scalarFieldTypes = ["string", "number", "boolean", "date", "datetime", "code"];
|
|
1353
1353
|
markdownFieldTypes = ["markdown", "mdx"];
|
|
1354
1354
|
primitiveFieldTypes = [...scalarFieldTypes, ...markdownFieldTypes];
|
|
1355
1355
|
fieldTypes = [
|
|
@@ -3495,7 +3495,7 @@ async function atomicWriteFile2(filePath, content) {
|
|
|
3495
3495
|
}
|
|
3496
3496
|
}
|
|
3497
3497
|
function isPlainRecord(value) {
|
|
3498
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3498
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
3499
3499
|
}
|
|
3500
3500
|
function resolveBlockItem(blockField, item) {
|
|
3501
3501
|
const templateName = typeof item.template === "string" ? item.template : typeof item._type === "string" ? item._type : void 0;
|
|
@@ -4068,6 +4068,20 @@ async function writeWorkerStatus(taskDir, report) {
|
|
|
4068
4068
|
function isBlockStructuralKey(key) {
|
|
4069
4069
|
return BLOCK_STRUCTURAL_KEYS.has(key);
|
|
4070
4070
|
}
|
|
4071
|
+
function withCalendarDays(fields, data) {
|
|
4072
|
+
let out = data;
|
|
4073
|
+
for (const field of fields) {
|
|
4074
|
+
if (field.type === "group") {
|
|
4075
|
+
out = withCalendarDays(field.fields, out);
|
|
4076
|
+
continue;
|
|
4077
|
+
}
|
|
4078
|
+
const value = out[field.name];
|
|
4079
|
+
const next = valueWithDays(field, value);
|
|
4080
|
+
if (next !== value)
|
|
4081
|
+
out = { ...out, [field.name]: next };
|
|
4082
|
+
}
|
|
4083
|
+
return out;
|
|
4084
|
+
}
|
|
4071
4085
|
function createDebugLogger2(options) {
|
|
4072
4086
|
return new DebugLogger2(options);
|
|
4073
4087
|
}
|
|
@@ -4711,9 +4725,9 @@ function asJson(value) {
|
|
|
4711
4725
|
const key = identityKey(value);
|
|
4712
4726
|
return key === void 0 || key === "undefined" ? void 0 : JSON.parse(key);
|
|
4713
4727
|
}
|
|
4714
|
-
function frontmatterAsRead(raw) {
|
|
4728
|
+
function frontmatterAsRead(raw, fields) {
|
|
4715
4729
|
try {
|
|
4716
|
-
return asJson(matter(raw, {}).data);
|
|
4730
|
+
return asJson(withCalendarDays(fields, matter(raw, {}).data));
|
|
4717
4731
|
} catch {
|
|
4718
4732
|
return void 0;
|
|
4719
4733
|
}
|
|
@@ -4781,7 +4795,7 @@ ${asKey ? `${text3}: v` : `v: ${text3}`}
|
|
|
4781
4795
|
return true;
|
|
4782
4796
|
}
|
|
4783
4797
|
}
|
|
4784
|
-
function serializeFrontmatter(editorBody, data, existingRaw, format) {
|
|
4798
|
+
function serializeFrontmatter(editorBody, data, existingRaw, format, fields = []) {
|
|
4785
4799
|
const file = { content: bodyToWrite(editorBody, data, format, existingRaw) };
|
|
4786
4800
|
if (existingRaw === void 0)
|
|
4787
4801
|
return matter.stringify(file, data);
|
|
@@ -4789,7 +4803,7 @@ function serializeFrontmatter(editorBody, data, existingRaw, format) {
|
|
|
4789
4803
|
if (existingFrontmatter === void 0)
|
|
4790
4804
|
return matter.stringify(file, data);
|
|
4791
4805
|
const onDisk = parseDocument2(existingFrontmatter);
|
|
4792
|
-
const toReconcile = onDisk.errors.length === 0 ? withUnchangedAsOnDisk(data, frontmatterAsRead(existingRaw), onDisk.toJS()) : data;
|
|
4806
|
+
const toReconcile = onDisk.errors.length === 0 ? withUnchangedAsOnDisk(data, frontmatterAsRead(existingRaw, fields), onDisk.toJS()) : data;
|
|
4793
4807
|
const reconciled = reconcileYamlSource(existingFrontmatter, isPlainRecord2(toReconcile) ? toReconcile : data, { rootAtColumnZero: true, quote: readsAsAnotherValue });
|
|
4794
4808
|
if (reconciled === void 0)
|
|
4795
4809
|
return matter.stringify(file, data);
|
|
@@ -4801,7 +4815,7 @@ function serializeFrontmatter(editorBody, data, existingRaw, format) {
|
|
|
4801
4815
|
}
|
|
4802
4816
|
}
|
|
4803
4817
|
});
|
|
4804
|
-
if (isDeepStrictEqual2(frontmatterAsRead(written), asJson(data)))
|
|
4818
|
+
if (isDeepStrictEqual2(frontmatterAsRead(written, fields), asJson(data)))
|
|
4805
4819
|
return written;
|
|
4806
4820
|
log22.debug("content-serialize", "reconciled frontmatter reads back differently; writing as gray-matter prints it");
|
|
4807
4821
|
return matter.stringify(file, data);
|
|
@@ -6131,6 +6145,7 @@ _${field.description}_` : "";
|
|
|
6131
6145
|
switch (field.type) {
|
|
6132
6146
|
case "string":
|
|
6133
6147
|
case "number":
|
|
6148
|
+
case "date":
|
|
6134
6149
|
case "datetime":
|
|
6135
6150
|
return `${heading} ${label}${descriptionLine}
|
|
6136
6151
|
|
|
@@ -8054,6 +8069,11 @@ function validateScalar(field, value, path272) {
|
|
|
8054
8069
|
return [{ fieldPath: path272, message: "Expected true or false" }];
|
|
8055
8070
|
}
|
|
8056
8071
|
return [];
|
|
8072
|
+
case "date":
|
|
8073
|
+
if (value !== "" && !isCalendarDay(value)) {
|
|
8074
|
+
return [{ fieldPath: path272, message: "Expected a real date, written YYYY-MM-DD" }];
|
|
8075
|
+
}
|
|
8076
|
+
return [];
|
|
8057
8077
|
case "datetime":
|
|
8058
8078
|
if (typeof value !== "string" || value !== "" && Number.isNaN(Date.parse(value))) {
|
|
8059
8079
|
return [{ fieldPath: path272, message: "Expected a valid date/time string" }];
|
|
@@ -8206,7 +8226,7 @@ function findInvalidEntries(items) {
|
|
|
8206
8226
|
for (const item of items) {
|
|
8207
8227
|
if (!item.schema)
|
|
8208
8228
|
continue;
|
|
8209
|
-
const data = normalizeDatesDeep(item.data);
|
|
8229
|
+
const data = normalizeDatesDeep(withCalendarDays(item.schema, item.data));
|
|
8210
8230
|
const errors = validateEntryData(item.schema, data);
|
|
8211
8231
|
if (errors.length > 0) {
|
|
8212
8232
|
invalid.push({ entryPath: item.entryPath, errors });
|
|
@@ -8434,7 +8454,7 @@ async function generateAIContentCLI(options) {
|
|
|
8434
8454
|
console.log(` Output: ${result.outputDir}
|
|
8435
8455
|
`);
|
|
8436
8456
|
}
|
|
8437
|
-
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, RESERVED_SETTINGS_BRANCH_PREFIX2, init_branch_name, BASE58_PATTERN2, CONTENT_ID_PATTERN2, PHYSICAL_SEGMENT_PATTERN2, MAX_BRANCH_NAME_LENGTH2, init_validation, 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, warned22, init_mode_env, forEachReferenceField, init_validation2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned3, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, warned4, DEPLOYMENT_PLACEHOLDER2, DEFAULT_BRANCH_NAMESPACE_TEMPLATE2, BRANCH_NAMESPACE_ENV2, init_branch_namespace, 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, warned5, SchemaUnavailableError, 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, SNAPSHOT_MEMO_MAX_BYTES, SCHEMA_CACHE_FILE, SCHEMA_GENERATION_RESOURCE, reportedIssues, BranchSchemaCache, RESOURCE, 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, GitProcessError, STDERR_CAP_BYTES, KILL_GRACE_MS, log9, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, GIT_NETWORK_ENV_PASSTHROUGH, NO_AUTO_GC_CONFIG, HASH_INACTIVITY_TIMEOUT_MS, MAX_HASH_RUNS, LS_REMOTE_INACTIVITY_TIMEOUT_MS, symlinkBlobSha, REMOTE_GIT_CONFIG, REMOTE_GIT_MAX_LOOSE_OBJECTS, REMOTE_GIT_MAX_PACKS, PARALLEL_CHECKOUT_ARGS, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, BaseBranchMissingError, 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, AI_USER_ID, log10, normalizePath, parseEntryParts, parseEntryData, 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;
|
|
8457
|
+
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, RESERVED_SETTINGS_BRANCH_PREFIX2, init_branch_name, BASE58_PATTERN2, CONTENT_ID_PATTERN2, PHYSICAL_SEGMENT_PATTERN2, MAX_BRANCH_NAME_LENGTH2, init_validation, 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, warned22, init_mode_env, forEachReferenceField, init_validation2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned3, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, warned4, DEPLOYMENT_PLACEHOLDER2, DEFAULT_BRANCH_NAMESPACE_TEMPLATE2, BRANCH_NAMESPACE_ENV2, init_branch_namespace, 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, DAY, MS_PER_DAY, isRealDay, dayParts, isCalendarDay, dayOfBareDate, mapItems, blockItemWithDays, valueWithDays, entryAsRead, 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, warned5, SchemaUnavailableError, 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, SNAPSHOT_MEMO_MAX_BYTES, SCHEMA_CACHE_FILE, SCHEMA_GENERATION_RESOURCE, reportedIssues, BranchSchemaCache, RESOURCE, 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, GitProcessError, STDERR_CAP_BYTES, KILL_GRACE_MS, log9, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, GIT_NETWORK_ENV_PASSTHROUGH, NO_AUTO_GC_CONFIG, HASH_INACTIVITY_TIMEOUT_MS, MAX_HASH_RUNS, LS_REMOTE_INACTIVITY_TIMEOUT_MS, symlinkBlobSha, REMOTE_GIT_CONFIG, REMOTE_GIT_MAX_LOOSE_OBJECTS, REMOTE_GIT_MAX_PACKS, PARALLEL_CHECKOUT_ARGS, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, BaseBranchMissingError, 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, AI_USER_ID, log10, normalizePath, parseEntryParts, parseEntryData, 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;
|
|
8438
8458
|
var init_generate_ai_content = __esm({
|
|
8439
8459
|
"dist/cli/generate-ai-content.js"() {
|
|
8440
8460
|
"use strict";
|
|
@@ -8616,7 +8636,7 @@ var init_generate_ai_content = __esm({
|
|
|
8616
8636
|
init_types22 = __esm3({
|
|
8617
8637
|
"dist/config/types.js"() {
|
|
8618
8638
|
"use strict";
|
|
8619
|
-
scalarFieldTypes2 = ["string", "number", "boolean", "datetime", "code"];
|
|
8639
|
+
scalarFieldTypes2 = ["string", "number", "boolean", "date", "datetime", "code"];
|
|
8620
8640
|
markdownFieldTypes2 = ["markdown", "mdx"];
|
|
8621
8641
|
primitiveFieldTypes2 = [...scalarFieldTypes2, ...markdownFieldTypes2];
|
|
8622
8642
|
fieldTypes2 = [
|
|
@@ -9350,6 +9370,54 @@ var init_generate_ai_content = __esm({
|
|
|
9350
9370
|
BLOCK_DISCRIMINATOR_KEYS = ["template", "_type"];
|
|
9351
9371
|
BLOCK_STRUCTURAL_KEYS = new Set(BLOCK_DISCRIMINATOR_KEYS);
|
|
9352
9372
|
init_item_keys();
|
|
9373
|
+
init_item_keys();
|
|
9374
|
+
init_field_traversal();
|
|
9375
|
+
DAY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
9376
|
+
MS_PER_DAY = 864e5;
|
|
9377
|
+
isRealDay = (year, month, day) => {
|
|
9378
|
+
const lastOfMonth = /* @__PURE__ */ new Date(0);
|
|
9379
|
+
lastOfMonth.setUTCFullYear(year, month, 0);
|
|
9380
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= lastOfMonth.getUTCDate();
|
|
9381
|
+
};
|
|
9382
|
+
dayParts = (value) => {
|
|
9383
|
+
const match = typeof value === "string" ? DAY.exec(value) : null;
|
|
9384
|
+
if (!match)
|
|
9385
|
+
return void 0;
|
|
9386
|
+
const [year, month, day] = match.slice(1).map(Number);
|
|
9387
|
+
return isRealDay(year, month, day) ? [year, month, day] : void 0;
|
|
9388
|
+
};
|
|
9389
|
+
isCalendarDay = (value) => dayParts(value) !== void 0;
|
|
9390
|
+
dayOfBareDate = (value) => value.getTime() % MS_PER_DAY === 0 ? value.toISOString().slice(0, 10) : void 0;
|
|
9391
|
+
mapItems = (items, mapItem) => {
|
|
9392
|
+
const next = items.map((item) => isPlainRecord(item) ? mapItem(item) : item);
|
|
9393
|
+
return next.every((item, i) => item === items[i]) ? items : next;
|
|
9394
|
+
};
|
|
9395
|
+
blockItemWithDays = (field, item) => {
|
|
9396
|
+
const resolved = resolveBlockItem(field, item);
|
|
9397
|
+
if (!resolved)
|
|
9398
|
+
return item;
|
|
9399
|
+
const data = withCalendarDays(resolved.fields, resolved.data);
|
|
9400
|
+
if (data === resolved.data)
|
|
9401
|
+
return item;
|
|
9402
|
+
return resolved.data === item ? data : { ...item, value: data };
|
|
9403
|
+
};
|
|
9404
|
+
valueWithDays = (field, value) => {
|
|
9405
|
+
if (field.type === "date") {
|
|
9406
|
+
return value instanceof Date ? dayOfBareDate(value) ?? value : value;
|
|
9407
|
+
}
|
|
9408
|
+
if (field.type === "object") {
|
|
9409
|
+
const { fields, list } = field;
|
|
9410
|
+
if (list) {
|
|
9411
|
+
return Array.isArray(value) ? mapItems(value, (item) => withCalendarDays(fields ?? [], item)) : value;
|
|
9412
|
+
}
|
|
9413
|
+
return isPlainRecord(value) ? withCalendarDays(fields ?? [], value) : value;
|
|
9414
|
+
}
|
|
9415
|
+
if (field.type === "block" && Array.isArray(value)) {
|
|
9416
|
+
return mapItems(value, (item) => blockItemWithDays(field, item));
|
|
9417
|
+
}
|
|
9418
|
+
return value;
|
|
9419
|
+
};
|
|
9420
|
+
entryAsRead = (fields, data) => withItemKeys(fields, withCalendarDays(fields, data), "content");
|
|
9353
9421
|
envFlag2 = (name) => typeof process !== "undefined" && process.env[name] === "true";
|
|
9354
9422
|
LOG_LEVELS2 = {
|
|
9355
9423
|
DEBUG: 0,
|
|
@@ -10317,7 +10385,6 @@ var init_generate_ai_content = __esm({
|
|
|
10317
10385
|
};
|
|
10318
10386
|
isDataOnlyFormat = (format) => format === "json" || format === "yaml";
|
|
10319
10387
|
asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
10320
|
-
init_item_keys();
|
|
10321
10388
|
DEFAULT_SEO_FIELD_NAMES = {
|
|
10322
10389
|
title: "metaTitle",
|
|
10323
10390
|
description: "metaDescription",
|
|
@@ -11624,7 +11691,7 @@ var init_generate_ai_content = __esm({
|
|
|
11624
11691
|
absolutePath
|
|
11625
11692
|
};
|
|
11626
11693
|
}
|
|
11627
|
-
doc.data =
|
|
11694
|
+
doc.data = entryAsRead(fields, doc.data);
|
|
11628
11695
|
if (options.resolveReferences !== false) {
|
|
11629
11696
|
doc.data = await this.resolveReferencesInData(doc.data, fields, void 0, options.referenceAccess, {
|
|
11630
11697
|
entry: slug ? entryLogicalPath(schemaItem.logicalPath, slug) : schemaItem.logicalPath,
|
|
@@ -11728,7 +11795,7 @@ var init_generate_ai_content = __esm({
|
|
|
11728
11795
|
} else if (input.format === "yaml") {
|
|
11729
11796
|
content = serializeYaml(input.data ?? {}, existingRaw);
|
|
11730
11797
|
} else {
|
|
11731
|
-
content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw, input.format);
|
|
11798
|
+
content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw, input.format, fields);
|
|
11732
11799
|
}
|
|
11733
11800
|
await atomicWriteFile2(absolutePath, content);
|
|
11734
11801
|
const liveIndex = this._idIndex;
|
|
@@ -12324,6 +12391,7 @@ var init_generate_ai_content = __esm({
|
|
|
12324
12391
|
FLAT_CELL_TYPES = /* @__PURE__ */ new Set([
|
|
12325
12392
|
"string",
|
|
12326
12393
|
"number",
|
|
12394
|
+
"date",
|
|
12327
12395
|
"datetime",
|
|
12328
12396
|
"boolean",
|
|
12329
12397
|
"select",
|
|
@@ -12391,6 +12459,7 @@ var init_generate_ai_content = __esm({
|
|
|
12391
12459
|
merged: "merged"
|
|
12392
12460
|
}).optional(),
|
|
12393
12461
|
mergedAt: z72.string().optional(),
|
|
12462
|
+
archivedAt: z72.string().optional(),
|
|
12394
12463
|
rebaseFailure: z72.object({ message: z72.string(), firstAt: z72.string(), lastAt: z72.string() }).optional(),
|
|
12395
12464
|
historyRewrittenFrom: z72.string().optional(),
|
|
12396
12465
|
lastGitHubTip: z72.string().optional(),
|
|
@@ -14595,7 +14664,6 @@ var init_generate_ai_content = __esm({
|
|
|
14595
14664
|
};
|
|
14596
14665
|
AI_USER_ID = "canopycms-ai";
|
|
14597
14666
|
init_error3();
|
|
14598
|
-
init_item_keys();
|
|
14599
14667
|
log10 = createDebugLogger2({ prefix: "ContentListing" });
|
|
14600
14668
|
normalizePath = (root, target) => {
|
|
14601
14669
|
const result = validateAndNormalizePath(root, target);
|
|
@@ -14705,7 +14773,7 @@ var init_generate_ai_content = __esm({
|
|
|
14705
14773
|
fs20.stat(absolutePath),
|
|
14706
14774
|
readEntryData(absolutePath, format, bodyField)
|
|
14707
14775
|
]);
|
|
14708
|
-
const data =
|
|
14776
|
+
const data = entryAsRead(entryType?.schema ?? [], rawData);
|
|
14709
14777
|
const item = {
|
|
14710
14778
|
logicalPath: entryLogicalPath(collection.logicalPath, slug),
|
|
14711
14779
|
contentId,
|
|
@@ -16599,7 +16667,7 @@ var scalarFieldTypes3, markdownFieldTypes3, primitiveFieldTypes3, fieldTypes3;
|
|
|
16599
16667
|
var init_types3 = __esm({
|
|
16600
16668
|
"dist/config/types.js"() {
|
|
16601
16669
|
"use strict";
|
|
16602
|
-
scalarFieldTypes3 = ["string", "number", "boolean", "datetime", "code"];
|
|
16670
|
+
scalarFieldTypes3 = ["string", "number", "boolean", "date", "datetime", "code"];
|
|
16603
16671
|
markdownFieldTypes3 = ["markdown", "mdx"];
|
|
16604
16672
|
primitiveFieldTypes3 = [...scalarFieldTypes3, ...markdownFieldTypes3];
|
|
16605
16673
|
fieldTypes3 = [
|
|
@@ -17868,6 +17936,7 @@ var init_branch_metadata_file = __esm({
|
|
|
17868
17936
|
merged: "merged"
|
|
17869
17937
|
}).optional(),
|
|
17870
17938
|
mergedAt: z16.string().optional(),
|
|
17939
|
+
archivedAt: z16.string().optional(),
|
|
17871
17940
|
rebaseFailure: z16.object({ message: z16.string(), firstAt: z16.string(), lastAt: z16.string() }).optional(),
|
|
17872
17941
|
historyRewrittenFrom: z16.string().optional(),
|
|
17873
17942
|
lastGitHubTip: z16.string().optional(),
|