@se-studio/contentful-cms 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/cli/program.d.ts.map +1 -1
- package/dist/cli/program.js +2 -0
- package/dist/cli/program.js.map +1 -1
- package/dist/commands/media.d.ts +4 -0
- package/dist/commands/media.d.ts.map +1 -0
- package/dist/commands/media.js +124 -0
- package/dist/commands/media.js.map +1 -0
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/hosted/index.d.ts +1 -0
- package/dist/hosted/index.d.ts.map +1 -1
- package/dist/hosted/index.js +1 -0
- package/dist/hosted/index.js.map +1 -1
- package/dist/hosted/next/routes/mcp.d.ts.map +1 -1
- package/dist/hosted/next/routes/mcp.js +3 -0
- package/dist/hosted/next/routes/mcp.js.map +1 -1
- package/dist/mcp/prompts/onboarding.d.ts.map +1 -1
- package/dist/mcp/prompts/onboarding.js +11 -8
- package/dist/mcp/prompts/onboarding.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/media.d.ts +4 -0
- package/dist/mcp/tools/media.d.ts.map +1 -0
- package/dist/mcp/tools/media.js +138 -0
- package/dist/mcp/tools/media.js.map +1 -0
- package/dist/media-pipeline/actions.d.ts +30 -0
- package/dist/media-pipeline/actions.d.ts.map +1 -0
- package/dist/media-pipeline/actions.js +257 -0
- package/dist/media-pipeline/actions.js.map +1 -0
- package/dist/media-pipeline/client.d.ts +67 -0
- package/dist/media-pipeline/client.d.ts.map +1 -0
- package/dist/media-pipeline/client.js +75 -0
- package/dist/media-pipeline/client.js.map +1 -0
- package/dist/media-pipeline/config.d.ts +17 -0
- package/dist/media-pipeline/config.d.ts.map +1 -0
- package/dist/media-pipeline/config.js +52 -0
- package/dist/media-pipeline/config.js.map +1 -0
- package/dist/media-pipeline/import.d.ts +19 -0
- package/dist/media-pipeline/import.d.ts.map +1 -0
- package/dist/media-pipeline/import.js +198 -0
- package/dist/media-pipeline/import.js.map +1 -0
- package/dist/media-pipeline/index.d.ts +6 -0
- package/dist/media-pipeline/index.d.ts.map +1 -0
- package/dist/media-pipeline/index.js +6 -0
- package/dist/media-pipeline/index.js.map +1 -0
- package/dist/media-pipeline/job-store.d.ts +41 -0
- package/dist/media-pipeline/job-store.d.ts.map +1 -0
- package/dist/media-pipeline/job-store.js +172 -0
- package/dist/media-pipeline/job-store.js.map +1 -0
- package/dist/media-pipeline/limits.d.ts +14 -0
- package/dist/media-pipeline/limits.d.ts.map +1 -0
- package/dist/media-pipeline/limits.js +14 -0
- package/dist/media-pipeline/limits.js.map +1 -0
- package/dist/media-pipeline/log.d.ts +3 -0
- package/dist/media-pipeline/log.d.ts.map +1 -0
- package/dist/media-pipeline/log.js +12 -0
- package/dist/media-pipeline/log.js.map +1 -0
- package/dist/media-pipeline/validate.d.ts +18 -0
- package/dist/media-pipeline/validate.d.ts.map +1 -0
- package/dist/media-pipeline/validate.js +38 -0
- package/dist/media-pipeline/validate.js.map +1 -0
- package/dist/runtime/context.d.ts +5 -0
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/context.js.map +1 -1
- package/docs/editor-tasks/media-pipeline-prepare.md +65 -69
- package/package.json +1 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { loadConfig, resolveSpaceConfig } from '../config/load.js';
|
|
2
|
+
import { uploadAssetBuffer } from '../contentful/assets.js';
|
|
3
|
+
import { createSafeEnvironment } from '../contentful/client.js';
|
|
4
|
+
import { createMediaEntry } from '../contentful/media.js';
|
|
5
|
+
import { fetchUploadSource } from '../contentful/upload-fetch.js';
|
|
6
|
+
import { findAssetByFileNameInIndex, mediaByAssetId } from '../index/query.js';
|
|
7
|
+
import { ensureContentIndex } from '../index/require.js';
|
|
8
|
+
import { getRuntimeContext } from '../runtime/context.js';
|
|
9
|
+
import { MediaPipelineClientError } from './client.js';
|
|
10
|
+
import { MediaJobStoreError } from './job-store.js';
|
|
11
|
+
import { mediaPipelineLog } from './log.js';
|
|
12
|
+
import { isPlausibleJobId } from './validate.js';
|
|
13
|
+
function fail(error, code, status, extra) {
|
|
14
|
+
return { ok: false, error, ...(code ? { code } : {}), ...(status ? { status } : {}), ...extra };
|
|
15
|
+
}
|
|
16
|
+
function overrideMap(overrides) {
|
|
17
|
+
const map = new Map();
|
|
18
|
+
for (const o of overrides ?? []) {
|
|
19
|
+
map.set(o.sourceName, o);
|
|
20
|
+
}
|
|
21
|
+
return map;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Reuse-first import of pipeline-ready assets into Contentful via the authenticated cms-edit user.
|
|
25
|
+
*/
|
|
26
|
+
export async function mediaImport(ctx, params) {
|
|
27
|
+
if (!ctx.config || !ctx.client) {
|
|
28
|
+
return fail('Media pipeline is not configured on this cms-edit host.', 'pipeline_unconfigured', 503);
|
|
29
|
+
}
|
|
30
|
+
if (!ctx.jobStore) {
|
|
31
|
+
return fail('Media pipeline job store requires hosted Redis.', 'store_unavailable', 503);
|
|
32
|
+
}
|
|
33
|
+
if (!ctx.userId) {
|
|
34
|
+
return fail('Media import requires an authenticated cms-edit user (Contentful OAuth).', 'unauthorized', 401);
|
|
35
|
+
}
|
|
36
|
+
const jobId = params.jobId?.trim();
|
|
37
|
+
if (!jobId || !isPlausibleJobId(jobId)) {
|
|
38
|
+
return fail('jobId is required and must be a valid media job id.', 'invalid_input', 400);
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
await ctx.jobStore.assertOwned(jobId, ctx.userId);
|
|
42
|
+
const status = await ctx.client.status(jobId);
|
|
43
|
+
if (status.status !== 'succeeded' && status.status !== 'partial') {
|
|
44
|
+
return fail(`Job is not ready to import (status=${status.status}). Wait until succeeded or partial.`, 'not_ready', 409, { status: status.status });
|
|
45
|
+
}
|
|
46
|
+
const assets = status.assets ?? [];
|
|
47
|
+
if (assets.length === 0) {
|
|
48
|
+
return fail('No ready assets on this job to import.', 'empty', 400, {
|
|
49
|
+
gaps: status.gaps ?? [],
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const runtime = getRuntimeContext();
|
|
53
|
+
const cmsConfig = runtime?.config ?? loadConfig();
|
|
54
|
+
const { space } = resolveSpaceConfig(cmsConfig);
|
|
55
|
+
const env = await createSafeEnvironment(space);
|
|
56
|
+
const locale = space.defaultLocale;
|
|
57
|
+
await ensureContentIndex(space, {});
|
|
58
|
+
const ov = overrideMap(params.overrides);
|
|
59
|
+
const results = [];
|
|
60
|
+
for (const asset of assets) {
|
|
61
|
+
results.push(await importOneAsset({
|
|
62
|
+
asset,
|
|
63
|
+
override: ov.get(asset.sourceName),
|
|
64
|
+
dryRun: Boolean(params.dryRun),
|
|
65
|
+
withMedia: Boolean(params.withMedia),
|
|
66
|
+
env,
|
|
67
|
+
space,
|
|
68
|
+
locale,
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
mediaPipelineLog('media.import', {
|
|
72
|
+
userId: ctx.userId,
|
|
73
|
+
projectKey: ctx.projectKey,
|
|
74
|
+
jobId,
|
|
75
|
+
dryRun: Boolean(params.dryRun),
|
|
76
|
+
count: results.length,
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
ok: true,
|
|
80
|
+
jobId,
|
|
81
|
+
dryRun: Boolean(params.dryRun),
|
|
82
|
+
imported: results,
|
|
83
|
+
gaps: status.gaps ?? [],
|
|
84
|
+
map: Object.fromEntries(results
|
|
85
|
+
.filter((r) => r.ok && r.assetId)
|
|
86
|
+
.map((r) => [
|
|
87
|
+
r.sourceName,
|
|
88
|
+
{
|
|
89
|
+
assetId: r.assetId,
|
|
90
|
+
mediaId: r.mediaId,
|
|
91
|
+
existing: r.existing,
|
|
92
|
+
fileName: r.fileName,
|
|
93
|
+
readyUrl: r.readyUrl,
|
|
94
|
+
previewUrl: r.previewUrl,
|
|
95
|
+
},
|
|
96
|
+
])),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
if (err instanceof MediaJobStoreError) {
|
|
101
|
+
return fail(err.message, err.code, err.status);
|
|
102
|
+
}
|
|
103
|
+
if (err instanceof MediaPipelineClientError) {
|
|
104
|
+
return fail(err.message, err.code, err.status);
|
|
105
|
+
}
|
|
106
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
107
|
+
return fail(message, 'error', 500);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function importOneAsset(opts) {
|
|
111
|
+
const { asset, override, dryRun, withMedia, env, space, locale } = opts;
|
|
112
|
+
const fileName = override?.fileName?.trim() || asset.fileName;
|
|
113
|
+
const title = override?.title?.trim() || asset.titleDraft || fileName;
|
|
114
|
+
const alt = override?.alt?.trim() || asset.altDraft || '';
|
|
115
|
+
const readyUrl = asset.url;
|
|
116
|
+
if (!readyUrl) {
|
|
117
|
+
return {
|
|
118
|
+
ok: false,
|
|
119
|
+
sourceName: asset.sourceName,
|
|
120
|
+
error: 'Asset has no readyUrl',
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const existing = findAssetByFileNameInIndex(space, fileName);
|
|
124
|
+
if (existing) {
|
|
125
|
+
let mediaId;
|
|
126
|
+
if (withMedia && !dryRun) {
|
|
127
|
+
const indexedMedia = mediaByAssetId(space, existing.id);
|
|
128
|
+
if (indexedMedia.length > 0) {
|
|
129
|
+
mediaId = indexedMedia[0].id;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
const media = await createMediaEntry(env, {
|
|
133
|
+
assetId: existing.id,
|
|
134
|
+
name: title,
|
|
135
|
+
locale,
|
|
136
|
+
});
|
|
137
|
+
mediaId = media.id;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
ok: true,
|
|
142
|
+
existing: true,
|
|
143
|
+
sourceName: asset.sourceName,
|
|
144
|
+
fileName,
|
|
145
|
+
assetId: existing.id,
|
|
146
|
+
mediaId,
|
|
147
|
+
readyUrl,
|
|
148
|
+
previewUrl: asset.previewUrl ?? null,
|
|
149
|
+
title,
|
|
150
|
+
alt,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (dryRun) {
|
|
154
|
+
return {
|
|
155
|
+
ok: true,
|
|
156
|
+
existing: false,
|
|
157
|
+
dryRun: true,
|
|
158
|
+
sourceName: asset.sourceName,
|
|
159
|
+
fileName,
|
|
160
|
+
wouldUpload: true,
|
|
161
|
+
readyUrl,
|
|
162
|
+
previewUrl: asset.previewUrl ?? null,
|
|
163
|
+
title,
|
|
164
|
+
alt,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const { buffer, contentType } = await fetchUploadSource(readyUrl);
|
|
168
|
+
const mimeType = asset.mime || contentType || 'application/octet-stream';
|
|
169
|
+
const summary = await uploadAssetBuffer(env, buffer, {
|
|
170
|
+
title,
|
|
171
|
+
description: alt,
|
|
172
|
+
fileName,
|
|
173
|
+
mimeType,
|
|
174
|
+
locale,
|
|
175
|
+
});
|
|
176
|
+
let mediaId;
|
|
177
|
+
if (withMedia) {
|
|
178
|
+
const media = await createMediaEntry(env, {
|
|
179
|
+
assetId: summary.id,
|
|
180
|
+
name: title,
|
|
181
|
+
locale,
|
|
182
|
+
});
|
|
183
|
+
mediaId = media.id;
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
ok: true,
|
|
187
|
+
existing: false,
|
|
188
|
+
sourceName: asset.sourceName,
|
|
189
|
+
fileName,
|
|
190
|
+
assetId: summary.id,
|
|
191
|
+
mediaId,
|
|
192
|
+
readyUrl,
|
|
193
|
+
previewUrl: asset.previewUrl ?? null,
|
|
194
|
+
title,
|
|
195
|
+
alt,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/media-pipeline/import.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { type MediaActionContext, type MediaActionResult, type MediaImportOverride, type MediaImportParams, mediaCatalog, mediaImport, mediaPrepare, mediaStatus, mediaWait, } from './actions.js';
|
|
2
|
+
export { MediaPipelineClient, MediaPipelineClientError, type PipelineJobStatusResponse, type PipelineManifestAsset, type PipelinePrepareResponse, } from './client.js';
|
|
3
|
+
export { loadMediaPipelineConfig, type MediaPipelineConfig, mediaPipelineConfiguredMessage, } from './config.js';
|
|
4
|
+
export { createMemoryRedis, type MediaJobBinding, MediaJobStoreError, MediaPipelineJobStore, } from './job-store.js';
|
|
5
|
+
export { MEDIA_DEFAULT_TTL_HOURS, MEDIA_JOB_BINDING_TTL_SECONDS, MEDIA_MAX_CONCURRENT_JOBS_PER_USER, MEDIA_PREPARE_RATE_LIMIT_PER_HOUR, } from './limits.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/media-pipeline/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,WAAW,EACX,SAAS,GACV,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,uBAAuB,EACvB,KAAK,mBAAmB,EACxB,8BAA8B,GAC/B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,KAAK,eAAe,EACpB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,kCAAkC,EAClC,iCAAiC,GAClC,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { mediaCatalog, mediaImport, mediaPrepare, mediaStatus, mediaWait, } from './actions.js';
|
|
2
|
+
export { MediaPipelineClient, MediaPipelineClientError, } from './client.js';
|
|
3
|
+
export { loadMediaPipelineConfig, mediaPipelineConfiguredMessage, } from './config.js';
|
|
4
|
+
export { createMemoryRedis, MediaJobStoreError, MediaPipelineJobStore, } from './job-store.js';
|
|
5
|
+
export { MEDIA_DEFAULT_TTL_HOURS, MEDIA_JOB_BINDING_TTL_SECONDS, MEDIA_MAX_CONCURRENT_JOBS_PER_USER, MEDIA_PREPARE_RATE_LIMIT_PER_HOUR, } from './limits.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/media-pipeline/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,WAAW,EACX,SAAS,GACV,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,GAIzB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,uBAAuB,EAEvB,8BAA8B,GAC/B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EAEjB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,kCAAkC,EAClC,iCAAiC,GAClC,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { RedisClient } from '../session/session-store.js';
|
|
2
|
+
export interface MediaJobBinding {
|
|
3
|
+
jobId: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
projectKey: string;
|
|
6
|
+
tenant: string;
|
|
7
|
+
profile: string;
|
|
8
|
+
folderId: string;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
/** Whether concurrent-slot was already released after terminal status. */
|
|
11
|
+
concurrentReleased?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare class MediaPipelineJobStore {
|
|
14
|
+
private readonly redis;
|
|
15
|
+
private readonly bindingTtlSeconds;
|
|
16
|
+
constructor(redis: RedisClient, bindingTtlSeconds?: number);
|
|
17
|
+
private jobKey;
|
|
18
|
+
private rateKey;
|
|
19
|
+
private concurrentKey;
|
|
20
|
+
assertPrepareRateLimit(userId: string, maxPerHour: number): Promise<void>;
|
|
21
|
+
assertConcurrentSlot(userId: string, maxConcurrent: number): Promise<void>;
|
|
22
|
+
reserveConcurrentSlot(userId: string): Promise<void>;
|
|
23
|
+
releaseConcurrentSlot(userId: string): Promise<void>;
|
|
24
|
+
saveBinding(binding: MediaJobBinding): Promise<void>;
|
|
25
|
+
getBinding(jobId: string): Promise<MediaJobBinding | null>;
|
|
26
|
+
assertOwned(jobId: string, userId: string): Promise<MediaJobBinding>;
|
|
27
|
+
markConcurrentReleased(jobId: string, userId: string): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export declare class MediaJobStoreError extends Error {
|
|
30
|
+
readonly code: string;
|
|
31
|
+
readonly status: number;
|
|
32
|
+
constructor(message: string, code: string, status: number);
|
|
33
|
+
}
|
|
34
|
+
/** In-memory RedisClient subset for unit tests and local CLI without Upstash. */
|
|
35
|
+
export declare function createMemoryRedis(): RedisClient & {
|
|
36
|
+
store: Map<string, {
|
|
37
|
+
value: string;
|
|
38
|
+
exp?: number;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=job-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-store.d.ts","sourceRoot":"","sources":["../../src/media-pipeline/job-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG/D,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAuBD,qBAAa,qBAAqB;IAE9B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;gBADjB,KAAK,EAAE,WAAW,EAClB,iBAAiB,SAAgC;IAGpE,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,aAAa;IAIf,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBzE,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc1E,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQpD,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAapD,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAapD,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAI1D,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAmBpE,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAW3E;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAGzC,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAFvB,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM;CAK1B;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,IAAI,WAAW,GAAG;IACjD,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD,CAkDA"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { MEDIA_JOB_BINDING_TTL_SECONDS } from './limits.js';
|
|
2
|
+
const JOB_PREFIX = 'cms-edit:media-job:';
|
|
3
|
+
const RATE_PREFIX = 'cms-edit:media-rate:prepare:';
|
|
4
|
+
const CONCURRENT_PREFIX = 'cms-edit:media-concurrent:';
|
|
5
|
+
function parseBinding(raw) {
|
|
6
|
+
if (!raw) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
if (typeof raw === 'string') {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(raw);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (typeof raw === 'object') {
|
|
18
|
+
return raw;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export class MediaPipelineJobStore {
|
|
23
|
+
redis;
|
|
24
|
+
bindingTtlSeconds;
|
|
25
|
+
constructor(redis, bindingTtlSeconds = MEDIA_JOB_BINDING_TTL_SECONDS) {
|
|
26
|
+
this.redis = redis;
|
|
27
|
+
this.bindingTtlSeconds = bindingTtlSeconds;
|
|
28
|
+
}
|
|
29
|
+
jobKey(jobId) {
|
|
30
|
+
return `${JOB_PREFIX}${jobId}`;
|
|
31
|
+
}
|
|
32
|
+
rateKey(userId, hourBucket) {
|
|
33
|
+
return `${RATE_PREFIX}${userId}:${String(hourBucket)}`;
|
|
34
|
+
}
|
|
35
|
+
concurrentKey(userId) {
|
|
36
|
+
return `${CONCURRENT_PREFIX}${userId}`;
|
|
37
|
+
}
|
|
38
|
+
async assertPrepareRateLimit(userId, maxPerHour) {
|
|
39
|
+
const hourBucket = Math.floor(Date.now() / (60 * 60 * 1000));
|
|
40
|
+
const key = this.rateKey(userId, hourBucket);
|
|
41
|
+
const count = await this.redis.incr(key);
|
|
42
|
+
if (count === 1) {
|
|
43
|
+
await this.redis.expire(key, 60 * 60);
|
|
44
|
+
}
|
|
45
|
+
if (count > maxPerHour) {
|
|
46
|
+
throw new MediaJobStoreError(`Media prepare rate limit exceeded (${String(maxPerHour)} per hour).`, 'rate_limited', 429);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async assertConcurrentSlot(userId, maxConcurrent) {
|
|
50
|
+
const key = this.concurrentKey(userId);
|
|
51
|
+
const raw = await this.redis.get(key);
|
|
52
|
+
const current = typeof raw === 'number' ? raw : typeof raw === 'string' ? Number.parseInt(raw, 10) || 0 : 0;
|
|
53
|
+
if (current >= maxConcurrent) {
|
|
54
|
+
throw new MediaJobStoreError(`Too many concurrent media jobs (max ${String(maxConcurrent)}). Wait for one to finish.`, 'rate_limited', 429);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async reserveConcurrentSlot(userId) {
|
|
58
|
+
const key = this.concurrentKey(userId);
|
|
59
|
+
const count = await this.redis.incr(key);
|
|
60
|
+
if (count === 1) {
|
|
61
|
+
await this.redis.expire(key, this.bindingTtlSeconds);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async releaseConcurrentSlot(userId) {
|
|
65
|
+
const key = this.concurrentKey(userId);
|
|
66
|
+
const raw = await this.redis.get(key);
|
|
67
|
+
const current = typeof raw === 'number' ? raw : typeof raw === 'string' ? Number.parseInt(raw, 10) || 0 : 0;
|
|
68
|
+
if (current <= 1) {
|
|
69
|
+
await this.redis.del(key);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Redis has no decr in our thin client interface — set n-1
|
|
73
|
+
await this.redis.set(key, String(current - 1), { ex: this.bindingTtlSeconds });
|
|
74
|
+
}
|
|
75
|
+
async saveBinding(binding) {
|
|
76
|
+
const set = await this.redis.set(this.jobKey(binding.jobId), JSON.stringify(binding), {
|
|
77
|
+
ex: this.bindingTtlSeconds,
|
|
78
|
+
nx: true,
|
|
79
|
+
});
|
|
80
|
+
if (set !== true && set !== undefined) {
|
|
81
|
+
// Upstash returns "OK" or true depending on client; treat false-ish as conflict
|
|
82
|
+
if (set === false) {
|
|
83
|
+
throw new MediaJobStoreError('Failed to reserve media job binding.', 'conflict', 409);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async getBinding(jobId) {
|
|
88
|
+
return parseBinding(await this.redis.get(this.jobKey(jobId)));
|
|
89
|
+
}
|
|
90
|
+
async assertOwned(jobId, userId) {
|
|
91
|
+
const binding = await this.getBinding(jobId);
|
|
92
|
+
if (!binding) {
|
|
93
|
+
throw new MediaJobStoreError('Unknown or expired media job. Prepare a new pack, or this job belongs to another session.', 'forbidden_job', 403);
|
|
94
|
+
}
|
|
95
|
+
if (binding.userId !== userId) {
|
|
96
|
+
throw new MediaJobStoreError('This media job is owned by another user.', 'forbidden_job', 403);
|
|
97
|
+
}
|
|
98
|
+
return binding;
|
|
99
|
+
}
|
|
100
|
+
async markConcurrentReleased(jobId, userId) {
|
|
101
|
+
const binding = await this.getBinding(jobId);
|
|
102
|
+
if (!binding || binding.userId !== userId || binding.concurrentReleased) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
binding.concurrentReleased = true;
|
|
106
|
+
await this.redis.set(this.jobKey(jobId), JSON.stringify(binding), {
|
|
107
|
+
ex: this.bindingTtlSeconds,
|
|
108
|
+
});
|
|
109
|
+
await this.releaseConcurrentSlot(userId);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export class MediaJobStoreError extends Error {
|
|
113
|
+
code;
|
|
114
|
+
status;
|
|
115
|
+
constructor(message, code, status) {
|
|
116
|
+
super(message);
|
|
117
|
+
this.code = code;
|
|
118
|
+
this.status = status;
|
|
119
|
+
this.name = 'MediaJobStoreError';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** In-memory RedisClient subset for unit tests and local CLI without Upstash. */
|
|
123
|
+
export function createMemoryRedis() {
|
|
124
|
+
const store = new Map();
|
|
125
|
+
const alive = (key) => {
|
|
126
|
+
const row = store.get(key);
|
|
127
|
+
if (!row) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
if (row.exp !== undefined && Date.now() > row.exp) {
|
|
131
|
+
store.delete(key);
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
return row;
|
|
135
|
+
};
|
|
136
|
+
return {
|
|
137
|
+
store,
|
|
138
|
+
async get(key) {
|
|
139
|
+
return alive(key)?.value ?? null;
|
|
140
|
+
},
|
|
141
|
+
async set(key, value, options) {
|
|
142
|
+
if (options?.nx && alive(key)) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
store.set(key, {
|
|
146
|
+
value,
|
|
147
|
+
exp: options?.ex !== undefined ? Date.now() + options.ex * 1000 : undefined,
|
|
148
|
+
});
|
|
149
|
+
return true;
|
|
150
|
+
},
|
|
151
|
+
async del(key) {
|
|
152
|
+
store.delete(key);
|
|
153
|
+
},
|
|
154
|
+
async keys(pattern) {
|
|
155
|
+
const prefix = pattern.replace(/\*$/, '');
|
|
156
|
+
return [...store.keys()].filter((k) => k.startsWith(prefix) && alive(k));
|
|
157
|
+
},
|
|
158
|
+
async incr(key) {
|
|
159
|
+
const row = alive(key);
|
|
160
|
+
const n = (row ? Number.parseInt(row.value, 10) || 0 : 0) + 1;
|
|
161
|
+
store.set(key, { value: String(n), exp: row?.exp });
|
|
162
|
+
return n;
|
|
163
|
+
},
|
|
164
|
+
async expire(key, seconds) {
|
|
165
|
+
const row = alive(key);
|
|
166
|
+
if (row) {
|
|
167
|
+
store.set(key, { value: row.value, exp: Date.now() + seconds * 1000 });
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=job-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../src/media-pipeline/job-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Default prepare rate limit per authenticated user per hour. */
|
|
2
|
+
export declare const MEDIA_PREPARE_RATE_LIMIT_PER_HOUR = 10;
|
|
3
|
+
/** Default max concurrent non-terminal jobs per user. */
|
|
4
|
+
export declare const MEDIA_MAX_CONCURRENT_JOBS_PER_USER = 2;
|
|
5
|
+
/** Job ownership binding TTL (seconds) — longer than default pack TTL (24h). */
|
|
6
|
+
export declare const MEDIA_JOB_BINDING_TTL_SECONDS: number;
|
|
7
|
+
/** Default pack TTL hours when agent omits ttlHours. */
|
|
8
|
+
export declare const MEDIA_DEFAULT_TTL_HOURS = 24;
|
|
9
|
+
/** Allowed ttlHours range. */
|
|
10
|
+
export declare const MEDIA_TTL_HOURS_MIN = 1;
|
|
11
|
+
export declare const MEDIA_TTL_HOURS_MAX = 24;
|
|
12
|
+
/** Profiles agents may request when host does not lock a single profile. */
|
|
13
|
+
export declare const MEDIA_DEFAULT_ALLOWED_PROFILES: readonly ["case-study-v1"];
|
|
14
|
+
//# sourceMappingURL=limits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/media-pipeline/limits.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD,yDAAyD;AACzD,eAAO,MAAM,kCAAkC,IAAI,CAAC;AAEpD,gFAAgF;AAChF,eAAO,MAAM,6BAA6B,QAAe,CAAC;AAE1D,wDAAwD;AACxD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C,8BAA8B;AAC9B,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,4EAA4E;AAC5E,eAAO,MAAM,8BAA8B,4BAA6B,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Default prepare rate limit per authenticated user per hour. */
|
|
2
|
+
export const MEDIA_PREPARE_RATE_LIMIT_PER_HOUR = 10;
|
|
3
|
+
/** Default max concurrent non-terminal jobs per user. */
|
|
4
|
+
export const MEDIA_MAX_CONCURRENT_JOBS_PER_USER = 2;
|
|
5
|
+
/** Job ownership binding TTL (seconds) — longer than default pack TTL (24h). */
|
|
6
|
+
export const MEDIA_JOB_BINDING_TTL_SECONDS = 48 * 60 * 60;
|
|
7
|
+
/** Default pack TTL hours when agent omits ttlHours. */
|
|
8
|
+
export const MEDIA_DEFAULT_TTL_HOURS = 24;
|
|
9
|
+
/** Allowed ttlHours range. */
|
|
10
|
+
export const MEDIA_TTL_HOURS_MIN = 1;
|
|
11
|
+
export const MEDIA_TTL_HOURS_MAX = 24;
|
|
12
|
+
/** Profiles agents may request when host does not lock a single profile. */
|
|
13
|
+
export const MEDIA_DEFAULT_ALLOWED_PROFILES = ['case-study-v1'];
|
|
14
|
+
//# sourceMappingURL=limits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/media-pipeline/limits.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAEpD,yDAAyD;AACzD,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,CAAC;AAEpD,gFAAgF;AAChF,MAAM,CAAC,MAAM,6BAA6B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAE1D,wDAAwD;AACxD,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAE1C,8BAA8B;AAC9B,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AACrC,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,4EAA4E;AAC5E,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,eAAe,CAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/media-pipeline/log.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC,GACnE,IAAI,CASN"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Structured media-pipeline facade logs — never log secrets. */
|
|
2
|
+
export function mediaPipelineLog(event, fields) {
|
|
3
|
+
const payload = { event };
|
|
4
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
5
|
+
if (v !== undefined && v !== null) {
|
|
6
|
+
payload[k] = v;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
// biome-ignore lint/suspicious/noConsole: intentional structured ops log
|
|
10
|
+
console.error(JSON.stringify({ scope: 'cms-edit:media-pipeline', ...payload }));
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/media-pipeline/log.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,MAAM,UAAU,gBAAgB,CAC9B,KAAa,EACb,MAAoE;IAEpE,MAAM,OAAO,GAA8C,EAAE,KAAK,EAAE,CAAC;IACrE,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAClC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IACD,yEAAyE;IACzE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;AAClF,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare function parseDriveFolderUrl(url: string): {
|
|
2
|
+
ok: true;
|
|
3
|
+
folderId: string;
|
|
4
|
+
url: string;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
error: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function clampTtlHours(value: number | undefined, defaultTtl: number): number;
|
|
10
|
+
export declare function assertAllowedProfile(profile: string, allowed: string[]): {
|
|
11
|
+
ok: true;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
error: string;
|
|
15
|
+
};
|
|
16
|
+
/** Job ids from media-pipeline are URL-safe tokens; keep validation loose but bounded. */
|
|
17
|
+
export declare function isPlausibleJobId(jobId: string): boolean;
|
|
18
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/media-pipeline/validate.ts"],"names":[],"mappings":"AAKA,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAC3C;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC3C;IACE,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf,CAeJ;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAMnF;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EAAE,GAChB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAQ7C;AAED,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvD"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { MEDIA_TTL_HOURS_MAX, MEDIA_TTL_HOURS_MIN } from './limits.js';
|
|
2
|
+
const DRIVE_FOLDER_RE = /^https:\/\/drive\.google\.com\/drive\/(?:u\/\d+\/)?folders\/([a-zA-Z0-9_-]+)(?:[/?#].*)?$/;
|
|
3
|
+
export function parseDriveFolderUrl(url) {
|
|
4
|
+
const trimmed = url.trim();
|
|
5
|
+
const match = DRIVE_FOLDER_RE.exec(trimmed);
|
|
6
|
+
if (!match) {
|
|
7
|
+
return {
|
|
8
|
+
ok: false,
|
|
9
|
+
error: 'driveFolderUrl must be a Google Drive folder URL (https://drive.google.com/drive/folders/…).',
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
ok: true,
|
|
14
|
+
folderId: match[1],
|
|
15
|
+
url: `https://drive.google.com/drive/folders/${match[1]}`,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function clampTtlHours(value, defaultTtl) {
|
|
19
|
+
const raw = value ?? defaultTtl;
|
|
20
|
+
if (!Number.isFinite(raw)) {
|
|
21
|
+
return defaultTtl;
|
|
22
|
+
}
|
|
23
|
+
return Math.min(MEDIA_TTL_HOURS_MAX, Math.max(MEDIA_TTL_HOURS_MIN, Math.floor(raw)));
|
|
24
|
+
}
|
|
25
|
+
export function assertAllowedProfile(profile, allowed) {
|
|
26
|
+
if (!allowed.includes(profile)) {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
error: `Profile "${profile}" is not allowed on this host. Allowed: ${allowed.join(', ')}`,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return { ok: true };
|
|
33
|
+
}
|
|
34
|
+
/** Job ids from media-pipeline are URL-safe tokens; keep validation loose but bounded. */
|
|
35
|
+
export function isPlausibleJobId(jobId) {
|
|
36
|
+
return /^[a-zA-Z0-9_-]{8,128}$/.test(jobId.trim());
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/media-pipeline/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvE,MAAM,eAAe,GACnB,2FAA2F,CAAC;AAE9F,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAM7C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EACH,8FAA8F;SACjG,CAAC;IACJ,CAAC;IACD,OAAO;QACL,EAAE,EAAE,IAAI;QACR,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAE;QACnB,GAAG,EAAE,0CAA0C,KAAK,CAAC,CAAC,CAAE,EAAE;KAC3D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAyB,EAAE,UAAkB;IACzE,MAAM,GAAG,GAAG,KAAK,IAAI,UAAU,CAAC;IAChC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,OAAe,EACf,OAAiB;IAEjB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,YAAY,OAAO,2CAA2C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SAC1F,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
2
|
import type { CmsEditConfig } from '../config/types.js';
|
|
3
3
|
import type { IndexCacheStore } from '../index/cache-store.js';
|
|
4
|
+
import type { MediaPipelineJobStore } from '../media-pipeline/job-store.js';
|
|
4
5
|
import type { SessionStore } from '../session/session-store.js';
|
|
5
6
|
import type { StagedUploadStore } from '../staged-upload/store.js';
|
|
6
7
|
export interface ContentfulUserIdentity {
|
|
@@ -18,6 +19,10 @@ export interface CmsEditRuntimeContext {
|
|
|
18
19
|
user?: ContentfulUserIdentity;
|
|
19
20
|
/** Staged file uploads for hosted asset upload (Redis). */
|
|
20
21
|
stagedUploadStore?: StagedUploadStore;
|
|
22
|
+
/** Media pipeline job ownership + rate limits (Redis on hosted). */
|
|
23
|
+
mediaPipelineJobStore?: MediaPipelineJobStore;
|
|
24
|
+
/** Hosted project key (e.g. se2026) for media job bindings / logs. */
|
|
25
|
+
projectKey?: string;
|
|
21
26
|
}
|
|
22
27
|
export declare const cmsEditContext: AsyncLocalStorage<CmsEditRuntimeContext>;
|
|
23
28
|
export declare function getRuntimeContext(): CmsEditRuntimeContext | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,qEAAqE;IACrE,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,2EAA2E;IAC3E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gEAAgE;IAChE,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,qEAAqE;IACrE,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,2EAA2E;IAC3E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gEAAgE;IAChE,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,oEAAoE;IACpE,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAC9C,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,0CAAiD,CAAC;AAE7E,wBAAgB,iBAAiB,IAAI,qBAAqB,GAAG,SAAS,CAErE;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,qBAAqB,EAC9B,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/runtime/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AA6BrD,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,iBAAiB,EAAyB,CAAC;AAE7E,MAAM,UAAU,iBAAiB;IAC/B,OAAO,cAAc,CAAC,QAAQ,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,OAA8B,EAC9B,EAAwB;IAExB,OAAO,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC"}
|