@se-studio/contentful-cms 3.9.3 → 3.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +3 -3
  3. package/dist/commands/asset.d.ts.map +1 -1
  4. package/dist/commands/asset.js +20 -5
  5. package/dist/commands/asset.js.map +1 -1
  6. package/dist/commands/host-scaffold.js +1 -1
  7. package/dist/commands/media.d.ts.map +1 -1
  8. package/dist/commands/media.js +10 -5
  9. package/dist/commands/media.js.map +1 -1
  10. package/dist/contentful/assets.d.ts +4 -0
  11. package/dist/contentful/assets.d.ts.map +1 -1
  12. package/dist/contentful/assets.js +9 -0
  13. package/dist/contentful/assets.js.map +1 -1
  14. package/dist/contentful/cma-user-agent.d.ts +1 -1
  15. package/dist/contentful/cma-user-agent.d.ts.map +1 -1
  16. package/dist/contentful/hosted-asset-upload.d.ts +1 -1
  17. package/dist/contentful/hosted-asset-upload.d.ts.map +1 -1
  18. package/dist/contentful/hosted-asset-upload.js +1 -1
  19. package/dist/contentful/hosted-asset-upload.js.map +1 -1
  20. package/dist/contentful/pipeline-encode-gate.d.ts +27 -0
  21. package/dist/contentful/pipeline-encode-gate.d.ts.map +1 -0
  22. package/dist/contentful/pipeline-encode-gate.js +102 -0
  23. package/dist/contentful/pipeline-encode-gate.js.map +1 -0
  24. package/dist/editor-pack/generate.js +1 -1
  25. package/dist/editor-pack/generate.js.map +1 -1
  26. package/dist/generated/hosted-guide.d.ts +1 -1
  27. package/dist/generated/hosted-guide.d.ts.map +1 -1
  28. package/dist/generated/hosted-guide.js +1 -1
  29. package/dist/generated/hosted-guide.js.map +1 -1
  30. package/dist/generated/version.d.ts +1 -1
  31. package/dist/generated/version.d.ts.map +1 -1
  32. package/dist/generated/version.js +1 -1
  33. package/dist/generated/version.js.map +1 -1
  34. package/dist/hosted/next/routes/staged-upload.d.ts +1 -0
  35. package/dist/hosted/next/routes/staged-upload.d.ts.map +1 -1
  36. package/dist/hosted/next/routes/staged-upload.js +38 -1
  37. package/dist/hosted/next/routes/staged-upload.js.map +1 -1
  38. package/dist/mcp/prompts/onboarding.d.ts.map +1 -1
  39. package/dist/mcp/prompts/onboarding.js +5 -5
  40. package/dist/mcp/prompts/onboarding.js.map +1 -1
  41. package/dist/mcp/tools/cms-edit.d.ts +1 -1
  42. package/dist/mcp/tools/cms-edit.d.ts.map +1 -1
  43. package/dist/mcp/tools/cms-edit.js +3 -2
  44. package/dist/mcp/tools/cms-edit.js.map +1 -1
  45. package/dist/mcp/tools/media.d.ts.map +1 -1
  46. package/dist/mcp/tools/media.js +18 -11
  47. package/dist/mcp/tools/media.js.map +1 -1
  48. package/dist/mcp/tools/staged-upload.d.ts.map +1 -1
  49. package/dist/mcp/tools/staged-upload.js +6 -4
  50. package/dist/mcp/tools/staged-upload.js.map +1 -1
  51. package/dist/media-pipeline/actions.d.ts +6 -8
  52. package/dist/media-pipeline/actions.d.ts.map +1 -1
  53. package/dist/media-pipeline/actions.js +10 -8
  54. package/dist/media-pipeline/actions.js.map +1 -1
  55. package/dist/media-pipeline/client.d.ts +11 -3
  56. package/dist/media-pipeline/client.d.ts.map +1 -1
  57. package/dist/media-pipeline/client.js +1 -4
  58. package/dist/media-pipeline/client.js.map +1 -1
  59. package/dist/media-pipeline/import-alt.d.ts +8 -0
  60. package/dist/media-pipeline/import-alt.d.ts.map +1 -0
  61. package/dist/media-pipeline/import-alt.js +23 -0
  62. package/dist/media-pipeline/import-alt.js.map +1 -0
  63. package/dist/media-pipeline/import.d.ts.map +1 -1
  64. package/dist/media-pipeline/import.js +45 -0
  65. package/dist/media-pipeline/import.js.map +1 -1
  66. package/dist/media-pipeline/limits.d.ts +2 -2
  67. package/dist/media-pipeline/limits.d.ts.map +1 -1
  68. package/dist/media-pipeline/limits.js +2 -2
  69. package/dist/media-pipeline/limits.js.map +1 -1
  70. package/dist/media-pipeline/prepare-source.d.ts +32 -0
  71. package/dist/media-pipeline/prepare-source.d.ts.map +1 -0
  72. package/dist/media-pipeline/prepare-source.js +132 -0
  73. package/dist/media-pipeline/prepare-source.js.map +1 -0
  74. package/dist/staged-upload/hmac.d.ts +4 -0
  75. package/dist/staged-upload/hmac.d.ts.map +1 -1
  76. package/dist/staged-upload/hmac.js +35 -0
  77. package/dist/staged-upload/hmac.js.map +1 -1
  78. package/dist/staged-upload/request.d.ts.map +1 -1
  79. package/dist/staged-upload/request.js +14 -11
  80. package/dist/staged-upload/request.js.map +1 -1
  81. package/dist/staged-upload/store.d.ts +2 -0
  82. package/dist/staged-upload/store.d.ts.map +1 -1
  83. package/dist/staged-upload/store.js +18 -0
  84. package/dist/staged-upload/store.js.map +1 -1
  85. package/docs/editor-tasks/create-from-document.md +10 -3
  86. package/docs/editor-tasks/media-pipeline-prepare.md +29 -18
  87. package/docs/editor-tasks/media-reuse-and-upload.md +23 -20
  88. package/docs/editor-tasks/source-readiness-review.md +4 -3
  89. package/docs/mcp/hosted-guide.md +9 -8
  90. package/package.json +1 -1
  91. package/skills/core/SKILL.md +4 -8
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prepare-source.d.ts","sourceRoot":"","sources":["../../src/media-pipeline/prepare-source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC7C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzF,MAAM,WAAW,kBAAkB;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;AAmD5F,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,oBAAoB,EACzB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CACR;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,qBAAqB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAC7F,CA6FA"}
@@ -0,0 +1,132 @@
1
+ import { getPublicOrigin } from '../hosted/oauth/config.js';
2
+ import { getRuntimeContext } from '../runtime/context.js';
3
+ import { buildStagedDownloadUrl, getUploadHmacSecret } from '../staged-upload/hmac.js';
4
+ import { STAGED_UPLOAD_TTL_SECONDS } from '../staged-upload/limits.js';
5
+ import { parseDriveFolderUrl } from './validate.js';
6
+ function sanitizeIds(ids) {
7
+ const out = [];
8
+ const seen = new Set();
9
+ for (const raw of ids ?? []) {
10
+ const t = typeof raw === 'string' ? raw.trim() : '';
11
+ if (!t || seen.has(t)) {
12
+ continue;
13
+ }
14
+ seen.add(t);
15
+ out.push(t);
16
+ }
17
+ return out;
18
+ }
19
+ function normalizeHttpsUrls(urls) {
20
+ const files = [];
21
+ const seen = new Set();
22
+ for (const raw of urls ?? []) {
23
+ const url = typeof raw === 'string' ? raw.trim() : raw.url?.trim();
24
+ if (!url || seen.has(url)) {
25
+ continue;
26
+ }
27
+ seen.add(url);
28
+ let sourceName;
29
+ if (typeof raw !== 'string' && raw.sourceName?.trim()) {
30
+ sourceName = raw.sourceName.trim();
31
+ }
32
+ else {
33
+ try {
34
+ const base = decodeURIComponent(new URL(url).pathname.split('/').pop() ?? '');
35
+ sourceName = base || `file-${String(files.length + 1)}`;
36
+ }
37
+ catch {
38
+ sourceName = `file-${String(files.length + 1)}`;
39
+ }
40
+ }
41
+ const mime = typeof raw === 'string' ? undefined : raw.mime?.trim() || undefined;
42
+ files.push(mime ? { sourceName, url, mime } : { sourceName, url });
43
+ }
44
+ return files;
45
+ }
46
+ function remainingTtlSeconds(createdAt) {
47
+ const elapsed = Math.floor((Date.now() - createdAt) / 1000);
48
+ return Math.max(60, STAGED_UPLOAD_TTL_SECONDS - elapsed);
49
+ }
50
+ export async function resolvePrepareSource(ctx, params) {
51
+ const driveUrl = params.driveFolderUrl?.trim();
52
+ const stagedIds = sanitizeIds(params.stagedUploadIds);
53
+ const httpsFiles = normalizeHttpsUrls(params.httpsUrls);
54
+ const hasDrive = Boolean(driveUrl);
55
+ const hasStaged = stagedIds.length > 0;
56
+ const hasHttps = httpsFiles.length > 0;
57
+ if (hasDrive && (hasStaged || hasHttps)) {
58
+ return {
59
+ ok: false,
60
+ error: 'Provide either driveFolderUrl or stagedUploadIds/httpsUrls, not both. Raster/video intake uses staged or HTTPS files in one prepare job.',
61
+ };
62
+ }
63
+ if (!hasDrive && !hasStaged && !hasHttps) {
64
+ return {
65
+ ok: false,
66
+ error: 'prepare requires driveFolderUrl, stagedUploadIds, or httpsUrls. Raster and video must go through the media pipeline — do not use asset upload --staged/--url/--base64.',
67
+ };
68
+ }
69
+ if (hasDrive && driveUrl) {
70
+ const folder = parseDriveFolderUrl(driveUrl);
71
+ if (!folder.ok) {
72
+ return { ok: false, error: folder.error };
73
+ }
74
+ return {
75
+ ok: true,
76
+ source: { type: 'google_drive_folder', url: folder.url },
77
+ folderId: folder.folderId,
78
+ };
79
+ }
80
+ const files = [...httpsFiles];
81
+ if (hasStaged) {
82
+ const store = ctx.stagedUploadStore ?? getRuntimeContext()?.stagedUploadStore ?? null;
83
+ if (!store) {
84
+ return {
85
+ ok: false,
86
+ error: 'stagedUploadIds require hosted Redis staged upload store.',
87
+ };
88
+ }
89
+ let origin;
90
+ let secret;
91
+ try {
92
+ origin = ctx.publicOrigin ?? getPublicOrigin();
93
+ secret = getUploadHmacSecret();
94
+ }
95
+ catch (err) {
96
+ return {
97
+ ok: false,
98
+ error: err instanceof Error ? err.message : String(err),
99
+ };
100
+ }
101
+ for (const uploadId of stagedIds) {
102
+ try {
103
+ const peeked = await store.peekFile(ctx.userId, uploadId);
104
+ const record = await store.getRecord(ctx.userId, uploadId);
105
+ const exp = Math.floor(Date.now() / 1000) + remainingTtlSeconds(record?.createdAt ?? Date.now());
106
+ const url = buildStagedDownloadUrl(origin, uploadId, ctx.userId, exp, secret);
107
+ files.push({
108
+ sourceName: peeked.fileName,
109
+ url,
110
+ mime: peeked.mimeType,
111
+ });
112
+ }
113
+ catch (err) {
114
+ return {
115
+ ok: false,
116
+ error: err instanceof Error
117
+ ? `Staged upload ${uploadId}: ${err.message}`
118
+ : `Staged upload ${uploadId} is not ready.`,
119
+ };
120
+ }
121
+ }
122
+ }
123
+ if (files.length === 0) {
124
+ return { ok: false, error: 'No files to prepare.' };
125
+ }
126
+ return {
127
+ ok: true,
128
+ source: { type: 'https_urls', files },
129
+ folderId: hasStaged ? `staged:${String(stagedIds.length)}` : 'https',
130
+ };
131
+ }
132
+ //# sourceMappingURL=prepare-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prepare-source.js","sourceRoot":"","sources":["../../src/media-pipeline/prepare-source.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,10 @@
1
1
  export declare function getUploadHmacSecret(): string;
