@se-studio/contentful-cms 3.4.3 → 3.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/commands/add-target-field.d.ts +15 -0
  3. package/dist/commands/add-target-field.d.ts.map +1 -0
  4. package/dist/commands/add-target-field.js +48 -0
  5. package/dist/commands/add-target-field.js.map +1 -0
  6. package/dist/commands/add.d.ts.map +1 -1
  7. package/dist/commands/add.js +89 -73
  8. package/dist/commands/add.js.map +1 -1
  9. package/dist/commands/media.d.ts.map +1 -1
  10. package/dist/commands/media.js +14 -0
  11. package/dist/commands/media.js.map +1 -1
  12. package/dist/generated/hosted-guide.d.ts +1 -1
  13. package/dist/generated/hosted-guide.d.ts.map +1 -1
  14. package/dist/generated/hosted-guide.js +1 -1
  15. package/dist/generated/hosted-guide.js.map +1 -1
  16. package/dist/generated/version.d.ts +1 -1
  17. package/dist/generated/version.js +1 -1
  18. package/dist/hosted/next/redis.d.ts.map +1 -1
  19. package/dist/hosted/next/redis.js +14 -0
  20. package/dist/hosted/next/redis.js.map +1 -1
  21. package/dist/hosted/next/routes/health.d.ts.map +1 -1
  22. package/dist/hosted/next/routes/health.js +3 -1
  23. package/dist/hosted/next/routes/health.js.map +1 -1
  24. package/dist/hosted/next/routes/mcp.js +1 -1
  25. package/dist/hosted/next/routes/mcp.js.map +1 -1
  26. package/dist/hosted/oauth/auth.d.ts +3 -1
  27. package/dist/hosted/oauth/auth.d.ts.map +1 -1
  28. package/dist/hosted/oauth/auth.js +4 -2
  29. package/dist/hosted/oauth/auth.js.map +1 -1
  30. package/dist/hosted/oauth/handlers.js +1 -1
  31. package/dist/hosted/oauth/handlers.js.map +1 -1
  32. package/dist/hosted/oauth/store.d.ts +13 -3
  33. package/dist/hosted/oauth/store.d.ts.map +1 -1
  34. package/dist/hosted/oauth/store.js +32 -6
  35. package/dist/hosted/oauth/store.js.map +1 -1
  36. package/dist/mcp/tools/cms-edit.d.ts.map +1 -1
  37. package/dist/mcp/tools/cms-edit.js +31 -2
  38. package/dist/mcp/tools/cms-edit.js.map +1 -1
  39. package/dist/mcp/tools/media.d.ts.map +1 -1
  40. package/dist/mcp/tools/media.js +33 -2
  41. package/dist/mcp/tools/media.js.map +1 -1
  42. package/dist/media-pipeline/import-filter.d.ts +36 -0
  43. package/dist/media-pipeline/import-filter.d.ts.map +1 -0
  44. package/dist/media-pipeline/import-filter.js +53 -0
  45. package/dist/media-pipeline/import-filter.js.map +1 -0
  46. package/dist/media-pipeline/import.d.ts +6 -0
  47. package/dist/media-pipeline/import.d.ts.map +1 -1
  48. package/dist/media-pipeline/import.js +28 -1
  49. package/dist/media-pipeline/import.js.map +1 -1
  50. package/dist/media-pipeline/job-store.d.ts.map +1 -1
  51. package/dist/media-pipeline/job-store.js +31 -0
  52. package/dist/media-pipeline/job-store.js.map +1 -1
  53. package/dist/preview/entry-urls.d.ts +13 -2
  54. package/dist/preview/entry-urls.d.ts.map +1 -1
  55. package/dist/preview/entry-urls.js +49 -11
  56. package/dist/preview/entry-urls.js.map +1 -1
  57. package/dist/session/redis-session-store.d.ts +20 -3
  58. package/dist/session/redis-session-store.d.ts.map +1 -1
  59. package/dist/session/redis-session-store.js +81 -74
  60. package/dist/session/redis-session-store.js.map +1 -1
  61. package/dist/session/session-store.d.ts +5 -0
  62. package/dist/session/session-store.d.ts.map +1 -1
  63. package/dist/session/session-store.js.map +1 -1
  64. package/docs/editor-tasks/create-from-document.md +3 -2
  65. package/docs/editor-tasks/media-pipeline-prepare.md +28 -8
  66. package/docs/editor-tasks/media-reuse-and-upload.md +13 -3
  67. package/docs/editor-tasks/preview-verify.md +7 -6
  68. package/docs/mcp/hosted-guide.md +11 -0
  69. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { getRuntimeContext } from '../runtime/context.js';
