@ttt-productions/media-processing-core 0.8.16 → 0.8.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio/audio-processor.d.ts +2 -2
- package/dist/audio/audio-processor.d.ts.map +1 -1
- package/dist/audio/audio-processor.js +63 -50
- package/dist/audio/audio-processor.js.map +1 -1
- package/dist/audio/probe.d.ts +1 -1
- package/dist/audio/probe.d.ts.map +1 -1
- package/dist/audio/probe.js +6 -5
- package/dist/audio/probe.js.map +1 -1
- package/dist/duration/media-duration.d.ts +2 -13
- package/dist/duration/media-duration.d.ts.map +1 -1
- package/dist/duration/media-duration.js +24 -49
- package/dist/duration/media-duration.js.map +1 -1
- package/dist/image/formats.d.ts +2 -13
- package/dist/image/formats.d.ts.map +1 -1
- package/dist/image/formats.js +25 -35
- package/dist/image/formats.js.map +1 -1
- package/dist/image/image-processor.d.ts +2 -2
- package/dist/image/image-processor.d.ts.map +1 -1
- package/dist/image/image-processor.js +48 -57
- package/dist/image/image-processor.js.map +1 -1
- package/dist/image/resize.d.ts +2 -16
- package/dist/image/resize.d.ts.map +1 -1
- package/dist/image/resize.js +13 -28
- package/dist/image/resize.js.map +1 -1
- package/dist/index.d.ts +19 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -16
- package/dist/index.js.map +1 -1
- package/dist/inspection/inspect-media.d.ts +13 -20
- package/dist/inspection/inspect-media.d.ts.map +1 -1
- package/dist/inspection/inspect-media.js +197 -201
- package/dist/inspection/inspect-media.js.map +1 -1
- package/dist/io/fs.d.ts +1 -1
- package/dist/io/fs.d.ts.map +1 -1
- package/dist/io/fs.js +4 -5
- package/dist/io/fs.js.map +1 -1
- package/dist/io/local-input.d.ts +1 -21
- package/dist/io/local-input.d.ts.map +1 -1
- package/dist/io/local-input.js +15 -35
- package/dist/io/local-input.js.map +1 -1
- package/dist/io/types.d.ts +0 -3
- package/dist/io/types.d.ts.map +1 -1
- package/dist/io/types.js.map +1 -1
- package/dist/moderation/merge.d.ts +1 -1
- package/dist/moderation/merge.d.ts.map +1 -1
- package/dist/moderation/merge.js +1 -2
- package/dist/moderation/merge.js.map +1 -1
- package/dist/moderation/types.d.ts +3 -5
- package/dist/moderation/types.d.ts.map +1 -1
- package/dist/moderation/types.js.map +1 -1
- package/dist/process-media.d.ts +2 -3
- package/dist/process-media.d.ts.map +1 -1
- package/dist/process-media.js +9 -9
- package/dist/process-media.js.map +1 -1
- package/dist/processing/errors.d.ts.map +1 -1
- package/dist/processing/errors.js +1 -1
- package/dist/processing/errors.js.map +1 -1
- package/dist/processing/result.d.ts +2 -2
- package/dist/processing/result.js +1 -1
- package/dist/processing/result.js.map +1 -1
- package/dist/run-pipeline.d.ts +5 -22
- package/dist/run-pipeline.d.ts.map +1 -1
- package/dist/run-pipeline.js +72 -81
- package/dist/run-pipeline.js.map +1 -1
- package/dist/server/download-token.d.ts +0 -6
- package/dist/server/download-token.d.ts.map +1 -1
- package/dist/server/download-token.js +3 -13
- package/dist/server/download-token.js.map +1 -1
- package/dist/server/firebase-media-io.d.ts +3 -13
- package/dist/server/firebase-media-io.d.ts.map +1 -1
- package/dist/server/firebase-media-io.js +1 -6
- package/dist/server/firebase-media-io.js.map +1 -1
- package/dist/server/gcs-errors.d.ts +0 -8
- package/dist/server/gcs-errors.d.ts.map +1 -1
- package/dist/server/gcs-errors.js +2 -10
- package/dist/server/gcs-errors.js.map +1 -1
- package/dist/server/index.d.ts +7 -6
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +7 -6
- package/dist/server/index.js.map +1 -1
- package/dist/server/pending-media.d.ts +3 -34
- package/dist/server/pending-media.d.ts.map +1 -1
- package/dist/server/pending-media.js +13 -30
- package/dist/server/pending-media.js.map +1 -1
- package/dist/server/r2-retry.d.ts +4 -52
- package/dist/server/r2-retry.d.ts.map +1 -1
- package/dist/server/r2-retry.js +49 -134
- package/dist/server/r2-retry.js.map +1 -1
- package/dist/server/staged-object.d.ts +2 -46
- package/dist/server/staged-object.d.ts.map +1 -1
- package/dist/server/staged-object.js +4 -48
- package/dist/server/staged-object.js.map +1 -1
- package/dist/server/storage-ops.d.ts +5 -67
- package/dist/server/storage-ops.d.ts.map +1 -1
- package/dist/server/storage-ops.js +110 -185
- package/dist/server/storage-ops.js.map +1 -1
- package/dist/types.d.ts +2 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/log.d.ts.map +1 -1
- package/dist/utils/log.js +3 -3
- package/dist/utils/log.js.map +1 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +1 -4
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/safe-path.d.ts.map +1 -1
- package/dist/utils/safe-path.js +8 -9
- package/dist/utils/safe-path.js.map +1 -1
- package/dist/validation/validate-duration.d.ts.map +1 -1
- package/dist/validation/validate-duration.js.map +1 -1
- package/dist/validation/validate-mime.d.ts.map +1 -1
- package/dist/validation/validate-mime.js.map +1 -1
- package/dist/validation/validate-size.d.ts.map +1 -1
- package/dist/validation/validate-size.js.map +1 -1
- package/dist/video/ffmpeg.d.ts +1 -23
- package/dist/video/ffmpeg.d.ts.map +1 -1
- package/dist/video/ffmpeg.js +57 -64
- package/dist/video/ffmpeg.js.map +1 -1
- package/dist/video/probe.d.ts +1 -1
- package/dist/video/probe.d.ts.map +1 -1
- package/dist/video/probe.js +10 -8
- package/dist/video/probe.js.map +1 -1
- package/dist/video/video-processor.d.ts +2 -2
- package/dist/video/video-processor.d.ts.map +1 -1
- package/dist/video/video-processor.js +125 -96
- package/dist/video/video-processor.js.map +1 -1
- package/dist/workspace/temp.js +4 -4
- package/dist/workspace/temp.js.map +1 -1
- package/package.json +2 -2
- package/dist/audio/index.d.ts +0 -3
- package/dist/audio/index.d.ts.map +0 -1
- package/dist/audio/index.js +0 -3
- package/dist/audio/index.js.map +0 -1
- package/dist/image/index.d.ts +0 -4
- package/dist/image/index.d.ts.map +0 -1
- package/dist/image/index.js +0 -4
- package/dist/image/index.js.map +0 -1
- package/dist/video/index.d.ts +0 -4
- package/dist/video/index.d.ts.map +0 -1
- package/dist/video/index.js +0 -4
- package/dist/video/index.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { MediaProcessingError, MediaProcessingResult } from
|
|
1
|
+
import type { MediaProcessingError, MediaProcessingResult } from '@ttt-productions/media-schemas';
|
|
2
2
|
export declare function success(result: Omit<Extract<MediaProcessingResult, {
|
|
3
3
|
ok: true;
|
|
4
|
-
}>,
|
|
4
|
+
}>, 'ok'>): Extract<MediaProcessingResult, {
|
|
5
5
|
ok: true;
|
|
6
6
|
}>;
|
|
7
7
|
export declare function failure(error: MediaProcessingError): Extract<MediaProcessingResult, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/processing/result.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,OAAO,CACrB,MAAgE;IAEhE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,KAA2B;IAE3B,OAAO;QACL,EAAE,EAAE,KAAK;QACT,SAAS,EAAE,OAAO;QAClB,KAAK;KACN,CAAC;AACJ,CAAC","sourcesContent":["import type { MediaProcessingError, MediaProcessingResult } from
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/processing/result.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,OAAO,CACrB,MAAgE;IAEhE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,KAA2B;IAE3B,OAAO;QACL,EAAE,EAAE,KAAK;QACT,SAAS,EAAE,OAAO;QAClB,KAAK;KACN,CAAC;AACJ,CAAC","sourcesContent":["import type { MediaProcessingError, MediaProcessingResult } from '@ttt-productions/media-schemas';\n\nexport function success(\n result: Omit<Extract<MediaProcessingResult, { ok: true }>, 'ok'>,\n): Extract<MediaProcessingResult, { ok: true }> {\n return { ok: true, ...result };\n}\n\nexport function failure(\n error: MediaProcessingError,\n): Extract<MediaProcessingResult, { ok: false }> {\n return {\n ok: false,\n mediaType: 'other',\n error,\n };\n}\n"]}
|
package/dist/run-pipeline.d.ts
CHANGED
|
@@ -1,30 +1,14 @@
|
|
|
1
|
-
import type { MediaProcessingResult, MediaProcessingSpec, MediaInspectionResult, MediaProcessingError } from
|
|
2
|
-
import { type InspectMediaDeps } from
|
|
3
|
-
import type { MediaIO } from
|
|
4
|
-
import type { ModerationAdapter } from
|
|
1
|
+
import type { MediaProcessingResult, MediaProcessingSpec, MediaInspectionResult, MediaProcessingError } from '@ttt-productions/media-schemas';
|
|
2
|
+
import { type InspectMediaDeps } from './inspection/inspect-media.js';
|
|
3
|
+
import type { MediaIO } from './io/types.js';
|
|
4
|
+
import type { ModerationAdapter } from './moderation/types.js';
|
|
5
5
|
export interface RunPipelineArgs {
|
|
6
6
|
spec: MediaProcessingSpec;
|
|
7
7
|
io: MediaIO;
|
|
8
8
|
outputBaseName?: string;
|
|
9
9
|
moderation?: ModerationAdapter;
|
|
10
10
|
signal?: AbortSignal;
|
|
11
|
-
onProgress?: (p: import(
|
|
12
|
-
/**
|
|
13
|
-
* CANONICAL INSPECTION SEAM (canonical-upload-content-classification).
|
|
14
|
-
* When present, the pipeline runs `inspectMedia` on the just-downloaded temp
|
|
15
|
-
* input — BEFORE moderation and processing — and calls this policy hook with
|
|
16
|
-
* the bounded result. The hook either:
|
|
17
|
-
* - returns `{ spec }` — the caller's policy adapter selected the
|
|
18
|
-
* processing spec FROM the inspection (the byte-derived kind, never the
|
|
19
|
-
* client MIME); the pipeline continues with that spec; or
|
|
20
|
-
* - returns `{ reject }` — a typed terminal (`kind_mismatch`,
|
|
21
|
-
* `unsupported_format`, `unsupported_codec`, …); the pipeline stops
|
|
22
|
-
* before any transcode/moderation work.
|
|
23
|
-
* The inspection result rides the returned `MediaProcessingResult.inspection`
|
|
24
|
-
* either way, so the finalizer hands the SAME object to the safety gate —
|
|
25
|
-
* one authority, no re-detection. Callers that omit the hook get the
|
|
26
|
-
* pre-existing behavior unchanged.
|
|
27
|
-
*/
|
|
11
|
+
onProgress?: (p: import('./types.js').MediaPipelineProgress) => void;
|
|
28
12
|
resolveAfterInspection?: (inspection: MediaInspectionResult) => Promise<{
|
|
29
13
|
spec: MediaProcessingSpec;
|
|
30
14
|
} | {
|
|
@@ -34,7 +18,6 @@ export interface RunPipelineArgs {
|
|
|
34
18
|
} | {
|
|
35
19
|
reject: MediaProcessingError;
|
|
36
20
|
});
|
|
37
|
-
/** Inspection tool overrides (tests). */
|
|
38
21
|
inspectionDeps?: InspectMediaDeps;
|
|
39
22
|
}
|
|
40
23
|
export declare function runMediaPipeline(args: RunPipelineArgs): Promise<MediaProcessingResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-pipeline.d.ts","sourceRoot":"","sources":["../src/run-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"run-pipeline.d.ts","sourceRoot":"","sources":["../src/run-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EAEnB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,gCAAgC,CAAC;AAIxC,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACpF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAK/D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,YAAY,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACrE,sBAAsB,CAAC,EAAE,CACvB,UAAU,EAAE,qBAAqB,KAE/B,OAAO,CAAC;QAAE,IAAI,EAAE,mBAAmB,CAAA;KAAE,GAAG;QAAE,MAAM,EAAE,oBAAoB,CAAA;KAAE,CAAC,GACzE,CAAC;QAAE,IAAI,EAAE,mBAAmB,CAAA;KAAE,GAAG;QAAE,MAAM,EAAE,oBAAoB,CAAA;KAAE,CAAC,CAAC;IACvE,cAAc,CAAC,EAAE,gBAAgB,CAAC;CACnC;AAmBD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAmP5F"}
|
package/dist/run-pipeline.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { parseMediaProcessingSpec } from
|
|
2
|
-
import { createTempWorkspace } from
|
|
3
|
-
import { processMedia } from
|
|
4
|
-
import { inspectMedia } from
|
|
5
|
-
import { mergeModeration } from
|
|
6
|
-
import path from
|
|
7
|
-
import { stat } from
|
|
1
|
+
import { parseMediaProcessingSpec } from '@ttt-productions/media-schemas';
|
|
2
|
+
import { createTempWorkspace } from './workspace/temp.js';
|
|
3
|
+
import { processMedia } from './process-media.js';
|
|
4
|
+
import { inspectMedia } from './inspection/inspect-media.js';
|
|
5
|
+
import { mergeModeration } from './moderation/merge.js';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { stat } from 'node:fs/promises';
|
|
8
8
|
function mediaTypeFromSpecKind(kind) {
|
|
9
|
-
if (kind ===
|
|
10
|
-
return
|
|
11
|
-
if (kind ===
|
|
12
|
-
return
|
|
13
|
-
if (kind ===
|
|
14
|
-
return
|
|
15
|
-
return
|
|
9
|
+
if (kind === 'image')
|
|
10
|
+
return 'image';
|
|
11
|
+
if (kind === 'video')
|
|
12
|
+
return 'video';
|
|
13
|
+
if (kind === 'audio')
|
|
14
|
+
return 'audio';
|
|
15
|
+
return 'other';
|
|
16
16
|
}
|
|
17
17
|
function attachProvider(m, provider) {
|
|
18
18
|
if (!m)
|
|
@@ -21,19 +21,16 @@ function attachProvider(m, provider) {
|
|
|
21
21
|
}
|
|
22
22
|
export async function runMediaPipeline(args) {
|
|
23
23
|
let { spec } = args;
|
|
24
|
-
const { io, outputBaseName =
|
|
25
|
-
// Carried onto every return path once produced — the finalizer must hand the
|
|
26
|
-
// SAME inspection object to the safety gate (one classification authority).
|
|
24
|
+
const { io, outputBaseName = 'media', moderation, signal, onProgress, resolveAfterInspection, } = args;
|
|
27
25
|
let inspection;
|
|
28
26
|
const withInspection = (r) => inspection ? { ...r, inspection } : r;
|
|
29
27
|
if (signal?.aborted) {
|
|
30
28
|
return {
|
|
31
29
|
ok: false,
|
|
32
30
|
mediaType: mediaTypeFromSpecKind(spec.kind),
|
|
33
|
-
error: { code:
|
|
31
|
+
error: { code: 'processing_canceled', message: 'Processing canceled.' },
|
|
34
32
|
};
|
|
35
33
|
}
|
|
36
|
-
// Runtime contract validation (fast-fail with actionable errors).
|
|
37
34
|
try {
|
|
38
35
|
spec = parseMediaProcessingSpec(spec);
|
|
39
36
|
}
|
|
@@ -42,25 +39,26 @@ export async function runMediaPipeline(args) {
|
|
|
42
39
|
ok: false,
|
|
43
40
|
mediaType: mediaTypeFromSpecKind(spec.kind),
|
|
44
41
|
error: {
|
|
45
|
-
code:
|
|
46
|
-
message:
|
|
42
|
+
code: 'invalid_spec',
|
|
43
|
+
message: 'Invalid MediaProcessingSpec.',
|
|
47
44
|
details: { issues: e?.issues ?? e?.message ?? String(e) },
|
|
48
45
|
},
|
|
49
46
|
};
|
|
50
47
|
}
|
|
51
48
|
const ws = await createTempWorkspace();
|
|
52
49
|
try {
|
|
53
|
-
const inputPath = path.join(ws.dir,
|
|
50
|
+
const inputPath = path.join(ws.dir, 'input');
|
|
54
51
|
const outputBasePath = path.join(ws.dir, outputBaseName);
|
|
55
52
|
if (signal?.aborted) {
|
|
56
|
-
return {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
mediaType: mediaTypeFromSpecKind(spec.kind),
|
|
56
|
+
error: { code: 'processing_canceled', message: 'Processing canceled.' },
|
|
57
|
+
};
|
|
57
58
|
}
|
|
58
|
-
onProgress?.({ phase:
|
|
59
|
+
onProgress?.({ phase: 'read_input', percent: 0 });
|
|
59
60
|
await io.input.readToFile(inputPath);
|
|
60
|
-
onProgress?.({ phase:
|
|
61
|
-
// CANONICAL INSPECTION (before moderation and processing): classify the
|
|
62
|
-
// just-downloaded bytes structurally and let the caller's policy adapter
|
|
63
|
-
// pick the spec from the result. See RunPipelineArgs.resolveAfterInspection.
|
|
61
|
+
onProgress?.({ phase: 'read_input', percent: 1 });
|
|
64
62
|
if (resolveAfterInspection) {
|
|
65
63
|
inspection = await inspectMedia({
|
|
66
64
|
localPath: inputPath,
|
|
@@ -68,7 +66,7 @@ export async function runMediaPipeline(args) {
|
|
|
68
66
|
...(args.inspectionDeps ? { deps: args.inspectionDeps } : {}),
|
|
69
67
|
});
|
|
70
68
|
const resolution = await resolveAfterInspection(inspection);
|
|
71
|
-
if (
|
|
69
|
+
if ('reject' in resolution) {
|
|
72
70
|
return withInspection({
|
|
73
71
|
ok: false,
|
|
74
72
|
mediaType: mediaTypeFromSpecKind(spec.kind),
|
|
@@ -83,46 +81,37 @@ export async function runMediaPipeline(args) {
|
|
|
83
81
|
ok: false,
|
|
84
82
|
mediaType: mediaTypeFromSpecKind(spec.kind),
|
|
85
83
|
error: {
|
|
86
|
-
code:
|
|
87
|
-
message:
|
|
84
|
+
code: 'invalid_spec',
|
|
85
|
+
message: 'Invalid MediaProcessingSpec from resolveAfterInspection.',
|
|
88
86
|
details: { issues: e?.issues ?? e?.message ?? String(e) },
|
|
89
87
|
},
|
|
90
88
|
});
|
|
91
89
|
}
|
|
92
90
|
}
|
|
93
|
-
// PRE moderation (original)
|
|
94
91
|
let mIn;
|
|
95
92
|
if (moderation?.moderateInput) {
|
|
96
|
-
onProgress?.({ phase:
|
|
93
|
+
onProgress?.({ phase: 'moderation_input', percent: 0 });
|
|
97
94
|
mIn = attachProvider(await moderation.moderateInput({
|
|
98
95
|
spec,
|
|
99
96
|
kind: spec.kind,
|
|
100
97
|
localPath: inputPath,
|
|
101
98
|
mime: io.input.mime,
|
|
102
99
|
}), moderation.provider);
|
|
103
|
-
onProgress?.({ phase:
|
|
104
|
-
|
|
105
|
-
// moderation check itself did not complete — quota/outage/API failure).
|
|
106
|
-
// A non-passing check must NEVER fall through to transcode + persist:
|
|
107
|
-
// published bytes must always have been scanned. An "error" is a system
|
|
108
|
-
// failure, not a content rejection, so it returns the distinct,
|
|
109
|
-
// retryable `processing_failed` code AND carries `moderation` (status
|
|
110
|
-
// "error") on the result — callers route that to their retryable
|
|
111
|
-
// moderation lane and land the row failed rather than publishing it.
|
|
112
|
-
if (mIn?.status === "rejected" || mIn?.status === "error") {
|
|
100
|
+
onProgress?.({ phase: 'moderation_input', percent: 1 });
|
|
101
|
+
if (mIn?.status === 'rejected' || mIn?.status === 'error') {
|
|
113
102
|
return withInspection({
|
|
114
103
|
ok: false,
|
|
115
104
|
mediaType: mediaTypeFromSpecKind(spec.kind),
|
|
116
105
|
moderation: mIn,
|
|
117
|
-
error: mIn.status ===
|
|
106
|
+
error: mIn.status === 'error'
|
|
118
107
|
? {
|
|
119
|
-
code:
|
|
120
|
-
message:
|
|
121
|
-
details: { provider: mIn.provider, reason:
|
|
108
|
+
code: 'processing_failed',
|
|
109
|
+
message: 'Moderation check did not complete.',
|
|
110
|
+
details: { provider: mIn.provider, reason: 'moderation_error' },
|
|
122
111
|
}
|
|
123
112
|
: {
|
|
124
|
-
code:
|
|
125
|
-
message:
|
|
113
|
+
code: 'rejected',
|
|
114
|
+
message: 'Rejected by moderation.',
|
|
126
115
|
details: { provider: mIn.provider, reasons: mIn.reasons, findings: mIn.findings },
|
|
127
116
|
},
|
|
128
117
|
});
|
|
@@ -133,10 +122,9 @@ export async function runMediaPipeline(args) {
|
|
|
133
122
|
outputBasePath,
|
|
134
123
|
inputMime: io.input.mime,
|
|
135
124
|
}, { signal, onProgress });
|
|
136
|
-
// Output size enforcement (processed outputs)
|
|
137
125
|
if (result.ok && result.outputs?.length) {
|
|
138
|
-
const perFileLimit = spec.maxOutputBytes ?? spec.maxBytes ?? 200 * 1024 * 1024;
|
|
139
|
-
const totalLimit = spec.maxTotalOutputBytes ?? spec.maxOutputBytes ?? spec.maxBytes ?? 400 * 1024 * 1024;
|
|
126
|
+
const perFileLimit = spec.maxOutputBytes ?? spec.maxBytes ?? 200 * 1024 * 1024;
|
|
127
|
+
const totalLimit = spec.maxTotalOutputBytes ?? spec.maxOutputBytes ?? spec.maxBytes ?? 400 * 1024 * 1024;
|
|
140
128
|
let total = 0;
|
|
141
129
|
for (const o of result.outputs) {
|
|
142
130
|
if (!o.path)
|
|
@@ -151,8 +139,8 @@ export async function runMediaPipeline(args) {
|
|
|
151
139
|
ok: false,
|
|
152
140
|
mediaType: mediaTypeFromSpecKind(spec.kind),
|
|
153
141
|
error: {
|
|
154
|
-
code:
|
|
155
|
-
message:
|
|
142
|
+
code: 'too_large',
|
|
143
|
+
message: 'Processed output exceeds allowed size.',
|
|
156
144
|
details: { key: o.key, sizeBytes: size, limitBytes: perFileLimit },
|
|
157
145
|
},
|
|
158
146
|
});
|
|
@@ -162,8 +150,8 @@ export async function runMediaPipeline(args) {
|
|
|
162
150
|
ok: false,
|
|
163
151
|
mediaType: mediaTypeFromSpecKind(spec.kind),
|
|
164
152
|
error: {
|
|
165
|
-
code:
|
|
166
|
-
message:
|
|
153
|
+
code: 'too_large',
|
|
154
|
+
message: 'Total processed outputs exceed allowed size.',
|
|
167
155
|
details: { totalBytes: total, limitBytes: totalLimit },
|
|
168
156
|
},
|
|
169
157
|
});
|
|
@@ -173,16 +161,12 @@ export async function runMediaPipeline(args) {
|
|
|
173
161
|
if (!result.ok || !result.outputs?.length) {
|
|
174
162
|
if (mIn)
|
|
175
163
|
result.moderation = mIn;
|
|
176
|
-
onProgress?.({ phase:
|
|
164
|
+
onProgress?.({ phase: 'done', percent: result.ok ? 1 : 0 });
|
|
177
165
|
return withInspection(result);
|
|
178
166
|
}
|
|
179
|
-
// POST moderation (processed outputs) — runs on the LOCAL output paths
|
|
180
|
-
// BEFORE any final persistence, so rejected output never reaches the
|
|
181
|
-
// final store (R2 / emulator). Ordering is load-bearing — see
|
|
182
|
-
// the consuming app's media-serving design docs.
|
|
183
167
|
let mOut;
|
|
184
168
|
if (moderation?.moderateOutput) {
|
|
185
|
-
onProgress?.({ phase:
|
|
169
|
+
onProgress?.({ phase: 'moderation_output', percent: 0 });
|
|
186
170
|
mOut = attachProvider(await moderation.moderateOutput({
|
|
187
171
|
spec,
|
|
188
172
|
kind: spec.kind,
|
|
@@ -190,39 +174,46 @@ export async function runMediaPipeline(args) {
|
|
|
190
174
|
.filter((o) => !!o.path)
|
|
191
175
|
.map((o) => ({ key: o.key, localPath: o.path, mime: o.mime })),
|
|
192
176
|
}), moderation.provider);
|
|
193
|
-
onProgress?.({ phase:
|
|
194
|
-
|
|
195
|
-
// and "error" before any persistence, so an incomplete check can never
|
|
196
|
-
// publish unscanned output. Ordering is load-bearing — see the
|
|
197
|
-
// consuming app's media-serving design docs.
|
|
198
|
-
if (mOut?.status === "rejected" || mOut?.status === "error") {
|
|
177
|
+
onProgress?.({ phase: 'moderation_output', percent: 1 });
|
|
178
|
+
if (mOut?.status === 'rejected' || mOut?.status === 'error') {
|
|
199
179
|
return withInspection({
|
|
200
180
|
ok: false,
|
|
201
181
|
mediaType: result.mediaType,
|
|
202
182
|
moderation: mOut,
|
|
203
|
-
error: mOut.status ===
|
|
183
|
+
error: mOut.status === 'error'
|
|
204
184
|
? {
|
|
205
|
-
code:
|
|
206
|
-
message:
|
|
207
|
-
details: { provider: mOut.provider, reason:
|
|
185
|
+
code: 'processing_failed',
|
|
186
|
+
message: 'Moderation check did not complete.',
|
|
187
|
+
details: { provider: mOut.provider, reason: 'moderation_error' },
|
|
208
188
|
}
|
|
209
189
|
: {
|
|
210
|
-
code:
|
|
211
|
-
message:
|
|
212
|
-
details: {
|
|
190
|
+
code: 'rejected',
|
|
191
|
+
message: 'Rejected by moderation.',
|
|
192
|
+
details: {
|
|
193
|
+
provider: mOut.provider,
|
|
194
|
+
reasons: mOut.reasons,
|
|
195
|
+
findings: mOut.findings,
|
|
196
|
+
},
|
|
213
197
|
},
|
|
214
198
|
});
|
|
215
199
|
}
|
|
216
200
|
}
|
|
217
|
-
|
|
218
|
-
onProgress?.({ phase: "persist_outputs", percent: 0 });
|
|
201
|
+
onProgress?.({ phase: 'persist_outputs', percent: 0 });
|
|
219
202
|
const totalOut = result.outputs.length;
|
|
220
203
|
let outIdx = 0;
|
|
221
204
|
for (const out of result.outputs) {
|
|
222
205
|
if (signal?.aborted) {
|
|
223
|
-
return withInspection({
|
|
206
|
+
return withInspection({
|
|
207
|
+
ok: false,
|
|
208
|
+
mediaType: result.mediaType,
|
|
209
|
+
error: { code: 'processing_canceled', message: 'Processing canceled.' },
|
|
210
|
+
});
|
|
224
211
|
}
|
|
225
|
-
onProgress?.({
|
|
212
|
+
onProgress?.({
|
|
213
|
+
phase: 'persist_outputs',
|
|
214
|
+
percent: totalOut ? outIdx / totalOut : 0,
|
|
215
|
+
detail: { key: out.key },
|
|
216
|
+
});
|
|
226
217
|
if (!out.path)
|
|
227
218
|
continue;
|
|
228
219
|
const persisted = await io.output.writeFromFile(out.path, out.key);
|
|
@@ -232,11 +223,11 @@ export async function runMediaPipeline(args) {
|
|
|
232
223
|
out.path = persisted.path;
|
|
233
224
|
outIdx += 1;
|
|
234
225
|
}
|
|
235
|
-
onProgress?.({ phase:
|
|
226
|
+
onProgress?.({ phase: 'persist_outputs', percent: 1 });
|
|
236
227
|
const merged = mergeModeration(mIn ?? undefined, mOut ?? undefined);
|
|
237
228
|
if (merged)
|
|
238
229
|
result.moderation = merged;
|
|
239
|
-
onProgress?.({ phase:
|
|
230
|
+
onProgress?.({ phase: 'done', percent: 1 });
|
|
240
231
|
return withInspection(result);
|
|
241
232
|
}
|
|
242
233
|
finally {
|
package/dist/run-pipeline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-pipeline.js","sourceRoot":"","sources":["../src/run-pipeline.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n MediaProcessingResult,\n MediaProcessingSpec,\n MediaModerationResult,\n MediaInspectionResult,\n MediaProcessingError,\n } from \"@ttt-productions/media-schemas\";\nimport { parseMediaProcessingSpec } from \"@ttt-productions/media-schemas\";\n import { createTempWorkspace } from \"./workspace/temp.js\";\n import { processMedia } from \"./process-media.js\";\n import { inspectMedia, type InspectMediaDeps } from \"./inspection/inspect-media.js\";\n import type { MediaIO } from \"./io/types.js\";\n import type { ModerationAdapter } from \"./moderation/types.js\";\n import { mergeModeration } from \"./moderation/merge.js\";\n import path from \"node:path\";\n import { stat } from \"node:fs/promises\";\n\n export interface RunPipelineArgs {\n spec: MediaProcessingSpec;\n io: MediaIO;\n outputBaseName?: string;\n moderation?: ModerationAdapter;\n signal?: AbortSignal;\n onProgress?: (p: import(\"./types.js\").MediaPipelineProgress) => void;\n /**\n * CANONICAL INSPECTION SEAM (canonical-upload-content-classification).\n * When present, the pipeline runs `inspectMedia` on the just-downloaded temp\n * input — BEFORE moderation and processing — and calls this policy hook with\n * the bounded result. The hook either:\n * - returns `{ spec }` — the caller's policy adapter selected the\n * processing spec FROM the inspection (the byte-derived kind, never the\n * client MIME); the pipeline continues with that spec; or\n * - returns `{ reject }` — a typed terminal (`kind_mismatch`,\n * `unsupported_format`, `unsupported_codec`, …); the pipeline stops\n * before any transcode/moderation work.\n * The inspection result rides the returned `MediaProcessingResult.inspection`\n * either way, so the finalizer hands the SAME object to the safety gate —\n * one authority, no re-detection. Callers that omit the hook get the\n * pre-existing behavior unchanged.\n */\n resolveAfterInspection?: (\n inspection: MediaInspectionResult,\n ) => Promise<{ spec: MediaProcessingSpec } | { reject: MediaProcessingError }> | ({ spec: MediaProcessingSpec } | { reject: MediaProcessingError });\n /** Inspection tool overrides (tests). */\n inspectionDeps?: InspectMediaDeps;\n }\n \n function mediaTypeFromSpecKind(kind: MediaProcessingSpec[\"kind\"]): \"image\" | \"video\" | \"audio\" | \"other\" {\n if (kind === \"image\") return \"image\";\n if (kind === \"video\") return \"video\";\n if (kind === \"audio\") return \"audio\";\n return \"other\";\n }\n \n function attachProvider(\n m: MediaModerationResult | null | undefined,\n provider?: string\n ): MediaModerationResult | null | undefined {\n if (!m) return m;\n return { ...m, provider: m.provider ?? provider, reviewedAt: m.reviewedAt ?? Date.now() };\n }\n \n export async function runMediaPipeline(args: RunPipelineArgs): Promise<MediaProcessingResult> {\n let { spec } = args;\n const { io, outputBaseName = \"media\", moderation, signal, onProgress, resolveAfterInspection } = args;\n // Carried onto every return path once produced — the finalizer must hand the\n // SAME inspection object to the safety gate (one classification authority).\n let inspection: MediaInspectionResult | undefined;\n const withInspection = (r: MediaProcessingResult): MediaProcessingResult =>\n inspection ? { ...r, inspection } : r;\n\n if (signal?.aborted) {\n return {\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: { code: \"processing_canceled\", message: \"Processing canceled.\" },\n };\n }\n\n // Runtime contract validation (fast-fail with actionable errors).\n try {\n spec = parseMediaProcessingSpec(spec);\n } catch (e: any) {\n return {\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: {\n code: \"invalid_spec\",\n message: \"Invalid MediaProcessingSpec.\",\n details: { issues: e?.issues ?? e?.message ?? String(e) },\n },\n };\n }\n \n const ws = await createTempWorkspace();\n \n try {\n const inputPath = path.join(ws.dir, \"input\");\n const outputBasePath = path.join(ws.dir, outputBaseName);\n \n if (signal?.aborted) {\n return { ok: false, mediaType: mediaTypeFromSpecKind(spec.kind), error: { code: \"processing_canceled\", message: \"Processing canceled.\" } };\n }\n onProgress?.({ phase: \"read_input\", percent: 0 });\n await io.input.readToFile(inputPath);\n onProgress?.({ phase: \"read_input\", percent: 1 });\n\n // CANONICAL INSPECTION (before moderation and processing): classify the\n // just-downloaded bytes structurally and let the caller's policy adapter\n // pick the spec from the result. See RunPipelineArgs.resolveAfterInspection.\n if (resolveAfterInspection) {\n inspection = await inspectMedia({\n localPath: inputPath,\n ...(signal ? { signal } : {}),\n ...(args.inspectionDeps ? { deps: args.inspectionDeps } : {}),\n });\n const resolution = await resolveAfterInspection(inspection);\n if (\"reject\" in resolution) {\n return withInspection({\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: resolution.reject,\n });\n }\n try {\n spec = parseMediaProcessingSpec(resolution.spec);\n } catch (e: any) {\n return withInspection({\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: {\n code: \"invalid_spec\",\n message: \"Invalid MediaProcessingSpec from resolveAfterInspection.\",\n details: { issues: e?.issues ?? e?.message ?? String(e) },\n },\n });\n }\n }\n\n // PRE moderation (original)\n let mIn: MediaModerationResult | null | undefined;\n if (moderation?.moderateInput) {\n onProgress?.({ phase: \"moderation_input\", percent: 0 });\n mIn = attachProvider(\n await moderation.moderateInput({\n spec,\n kind: spec.kind,\n localPath: inputPath,\n mime: io.input.mime,\n }),\n moderation.provider\n );\n onProgress?.({ phase: \"moderation_input\", percent: 1 });\n \n // Fail closed on both \"rejected\" (policy violation) and \"error\" (the\n // moderation check itself did not complete — quota/outage/API failure).\n // A non-passing check must NEVER fall through to transcode + persist:\n // published bytes must always have been scanned. An \"error\" is a system\n // failure, not a content rejection, so it returns the distinct,\n // retryable `processing_failed` code AND carries `moderation` (status\n // \"error\") on the result — callers route that to their retryable\n // moderation lane and land the row failed rather than publishing it.\n if (mIn?.status === \"rejected\" || mIn?.status === \"error\") {\n return withInspection({\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n moderation: mIn,\n error:\n mIn.status === \"error\"\n ? {\n code: \"processing_failed\",\n message: \"Moderation check did not complete.\",\n details: { provider: mIn.provider, reason: \"moderation_error\" },\n }\n : {\n code: \"rejected\",\n message: \"Rejected by moderation.\",\n details: { provider: mIn.provider, reasons: mIn.reasons, findings: mIn.findings },\n },\n });\n }\n }\n \n const result = await processMedia(spec, {\n inputPath,\n outputBasePath,\n inputMime: io.input.mime,\n }, { signal, onProgress });\n\n // Output size enforcement (processed outputs)\n if (result.ok && result.outputs?.length) {\n const perFileLimit = spec.maxOutputBytes ?? spec.maxBytes ?? 200 * 1024 * 1024; // 200MB default\n const totalLimit = spec.maxTotalOutputBytes ?? spec.maxOutputBytes ?? spec.maxBytes ?? 400 * 1024 * 1024; // 400MB default\n\n let total = 0;\n for (const o of result.outputs) {\n if (!o.path) continue;\n const s = await stat(o.path);\n const size = Number(s.size ?? 0);\n total += size;\n if (!o.sizeBytes) o.sizeBytes = size;\n if (size > perFileLimit) {\n return withInspection({\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: {\n code: \"too_large\",\n message: \"Processed output exceeds allowed size.\",\n details: { key: o.key, sizeBytes: size, limitBytes: perFileLimit },\n },\n });\n }\n if (total > totalLimit) {\n return withInspection({\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: {\n code: \"too_large\",\n message: \"Total processed outputs exceed allowed size.\",\n details: { totalBytes: total, limitBytes: totalLimit },\n },\n });\n }\n }\n }\n \n if (!result.ok || !result.outputs?.length) {\n if (mIn) result.moderation = mIn;\n onProgress?.({ phase: \"done\", percent: result.ok ? 1 : 0 });\n return withInspection(result);\n }\n \n // POST moderation (processed outputs) — runs on the LOCAL output paths\n // BEFORE any final persistence, so rejected output never reaches the\n // final store (R2 / emulator). Ordering is load-bearing — see\n // the consuming app's media-serving design docs.\n let mOut: MediaModerationResult | null | undefined;\n if (moderation?.moderateOutput) {\n onProgress?.({ phase: \"moderation_output\", percent: 0 });\n mOut = attachProvider(\n await moderation.moderateOutput({\n spec,\n kind: spec.kind,\n outputs: (result.outputs ?? [])\n .filter((o) => !!o.path)\n .map((o) => ({ key: o.key, localPath: o.path!, mime: o.mime })),\n }),\n moderation.provider\n );\n onProgress?.({ phase: \"moderation_output\", percent: 1 });\n\n // Same fail-closed posture as the input check: block both \"rejected\"\n // and \"error\" before any persistence, so an incomplete check can never\n // publish unscanned output. Ordering is load-bearing — see the\n // consuming app's media-serving design docs.\n if (mOut?.status === \"rejected\" || mOut?.status === \"error\") {\n return withInspection({\n ok: false,\n mediaType: result.mediaType,\n moderation: mOut,\n error:\n mOut.status === \"error\"\n ? {\n code: \"processing_failed\",\n message: \"Moderation check did not complete.\",\n details: { provider: mOut.provider, reason: \"moderation_error\" },\n }\n : {\n code: \"rejected\",\n message: \"Rejected by moderation.\",\n details: { provider: mOut.provider, reasons: mOut.reasons, findings: mOut.findings },\n },\n });\n }\n }\n\n // Persist outputs — only after input AND output moderation both passed.\n onProgress?.({ phase: \"persist_outputs\", percent: 0 });\n const totalOut = result.outputs.length;\n let outIdx = 0;\n for (const out of result.outputs) {\n if (signal?.aborted) {\n return withInspection({ ok: false, mediaType: result.mediaType, error: { code: \"processing_canceled\", message: \"Processing canceled.\" } });\n }\n onProgress?.({ phase: \"persist_outputs\", percent: totalOut ? outIdx / totalOut : 0, detail: { key: out.key } });\n if (!out.path) continue;\n const persisted = await io.output.writeFromFile(out.path, out.key);\n if (persisted.url) out.url = persisted.url;\n if (persisted.path) out.path = persisted.path;\n outIdx += 1;\n }\n onProgress?.({ phase: \"persist_outputs\", percent: 1 });\n\n const merged = mergeModeration(mIn ?? undefined, mOut ?? undefined);\n if (merged) result.moderation = merged;\n\n onProgress?.({ phase: \"done\", percent: 1 });\n \n return withInspection(result);\n } finally {\n await ws.cleanup();\n }\n }\n "]}
|
|
1
|
+
{"version":3,"file":"run-pipeline.js","sourceRoot":"","sources":["../src/run-pipeline.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n MediaProcessingResult,\n MediaProcessingSpec,\n MediaModerationResult,\n MediaInspectionResult,\n MediaProcessingError,\n} from '@ttt-productions/media-schemas';\nimport { parseMediaProcessingSpec } from '@ttt-productions/media-schemas';\nimport { createTempWorkspace } from './workspace/temp.js';\nimport { processMedia } from './process-media.js';\nimport { inspectMedia, type InspectMediaDeps } from './inspection/inspect-media.js';\nimport type { MediaIO } from './io/types.js';\nimport type { ModerationAdapter } from './moderation/types.js';\nimport { mergeModeration } from './moderation/merge.js';\nimport path from 'node:path';\nimport { stat } from 'node:fs/promises';\n\nexport interface RunPipelineArgs {\n spec: MediaProcessingSpec;\n io: MediaIO;\n outputBaseName?: string;\n moderation?: ModerationAdapter;\n signal?: AbortSignal;\n onProgress?: (p: import('./types.js').MediaPipelineProgress) => void;\n resolveAfterInspection?: (\n inspection: MediaInspectionResult,\n ) =>\n | Promise<{ spec: MediaProcessingSpec } | { reject: MediaProcessingError }>\n | ({ spec: MediaProcessingSpec } | { reject: MediaProcessingError });\n inspectionDeps?: InspectMediaDeps;\n}\n\nfunction mediaTypeFromSpecKind(\n kind: MediaProcessingSpec['kind'],\n): 'image' | 'video' | 'audio' | 'other' {\n if (kind === 'image') return 'image';\n if (kind === 'video') return 'video';\n if (kind === 'audio') return 'audio';\n return 'other';\n}\n\nfunction attachProvider(\n m: MediaModerationResult | null | undefined,\n provider?: string,\n): MediaModerationResult | null | undefined {\n if (!m) return m;\n return { ...m, provider: m.provider ?? provider, reviewedAt: m.reviewedAt ?? Date.now() };\n}\n\nexport async function runMediaPipeline(args: RunPipelineArgs): Promise<MediaProcessingResult> {\n let { spec } = args;\n const {\n io,\n outputBaseName = 'media',\n moderation,\n signal,\n onProgress,\n resolveAfterInspection,\n } = args;\n let inspection: MediaInspectionResult | undefined;\n const withInspection = (r: MediaProcessingResult): MediaProcessingResult =>\n inspection ? { ...r, inspection } : r;\n\n if (signal?.aborted) {\n return {\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: { code: 'processing_canceled', message: 'Processing canceled.' },\n };\n }\n\n try {\n spec = parseMediaProcessingSpec(spec);\n } catch (e: any) {\n return {\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: {\n code: 'invalid_spec',\n message: 'Invalid MediaProcessingSpec.',\n details: { issues: e?.issues ?? e?.message ?? String(e) },\n },\n };\n }\n\n const ws = await createTempWorkspace();\n\n try {\n const inputPath = path.join(ws.dir, 'input');\n const outputBasePath = path.join(ws.dir, outputBaseName);\n\n if (signal?.aborted) {\n return {\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: { code: 'processing_canceled', message: 'Processing canceled.' },\n };\n }\n onProgress?.({ phase: 'read_input', percent: 0 });\n await io.input.readToFile(inputPath);\n onProgress?.({ phase: 'read_input', percent: 1 });\n\n if (resolveAfterInspection) {\n inspection = await inspectMedia({\n localPath: inputPath,\n ...(signal ? { signal } : {}),\n ...(args.inspectionDeps ? { deps: args.inspectionDeps } : {}),\n });\n const resolution = await resolveAfterInspection(inspection);\n if ('reject' in resolution) {\n return withInspection({\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: resolution.reject,\n });\n }\n try {\n spec = parseMediaProcessingSpec(resolution.spec);\n } catch (e: any) {\n return withInspection({\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: {\n code: 'invalid_spec',\n message: 'Invalid MediaProcessingSpec from resolveAfterInspection.',\n details: { issues: e?.issues ?? e?.message ?? String(e) },\n },\n });\n }\n }\n\n let mIn: MediaModerationResult | null | undefined;\n if (moderation?.moderateInput) {\n onProgress?.({ phase: 'moderation_input', percent: 0 });\n mIn = attachProvider(\n await moderation.moderateInput({\n spec,\n kind: spec.kind,\n localPath: inputPath,\n mime: io.input.mime,\n }),\n moderation.provider,\n );\n onProgress?.({ phase: 'moderation_input', percent: 1 });\n\n if (mIn?.status === 'rejected' || mIn?.status === 'error') {\n return withInspection({\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n moderation: mIn,\n error:\n mIn.status === 'error'\n ? {\n code: 'processing_failed',\n message: 'Moderation check did not complete.',\n details: { provider: mIn.provider, reason: 'moderation_error' },\n }\n : {\n code: 'rejected',\n message: 'Rejected by moderation.',\n details: { provider: mIn.provider, reasons: mIn.reasons, findings: mIn.findings },\n },\n });\n }\n }\n\n const result = await processMedia(\n spec,\n {\n inputPath,\n outputBasePath,\n inputMime: io.input.mime,\n },\n { signal, onProgress },\n );\n\n if (result.ok && result.outputs?.length) {\n const perFileLimit = spec.maxOutputBytes ?? spec.maxBytes ?? 200 * 1024 * 1024;\n const totalLimit =\n spec.maxTotalOutputBytes ?? spec.maxOutputBytes ?? spec.maxBytes ?? 400 * 1024 * 1024;\n\n let total = 0;\n for (const o of result.outputs) {\n if (!o.path) continue;\n const s = await stat(o.path);\n const size = Number(s.size ?? 0);\n total += size;\n if (!o.sizeBytes) o.sizeBytes = size;\n if (size > perFileLimit) {\n return withInspection({\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: {\n code: 'too_large',\n message: 'Processed output exceeds allowed size.',\n details: { key: o.key, sizeBytes: size, limitBytes: perFileLimit },\n },\n });\n }\n if (total > totalLimit) {\n return withInspection({\n ok: false,\n mediaType: mediaTypeFromSpecKind(spec.kind),\n error: {\n code: 'too_large',\n message: 'Total processed outputs exceed allowed size.',\n details: { totalBytes: total, limitBytes: totalLimit },\n },\n });\n }\n }\n }\n\n if (!result.ok || !result.outputs?.length) {\n if (mIn) result.moderation = mIn;\n onProgress?.({ phase: 'done', percent: result.ok ? 1 : 0 });\n return withInspection(result);\n }\n\n let mOut: MediaModerationResult | null | undefined;\n if (moderation?.moderateOutput) {\n onProgress?.({ phase: 'moderation_output', percent: 0 });\n mOut = attachProvider(\n await moderation.moderateOutput({\n spec,\n kind: spec.kind,\n outputs: (result.outputs ?? [])\n .filter((o) => !!o.path)\n .map((o) => ({ key: o.key, localPath: o.path!, mime: o.mime })),\n }),\n moderation.provider,\n );\n onProgress?.({ phase: 'moderation_output', percent: 1 });\n\n if (mOut?.status === 'rejected' || mOut?.status === 'error') {\n return withInspection({\n ok: false,\n mediaType: result.mediaType,\n moderation: mOut,\n error:\n mOut.status === 'error'\n ? {\n code: 'processing_failed',\n message: 'Moderation check did not complete.',\n details: { provider: mOut.provider, reason: 'moderation_error' },\n }\n : {\n code: 'rejected',\n message: 'Rejected by moderation.',\n details: {\n provider: mOut.provider,\n reasons: mOut.reasons,\n findings: mOut.findings,\n },\n },\n });\n }\n }\n\n onProgress?.({ phase: 'persist_outputs', percent: 0 });\n const totalOut = result.outputs.length;\n let outIdx = 0;\n for (const out of result.outputs) {\n if (signal?.aborted) {\n return withInspection({\n ok: false,\n mediaType: result.mediaType,\n error: { code: 'processing_canceled', message: 'Processing canceled.' },\n });\n }\n onProgress?.({\n phase: 'persist_outputs',\n percent: totalOut ? outIdx / totalOut : 0,\n detail: { key: out.key },\n });\n if (!out.path) continue;\n const persisted = await io.output.writeFromFile(out.path, out.key);\n if (persisted.url) out.url = persisted.url;\n if (persisted.path) out.path = persisted.path;\n outIdx += 1;\n }\n onProgress?.({ phase: 'persist_outputs', percent: 1 });\n\n const merged = mergeModeration(mIn ?? undefined, mOut ?? undefined);\n if (merged) result.moderation = merged;\n\n onProgress?.({ phase: 'done', percent: 1 });\n\n return withInspection(result);\n } finally {\n await ws.cleanup();\n }\n}\n"]}
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
/** The one method of a Storage object handle this module uses. */
|
|
2
1
|
export interface DownloadTokenWritable {
|
|
3
2
|
setMetadata(update: {
|
|
4
3
|
metadata: Record<string, string | null>;
|
|
5
4
|
}): Promise<unknown>;
|
|
6
5
|
}
|
|
7
|
-
/** True when the object's stored metadata still carries a download token. */
|
|
8
6
|
export declare function hasDownloadToken(objectMetadata: Readonly<Record<string, unknown>> | undefined): boolean;
|
|
9
|
-
/**
|
|
10
|
-
* Remove the object's download token and nothing else: a null custom-metadata value deletes that
|
|
11
|
-
* one key, leaving every other entry. A no-op write when the object has none.
|
|
12
|
-
*/
|
|
13
7
|
export declare function removeDownloadToken(file: DownloadTokenWritable): Promise<void>;
|
|
14
8
|
//# sourceMappingURL=download-token.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download-token.d.ts","sourceRoot":"","sources":["../../src/server/download-token.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"download-token.d.ts","sourceRoot":"","sources":["../../src/server/download-token.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpF;AAID,wBAAgB,gBAAgB,CAC9B,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,GAC5D,OAAO,CAGT;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpF"}
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
// every object it uploads a `firebaseStorageDownloadTokens` metadata entry: a never-expiring link
|
|
3
|
-
// that reads the object signed out, around the Storage rules. An app that serves no object through
|
|
4
|
-
// one removes it as soon as the server takes the object over.
|
|
5
|
-
/** The custom-metadata key the Firebase Storage client writes on upload. */
|
|
6
|
-
const DOWNLOAD_TOKENS_KEY = "firebaseStorageDownloadTokens";
|
|
7
|
-
/** True when the object's stored metadata still carries a download token. */
|
|
1
|
+
const DOWNLOAD_TOKENS_KEY = 'firebaseStorageDownloadTokens';
|
|
8
2
|
export function hasDownloadToken(objectMetadata) {
|
|
9
|
-
const custom = objectMetadata?.[
|
|
10
|
-
return typeof custom ===
|
|
3
|
+
const custom = objectMetadata?.['metadata'];
|
|
4
|
+
return typeof custom === 'object' && custom !== null && DOWNLOAD_TOKENS_KEY in custom;
|
|
11
5
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Remove the object's download token and nothing else: a null custom-metadata value deletes that
|
|
14
|
-
* one key, leaving every other entry. A no-op write when the object has none.
|
|
15
|
-
*/
|
|
16
6
|
export async function removeDownloadToken(file) {
|
|
17
7
|
await file.setMetadata({ metadata: { [DOWNLOAD_TOKENS_KEY]: null } });
|
|
18
8
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download-token.js","sourceRoot":"","sources":["../../src/server/download-token.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"download-token.js","sourceRoot":"","sources":["../../src/server/download-token.ts"],"names":[],"mappings":"AAIA,MAAM,mBAAmB,GAAG,+BAA+B,CAAC;AAE5D,MAAM,UAAU,gBAAgB,CAC9B,cAA6D;IAE7D,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC,UAAU,CAAC,CAAC;IAC5C,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,mBAAmB,IAAI,MAAM,CAAC;AACxF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAA2B;IACnE,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC,mBAAmB,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC","sourcesContent":["export interface DownloadTokenWritable {\n setMetadata(update: { metadata: Record<string, string | null> }): Promise<unknown>;\n}\n\nconst DOWNLOAD_TOKENS_KEY = 'firebaseStorageDownloadTokens';\n\nexport function hasDownloadToken(\n objectMetadata: Readonly<Record<string, unknown>> | undefined,\n): boolean {\n const custom = objectMetadata?.['metadata'];\n return typeof custom === 'object' && custom !== null && DOWNLOAD_TOKENS_KEY in custom;\n}\n\nexport async function removeDownloadToken(file: DownloadTokenWritable): Promise<void> {\n await file.setMetadata({ metadata: { [DOWNLOAD_TOKENS_KEY]: null } });\n}\n"]}
|
|
@@ -1,21 +1,11 @@
|
|
|
1
|
-
import type { MediaIO } from
|
|
2
|
-
import type { MediaObjectStore } from
|
|
1
|
+
import type { MediaIO } from '../io/types.js';
|
|
2
|
+
import type { MediaObjectStore } from './storage-ops.js';
|
|
3
3
|
export interface CreateObjectStoreMediaIOArgs {
|
|
4
|
-
/** Firebase Storage staging path of the uploaded source file. */
|
|
5
4
|
inputStoragePath: string;
|
|
6
|
-
/** Store receiving the processed outputs. */
|
|
7
5
|
outputStore: MediaObjectStore;
|
|
8
|
-
/** Key prefix for outputs, e.g. `mediaAssets/{mediaAssetId}`. */
|
|
9
6
|
outputKeyPrefix: string;
|
|
10
|
-
/** Optional mime hint for the input. */
|
|
11
7
|
inputMime?: string;
|
|
12
|
-
/**
|
|
13
|
-
* Optional object generation to pin the input download to. When set, the
|
|
14
|
-
* download resolves the exact immutable generation of the staged object
|
|
15
|
-
* (per GCS generation-precondition semantics), so a concurrent overwrite of
|
|
16
|
-
* the source path cannot swap the bytes that get read. Accepts the numeric
|
|
17
|
-
* generation or its string form (GCS generations exceed 2^53).
|
|
18
|
-
*/
|
|
8
|
+
/** GCS generations exceed 2^53, so the string form is accepted too. */
|
|
19
9
|
inputGeneration?: string | number;
|
|
20
10
|
}
|
|
21
11
|
export declare function createObjectStoreMediaIO(args: CreateObjectStoreMediaIOArgs): MediaIO;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firebase-media-io.d.ts","sourceRoot":"","sources":["../../src/server/firebase-media-io.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"firebase-media-io.d.ts","sourceRoot":"","sources":["../../src/server/firebase-media-io.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,MAAM,WAAW,4BAA4B;IAC3C,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,4BAA4B,GAAG,OAAO,CAsBpF"}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
// processed outputs through whichever MediaObjectStore the app selected for the
|
|
3
|
-
// environment (see storage-ops.ts). Output keys are extension-less:
|
|
4
|
-
// `${outputKeyPrefix}/${outputKey}` — meaning lives in the asset registry,
|
|
5
|
-
// not the key. Writes return the object key; no URLs anywhere.
|
|
6
|
-
import { getStorage } from "firebase-admin/storage";
|
|
1
|
+
import { getStorage } from 'firebase-admin/storage';
|
|
7
2
|
export function createObjectStoreMediaIO(args) {
|
|
8
3
|
const bucket = getStorage().bucket();
|
|
9
4
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firebase-media-io.js","sourceRoot":"","sources":["../../src/server/firebase-media-io.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"firebase-media-io.js","sourceRoot":"","sources":["../../src/server/firebase-media-io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAapD,MAAM,UAAU,wBAAwB,CAAC,IAAkC;IACzE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC,MAAM,EAAE,CAAC;IAErC,OAAO;QACL,KAAK,EAAE;YACL,IAAI,EAAE,IAAI,CAAC,SAAS;YACpB,KAAK,CAAC,UAAU,CAAC,SAAiB;gBAChC,MAAM,IAAI,GACR,IAAI,CAAC,eAAe,IAAI,IAAI;oBAC1B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC;oBAC1E,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;gBACzC,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC;YAClD,CAAC;SACF;QACD,MAAM,EAAE;YACN,KAAK,CAAC,aAAa,CAAC,SAAiB,EAAE,SAAiB;gBACtD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,eAAe,IAAI,SAAS,EAAE,CAAC;gBACnD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;gBAClE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;YAC9B,CAAC;SACF;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { getStorage } from 'firebase-admin/storage';\nimport type { MediaIO } from '../io/types.js';\nimport type { MediaObjectStore } from './storage-ops.js';\n\nexport interface CreateObjectStoreMediaIOArgs {\n inputStoragePath: string;\n outputStore: MediaObjectStore;\n outputKeyPrefix: string;\n inputMime?: string;\n /** GCS generations exceed 2^53, so the string form is accepted too. */\n inputGeneration?: string | number;\n}\n\nexport function createObjectStoreMediaIO(args: CreateObjectStoreMediaIOArgs): MediaIO {\n const bucket = getStorage().bucket();\n\n return {\n input: {\n mime: args.inputMime,\n async readToFile(localPath: string) {\n const file =\n args.inputGeneration != null\n ? bucket.file(args.inputStoragePath, { generation: args.inputGeneration })\n : bucket.file(args.inputStoragePath);\n await file.download({ destination: localPath });\n },\n },\n output: {\n async writeFromFile(localPath: string, outputKey: string) {\n const key = `${args.outputKeyPrefix}/${outputKey}`;\n const result = await args.outputStore.putFile({ localPath, key });\n return { path: result.key };\n },\n },\n };\n}\n"]}
|
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* True when a storage read or delete failed because the object does not exist, from
|
|
3
|
-
* either backend this package's stores use, decided from structured fields only — never
|
|
4
|
-
* from message text, which a library may rewrite and which carries no contract. The R2
|
|
5
|
-
* store's `R2StorageError` answers with status `404` and S3 error code `NoSuchKey`, which
|
|
6
|
-
* tells a missing key from a missing bucket (`NoSuchBucket`); the Cloud Storage Admin SDK
|
|
7
|
-
* answers with the numeric HTTP code `404`.
|
|
8
|
-
*/
|
|
9
1
|
export declare function isObjectNotFoundError(e: unknown): boolean;
|
|
10
2
|
//# sourceMappingURL=gcs-errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcs-errors.d.ts","sourceRoot":"","sources":["../../src/server/gcs-errors.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"gcs-errors.d.ts","sourceRoot":"","sources":["../../src/server/gcs-errors.ts"],"names":[],"mappings":"AAEA,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAGzD"}
|