canopycms 0.0.55 → 0.0.57
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/dist/api/branch.d.ts +6 -0
- package/dist/api/branch.d.ts.map +1 -1
- package/dist/api/branch.js +63 -19
- package/dist/api/branch.js.map +1 -1
- package/dist/api/content.d.ts +2 -2
- package/dist/api/entries.d.ts.map +1 -1
- package/dist/api/entries.js +10 -2
- package/dist/api/entries.js.map +1 -1
- package/dist/api/github-sync.d.ts.map +1 -1
- package/dist/api/github-sync.js +28 -7
- package/dist/api/github-sync.js.map +1 -1
- package/dist/auth/caching-auth-plugin.d.ts +8 -1
- package/dist/auth/caching-auth-plugin.d.ts.map +1 -1
- package/dist/auth/caching-auth-plugin.js +6 -1
- package/dist/auth/caching-auth-plugin.js.map +1 -1
- package/dist/auth/plugin.d.ts +19 -10
- package/dist/auth/plugin.d.ts.map +1 -1
- package/dist/auth/plugin.js +12 -10
- package/dist/auth/plugin.js.map +1 -1
- package/dist/branch-metadata.d.ts +71 -6
- package/dist/branch-metadata.d.ts.map +1 -1
- package/dist/branch-metadata.js +132 -116
- package/dist/branch-metadata.js.map +1 -1
- package/dist/branch-registry.d.ts +78 -9
- package/dist/branch-registry.d.ts.map +1 -1
- package/dist/branch-registry.js +155 -29
- package/dist/branch-registry.js.map +1 -1
- package/dist/branch-schema-cache.d.ts +126 -11
- package/dist/branch-schema-cache.d.ts.map +1 -1
- package/dist/branch-schema-cache.js +208 -58
- package/dist/branch-schema-cache.js.map +1 -1
- package/dist/build/generate-ai-content.d.ts.map +1 -1
- package/dist/build/generate-ai-content.js +7 -0
- package/dist/build/generate-ai-content.js.map +1 -1
- package/dist/cli/generate-ai-content.js +2358 -1556
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +16 -8
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/migrate.js +25 -0
- package/dist/cli/migrate.js.map +1 -1
- package/dist/cli/sync.js +5 -5
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/templates.d.ts.map +1 -1
- package/dist/cli/templates.js +6 -3
- package/dist/cli/templates.js.map +1 -1
- package/dist/comment-store.d.ts +45 -15
- package/dist/comment-store.d.ts.map +1 -1
- package/dist/comment-store.js +80 -101
- package/dist/comment-store.js.map +1 -1
- package/dist/config/helpers.d.ts.map +1 -1
- package/dist/config/helpers.js +3 -0
- package/dist/config/helpers.js.map +1 -1
- package/dist/config/schemas/config.d.ts +3 -4
- package/dist/config/schemas/config.d.ts.map +1 -1
- package/dist/config/schemas/config.js +6 -3
- package/dist/config/schemas/config.js.map +1 -1
- package/dist/config/types.d.ts +6 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config-test.d.ts +5 -3
- package/dist/config-test.d.ts.map +1 -1
- package/dist/config-test.js +9 -1
- package/dist/config-test.js.map +1 -1
- package/dist/content-index-generation.d.ts +40 -4
- package/dist/content-index-generation.d.ts.map +1 -1
- package/dist/content-index-generation.js +70 -73
- package/dist/content-index-generation.js.map +1 -1
- package/dist/content-listing.d.ts +3 -1
- package/dist/content-listing.d.ts.map +1 -1
- package/dist/content-listing.js +1 -0
- package/dist/content-listing.js.map +1 -1
- package/dist/content-store.d.ts +63 -0
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +421 -184
- package/dist/content-store.js.map +1 -1
- package/dist/editor/Editor.d.ts.map +1 -1
- package/dist/editor/Editor.js +19 -15
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/hooks/useDraftManager.d.ts +7 -0
- package/dist/editor/hooks/useDraftManager.d.ts.map +1 -1
- package/dist/editor/hooks/useDraftManager.js +69 -31
- package/dist/editor/hooks/useDraftManager.js.map +1 -1
- package/dist/editor/hooks/useSchemaManager.d.ts +34 -10
- package/dist/editor/hooks/useSchemaManager.d.ts.map +1 -1
- package/dist/editor/hooks/useSchemaManager.js +20 -24
- package/dist/editor/hooks/useSchemaManager.js.map +1 -1
- package/dist/editor/schema-editor/CollectionEditor.d.ts +6 -5
- package/dist/editor/schema-editor/CollectionEditor.d.ts.map +1 -1
- package/dist/editor/schema-editor/CollectionEditor.js +9 -4
- package/dist/editor/schema-editor/CollectionEditor.js.map +1 -1
- package/dist/git-manager.d.ts +15 -10
- package/dist/git-manager.d.ts.map +1 -1
- package/dist/git-manager.js +16 -11
- package/dist/git-manager.js.map +1 -1
- package/dist/github-service.d.ts +63 -7
- package/dist/github-service.d.ts.map +1 -1
- package/dist/github-service.js +147 -39
- package/dist/github-service.js.map +1 -1
- package/dist/resource-generation.d.ts +54 -0
- package/dist/resource-generation.d.ts.map +1 -0
- package/dist/resource-generation.js +156 -0
- package/dist/resource-generation.js.map +1 -0
- package/dist/schema/schema-store.d.ts +29 -2
- package/dist/schema/schema-store.d.ts.map +1 -1
- package/dist/schema/schema-store.js +47 -8
- package/dist/schema/schema-store.js.map +1 -1
- package/dist/services.js +4 -0
- package/dist/services.js.map +1 -1
- package/dist/static/index.d.ts +32 -0
- package/dist/static/index.d.ts.map +1 -1
- package/dist/static/index.js +90 -0
- package/dist/static/index.js.map +1 -1
- package/dist/sync-core.js +2 -2
- package/dist/sync-core.js.map +1 -1
- package/dist/utils/occ-json-write.d.ts +100 -0
- package/dist/utils/occ-json-write.d.ts.map +1 -0
- package/dist/utils/occ-json-write.js +234 -0
- package/dist/utils/occ-json-write.js.map +1 -0
- package/dist/worker/cms-worker.d.ts +6 -3
- package/dist/worker/cms-worker.d.ts.map +1 -1
- package/dist/worker/cms-worker.js +42 -41
- package/dist/worker/cms-worker.js.map +1 -1
- package/package.json +2 -1
package/dist/branch-registry.js
CHANGED
|
@@ -1,85 +1,211 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { BranchMetadataFileManager } from './branch-metadata.js';
|
|
4
|
-
import { isNotFoundError } from './utils/error.js';
|
|
4
|
+
import { isNotFoundError, getErrorMessage } from './utils/error.js';
|
|
5
|
+
import { createDebugLogger } from './utils/debug.js';
|
|
6
|
+
import { bumpResourceGeneration, readResourceGeneration, isGenerationCurrent, } from './resource-generation.js';
|
|
7
|
+
const log = createDebugLogger({ prefix: 'BranchRegistry' });
|
|
5
8
|
// Registry files are stored directly in the branches root (not in a subdirectory)
|
|
6
9
|
const REGISTRY_FILE = 'branches.json';
|
|
10
|
+
// Legacy rename-based invalidation scheme's stale marker file. No longer written -
|
|
11
|
+
// invalidate() now bumps the cross-process generation marker instead - but a
|
|
12
|
+
// process upgraded mid-flight may find one left over from before the deploy, so
|
|
13
|
+
// regenerate() opportunistically deletes it. Keep this name only for that cleanup.
|
|
7
14
|
const REGISTRY_STALE_FILE = 'branches.stale.json';
|
|
8
15
|
const REGISTRY_TEMP_FILE = 'branches.tmp.json';
|
|
9
|
-
const REGISTRY_VERSION =
|
|
16
|
+
const REGISTRY_VERSION = 2;
|
|
17
|
+
/** resource-generation.ts resource key for the branch registry's marker. */
|
|
18
|
+
const RESOURCE = 'branch-registry';
|
|
19
|
+
/** Throttle for the get() suspicious-miss backstop; mirrors content-store's FORCED_REFRESH_MIN_INTERVAL_MS. */
|
|
20
|
+
const GET_MISS_REFRESH_MIN_INTERVAL_MS = 5000;
|
|
10
21
|
/**
|
|
11
22
|
* BranchRegistry is a read-only cache for fast branch listing.
|
|
12
23
|
* Individual branch.json files are the source of truth.
|
|
13
24
|
*
|
|
25
|
+
* ## Cross-process freshness (resource-generation.ts marker protocol)
|
|
26
|
+
*
|
|
27
|
+
* Several warm Lambda containers plus the EC2 worker can share one branch-clone
|
|
28
|
+
* root over EFS, each with its own in-process cache of `branches.json`. There is
|
|
29
|
+
* no shared memory and no cross-host file watching, so freshness is coordinated
|
|
30
|
+
* via the generic on-disk generation marker in resource-generation.ts: every
|
|
31
|
+
* snapshot embeds the marker token it was built against, and every read
|
|
32
|
+
* cheaply re-checks the live marker before trusting the cached snapshot. See
|
|
33
|
+
* that module's doc comment for the full protocol and residual staleness
|
|
34
|
+
* windows (A/B/C/E).
|
|
35
|
+
*
|
|
36
|
+
* BranchRegistry is exactly the "durable snapshot consumer" case called out
|
|
37
|
+
* there: a regeneration whose scan is served from stale NFS dentry/attribute
|
|
38
|
+
* caches can record a FRESH token over STALE data (window E), and because the
|
|
39
|
+
* result is written to `branches.json`, that staleness becomes durable and
|
|
40
|
+
* shared with every other host that reads the marker - not just one process's
|
|
41
|
+
* memory. Two mitigations close the practical gap:
|
|
42
|
+
*
|
|
43
|
+
* - invalidate() eager-regenerates immediately after its own bump. The
|
|
44
|
+
* mutating host's own scan is necessarily coherent with the mutation it just
|
|
45
|
+
* made (no NFS round trip was needed to observe its own write), so this
|
|
46
|
+
* closes window (E) for the common case without waiting for some other host
|
|
47
|
+
* to lazily pull the change.
|
|
48
|
+
* - get() implements a suspicious-miss backstop: a branch that "should" exist
|
|
49
|
+
* but is absent from the cached snapshot forces one fresh regeneration
|
|
50
|
+
* (throttled), bounding how long a bad snapshot can hide a real branch.
|
|
51
|
+
*
|
|
52
|
+
* regenerate() also skips persisting a snapshot when the marker read itself
|
|
53
|
+
* fails (`{ ok: false }`, e.g. unreadable for a non-ENOENT reason): a snapshot
|
|
54
|
+
* embedding a token we cannot attribute to "matches this scan" would be
|
|
55
|
+
* indistinguishable from a correctly-attributed one to every future reader, so
|
|
56
|
+
* the safer behavior is to serve the fresh scan without writing it durably and
|
|
57
|
+
* let the next read retry the marker.
|
|
58
|
+
*
|
|
14
59
|
* Design:
|
|
15
|
-
* - list() returns cached
|
|
16
|
-
*
|
|
17
|
-
* -
|
|
60
|
+
* - list() returns the cached snapshot if its embedded generation token still
|
|
61
|
+
* matches the live marker, regenerates from branch.json files otherwise
|
|
62
|
+
* - invalidate() bumps the marker (durable, cross-process) and eager-regenerates
|
|
63
|
+
* - Concurrent regeneration within one process is deduped to a single scan
|
|
64
|
+
* (regenInFlight); across processes, regeneration is still safe since all
|
|
65
|
+
* processes produce identical output from the same branch.json files
|
|
18
66
|
*/
|
|
19
67
|
export class BranchRegistry {
|
|
20
68
|
constructor(root) {
|
|
69
|
+
/** Shared in-flight scan so concurrent list()/get() callers on one instance await a single regeneration. */
|
|
70
|
+
this.regenInFlight = null;
|
|
71
|
+
/** Throttle clock for the get() suspicious-miss backstop. */
|
|
72
|
+
this.lastForcedRefreshMs = 0;
|
|
21
73
|
this.root = path.resolve(root);
|
|
22
74
|
this.registryPath = path.join(this.root, REGISTRY_FILE);
|
|
23
75
|
this.stalePath = path.join(this.root, REGISTRY_STALE_FILE);
|
|
24
76
|
this.tempPath = path.join(this.root, REGISTRY_TEMP_FILE);
|
|
25
77
|
}
|
|
26
78
|
/**
|
|
27
|
-
* Returns all branches. Uses
|
|
79
|
+
* Returns all branches. Uses the cached snapshot if its embedded generation
|
|
80
|
+
* token matches the live marker, regenerates otherwise.
|
|
28
81
|
*/
|
|
29
82
|
async list() {
|
|
83
|
+
let parsed;
|
|
30
84
|
try {
|
|
31
85
|
const raw = await fs.readFile(this.registryPath, 'utf8');
|
|
32
|
-
|
|
33
|
-
if (!parsed.version || !Array.isArray(parsed.branches)) {
|
|
34
|
-
// Invalid cache, regenerate
|
|
35
|
-
return await this.regenerate();
|
|
36
|
-
}
|
|
37
|
-
return parsed.branches;
|
|
86
|
+
parsed = JSON.parse(raw);
|
|
38
87
|
}
|
|
39
88
|
catch (err) {
|
|
40
89
|
if (isNotFoundError(err)) {
|
|
41
|
-
// Cache missing or stale, regenerate
|
|
42
90
|
return await this.regenerate();
|
|
43
91
|
}
|
|
44
92
|
throw err;
|
|
45
93
|
}
|
|
94
|
+
// Strict version check: a truthy-only check would accept a persisted v1
|
|
95
|
+
// snapshot (no `generation` field) left on EFS after a rolling deploy,
|
|
96
|
+
// and `parsed.generation` would then be `undefined` rather than a real
|
|
97
|
+
// token or explicit `null`, breaking the freshness comparison below.
|
|
98
|
+
if (parsed.version !== REGISTRY_VERSION || !Array.isArray(parsed.branches)) {
|
|
99
|
+
return await this.regenerate();
|
|
100
|
+
}
|
|
101
|
+
const read = await readResourceGeneration(this.root, RESOURCE);
|
|
102
|
+
if (isGenerationCurrent(parsed.generation, read)) {
|
|
103
|
+
return parsed.branches;
|
|
104
|
+
}
|
|
105
|
+
return await this.regenerate();
|
|
46
106
|
}
|
|
47
107
|
/**
|
|
48
108
|
* Returns a single branch by name. Uses cache if available.
|
|
109
|
+
*
|
|
110
|
+
* Suspicious-miss backstop: if `name` isn't in the (possibly cached) list,
|
|
111
|
+
* that's a signal the snapshot may be stale in the specific direction where
|
|
112
|
+
* a branch that now exists is missing from it - force one fresh
|
|
113
|
+
* regeneration and re-search before giving up, throttled per instance so a
|
|
114
|
+
* genuinely-absent branch costs at most one extra scan per throttle window.
|
|
115
|
+
* This bounds durable window-E staleness for the "branch exists but
|
|
116
|
+
* snapshot predates it" direction; see the class doc comment.
|
|
49
117
|
*/
|
|
50
118
|
async get(name) {
|
|
51
119
|
const branches = await this.list();
|
|
52
|
-
|
|
120
|
+
const found = branches.find((b) => b.branch.name === name);
|
|
121
|
+
if (found)
|
|
122
|
+
return found;
|
|
123
|
+
if (!this.shouldForceRefresh())
|
|
124
|
+
return undefined;
|
|
125
|
+
const refreshed = await this.regenerate();
|
|
126
|
+
return refreshed.find((b) => b.branch.name === name);
|
|
127
|
+
}
|
|
128
|
+
shouldForceRefresh() {
|
|
129
|
+
const now = Date.now();
|
|
130
|
+
if (now - this.lastForcedRefreshMs < GET_MISS_REFRESH_MIN_INTERVAL_MS)
|
|
131
|
+
return false;
|
|
132
|
+
this.lastForcedRefreshMs = now;
|
|
133
|
+
return true;
|
|
53
134
|
}
|
|
54
135
|
/**
|
|
55
|
-
* Marks the cache as stale
|
|
56
|
-
*
|
|
136
|
+
* Marks the cache as stale for every process sharing this root by bumping
|
|
137
|
+
* the cross-process generation marker, then eager-regenerates on this host
|
|
138
|
+
* (see class doc comment for why). The bump must succeed: a swallowed
|
|
139
|
+
* failure here would leave the registry stale indefinitely with no
|
|
140
|
+
* bounding backstop, unlike a failed eager regen below, which only forgoes
|
|
141
|
+
* closing window (E) early - the bump alone already restored correctness
|
|
142
|
+
* for every future reader. A failed eager regen therefore must not fail the
|
|
143
|
+
* caller's save/delete.
|
|
57
144
|
*/
|
|
58
145
|
async invalidate() {
|
|
146
|
+
await bumpResourceGeneration(this.root, RESOURCE, { mustSucceed: true });
|
|
59
147
|
try {
|
|
60
|
-
|
|
148
|
+
// A scan already in flight (a concurrent list() on this shared
|
|
149
|
+
// instance) captured the PRE-bump token and possibly pre-mutation
|
|
150
|
+
// state; joining it via regenerate()'s dedup would skip the eager
|
|
151
|
+
// post-bump scan this method exists for. Let it drain — its snapshot
|
|
152
|
+
// self-describes as stale either way — then run a fresh scan.
|
|
153
|
+
if (this.regenInFlight)
|
|
154
|
+
await this.regenInFlight.catch(() => { });
|
|
155
|
+
await this.regenerate();
|
|
61
156
|
}
|
|
62
157
|
catch (err) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
158
|
+
log.warn('invalidate', 'Eager regeneration after invalidate() failed', {
|
|
159
|
+
error: getErrorMessage(err),
|
|
160
|
+
});
|
|
67
161
|
}
|
|
68
162
|
}
|
|
69
163
|
/**
|
|
70
|
-
* Scans branch directories and rebuilds the cache
|
|
71
|
-
*
|
|
164
|
+
* Scans branch directories and rebuilds the cache, deduping concurrent
|
|
165
|
+
* callers on this instance to a single underlying scan. Never loops waiting
|
|
166
|
+
* for the snapshot's embedded token to match the marker - under a bump
|
|
167
|
+
* storm that could livelock; a caller that wants the very latest state
|
|
168
|
+
* after a concurrent bump should call list() again after this resolves.
|
|
72
169
|
*/
|
|
73
170
|
async regenerate() {
|
|
171
|
+
if (this.regenInFlight)
|
|
172
|
+
return this.regenInFlight;
|
|
173
|
+
const run = this.performRegenerate();
|
|
174
|
+
this.regenInFlight = run;
|
|
175
|
+
try {
|
|
176
|
+
return await run;
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
this.regenInFlight = null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async performRegenerate() {
|
|
183
|
+
// Capture the marker strictly BEFORE scanning: a bump landing mid-scan
|
|
184
|
+
// then differs from the token recorded below, forcing a rebuild on the
|
|
185
|
+
// next probe instead of silently resurrecting stale data.
|
|
186
|
+
const read = await readResourceGeneration(this.root, RESOURCE);
|
|
74
187
|
const branches = await this.scanBranchDirectories();
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
188
|
+
// Opportunistic cleanup of a stale file left by the old rename-based
|
|
189
|
+
// invalidation scheme (e.g. a process upgraded mid-flight). Not load-bearing.
|
|
190
|
+
await fs.unlink(this.stalePath).catch(() => { });
|
|
191
|
+
if (!read.ok) {
|
|
192
|
+
// The marker couldn't be read for a reason other than "never bumped" -
|
|
193
|
+
// we cannot attribute a token to this scan, and stamping the snapshot
|
|
194
|
+
// with an unattributable token would make it indistinguishable from a
|
|
195
|
+
// correctly-attributed one to every future reader on any host. Serve
|
|
196
|
+
// the fresh scan result without persisting it; the next read retries
|
|
197
|
+
// the marker read and, on success, regenerates and persists normally.
|
|
198
|
+
return branches;
|
|
199
|
+
}
|
|
79
200
|
const snapshot = {
|
|
80
201
|
version: REGISTRY_VERSION,
|
|
81
202
|
branches,
|
|
203
|
+
generation: read.token,
|
|
82
204
|
};
|
|
205
|
+
// Write to unique temp file first, then atomic rename.
|
|
206
|
+
// Use random suffix to avoid conflicts between concurrent regenerations.
|
|
207
|
+
const uniqueTempPath = `${this.tempPath}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
208
|
+
await fs.mkdir(this.root, { recursive: true });
|
|
83
209
|
await fs.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + '\n', 'utf8');
|
|
84
210
|
try {
|
|
85
211
|
await fs.rename(uniqueTempPath, this.registryPath);
|
|
@@ -89,12 +215,12 @@ export class BranchRegistry {
|
|
|
89
215
|
await fs.unlink(uniqueTempPath).catch(() => { });
|
|
90
216
|
throw err;
|
|
91
217
|
}
|
|
92
|
-
// Clean up stale file (ignore errors)
|
|
93
|
-
await fs.unlink(this.stalePath).catch(() => { });
|
|
94
218
|
return branches;
|
|
95
219
|
}
|
|
96
220
|
/**
|
|
97
221
|
* Scans the root directory for branch subdirectories with valid branch.json files.
|
|
222
|
+
* Protected (rather than exported as a test hook) so tests can subclass and
|
|
223
|
+
* override to simulate cross-process interleavings.
|
|
98
224
|
*/
|
|
99
225
|
async scanBranchDirectories() {
|
|
100
226
|
const branches = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-registry.js","sourceRoot":"","sources":["../src/branch-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACjC,OAAO,IAAI,MAAM,WAAW,CAAA;AAG5B,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"branch-registry.js","sourceRoot":"","sources":["../src/branch-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { RootCollectionConfig } from './config';
|
|
2
2
|
import type { FlatSchemaItem } from './config/types';
|
|
3
3
|
import type { OperatingMode } from './operating-mode';
|
|
4
|
-
import type { EntrySchemaRegistry } from './schema/types';
|
|
4
|
+
import type { EntrySchemaRegistry, SchemaResolutionResult } from './schema/types';
|
|
5
|
+
/** resource-generation.ts resource key for the schema cache's marker. */
|
|
6
|
+
export declare const SCHEMA_GENERATION_RESOURCE = "schema";
|
|
5
7
|
/**
|
|
6
8
|
* Schema cache structure stored in {branchRoot}/.canopy-meta/schema-cache.json
|
|
7
9
|
*/
|
|
@@ -10,19 +12,84 @@ export interface BranchSchemaCacheEntry {
|
|
|
10
12
|
schema: RootCollectionConfig;
|
|
11
13
|
flatSchema: FlatSchemaItem[];
|
|
12
14
|
cachedAt: string;
|
|
15
|
+
/**
|
|
16
|
+
* The resource-generation.ts marker token this snapshot was resolved
|
|
17
|
+
* against, or null if it was built before any bump ever occurred on this
|
|
18
|
+
* root. Compared against the live marker (via isGenerationCurrent) to
|
|
19
|
+
* decide freshness. See the class doc comment.
|
|
20
|
+
*/
|
|
21
|
+
generation: string | null;
|
|
13
22
|
}
|
|
14
23
|
/**
|
|
15
|
-
* Manages per-branch schema caching with lazy loading and automatic
|
|
24
|
+
* Manages per-branch schema caching with lazy loading and automatic
|
|
25
|
+
* invalidation.
|
|
16
26
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* -
|
|
27
|
+
* ## Cross-process freshness (resource-generation.ts marker protocol)
|
|
28
|
+
*
|
|
29
|
+
* Several warm Lambda containers plus the EC2 worker can share one
|
|
30
|
+
* branch-clone root over EFS, each with its own copy of
|
|
31
|
+
* `schema-cache.json`. There is no shared memory and no cross-host file
|
|
32
|
+
* watching, so freshness is coordinated via the generic on-disk generation
|
|
33
|
+
* marker in resource-generation.ts: every cached snapshot embeds the marker
|
|
34
|
+
* token it was resolved against, and every read cheaply re-checks the live
|
|
35
|
+
* marker before trusting the cache. See that module's doc comment for the
|
|
36
|
+
* full protocol and residual staleness windows (A/B/C/E).
|
|
37
|
+
*
|
|
38
|
+
* BranchSchemaCache is the "durable snapshot consumer" case called out there:
|
|
39
|
+
* a resolve whose scan is served from stale NFS dentry/attribute caches can
|
|
40
|
+
* record a FRESH token over STALE data (window E), and because the result is
|
|
41
|
+
* written to `schema-cache.json`, that staleness becomes durable and shared
|
|
42
|
+
* with every other host that reads the marker - not just one process's
|
|
43
|
+
* memory. `loadFromCacheOrResolve` mitigates this the same way
|
|
44
|
+
* BranchRegistry does:
|
|
21
45
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
46
|
+
* - The marker is captured strictly BEFORE `resolveFresh()` runs, and the
|
|
47
|
+
* captured token (not one read after resolving) is what gets embedded in
|
|
48
|
+
* the persisted snapshot. A bump landing mid-resolve therefore leaves the
|
|
49
|
+
* embedded token older than the live marker, forcing a re-resolve on the
|
|
50
|
+
* next read instead of silently resurrecting stale schema.
|
|
51
|
+
* - A snapshot is only persisted when the marker read itself succeeded
|
|
52
|
+
* (`{ ok: true }`). If the read fails for a reason other than "never
|
|
53
|
+
* bumped", we cannot attribute a token to this resolve, and stamping the
|
|
54
|
+
* snapshot with an unattributable token would make it indistinguishable
|
|
55
|
+
* from a correctly-attributed one to every future reader. The fresh result
|
|
56
|
+
* is still served to this caller; it just isn't written durably.
|
|
57
|
+
* - invalidate() bumps the marker with `mustSucceed: true`: an explicit
|
|
58
|
+
* invalidation (e.g. after a schema mutation) must not silently fail and
|
|
59
|
+
* leave every reader confidently stale with no bounding backstop.
|
|
60
|
+
* invalidate() itself does NOT eager-regenerate - it isn't given the
|
|
61
|
+
* entrySchemaRegistry/contentRootName that resolveSchema needs. The
|
|
62
|
+
* eager re-resolve (window-E mitigation, mirroring BranchRegistry's
|
|
63
|
+
* eager regen) lives one level up in SchemaOps.invalidateSchemaCache()
|
|
64
|
+
* (schema/schema-store.ts), which has those arguments and calls
|
|
65
|
+
* {@link resolveAndPersist} (NOT getSchema()) right after invalidating -
|
|
66
|
+
* so every SchemaOps mutation re-resolves on the mutating host via a scan
|
|
67
|
+
* that is GUARANTEED to run, not merely likely to run. getSchema() would
|
|
68
|
+
* be the wrong call here: its cache-read fast path can short-circuit
|
|
69
|
+
* through a snapshot a DIFFERENT concurrent host just wrote (see
|
|
70
|
+
* resolveAndPersist()'s doc comment for the exact race), silently
|
|
71
|
+
* skipping the one scan this eager re-resolve exists to guarantee. (The
|
|
72
|
+
* editor's follow-up schema GET is a separate Lambda invocation with no
|
|
73
|
+
* container affinity, so it could not serve this purpose either.)
|
|
74
|
+
* Callers of invalidate() that bypass SchemaOps (api/schema.ts's explicit
|
|
75
|
+
* invalidate endpoint; the bulk-mutation bump in
|
|
76
|
+
* invalidateBranchContentCaches) accept the lazy next-read regen.
|
|
77
|
+
*
|
|
78
|
+
* This is also why prod needs no mtime walk: the dev-only mtime check below
|
|
79
|
+
* exists solely to catch hand edits to .collection.json made outside the CMS
|
|
80
|
+
* (which bypass SchemaOps and therefore never bump the marker). Every
|
|
81
|
+
* mutation path that matters in prod - SchemaOps writes, git working-tree
|
|
82
|
+
* rewrites (checkout/merge/rebase/sync/migrate) - bumps the marker via
|
|
83
|
+
* SchemaOps.invalidateSchemaCache() or the combined
|
|
84
|
+
* invalidateBranchContentCaches() helper in content-index-generation.ts, so
|
|
85
|
+
* the marker alone is a sufficient prod backstop.
|
|
86
|
+
*
|
|
87
|
+
* Caching Strategy:
|
|
88
|
+
* - File-based cache at {branchRoot}/.canopy-meta/schema-cache.json (no
|
|
89
|
+
* in-memory layer - intentional: matches prod behavior and keeps cache
|
|
90
|
+
* coherent across Lambda invocations)
|
|
91
|
+
* - Invalidation: invalidate() bumps the cross-process generation marker;
|
|
92
|
+
* every reader sharing this branchRoot re-resolves at its next access.
|
|
26
93
|
*/
|
|
27
94
|
export declare class BranchSchemaCache {
|
|
28
95
|
/** Tracks when we last checked mtimes per contentRoot, to debounce rapid requests */
|
|
@@ -51,12 +118,60 @@ export declare class BranchSchemaCache {
|
|
|
51
118
|
schema: RootCollectionConfig;
|
|
52
119
|
flatSchema: FlatSchemaItem[];
|
|
53
120
|
}>;
|
|
121
|
+
/**
|
|
122
|
+
* Resolve the schema from disk. Wrapped in a protected method (rather than
|
|
123
|
+
* calling the imported resolveSchema directly) so tests can subclass and
|
|
124
|
+
* override with a deferred/blocking implementation to simulate cross-process
|
|
125
|
+
* interleavings — mirrors BranchRegistry's scanBranchDirectories() hook.
|
|
126
|
+
*/
|
|
127
|
+
protected resolveFresh(contentRoot: string, entrySchemaRegistry: EntrySchemaRegistry): Promise<SchemaResolutionResult>;
|
|
54
128
|
/**
|
|
55
129
|
* Load schema from cache or resolve fresh if cache is missing or stale.
|
|
56
130
|
*/
|
|
57
131
|
private loadFromCacheOrResolve;
|
|
58
132
|
/**
|
|
59
|
-
*
|
|
133
|
+
* Resolve the schema fresh from disk and persist it (subject to the
|
|
134
|
+
* skip-persist rule below), UNCONDITIONALLY -- never through the cache-read
|
|
135
|
+
* fast path in {@link loadFromCacheOrResolve}. Shared by that method's
|
|
136
|
+
* cache-miss fallback and by {@link resolveAndPersist}.
|
|
137
|
+
*/
|
|
138
|
+
private resolveFreshAndPersist;
|
|
139
|
+
/**
|
|
140
|
+
* Resolve the schema fresh from disk and persist it, SKIPPING the cache
|
|
141
|
+
* read entirely -- unlike {@link getSchema}, which can short-circuit
|
|
142
|
+
* through a cache HIT. That distinction matters for eager re-resolve after
|
|
143
|
+
* invalidation (see {@link invalidate}'s doc comment and
|
|
144
|
+
* `SchemaOps.invalidateSchemaCache()` in schema/schema-store.ts, the sole
|
|
145
|
+
* intended caller): the whole point of an eager re-resolve is to guarantee
|
|
146
|
+
* ONE scan that is coherent with the mutation this host just made. A plain
|
|
147
|
+
* `getSchema()` call right after `invalidate()` is NOT that guarantee --
|
|
148
|
+
* `getSchema()`'s cache-read fast path (`loadFromCacheOrResolve`) can still
|
|
149
|
+
* return a snapshot written by a DIFFERENT, concurrent host: if that
|
|
150
|
+
* foreign host's own eager re-resolve raced this one and embedded the
|
|
151
|
+
* (now-current) marker token over ITS OWN stale-NFS-cache scan (the
|
|
152
|
+
* window-E case this class's doc comment describes), this host's
|
|
153
|
+
* `getSchema()` would happily accept that foreign snapshot as "current"
|
|
154
|
+
* and skip the one scan this call was specifically trying to guarantee.
|
|
155
|
+
* `resolveAndPersist()` never reads the cache file at all, so it cannot be
|
|
156
|
+
* short-circuited that way.
|
|
157
|
+
*/
|
|
158
|
+
resolveAndPersist(branchRoot: string, entrySchemaRegistry: EntrySchemaRegistry, contentRootName?: string): Promise<{
|
|
159
|
+
schema: RootCollectionConfig;
|
|
160
|
+
flatSchema: FlatSchemaItem[];
|
|
161
|
+
}>;
|
|
162
|
+
/**
|
|
163
|
+
* Invalidate cache for a branch by bumping the cross-process generation
|
|
164
|
+
* marker (resource-generation.ts). Every process sharing this branchRoot
|
|
165
|
+
* will re-resolve at its next read.
|
|
166
|
+
*
|
|
167
|
+
* The bump must succeed: a swallowed failure here would leave the schema
|
|
168
|
+
* cache stale indefinitely with no bounding backstop (unlike
|
|
169
|
+
* BranchRegistry, there is no get-miss backstop for a resolved schema).
|
|
170
|
+
*
|
|
171
|
+
* No eager regeneration here (unlike BranchRegistry.invalidate()): this
|
|
172
|
+
* method isn't given the entrySchemaRegistry/contentRootName resolveSchema
|
|
173
|
+
* needs. See the class doc comment for why the mutating request's own
|
|
174
|
+
* follow-up schema read closes the same window-E gap instead.
|
|
60
175
|
*
|
|
61
176
|
* @param branchRoot - Root directory of the branch
|
|
62
177
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-schema-cache.d.ts","sourceRoot":"","sources":["../src/branch-schema-cache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"branch-schema-cache.d.ts","sourceRoot":"","sources":["../src/branch-schema-cache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAiBjF,yEAAyE;AACzE,eAAO,MAAM,0BAA0B,WAAW,CAAA;AAElD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,oBAAoB,CAAA;IAC5B,UAAU,EAAE,cAAc,EAAE,CAAA;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAmCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AACH,qBAAa,iBAAiB;IAC5B,qFAAqF;IACrF,OAAO,CAAC,cAAc,CAA4B;IAElD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,IAAI,GAAE,aAAsB;IAIxC;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa;IAIrB;;;;;;;OAOG;IACG,SAAS,CACb,UAAU,EAAE,MAAM,EAClB,mBAAmB,EAAE,mBAAmB,EACxC,eAAe,GAAE,MAAkB,GAClC,OAAO,CAAC;QAAE,MAAM,EAAE,oBAAoB,CAAC;QAAC,UAAU,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC;IAI1E;;;;;OAKG;cACa,YAAY,CAC1B,WAAW,EAAE,MAAM,EACnB,mBAAmB,EAAE,mBAAmB,GACvC,OAAO,CAAC,sBAAsB,CAAC;IAIlC;;OAEG;YACW,sBAAsB;IA+DpC;;;;;OAKG;YACW,sBAAsB;IA0EpC;;;;;;;;;;;;;;;;;;OAkBG;IACG,iBAAiB,CACrB,UAAU,EAAE,MAAM,EAClB,mBAAmB,EAAE,mBAAmB,EACxC,eAAe,GAAE,MAAkB,GAClC,OAAO,CAAC;QAAE,MAAM,EAAE,oBAAoB,CAAC;QAAC,UAAU,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC;IAO1E;;;;;;;;;;;;;;;OAeG;IACG,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAKpD"}
|