2
- import { isSessionLoggingEnabled, sessionLog, sessionLogVerbose } from './log.js';
2
+ import { sessionLog, sessionLogVerbose } from './log.js';
3
3
  import { buildSessionKey } from './session-store.js';
4
4
  const SESSION_TTL_SECONDS = 60 * 60 * 24 * 7; // 7 days
5
5
  /** Parses session JSON from Redis — Upstash may return a deserialized object. */
@@ -31,6 +31,17 @@ export function coerceActivePointerValue(raw) {
31
31
  }
32
32
  return null;
33
33
  }
34
+ /**
35
+ * Space id for multi-tenant Redis isolation on shared cmsedit-sessions.
36
+ * Prefer explicit arg; fall back to host env (CONTENTFUL_SPACE_ID).
37
+ */
38
+ export function resolveSessionSpaceId(explicit) {
39
+ const fromArg = explicit?.trim();
40
+ if (fromArg)
41
+ return fromArg;
42
+ const fromEnv = process.env['CONTENTFUL_SPACE_ID']?.trim();
43
+ return fromEnv || undefined;
44
+ }
34
45
  export class RedisSessionStore {
35
46
  redis;
36
47
  keyPrefix;
@@ -41,11 +52,24 @@ export class RedisSessionStore {
41
52
  fullKey(sessionKey) {
42
53
  return `${this.keyPrefix}${sessionKey}`;
43
54
  }
44
- activePointerKey(userId, sessionName) {
55
+ /**
56
+ * Active pointer is space-scoped so shared Redis across hosts does not
57
+ * cross-load sessions for the same Contentful user.
58
+ * - default: cms-edit:session:{userId}:active:{spaceId}
59
+ * - named: cms-edit:session:{userId}:active:{spaceId}:{sessionName}
60
+ */
61
+ activePointerKey(userId, spaceId, sessionName) {
45
62
  if (sessionName) {
46
- return `${this.keyPrefix}${userId}:active:${sessionName}`;
63
+ return `${this.keyPrefix}${userId}:active:${spaceId}:${sessionName}`;
47
64
  }
48
- return `${this.keyPrefix}${userId}:active`;
65
+ return `${this.keyPrefix}${userId}:active:${spaceId}`;
66
+ }
67
+ /**
68
+ * SET of full Redis keys (session blobs + pointers) for this user+space.
69
+ * Enables clearAll without KEYS scans on shared Upstash.
70
+ */
71
+ spaceIndexKey(userId, spaceId) {
72
+ return `${this.keyPrefix}${userId}:index:${spaceId}`;
49
73
  }
50
74
  getUserId() {
51
75
  const userId = getRuntimeContext()?.user?.userId;
@@ -58,38 +82,53 @@ export class RedisSessionStore {
58
82
  const userId = this.getUserId();
59
83
  if (spaceId && rootEntryId) {
60
84
  const sessionKey = buildSessionKey(userId, spaceId, rootEntryId, sessionName);
61
- sessionLogVerbose('redis load by id', { userId, sessionName, sessionKey });
85
+ sessionLogVerbose('redis load by id', { userId, sessionName, sessionKey, spaceId });
62
86
  const state = await this.readSession(sessionKey);
63
87
  sessionLog(state ? 'redis load hit' : 'redis load miss', {
64
88
  userId,
65
89
  sessionName,
66
90
  sessionKey,
91
+ spaceId,
67
92
  lookup: 'explicit',
68
93
  });
69
94
  return state;
70
95
  }
71
- const pointerKey = this.activePointerKey(userId, sessionName);
72
- const resolvedKey = await this.resolveActiveSessionKey(userId, sessionName);
96
+ const resolvedSpaceId = resolveSessionSpaceId(spaceId);
97
+ if (!resolvedSpaceId) {
98
+ sessionLog('redis load miss', {
99
+ userId,
100
+ sessionName,
101
+ lookup: 'active',
102
+ reason: 'missing_space_id',
103
+ });
104
+ return null;
105
+ }
106
+ // Active load is pointer-only — no KEYS scan / "latest session" guessing.
107
+ const pointerKey = this.activePointerKey(userId, resolvedSpaceId, sessionName);
108
+ const activeKey = coerceActivePointerValue(await this.redis.get(pointerKey));
73
109
  sessionLogVerbose('redis load active', {
74
110
  userId,
75
111
  sessionName,
112
+ spaceId: resolvedSpaceId,
76
113
  pointerKey,
77
- resolvedKey,
114
+ resolvedKey: activeKey,
78
115
  });
79
- if (!resolvedKey) {
116
+ if (!activeKey) {
80
117
  sessionLog('redis load miss', {
81
118
  userId,
82
119
  sessionName,
120
+ spaceId: resolvedSpaceId,
83
121
  pointerKey,
84
122
  lookup: 'active',
85
123
  });
86
124
  return null;
87
125
  }
88
- const state = await this.readSession(resolvedKey);
126
+ const state = await this.readSession(activeKey);
89
127
  sessionLog(state ? 'redis load hit' : 'redis load miss', {
90
128
  userId,
91
129
  sessionName,
92
- sessionKey: resolvedKey,
130
+ spaceId: resolvedSpaceId,
131
+ sessionKey: activeKey,
93
132
  pointerKey,
94
133
  lookup: 'active',
95
134
  });
@@ -98,10 +137,14 @@ export class RedisSessionStore {
98
137
  async save(state, sessionName) {
99
138
  const userId = this.getUserId();
100
139
  const sessionKey = buildSessionKey(userId, state.spaceId, state.rootEntryId, sessionName);
101
- const pointerKey = this.activePointerKey(userId, sessionName);
140
+ const blobKey = this.fullKey(sessionKey);
141
+ const pointerKey = this.activePointerKey(userId, state.spaceId, sessionName);
142
+ const indexKey = this.spaceIndexKey(userId, state.spaceId);
102
143
  const ttl = { ex: SESSION_TTL_SECONDS };
103
- await this.redis.set(this.fullKey(sessionKey), JSON.stringify(state), ttl);
144
+ await this.redis.set(blobKey, JSON.stringify(state), ttl);
104
145
  await this.redis.set(pointerKey, sessionKey, ttl);
146
+ await this.redis.sadd(indexKey, blobKey, pointerKey);
147
+ await this.redis.expire(indexKey, SESSION_TTL_SECONDS);
105
148
  sessionLog('redis save ok', {
106
149
  userId,
107
150
  sessionName,
@@ -115,78 +158,42 @@ export class RedisSessionStore {
115
158
  async clear(spaceId, rootEntryId, sessionName) {
116
159
  const userId = this.getUserId();
117
160
  const sessionKey = buildSessionKey(userId, spaceId, rootEntryId, sessionName);
118
- await this.redis.del(this.fullKey(sessionKey));
119
- const pointerKey = this.activePointerKey(userId, sessionName);
161
+ const blobKey = this.fullKey(sessionKey);
162
+ const pointerKey = this.activePointerKey(userId, spaceId, sessionName);
163
+ const indexKey = this.spaceIndexKey(userId, spaceId);
164
+ await this.redis.del(blobKey);
120
165
  const activeKey = coerceActivePointerValue(await this.redis.get(pointerKey));
121
166
  if (activeKey === sessionKey) {
122
167
  await this.redis.del(pointerKey);
168
+ await this.redis.srem(indexKey, pointerKey);
123
169
  }
170
+ await this.redis.srem(indexKey, blobKey);
124
171
  }
172
+ /**
173
+ * Clears sessions for this host's space only (CONTENTFUL_SPACE_ID / multi-tenant safe).
174
+ * Uses the per-user+space SET index — no KEYS scan.
175
+ */
125
176
  async clearAll() {
126
177
  const userId = this.getUserId();
127
- const keys = await this.redis.keys(`${this.keyPrefix}${userId}:*`);
128
- await Promise.all(keys.map((key) => this.redis.del(key)));
129
- }
130
- async readSession(sessionKey) {
131
- const raw = await this.redis.get(this.fullKey(sessionKey));
132
- return parseSessionValue(raw);
133
- }
134
- async resolveActiveSessionKey(userId, sessionName) {
135
- const pointerKey = this.activePointerKey(userId, sessionName);
136
- const activeKey = coerceActivePointerValue(await this.redis.get(pointerKey));
137
- if (activeKey) {
138
- sessionLogVerbose('redis active pointer hit', { userId, sessionName, pointerKey, activeKey });
139
- return activeKey;
178
+ const spaceId = resolveSessionSpaceId();
179
+ if (!spaceId) {
180
+ sessionLog('redis clearAll skipped', { userId, reason: 'missing_space_id' });
181
+ return;
140
182
  }
141
- sessionLogVerbose('redis active pointer miss, scanning keys', {
183
+ const indexKey = this.spaceIndexKey(userId, spaceId);
184
+ const members = await this.redis.smembers(indexKey);
185
+ sessionLog('redis clearAll', {
142
186
  userId,
143
- sessionName,
144
- pointerKey,
145
- });
146
- const fallback = await this.findLatestSessionKeyFallback(userId, sessionName);
147
- sessionLogVerbose('redis fallback result', {
148
- userId,
149
- sessionName,
150
- resolvedKey: fallback,
187
+ spaceId,
188
+ indexKey,
189
+ memberCount: members.length,
151
190
  });
152
- return fallback;
191
+ await Promise.all(members.map((key) => this.redis.del(key)));
192
+ await this.redis.del(indexKey);
153
193
  }
154
- /** Legacy lookup for sessions saved before active pointers existed. */
155
- async findLatestSessionKeyFallback(userId, sessionName) {
156
- const keys = await this.redis.keys(`${this.keyPrefix}${userId}:*`);
157
- const activeSuffix = sessionName ? `:active:${sessionName}` : ':active';
158
- const suffix = sessionName ? `-${sessionName}` : undefined;
159
- const candidates = keys.filter((key) => {
160
- if (key.endsWith(activeSuffix))
161
- return false;
162
- if (suffix)
163
- return key.endsWith(suffix);
164
- return !key.includes(':active');
165
- });
166
- if (candidates.length === 0)
167
- return null;
168
- sessionLogVerbose('redis fallback candidates', {
169
- userId,
170
- sessionName,
171
- count: candidates.length,
172
- keys: isSessionLoggingEnabled() ? candidates : undefined,
173
- });
174
- let latestKey = null;
175
- let latestFetchedAt = '';
176
- for (const fullKey of candidates) {
177
- const raw = await this.redis.get(fullKey);
178
- const state = parseSessionValue(raw);
179
- if (!state)
180
- continue;
181
- const fetchedAt = state.fetchedAt ?? '';
182
- if (!latestKey || fetchedAt > latestFetchedAt) {
183
- latestKey = fullKey;
184
- latestFetchedAt = fetchedAt;
185
- }
186
- }
187
- if (!latestKey)
188
- return null;
189
- return latestKey.slice(this.keyPrefix.length);
194
+ async readSession(sessionKey) {
195
+ const raw = await this.redis.get(this.fullKey(sessionKey));
196
+ return parseSessionValue(raw);
190
197
  }
191
198
  }
192
199
  //# sourceMappingURL=redis-session-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"redis-session-store.js","sourceRoot":"","sources":["../../src/session/redis-session-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"redis-session-store.js","sourceRoot":"","sources":["../../src/session/redis-session-store.ts"],"names":[],"mappings":"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"}
@@ -13,9 +13,14 @@ export interface RedisClient {
13
13
  nx?: boolean;
14
14
  }): Promise<boolean | void>;
15
15
  del(key: string): Promise<void>;
16
+ /** Prefer not to use for multi-tenant session isolation — prefer explicit keys / SETs. */
16
17
  keys(pattern: string): Promise<string[]>;
17
18
  incr(key: string): Promise<number>;
18
19
  expire(key: string, seconds: number): Promise<void>;
20
+ /** Set members — used for per-user+space session index (clearAll without KEYS). */
21
+ sadd(key: string, ...members: string[]): Promise<number>;
22
+ srem(key: string, ...members: string[]): Promise<number>;
23
+ smembers(key: string): Promise<string[]>;
19
24
  }
20
25
  export declare function buildSessionKey(userId: string | undefined, spaceId: string, rootEntryId: string, sessionName?: string): string;
21
26
  export declare function buildSessionNameSuffix(sessionName: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../src/session/session-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,IAAI,CACF,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,YAAY,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxF,QAAQ,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAClG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAIR;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAElE"}
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../src/session/session-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,IAAI,CACF,OAAO,CAAC,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,YAAY,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxF,QAAQ,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAClG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,0FAA0F;IAC1F,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,mFAAmF;IACnF,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC1C;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAIR;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAElE"}
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/session/session-store.ts"],"names":[],"mappings":"AAuBA,MAAM,UAAU,eAAe,CAC7B,MAA0B,EAC1B,OAAe,EACf,WAAmB,EACnB,WAAoB;IAEpB,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,MAAM,IAAI,GAAG,GAAG,OAAO,IAAI,WAAW,GAAG,MAAM,EAAE,CAAC;IAClD,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,WAAmB;IACxD,OAAO,IAAI,WAAW,EAAE,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/session/session-store.ts"],"names":[],"mappings":"AA4BA,MAAM,UAAU,eAAe,CAC7B,MAA0B,EAC1B,OAAe,EACf,WAAmB,EACnB,WAAoB;IAEpB,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,MAAM,IAAI,GAAG,GAAG,OAAO,IAAI,WAAW,GAAG,MAAM,EAAE,CAAC;IAClD,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,WAAmB;IACxD,OAAO,IAAI,WAAW,EAAE,CAAC;AAC3B,CAAC"}
@@ -31,8 +31,9 @@
31
31
 
32
32
  1. If the package includes a **folder of images/videos** (typical case study): run **`task-media-pipeline-prepare`** **before** building blocks.
33
33
  2. Prefer pipeline **ready public URLs** + reuse-first import over mid-build staged upload of oversized masters.
34
- 3. Keep the `sourceName → assetId` map for Phase 2 field linking.
35
- 4. Skip this phase only when every needed visual is already in the Contentful library (confirmed via `asset search`).
34
+ 3. On import: use **`onlyWithOverrides` or `sourceNames`** so only beat-sheet assets land in Contentful; **dryRun first**; use unique `fileName` overrides when you must not reuse prior lab assets.
35
+ 4. Keep the `sourceName → assetId` / `mediaId` map for Phase 2 field linking. Singular component visuals: `set @cN visual <mediaId> --link` (never `add --target visual`).
36
+ 5. Skip this phase only when every needed visual is already in the Contentful library (confirmed via `asset search`).
36
37
 
37
38
  ### Phase 2 — Plan and create
38
39
 
@@ -25,7 +25,8 @@ Normalize a **folder of designer masters** into short-lived **public HTTPS URLs*
25
25
  - `MEDIA_PIPELINE_TENANT` (e.g. `se`, not the cms-edit projectKey)
26
26
  - Drive folder shared **Viewer** with pipeline service account
27
27
  `media-pipeline-reader@se-media-pipeline.iam.gserviceaccount.com`
28
- (or “anyone with the link” if using API-key Drive mode on the pipeline)
28
+ (or “anyone with the link” if using API-key Drive mode on the pipeline).
29
+ **If prepare/status reports `drive_inaccessible` or `drive_empty_or_unshared`:** fix the share first, then re-prepare **once**. Do not treat “no media files” as a wrong folder path until share is confirmed.
29
30
  - Profile default: `case-study-v1`
30
31
 
31
32
  ## When to use
@@ -106,26 +107,44 @@ Each asset includes:
106
107
 
107
108
  After drafting titles/alts from **previewUrl** / **posterUrl**:
108
109
 
110
+ **Prefer filtering** so unused files in a shared Drive folder are not uploaded:
111
+
112
+ | Param | Use |
113
+ |-------|-----|
114
+ | `sourceNames` | Exact catalog `sourceName` allowlist (Drive file names) |
115
+ | `excludeSourceNames` | Skip specific files |
116
+ | `onlyWithOverrides` | Import only assets that have an `overrides` row (typical case-study path) |
117
+
109
118
  ```json
110
119
  {
111
120
  "action": "import",
112
121
  "jobId": "<jobId>",
113
122
  "withMedia": true,
123
+ "onlyWithOverrides": true,
114
124
  "overrides": [
115
- { "sourceName": "OM1_Hero.jpg", "title": "OM1 hero", "alt": "…" }
116
- ]
125
+ { "sourceName": "OM1_Hero.jpg", "title": "OM1 hero", "alt": "…", "fileName": "se-om1-hero-v3.jpg" }
126
+ ],
127
+ "dryRun": true
117
128
  }
118
129
  ```
119
130
 
120
- Optional: `"dryRun": true` to preview the map without writing.
131
+ 1. Always **`dryRun: true` first** — check `existing: true/false` per asset (reuse is by **fileName**).
132
+ 2. When you need **new** assets instead of reusing prior runs, set `overrides[].fileName` to unique names.
133
+ 3. Then import without `dryRun`.
121
134
 
122
- CLI: `cms-edit media import --job <id> [--dry-run] [--with-media] [--overrides-json '[…]']`
135
+ CLI:
136
+ `cms-edit media import --job <id> [--dry-run] [--with-media] [--only-with-overrides] [--source-names a.jpg,b.jpg] [--exclude-source-names x.jpg] [--overrides-json '[…]']`
123
137
 
124
138
  Import:
125
139
 
126
- 1. Searches existing assets by fileName
127
- 2. Else uploads from `readyUrl` as the authenticated Contentful user
128
- 3. Returns `map: { sourceName → { assetId, mediaId?, … } }` for page build
140
+ 1. Filters catalog (allowlist / exclude / onlyWithOverrides)
141
+ 2. Searches existing assets by fileName
142
+ 3. Else uploads from `readyUrl` as the authenticated Contentful user
143
+ 4. Returns `map`, `skipped[]`, and `counts` for page build
144
+
145
+ **Linking Media to components:** use singular Entry links with
146
+ `cms_edit ["set", "@cN", "visual", "<mediaId>", "--link"]`
147
+ — **never** `add … --target visual` (`add` only supports content **arrays**).
129
148
 
130
149
  Manual `asset upload --url` remains available for one-offs.
131
150
 
@@ -135,3 +154,4 @@ Manual `asset upload --url` remains available for one-offs.
135
154
  - Rate limits apply (prepares/hour, concurrent jobs).
136
155
  - Never document or request `MEDIA_PIPELINE_API_KEY` or Blob tokens in chat.
137
156
  - `provisioning` is **not** a real pipeline phase; do not treat it as “still encoding” for many minutes.
157
+ - If the MCP client times out (~180s) on `set`/`rtf`/`import`, **read / catalog / diff before retrying** — the server may already have applied the change.
@@ -23,8 +23,12 @@
23
23
  - `cms_edit ["asset", "search", "--filename-match", "<partial-name>"]`
24
24
  - `cms_edit ["list", "--type", "media", "--asset-filename-match", "<partial>"]`
25
25
  3. If match found — link by asset ID or filename in `set` / `create from-json` (`featuredImageAssetFilename`, `visualAssetFilename`, etc.)
26
- 4. For **`visuals`** (Media entry array): prefer an **existing Media entry** — `cms_edit ["list", "--type", "media", "--asset-id", "<assetId>"]` — then `set @root visuals <mediaEntryId> --links`. Do **not** `create media` or use `asset upload --with-media` when a wrapper already exists.
27
- 5. `create media` only when no Media entry wraps that asset (RTF embeds, or first article in a new composite batch — then **reuse that media ID** on sibling entries)
26
+ 4. For **`visuals`** (Media entry **array** on page/article): prefer an **existing Media entry** — `cms_edit ["list", "--type", "media", "--asset-id", "<assetId>"]` — then `set @root visuals <mediaEntryId> --links`. Do **not** `create media` or use `asset upload --with-media` when a wrapper already exists.
27
+ 5. For **singular** component fields (e.g. `visual` on a component):
28
+ `cms_edit ["set", "@cN", "visual", "<mediaEntryId>", "--link"]`
29
+ **Never** `add media --parent @cN --target visual` — `add --target` only works for Entry-link **arrays** (`content`, `contents`, …). Bad `add --target` is rejected; it used to orphan Media drafts.
30
+ 6. `create media` only when no Media entry wraps that asset (RTF embeds, or first article in a new composite batch — then **reuse that media ID** on sibling entries)
31
+ 7. Bare Contentful entry IDs work with `set` (no `@` required) for mid-session Media field tweaks (width/autoplay/loop) after import — `set <mediaEntryId> width 75`.
28
32
 
29
33
  ## Upload only when no match
30
34
 
@@ -89,7 +93,13 @@ cms_edit ["diff"]
89
93
  cms_edit ["save"]
90
94
  ```
91
95
 
92
- **Component visual / article featured image:** same pattern — `asset set <ref> <field> <asset-id>` (or `set … --asset`).
96
+ **Component `visual` (Media Entry link, singular):**
97
+
98
+ ```
99
+ cms_edit ["set", "@cN", "visual", "<media-entry-id>", "--link"]
100
+ ```
101
+
102
+ **Article featured image / asset fields:** `asset set <ref> <field> <asset-id>` (or `set … --asset`) when the field is an Asset link.
93
103
 
94
104
  Always `diff` → `save`. Verify with `preview urls` on the public path.
95
105
 
@@ -23,11 +23,11 @@ After saving a draft, agents should return **all applicable URLs** and explain w
23
23
 
24
24
  | Target | Label | When to use |
25
25
  |--------|-------|-------------|
26
- | **contentfulLivePreview** | Live preview (publish here) | **Primary for editors.** Opens `app.contentful.com/.../entries/.../preview/...` — in-app preview with inspector. User clicks the green **Publish** button at the top to go live. |
27
- | **staging** | Preview (staging site) | Full-page check on staging, e.g. `{devBaseUrl}/resources/news/{tag}/{slug}/` (includes Vercel bypass params when configured). |
26
+ | **contentfulLivePreview** | Live preview (publish here) | **Primary for editors when `url` is present.** Opens `app.contentful.com/.../entries/.../preview/...` — in-app preview with inspector. User clicks the green **Publish** button at the top to go live. If `url` is `null`, read `unavailableReason` / `notes` — do not invent a Live Preview link; use **staging** + **contentfulEntry** instead. |
27
+ | **staging** | Preview (staging site) | Full-page check on staging, e.g. `{devBaseUrl}/resources/news/{tag}/{slug}/` (includes Vercel bypass params when configured). Always valid for draft visibility when staging is configured. |
28
28
  | **siteLivePreview** | Preview redirect | `{devBaseUrl}/preview?id={entryId}` — entry-aware redirect to the staging path. |
29
29
  | **production** | Production site | Published content only — compare after publish. |
30
- | **contentfulEntry** | Contentful entry editor | Classic entry form. Prefer **Live preview** for review and publish. |
30
+ | **contentfulEntry** | Contentful entry editor | Classic entry form. Prefer **Live preview** when available; otherwise this is the publish path. |
31
31
 
32
32
  ## Steps
33
33
 
@@ -41,9 +41,10 @@ After saving a draft, agents should return **all applicable URLs** and explain w
41
41
  ```
42
42
  Use `--json` for machine-readable output.
43
43
 
44
- 2. **Hand off to the editor with both links:**
45
- - **Live preview** — for reviewing the draft and publishing (green Publish button in Contentful).
46
- - **Preview (staging site)** — for a full-screen check on the staging deployment.
44
+ 2. **Hand off to the editor:**
45
+ - **Live preview** — when `url` is non-null: reviewing the draft and publishing (green Publish button in Contentful).
46
+ - **Preview (staging site)** — full-screen check on the staging deployment (always offer when available).
47
+ - If Live Preview is unavailable, say so briefly using `unavailableReason` and point at staging + classic entry editor.
47
48
 
48
49
  3. Summarize what to check before publish handoff — use the checklist below (add multi-block items when relevant).
49
50
 
@@ -148,6 +148,17 @@ Hosted connectors have **no server filesystem** and **reject `asset upload --bas
148
148
 
149
149
  Full workflow: `cms-edit://customer/task-media-reuse-and-upload`.
150
150
 
151
+ ### Common pitfalls (agents)
152
+
153
+ | Issue | Do this |
154
+ |-------|---------|
155
+ | Singular Media on a component (`visual`) | `set @cN visual <mediaId> --link` — **not** `add … --target visual` |
156
+ | Shared Drive folder has extra files | Import with `sourceNames` or `onlyWithOverrides` after catalog |
157
+ | Prepare says no media / empty folder | Share folder **Viewer** with `media-pipeline-reader@se-media-pipeline.iam.gserviceaccount.com`, then re-prepare once |
158
+ | MCP tool times out ~180s | `read` / `diff` / re-`catalog` before retrying the same write — server may already have saved |
159
+ | `preview urls` has no Live Preview URL | Use staging + classic entry editor; row includes `unavailableReason` when CMA preview fails |
160
+ | Reuse-first import matches old fileNames | dry-run first; set `overrides[].fileName` for unique names when duplication is required |
161
+
151
162
  ### Staged upload
152
163
 
153
164
  Keeps binary **out of MCP tool args**. The agent uploads via `curl`, then passes only a short `uploadId`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@se-studio/contentful-cms",
3
- "version": "3.4.3",
3
+ "version": "3.4.5",
4
4
  "description": "CLI tool for AI agents to read and edit Contentful draft content without publish permissions",
5
5
  "repository": {
6
6
  "type": "git",