create-forgeon 0.3.9 → 0.3.11
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/package.json
CHANGED
|
@@ -479,6 +479,13 @@ function assertFilesImageWiring(projectRoot) {
|
|
|
479
479
|
const filesTypes = fs.readFileSync(path.join(projectRoot, 'packages', 'files', 'src', 'files.types.ts'), 'utf8');
|
|
480
480
|
assert.match(filesTypes, /auditContext\?: \{/);
|
|
481
481
|
|
|
482
|
+
const filesImageService = fs.readFileSync(
|
|
483
|
+
path.join(projectRoot, 'packages', 'files-image', 'src', 'files-image.service.ts'),
|
|
484
|
+
'utf8',
|
|
485
|
+
);
|
|
486
|
+
assert.match(filesImageService, /loadFileTypeModule/);
|
|
487
|
+
assert.match(filesImageService, /new Function\('specifier', 'return import\(specifier\)'\)/);
|
|
488
|
+
|
|
482
489
|
const apiDockerfile = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'Dockerfile'), 'utf8');
|
|
483
490
|
assert.match(
|
|
484
491
|
apiDockerfile,
|
|
@@ -529,6 +536,10 @@ function assertFilesImageWiring(projectRoot) {
|
|
|
529
536
|
assert.match(filesImagePackage, /"sharp":/);
|
|
530
537
|
assert.match(filesImagePackage, /"file-type":/);
|
|
531
538
|
|
|
539
|
+
const rootPackage = fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8');
|
|
540
|
+
assert.match(rootPackage, /"onlyBuiltDependencies"/);
|
|
541
|
+
assert.match(rootPackage, /"sharp"/);
|
|
542
|
+
|
|
532
543
|
const readme = fs.readFileSync(path.join(projectRoot, 'README.md'), 'utf8');
|
|
533
544
|
assert.match(readme, /## Files Image Module/);
|
|
534
545
|
assert.match(readme, /metadata is stripped before storage/i);
|
|
@@ -53,6 +53,29 @@ function patchFilesPackage(targetRoot) {
|
|
|
53
53
|
writeJson(packagePath, packageJson);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
function patchRootPackage(targetRoot) {
|
|
57
|
+
const packagePath = path.join(targetRoot, 'package.json');
|
|
58
|
+
if (!fs.existsSync(packagePath)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
63
|
+
if (!packageJson.pnpm || typeof packageJson.pnpm !== 'object' || Array.isArray(packageJson.pnpm)) {
|
|
64
|
+
packageJson.pnpm = {};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const onlyBuiltDependencies = Array.isArray(packageJson.pnpm.onlyBuiltDependencies)
|
|
68
|
+
? packageJson.pnpm.onlyBuiltDependencies
|
|
69
|
+
: [];
|
|
70
|
+
|
|
71
|
+
if (!onlyBuiltDependencies.includes('sharp')) {
|
|
72
|
+
onlyBuiltDependencies.push('sharp');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
packageJson.pnpm.onlyBuiltDependencies = onlyBuiltDependencies;
|
|
76
|
+
writeJson(packagePath, packageJson);
|
|
77
|
+
}
|
|
78
|
+
|
|
56
79
|
function patchFilesTypes(targetRoot) {
|
|
57
80
|
const filePath = path.join(targetRoot, 'packages', 'files', 'src', 'files.types.ts');
|
|
58
81
|
if (!fs.existsSync(filePath)) {
|
|
@@ -526,6 +549,7 @@ export function applyFilesImageModule({ packageRoot, targetRoot }) {
|
|
|
526
549
|
copyFromPreset(packageRoot, targetRoot, path.join('packages', 'files-image'));
|
|
527
550
|
patchApiPackage(targetRoot);
|
|
528
551
|
patchFilesPackage(targetRoot);
|
|
552
|
+
patchRootPackage(targetRoot);
|
|
529
553
|
patchFilesTypes(targetRoot);
|
|
530
554
|
patchAppModule(targetRoot);
|
|
531
555
|
patchFilesModule(targetRoot);
|
package/templates/module-presets/files-image/packages/files-image/src/files-image.service.ts
CHANGED
|
@@ -15,6 +15,10 @@ const MIME_TO_ALLOWED_EXTS: Record<string, string[]> = {
|
|
|
15
15
|
'image/webp': ['.webp'],
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
interface FileTypeModuleLike {
|
|
19
|
+
fileTypeFromBuffer(buffer: Buffer): Promise<{ mime: string; ext: string } | undefined>;
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
@Injectable()
|
|
19
23
|
export class FilesImageService {
|
|
20
24
|
private readonly logger = new Logger(FilesImageService.name);
|
|
@@ -146,54 +150,89 @@ export class FilesImageService {
|
|
|
146
150
|
return this.configService.enabled;
|
|
147
151
|
}
|
|
148
152
|
|
|
149
|
-
async getProbeStatus(): Promise<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
153
|
+
async getProbeStatus(): Promise<
|
|
154
|
+
| {
|
|
155
|
+
status: 'ok';
|
|
156
|
+
feature: 'files-image';
|
|
157
|
+
stripMetadata: boolean;
|
|
158
|
+
maxWidth: number;
|
|
159
|
+
maxHeight: number;
|
|
160
|
+
maxPixels: number;
|
|
161
|
+
maxFrames: number;
|
|
162
|
+
inputBytes: number;
|
|
163
|
+
outputBytes: number;
|
|
164
|
+
outputMimeType: string;
|
|
165
|
+
transformed: boolean;
|
|
166
|
+
}
|
|
167
|
+
| {
|
|
168
|
+
status: 'error';
|
|
169
|
+
feature: 'files-image';
|
|
170
|
+
stripMetadata: boolean;
|
|
171
|
+
maxWidth: number;
|
|
172
|
+
maxHeight: number;
|
|
173
|
+
maxPixels: number;
|
|
174
|
+
maxFrames: number;
|
|
175
|
+
errorCode: string;
|
|
176
|
+
errorMessage: string;
|
|
177
|
+
}
|
|
178
|
+
> {
|
|
179
|
+
try {
|
|
180
|
+
const sample = await sharp({
|
|
181
|
+
create: {
|
|
182
|
+
width: 4,
|
|
183
|
+
height: 4,
|
|
184
|
+
channels: 3,
|
|
185
|
+
background: { r: 120, g: 80, b: 40 },
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
.jpeg({ quality: 90 })
|
|
189
|
+
.withMetadata()
|
|
190
|
+
.toBuffer();
|
|
191
|
+
|
|
192
|
+
const result = await this.sanitizeForStorage({
|
|
193
|
+
buffer: sample,
|
|
194
|
+
declaredMimeType: 'image/jpeg',
|
|
195
|
+
originalName: 'probe.jpg',
|
|
196
|
+
});
|
|
179
197
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
198
|
+
return {
|
|
199
|
+
status: 'ok',
|
|
200
|
+
feature: 'files-image',
|
|
201
|
+
stripMetadata: this.configService.stripMetadata,
|
|
202
|
+
maxWidth: this.configService.maxWidth,
|
|
203
|
+
maxHeight: this.configService.maxHeight,
|
|
204
|
+
maxPixels: this.configService.maxPixels,
|
|
205
|
+
maxFrames: this.configService.maxFrames,
|
|
206
|
+
inputBytes: sample.byteLength,
|
|
207
|
+
outputBytes: result.buffer.byteLength,
|
|
208
|
+
outputMimeType: result.mimeType,
|
|
209
|
+
transformed: result.transformed,
|
|
210
|
+
};
|
|
211
|
+
} catch (error) {
|
|
212
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown files-image probe error';
|
|
213
|
+
this.logger.error(
|
|
214
|
+
JSON.stringify({
|
|
215
|
+
event: 'files.image.probe_failed',
|
|
216
|
+
errorMessage,
|
|
217
|
+
}),
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
status: 'error',
|
|
222
|
+
feature: 'files-image',
|
|
223
|
+
stripMetadata: this.configService.stripMetadata,
|
|
224
|
+
maxWidth: this.configService.maxWidth,
|
|
225
|
+
maxHeight: this.configService.maxHeight,
|
|
226
|
+
maxPixels: this.configService.maxPixels,
|
|
227
|
+
maxFrames: this.configService.maxFrames,
|
|
228
|
+
errorCode: 'FILES_IMAGE_PROBE_FAILED',
|
|
229
|
+
errorMessage,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
193
232
|
}
|
|
194
233
|
|
|
195
234
|
private async detectFileType(buffer: Buffer): Promise<{ mime: string; ext: string } | null> {
|
|
196
|
-
const { fileTypeFromBuffer } = await
|
|
235
|
+
const { fileTypeFromBuffer } = await this.loadFileTypeModule();
|
|
197
236
|
const detected = await fileTypeFromBuffer(buffer);
|
|
198
237
|
if (!detected) {
|
|
199
238
|
return null;
|
|
@@ -267,6 +306,13 @@ export class FilesImageService {
|
|
|
267
306
|
}
|
|
268
307
|
}
|
|
269
308
|
|
|
309
|
+
private async loadFileTypeModule(): Promise<FileTypeModuleLike> {
|
|
310
|
+
const importModule = new Function('specifier', 'return import(specifier)') as (
|
|
311
|
+
specifier: string,
|
|
312
|
+
) => Promise<FileTypeModuleLike>;
|
|
313
|
+
return importModule('file-type');
|
|
314
|
+
}
|
|
315
|
+
|
|
270
316
|
private async validateImageShape(
|
|
271
317
|
buffer: Buffer,
|
|
272
318
|
detectedMimeType: string,
|