2
2
  export declare function buildUploadSignaturePayload(uploadId: string, userId: string, exp: number): string;
3
+ export declare function buildDownloadSignaturePayload(uploadId: string, userId: string, exp: number): string;
3
4
  export declare function signUploadUrl(uploadId: string, userId: string, exp: number, secret: string): string;
4
5
  export declare function verifyUploadSignature(uploadId: string, userId: string, exp: number, sig: string, secret: string): boolean;
5
6
  export declare function buildStagedUploadUrl(origin: string, uploadId: string, userId: string, exp: number, secret: string): string;
7
+ export declare function signDownloadUrl(uploadId: string, userId: string, exp: number, secret: string): string;
8
+ export declare function verifyDownloadSignature(uploadId: string, userId: string, exp: number, sig: string, secret: string): boolean;
9
+ export declare function buildStagedDownloadUrl(origin: string, uploadId: string, userId: string, exp: number, secret: string): string;
6
10
  //# sourceMappingURL=hmac.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hmac.d.ts","sourceRoot":"","sources":["../../src/staged-upload/hmac.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,IAAI,MAAM,CAM5C;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjG;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,MAAM,CAGR;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAeT;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,MAAM,CASR"}
1
+ {"version":3,"file":"hmac.d.ts","sourceRoot":"","sources":["../../src/staged-upload/hmac.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,IAAI,MAAM,CAM5C;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjG;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,GACV,MAAM,CAER;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,MAAM,CAGR;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAeT;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,MAAM,CASR;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,MAAM,CAGR;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAeT;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,MAAM,CAUR"}
@@ -9,6 +9,9 @@ export function getUploadHmacSecret() {
9
9
  export function buildUploadSignaturePayload(uploadId, userId, exp) {
10
10
  return `${uploadId}:${userId}:${String(exp)}`;
11
11
  }
12
+ export function buildDownloadSignaturePayload(uploadId, userId, exp) {
13
+ return `get:${uploadId}:${userId}:${String(exp)}`;
14
+ }
12
15
  export function signUploadUrl(uploadId, userId, exp, secret) {
13
16
  const payload = buildUploadSignaturePayload(uploadId, userId, exp);
14
17
  return crypto.createHmac('sha256', secret).update(payload).digest('hex');
@@ -40,4 +43,36 @@ export function buildStagedUploadUrl(origin, uploadId, userId, exp, secret) {
40
43
  });
41
44
  return `${origin.replace(/\/$/, '')}/api/staged-upload?${params.toString()}`;
42
45
  }
46
+ export function signDownloadUrl(uploadId, userId, exp, secret) {
47
+ const payload = buildDownloadSignaturePayload(uploadId, userId, exp);
48
+ return crypto.createHmac('sha256', secret).update(payload).digest('hex');
49
+ }
50
+ export function verifyDownloadSignature(uploadId, userId, exp, sig, secret) {
51
+ if (!sig || !uploadId || !userId) {
52
+ return false;
53
+ }
54
+ const expected = signDownloadUrl(uploadId, userId, exp, secret);
55
+ try {
56
+ const expectedBuf = Buffer.from(expected, 'hex');
57
+ const actualBuf = Buffer.from(sig, 'hex');
58
+ if (expectedBuf.length !== actualBuf.length) {
59
+ return false;
60
+ }
61
+ return crypto.timingSafeEqual(expectedBuf, actualBuf);
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ export function buildStagedDownloadUrl(origin, uploadId, userId, exp, secret) {
68
+ const sig = signDownloadUrl(uploadId, userId, exp, secret);
69
+ const params = new URLSearchParams({
70
+ id: uploadId,
71
+ uid: userId,
72
+ exp: String(exp),
73
+ sig,
74
+ op: 'get',
75
+ });
76
+ return `${origin.replace(/\/$/, '')}/api/staged-upload?${params.toString()}`;
77
+ }
43
78
  //# sourceMappingURL=hmac.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hmac.js","sourceRoot":"","sources":["../../src/staged-upload/hmac.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,MAAM,UAAU,mBAAmB;IACjC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,EAAE,IAAI,EAAE,CAAC;IACjE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,QAAgB,EAAE,MAAc,EAAE,GAAW;IACvF,OAAO,GAAG,QAAQ,IAAI,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,QAAgB,EAChB,MAAc,EACd,GAAW,EACX,MAAc;IAEd,MAAM,OAAO,GAAG,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;IACnE,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,QAAgB,EAChB,MAAc,EACd,GAAW,EACX,GAAW,EACX,MAAc;IAEd,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC9D,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACjD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1C,IAAI,WAAW,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,MAAM,CAAC,eAAe,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,MAAc,EACd,QAAgB,EAChB,MAAc,EACd,GAAW,EACX,MAAc;IAEd,MAAM,GAAG,GAAG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,EAAE,EAAE,QAAQ;QACZ,GAAG,EAAE,MAAM;QACX,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC;QAChB,GAAG;KACJ,CAAC,CAAC;IACH,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,sBAAsB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC/E,CAAC"}
1
+ {"version":3,"file":"hmac.js","sourceRoot":"","sources":["../../src/staged-upload/hmac.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/staged-upload/request.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,+BAA+B,GACtC,OAAO,CAAC,yBAAyB,CAAC,CAoCpC"}
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/staged-upload/request.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,+BAA+B,GACtC,OAAO,CAAC,yBAAyB,CAAC,CAsCpC"}
@@ -1,3 +1,4 @@
1
+ import { requiresPipelineEncode } from '../contentful/pipeline-encode-gate.js';
1
2
  import { getPublicOrigin } from '../hosted/oauth/config.js';
2
3
  import { buildStagedUploadUrl, getUploadHmacSecret } from './hmac.js';
3
4
  import { hostedStagedMaxBytes, STAGED_UPLOAD_TTL_SECONDS } from './limits.js';
@@ -15,17 +16,19 @@ export async function createStagedUploadRequest(store, params) {
15
16
  const origin = getPublicOrigin();
16
17
  const exp = Math.floor(Date.now() / 1000) + STAGED_UPLOAD_TTL_SECONDS;
17
18
  const uploadUrl = buildStagedUploadUrl(origin, uploadId, params.userId, exp, secret);
18
- const consumeArgs = [
19
- 'asset',
20
- 'upload',
21
- '--staged',
22
- uploadId,
23
- '--mime',
24
- params.mimeType,
25
- '--file-name',
26
- params.fileName,
27
- ...(params.title ? ['--title', params.title] : []),
28
- ];
19
+ const consumeArgs = requiresPipelineEncode(params.mimeType, params.fileName)
20
+ ? ['media', 'prepare', '--staged-ids', uploadId]
21
+ : [
22
+ 'asset',
23
+ 'upload',
24
+ '--staged',
25
+ uploadId,
26
+ '--mime',
27
+ params.mimeType,
28
+ '--file-name',
29
+ params.fileName,
30
+ ...(params.title ? ['--title', params.title] : []),
31
+ ];
29
32
  const curlCommand = `curl -f -X POST -F ${shellQuote(`file=@/path/to/${params.fileName}`)} ${shellQuote(uploadUrl)}`;
30
33
  return {
31
34
  uploadId,
@@ -1 +1 @@
1
- {"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/staged-upload/request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAY9E,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,KAAwB,EACxB,MAAuC;IAEvC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QACzC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,UAAU,EAAE,MAAM,CAAC,UAAU;KAC9B,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,yBAAyB,CAAC;IACtE,MAAM,SAAS,GAAG,oBAAoB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAErF,MAAM,WAAW,GAAG;QAClB,OAAO;QACP,QAAQ;QACR,UAAU;QACV,QAAQ;QACR,QAAQ;QACR,MAAM,CAAC,QAAQ;QACf,aAAa;QACb,MAAM,CAAC,QAAQ;QACf,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;KACnD,CAAC;IAEF,MAAM,WAAW,GAAG,sBAAsB,UAAU,CAAC,kBAAkB,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;IAErH,OAAO;QACL,QAAQ;QACR,SAAS;QACT,SAAS,EAAE,yBAAyB;QACpC,SAAS,EAAE,GAAG;QACd,QAAQ,EAAE,oBAAoB,EAAE;QAChC,WAAW;QACX,WAAW;KACZ,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/staged-upload/request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAY9E,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,KAAwB,EACxB,MAAuC;IAEvC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC;QACzC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,UAAU,EAAE,MAAM,CAAC,UAAU;KAC9B,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,yBAAyB,CAAC;IACtE,MAAM,SAAS,GAAG,oBAAoB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAErF,MAAM,WAAW,GAAG,sBAAsB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC1E,CAAC,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,CAAC;QAChD,CAAC,CAAC;YACE,OAAO;YACP,QAAQ;YACR,UAAU;YACV,QAAQ;YACR,QAAQ;YACR,MAAM,CAAC,QAAQ;YACf,aAAa;YACb,MAAM,CAAC,QAAQ;YACf,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SACnD,CAAC;IAEN,MAAM,WAAW,GAAG,sBAAsB,UAAU,CAAC,kBAAkB,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;IAErH,OAAO;QACL,QAAQ;QACR,SAAS;QACT,SAAS,EAAE,yBAAyB;QACpC,SAAS,EAAE,GAAG;QACd,QAAQ,EAAE,oBAAoB,EAAE;QAChC,WAAW;QACX,WAAW;KACZ,CAAC;AACJ,CAAC"}
@@ -14,6 +14,8 @@ export declare class StagedUploadStore {
14
14
  }): Promise<string>;
15
15
  storeFile(userId: string, uploadId: string, buffer: Buffer, mimeType: string): Promise<StagedUploadRecord>;
16
16
  consume(userId: string, uploadId: string): Promise<StagedUploadConsumeResult>;
17
+ /** Read staged bytes without deleting (pipeline fetch). */
18
+ peekFile(userId: string, uploadId: string): Promise<StagedUploadConsumeResult>;
17
19
  getRecord(userId: string, uploadId: string): Promise<StagedUploadRecord | null>;
18
20
  }
19
21
  //# sourceMappingURL=store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/staged-upload/store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAO/D,OAAO,KAAK,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA0BhF,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAF5B,YACmB,KAAK,EAAE,WAAW,EAClB,SAAS,SAAa,EACrC;IAEJ,OAAO,CAAC,OAAO;IAIT,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYnD;IAEK,aAAa,CAAC,MAAM,EAAE;QAC1B,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmClB;IAEK,SAAS,CACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,CA8B7B;IAEK,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAqBlF;IAEK,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAEpF;CACF"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/staged-upload/store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAO/D,OAAO,KAAK,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AA0BhF,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAF5B,YACmB,KAAK,EAAE,WAAW,EAClB,SAAS,SAAa,EACrC;IAEJ,OAAO,CAAC,OAAO;IAIT,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYnD;IAEK,aAAa,CAAC,MAAM,EAAE;QAC1B,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmClB;IAEK,SAAS,CACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,CA8B7B;IAEK,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAqBlF;IAED,2DAA2D;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAgBnF;IAEK,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAEpF;CACF"}
@@ -117,6 +117,24 @@ export class StagedUploadStore {
117
117
  byteLength: buffer.length,
118
118
  };
119
119
  }
120
+ /** Read staged bytes without deleting (pipeline fetch). */
121
+ async peekFile(userId, uploadId) {
122
+ const existing = parseRecord(await this.redis.get(this.fullKey(userId, uploadId)));
123
+ if (!existing || existing.userId !== userId || existing.uploadId !== uploadId) {
124
+ throw new Error('Staged upload not found or expired.');
125
+ }
126
+ if (existing.status !== 'ready' || !existing.dataBase64) {
127
+ throw new Error('Staged upload has no file data yet. POST the file to uploadUrl first.');
128
+ }
129
+ const buffer = Buffer.from(existing.dataBase64, 'base64');
130
+ assertUploadBufferSize(buffer);
131
+ return {
132
+ buffer,
133
+ fileName: existing.fileName,
134
+ mimeType: existing.mimeType,
135
+ byteLength: buffer.length,
136
+ };
137
+ }
120
138
  async getRecord(userId, uploadId) {
121
139
  return parseRecord(await this.redis.get(this.fullKey(userId, uploadId)));
122
140
  }
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/staged-upload/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/staged-upload/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAExE,OAAO,EACL,oBAAoB,EACpB,iCAAiC,EACjC,yBAAyB,GAC1B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAGpD,MAAM,UAAU,GAAG,kBAAkB,CAAC;AACtC,MAAM,WAAW,GAAG,uBAAuB,CAAC;AAE5C,SAAS,OAAO,CAAC,MAAc,EAAE,UAAkB;IACjD,OAAO,GAAG,WAAW,GAAG,MAAM,IAAI,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;AACzD,CAAC;AAED,SAAS,WAAW,CAAC,GAAY;IAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,GAAyB,CAAC;IACnC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,OAAO,iBAAiB;IAET,KAAK;IACL,SAAS;IAF5B,YACmB,KAAkB,EAClB,SAAS,GAAG,UAAU;qBADtB,KAAK;yBACL,SAAS;IACzB,CAAC;IAEI,OAAO,CAAC,MAAc,EAAE,QAAgB;QAC9C,OAAO,GAAG,IAAI,CAAC,SAAS,GAAG,MAAM,IAAI,QAAQ,EAAE,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,MAAc;QAClC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;QAC7D,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,KAAK,GAAG,iCAAiC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CACb,sCAAsC,MAAM,CAAC,iCAAiC,CAAC,aAAa,CAC7F,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,MAKnB;QACC,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,4CAA4C,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,MAAM,CAAC,UAAU,GAAG,oBAAoB,EAAE,EAAE,CAAC;YAClF,MAAM,IAAI,KAAK,CACb,2DAA2D,MAAM,CAAC,oBAAoB,EAAE,CAAC,UAAU,CACpG,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAE1C,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvD,MAAM,MAAM,GAAuB;YACjC,QAAQ;YACR,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACtB,CAAC;QAEF,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAC9B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EACrC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EACtB;YACE,EAAE,EAAE,yBAAyB;YAC7B,EAAE,EAAE,IAAI;SACT,CACF,CAAC;QACF,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,SAAS,CACb,MAAc,EACd,QAAgB,EAChB,MAAc,EACd,QAAgB;QAEhB,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,MAAM,CAAC,MAAM,GAAG,oBAAoB,EAAE,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CACb,4CAA4C,MAAM,CAAC,oBAAoB,EAAE,CAAC,UAAU,CACrF,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,uBAAuB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,4CAA4C,QAAQ,EAAE,CAAC,CAAC;QAC1E,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC9D,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,MAAM,GAAuB;YACjC,GAAG,QAAQ;YACX,MAAM,EAAE,OAAO;YACf,QAAQ;YACR,UAAU,EAAE,MAAM,CAAC,MAAM;YACzB,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;SACtC,CAAC;QAEF,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,yBAAyB,EAAE,CAAC,CAAC;QACrF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,QAAgB;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;YACxD,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;QAC3F,CAAC;QAED,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE1B,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC1D,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAE/B,OAAO;YACL,MAAM;YACN,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,UAAU,EAAE,MAAM,CAAC,MAAM;SAC1B,CAAC;IACJ,CAAC;IAED,2DAA2D;IAC3D,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,QAAgB;QAC7C,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;QACnF,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;YACxD,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;QAC3F,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC1D,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAC/B,OAAO;YACL,MAAM;YACN,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,UAAU,EAAE,MAAM,CAAC,MAAM;SAC1B,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,MAAc,EAAE,QAAgB;QAC9C,OAAO,WAAW,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC3E,CAAC;CACF"}
@@ -14,8 +14,9 @@
14
14
 
15
15
  ## Prerequisites
16
16
 
17
- - Fetch package materials (Google Drive MCP, paste, Figma link, zip, production ref, or mix)
17
+ - Fetch package materials (Google Drive MCP, paste, Figma link, zip, production ref, **or designer HTML**)
18
18
  - Read `cms-edit://customer/components-index`, `routing`, and for articles **`articles`**
19
+ - **Designer HTML / interactive prototype:** if `cms-edit://customer/task-create-html-component` (or another site HtmlComponent playbook) exists, **switch to that playbook** and stop this one. Otherwise follow the HtmlComponent fork in Phase 2 — do not flatten custom CSS/JS/scroll into registered cards.
19
20
  - **Non-trivial packages:** complete **`task-source-readiness-review`** first (inventory → beat sheet → verdict → light human yes)
20
21
 
21
22
  ## Steps
@@ -38,8 +39,12 @@
38
39
  ### Phase 2 — Plan and create
39
40
 
40
41
  1. Read `cms-edit://customer/tasks-index` and relevant playbooks (`articles` / `pages`).
41
- 2. Map soft-proof beats to **registered** component types only — prefer **source order** over CMS convenience (do not invent side-by-side pairs unless the source is a true pair).
42
- 3. **Propose plan** (can reuse the soft-proof): slug, title, template/article type, ordered component list, gap/placeholder list, **asset map** — **ask user to confirm** with a light **yes** if not already confirmed.
42
+ 2. **Per-section mapping:**
43
+ - If **layout and interaction** match a registered type (e.g. FAQ accordion → FAQs), use that type.
44
+ - If the source is custom HTML/JS, sticky/scroll, diagrams, or a private layout shell, and this space has `htmlComponent`, map that beat to **`htmlComponent`** — not cards/lists that only match the copy.
45
+ - Mixed pages are expected. Merge consecutive custom sections that share CSS/JS/sticky into **one** HtmlComponent. Do not emit one HtmlComponent per `<section>`.
46
+ - Prefer **source order** over CMS convenience (do not invent side-by-side pairs unless the source is a true pair).
47
+ 3. **Propose plan** (can reuse the soft-proof): slug, title, template/article type, ordered component list (registered **and** `htmlComponent` where needed), gap/placeholder list, **asset map** — **ask user to confirm** with a light **yes** if not already confirmed. For HtmlComponent beats: extract `data:` media first; run `schema htmlComponent` for this space’s field IDs (`html` vs `htmlContent`).
43
48
  4. For pages: `list --type template` — pick template; check template `preContent` / `postContent` so heroes are not duplicated.
44
49
  5. For articles: resolve `articleType`; apply site SEO and lab rules from `articles` playbook.
45
50
  6. Build `create from-json` payload **or** prefer incremental `open` → `add` / `set` / `rtf` for large multi-block work. Link visuals using the pack map from Phase 1.
@@ -64,6 +69,7 @@ Use MCP prompts `cms_edit_create_from_doc` or `cms_edit_create_article_from_doc`
64
69
  - Asset upload without reuse check (see **Media** capability)
65
70
  - Mid-article ffmpeg / Drive size thrash — use **`task-media-pipeline-prepare`** first
66
71
  - Inventing layout, section titles, or pair collections absent from the source
72
+ - Flattening designer HTML / custom interaction into registered cards because the copy looks like a card grid
67
73
 
68
74
  ## Related resources
69
75
 
@@ -75,3 +81,4 @@ Use MCP prompts `cms_edit_create_from_doc` or `cms_edit_create_article_from_doc`
75
81
  - `task-create-page`
76
82
  - `task-media-reuse-and-upload`
77
83
  - `task-preview-verify`
84
+ - Site HtmlComponent playbook when present (`cms-edit://customer/task-create-html-component`)
@@ -33,10 +33,13 @@ Normalize a **folder of designer masters** into short-lived **public HTTPS URLs*
33
33
 
34
34
  | Situation | Action |
35
35
  |-----------|--------|
36
- | Drive/Figma package with large JPEGs/videos | **This task first**, then structure build |
37
- | Single small image already in library | `task-media-reuse-and-upload` only |
38
- | Masters already under limits and catalogued | Reuse search only |
39
- | Prior prepare already reached `succeeded` + catalog | **Reuse that jobId** — do not re-prepare the whole folder |
36
+ | Drive folder of masters | This task with `driveFolderUrl` |
37
+ | Staged files (curl after `cms_edit_request_staged_upload`) | This task with `stagedUploadIds` — one prepare for the batch |
38
+ | Public HTTPS on allowed hosts | This task with `httpsUrls` |
39
+ | Single SVG/PDF/Lottie | `task-media-reuse-and-upload` asset upload --staged |
40
+ | Prior prepare already reached `succeeded` + catalog | **Reuse that jobId** — do not re-prepare |
41
+
42
+ Do **not** use `asset upload --staged/--base64` for jpeg/png/webp/gif/mp4. **Video** must use **import** (not `asset upload --url`, even on a Blob ready host). Raster `--url` is allowed only for pipeline ready image hosts.
40
43
 
41
44
  ## Steps (agent — single auth)
42
45
 
@@ -44,6 +47,8 @@ Normalize a **folder of designer masters** into short-lived **public HTTPS URLs*
44
47
 
45
48
  Call MCP tool **`cms_edit_media`**:
46
49
 
50
+ Drive:
51
+
47
52
  ```json
48
53
  {
49
54
  "action": "prepare",
@@ -51,23 +56,27 @@ Call MCP tool **`cms_edit_media`**:
51
56
  "profile": "case-study-v1",
52
57
  "sourceNames": [
53
58
  "BusinessCards copy.jpg",
54
- "ArcticDB x3 Screens Tablet-1920.mp4",
55
- "ADB_Macbook_HomeLoop.mp4"
59
+ "ArcticDB x3 Screens Tablet-1920.mp4"
56
60
  ]
57
61
  }
58
62
  ```
59
63
 
60
- | Param | On prepare |
61
- |-------|------------|
62
- | omit `sourceNames` | Encode **entire** folder (slow for large case-study packs) |
63
- | non-empty `sourceNames` | **Allowlist** — only exact Drive file names are downloaded/encoded |
64
- | `excludeSourceNames` | Skip these names (always) |
64
+ Staged (after curl PUT/POST to each `uploadUrl`):
65
+
66
+ ```json
67
+ {
68
+ "action": "prepare",
69
+ "stagedUploadIds": ["<uploadId1>", "<uploadId2>"]
70
+ }
71
+ ```
65
72
 
66
73
  CLI:
67
74
 
68
75
  ```bash
69
76
  cms-edit media prepare --drive-folder "https://drive.google.com/drive/folders/…" \
70
77
  --source-names "BusinessCards copy.jpg,ADB_Macbook_HomeLoop.mp4"
78
+
79
+ cms-edit media prepare --staged-ids "id1,id2"
71
80
  ```
72
81
 
73
82
  Expect `{ ok: true, jobId, status: "queued", pollHint }`.
@@ -103,7 +112,7 @@ or
103
112
 
104
113
  - Do **not** invent assets for gaps.
105
114
  - Do **not** wait 5–10+ minutes on `provisioning`.
106
- - Do **not** thrash prepares (rate limit + concurrent max typically **2** per user).
115
+ - Do **not** thrash prepares (rate limit **100**/hour, concurrent max typically **4** per user).
107
116
  - On `429` / too many concurrent: call `status` once on any stuck old `jobId` (to free slots if past grace), then prepare again **once**.
108
117
  - Prefer **reusing** a job that already reached `succeeded` + catalog over preparing again for the same Drive folder.
109
118
 
@@ -117,9 +126,10 @@ Each asset includes:
117
126
 
118
127
  | Field | Use |
119
128
  |-------|-----|
120
- | `readyUrl` | `asset upload --url` into Contentful |
129
+ | `readyUrl` | Import via `cms_edit_media` import only (CMA gate: pipeline ready hosts) |
121
130
  | `previewUrl` / `posterUrl` | Agent vision for **title / fileName / alt** (when present) |
122
- | `altDraft` / `titleDraft` | Filename seeds only — improve before save |
131
+ | `altDraft` / `titleDraft` | Filename seeds only — improve before import |
132
+ | `role` `gif-converted` | Animated GIF became MP4 — Media mute-loop, not `featuredImage` |
123
133
 
124
134
  ### 4. Import into Contentful (reuse-first)
125
135
 
@@ -146,9 +156,10 @@ After drafting titles/alts from **previewUrl** / **posterUrl**:
146
156
  }
147
157
  ```
148
158
 
149
- 1. Always **`dryRun: true` first** — check `existing: true/false` per asset (reuse is by **fileName**).
150
- 2. When you need **new** assets instead of reusing prior runs, set `overrides[].fileName` to unique names.
151
- 3. Then import without `dryRun`.
159
+ 1. Always **`dryRun: true` first** — check `existing: true/false` per asset (reuse is by **fileName**). Missing alt on new files is reported as `altFailures` / per-row `alt_required`; dryRun still returns `ok: true`.
160
+ 2. New uploads **fail** without review-quality `overrides[].alt` when you import for real (not empty, not the filename).
161
+ 3. When you need **new** assets instead of reusing prior runs, set `overrides[].fileName` to unique names.
162
+ 4. Then import without `dryRun`.
152
163
 
153
164
  CLI:
154
165
  `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 '[…]']`
@@ -164,7 +175,7 @@ Import:
164
175
  `cms_edit ["set", "@cN", "visual", "<mediaId>", "--link"]`
165
176
  — **never** `add … --target visual` (`add` only supports content **arrays**).
166
177
 
167
- Manual `asset upload --url` remains available for one-offs.
178
+ Manual `asset upload --url` is allowed only for **pipeline-ready images** (Vercel Blob / `MEDIA_PIPELINE_READY_HOSTS`, https in production). **Video** must use **import**. Always prefer import for new masters.
168
179
 
169
180
  ### Hardening notes (for agents)
170
181
 
@@ -32,30 +32,39 @@
32
32
 
33
33
  ## Upload only when no match
34
34
 
35
- ### Oversized masters / whole Drive folders
35
+ ### Raster images and video (required)
36
36
 
37
- If files are near or over **4 MB** (hosted staged) or **10 MB** (URL/local), or the user shared a **Drive folder of masters**, do **not** improvise compression in chat.
37
+ Encode via **`task-media-pipeline-prepare`**. Intake can be:
38
38
 
39
- 1. Run **`task-media-pipeline-prepare`** (Drive folder → ready public URLs under limits)
40
- 2. Return here and import with **`--url`** + reuse-first filename search
39
+ 1. A **Drive folder**, or
40
+ 2. **Staged upload** (4 MB): `cms_edit_request_staged_upload` → curl → `cms_edit_media` prepare with `stagedUploadIds` (see returned `consumeArgs`), or
41
+ 3. **httpsUrls** on pipeline-allowed hosts
41
42
 
42
- ### Hosted MCP (default)
43
+ Then wait → catalog → **import** with `overrides[].alt`. Do **not** `asset upload --staged/--base64` for jpeg/png/webp/gif/mp4. Do **not** `asset upload --url` for **video** (import only). Raster `--url` only for pipeline ready image hosts.
43
44
 
44
- **Always use staged upload** for ad-hoc small binaries. Do not pass base64 in `cms_edit` args — hosted MCP rejects `asset upload --base64`.
45
+ Animated GIFs become **MP4** (`gif-converted`) — attach as mute-loop Media / video visual, not `featuredImage`.
46
+
47
+ ### SVG, PDF, Lottie
48
+
49
+ These may skip the pipeline. On hosted MCP use staged upload then `asset upload --staged`.
50
+
51
+ ### Hosted MCP (SVG/PDF/Lottie only)
52
+
53
+ **Always use staged upload** for ad-hoc binaries. Do not pass base64 in `cms_edit` args — hosted MCP rejects `asset upload --base64`.
45
54
 
46
55
  1. `cms_edit_request_staged_upload` with `fileName`, `mimeType`, optional `byteLength`, optional `title`
47
56
  2. Run the returned `curlCommand` in a shell (POST file to `uploadUrl`)
48
- 3. `cms_edit` with the returned `consumeArgs` (e.g. `["asset","upload","--staged","<uploadId>",…]`)
57
+ 3. Follow `consumeArgs`: **media prepare** for photos/video; **asset upload --staged** for SVG/PDF/Lottie
49
58
 
50
59
  Rules:
51
60
 
52
- - Staged upload keeps binary **out of MCP args** — only a short `uploadId` is passed to Contentful
53
- - Hosted limit: **4 MB** per file; **1000 upload URLs per user per hour**; TTL **60 minutes**; single-use
54
- - **PNG** when the source has transparency (portraits on brand-colour cards); JPEG flattens alpha
55
- - Upload alone does **not** attach the asset — follow with **link + save** (below)
61
+ - Staged upload keeps binary **out of MCP args**
62
+ - Hosted limit: **4 MB** per file; **1000 upload URLs per user per hour**; TTL **60 minutes**
63
+ - Raster/video staged files stay in Redis until pipeline fetches them (GET); they are not written to CMA
64
+ - Upload/import alone does **not** attach the asset — follow with **link + save** (below)
56
65
  - Use `--if-exists-by-filename` (with `--file-name`) to avoid duplicate assets when re-running
57
66
 
58
- After staged upload, create a Media wrapper **only when none exists** for that asset:
67
+ After import, create a Media wrapper **only when none exists** for that asset:
59
68
 
60
69
  ```
61
70
  cms_edit ["list", "--type", "media", "--asset-id", "<newPortraitAssetId>"]
@@ -68,17 +77,11 @@ To rename an existing Media wrapper: `cms_edit ["batch", "set", "<media-entry-id
68
77
 
69
78
  Avoid `asset upload --with-media` when multiple entries will share the same image (e.g. news composites) — it creates duplicate Media entries. Upload the asset only, then one shared `create media`, then link the media ID on each entry.
70
79
 
71
- **Prefer `--url`** when the file is already on **public HTTPS** under size limits — especially **media-pipeline ready pack URLs**:
72
-
73
- ```
74
- cms_edit ["asset", "upload", "--url", "https://<public-https-url>/file.png", "--mime", "image/png", "--file-name", "headshot-name-1200.png", "--title", "…", "--if-exists-by-filename"]
75
- ```
76
-
77
- The URL must be fetchable by the hosted MCP server. Do not use local-only URLs.
80
+ **Prefer pipeline import** for encoded masters. **Video** must use import. `asset upload --url` is only for **pipeline ready images** (or SVG/PDF/Lottie). A random PNG or Contentful CDN master is rejected.
78
81
 
79
82
  ### Local CLI (only when user approves)
80
83
 
81
- Hosted MCP uses `cms_edit_request_staged_upload` then `asset upload --staged`. Do not use the local `cms-edit` CLI for uploads.
84
+ Hosted MCP uses `cms_edit_request_staged_upload` then **media prepare** for raster/video (or `asset upload --staged` for SVG/PDF/Lottie). Do not use the local `cms-edit` CLI for uploads.
82
85
 
83
86
  ## Link uploaded asset to an entry
84
87
 
@@ -45,11 +45,11 @@ If title, order, or hero asset **conflict** and the user has not decided → **N
45
45
  |---|------|-------------------|-----------------------------------|----------------------|
46
46
  | 1 | … | … | … | … |
47
47
 
48
- 3. **Build beat sheet** — ordered narrative + visual beats mapped to **registered** component/collection types only (from `components-index` / site playbook). Prefer **source order** over CMS convenience.
48
+ 3. **Build beat sheet** — ordered narrative + visual beats. For each beat: use a **registered** component/collection type when **layout and interaction** match (`components-index` / site playbook). If the source is designer HTML, custom CSS/JS, sticky/scroll, or a diagram that no registered type can reproduce, map to **`htmlComponent`** when that type exists in the space (and follow a site `task-create-html-component` playbook if present). Mixed stacks are valid. Merge consecutive custom HTML beats that share CSS/JS. Prefer **source order** over CMS convenience.
49
49
  4. **Score verdict** using the threshold below.
50
50
  5. **Post one-screen soft-proof** — numbered final `content` (or page stack) order + gap list + verdict.
51
51
  6. **Ask for a light human confirm** — accept **yes** / **go** / **proceed** (or equivalent). Do not build on silence if the package is non-trivial.
52
- 7. On **GO** or **GO-WITH-GAPS** + yes → hand off to `task-create-from-document` / `task-create-article` / `task-create-page` with the soft-proof as the plan.
52
+ 7. On **GO** or **GO-WITH-GAPS** + yes → hand off to `task-create-html-component` (if the site has it and the source is designer HTML), otherwise `task-create-from-document` / `task-create-article` / `task-create-page` with the soft-proof as the plan.
53
53
  8. On **NO-GO** → stop; list what is blocking (do not invent structure to unblock).
54
54
 
55
55
  ## Gap threshold
@@ -75,7 +75,7 @@ If title, order, or hero asset **conflict** and the user has not decided → **N
75
75
  ## Faithful structure rules
76
76
 
77
77
  1. **Do not rewrite copy** — structure and place only; flag issues for the human.
78
- 2. **Design sequence beats CMS convenience** — do not invent pair/side-by-side collections unless the source is a true pair.
78
+ 2. **Design sequence beats CMS convenience** — do not invent pair/side-by-side collections unless the source is a true pair. If a beat is custom HTML/JS/scroll/diagram and `htmlComponent` exists, **remap that beat to `htmlComponent` and continue** (GO / GO-WITH-GAPS) — do not NO-GO the package and do not flatten it onto cards.
79
79
  3. **Placeholders beat reordering** — keep the slot; label the gap (do not drop missing beats and renumber silently).
80
80
  4. **Merge continuous prose** — do not split body-only blocks without a layout reason (art, quote, or design break between).
81
81
  5. **Hero / template owns page H1** when the site template provides it — body headings only when the source has section titles.
@@ -114,3 +114,4 @@ Copy: unchanged from source (list any human decisions only)
114
114
  - `task-media-pipeline-prepare`
115
115
  - `task-media-reuse-and-upload`
116
116
  - `task-preview-verify`
117
+ - Site `task-create-html-component` when present