@happyvertical/smrt-assets-local 0.30.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/AGENTS.md ADDED
@@ -0,0 +1,67 @@
1
+ # @happyvertical/smrt-assets-local
2
+
3
+ Local processing adapter for the SMRT asset runtime.
4
+
5
+ ## Purpose
6
+
7
+ This package handles asset work that does not require a MAM or cloud processor.
8
+
9
+ Use it for local image metadata extraction, EXIF normalization, auto-orientation, dimensions, and standard image variants.
10
+
11
+ It is the lightweight default adapter for apps that need practical image handling without Ergot.
12
+
13
+ It should remain optional so `@happyvertical/smrt-assets` core stays free of Sharp and EXIF dependencies.
14
+
15
+ ## Runtime Role
16
+
17
+ Register this adapter with the SMRT asset runtime when local processing is acceptable for the deployment environment.
18
+
19
+ The adapter should implement provider capabilities such as `processAsset` and `ensureVariant`.
20
+
21
+ It should not own asset identity, tenant policy, or publishing decisions.
22
+
23
+ Those belong in `@happyvertical/smrt-assets` and the host app.
24
+
25
+ ## Image Metadata
26
+
27
+ Metadata extraction must normalize orientation before reporting dimensions.
28
+
29
+ Captured time should be stored in a stable ISO format when present.
30
+
31
+ GPS coordinates should be converted to decimal latitude and longitude.
32
+
33
+ Do not round GPS data aggressively; nearby-photo search depends on precision.
34
+
35
+ Store extraction results in SMRT asset metadata using exported conventions from `@happyvertical/smrt-assets`.
36
+
37
+ ## Variants
38
+
39
+ Standard variants should use the shared SMRT names: `thumb`, `card`, `preview`, and `publish`.
40
+
41
+ Variant generation should be deterministic for the same source version and request parameters.
42
+
43
+ Prefer cache reuse when a matching derived asset already exists.
44
+
45
+ Variant assets should preserve parent or lineage metadata back to the source asset.
46
+
47
+ Generated bytes are local outputs; callers may still choose to sync them to Ergot later.
48
+
49
+ ## Dependency Notes
50
+
51
+ Sharp belongs in this package, not in `@happyvertical/smrt-assets` core.
52
+
53
+ Any additional EXIF dependency should stay here unless the core type contract truly needs it.
54
+
55
+ Tests should build tiny fixture images in memory where possible.
56
+
57
+ Avoid checked-in large binary fixtures.
58
+
59
+ ## Gotchas
60
+
61
+ Do not add Ergot-specific behavior here.
62
+
63
+ Do not call dashboard APIs from this package.
64
+
65
+ Do not silently drop extraction failures; return status and error metadata so hosts can decide what to display.
66
+
67
+ Keep the provider deterministic enough for CI and local development to agree.
package/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ @AGENTS.md
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright <2025> <Happy Vertical Corporation>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
1
+ import type { AssetCapabilityProvider, AssetVariantFit } from '@happyvertical/smrt-assets';
2
+ export { extractAssetImageMetadataFromBuffer, type NormalizedAssetImageMetadata, normalizeAssetImageMetadata, } from './metadata';
3
+ export interface LocalAssetImageVariantPreset {
4
+ width: number;
5
+ height: number;
6
+ fit: AssetVariantFit;
7
+ }
8
+ export interface LocalAssetProcessorOptions {
9
+ variants?: Record<string, LocalAssetImageVariantPreset>;
10
+ quality?: number;
11
+ }
12
+ export declare function createLocalAssetProcessor(options?: LocalAssetProcessorOptions): AssetCapabilityProvider;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,uBAAuB,EAIvB,eAAe,EAGhB,MAAM,4BAA4B,CAAC;AAWpC,OAAO,EACL,mCAAmC,EACnC,KAAK,4BAA4B,EACjC,2BAA2B,GAC5B,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,eAAe,CAAC;CACtB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,4BAA4B,CAAC,CAAC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyGD,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,0BAA+B,GACvC,uBAAuB,CAgHzB"}
package/dist/index.js ADDED
@@ -0,0 +1,163 @@
1
+ import { ASSET_ROLES, AssetCapabilitySkippedError, } from '@happyvertical/smrt-assets';
2
+ import sharp from 'sharp';
3
+ import { extractAssetImageMetadataFromBuffer, } from './metadata';
4
+ export { extractAssetImageMetadataFromBuffer, normalizeAssetImageMetadata, } from './metadata';
5
+ const DEFAULT_VARIANTS = {
6
+ thumb: { width: 160, height: 160, fit: 'cover' },
7
+ card: { width: 480, height: 270, fit: 'cover' },
8
+ preview: { width: 960, height: 960, fit: 'inside' },
9
+ publish: { width: 1200, height: 630, fit: 'cover' },
10
+ };
11
+ function parseAssetRecord(value) {
12
+ if (!value?.trim())
13
+ return {};
14
+ try {
15
+ const parsed = JSON.parse(value);
16
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
17
+ ? parsed
18
+ : {};
19
+ }
20
+ catch {
21
+ return {};
22
+ }
23
+ }
24
+ async function writeImageMetadata(asset, metadata) {
25
+ const structuredAsset = asset;
26
+ const sidecar = structuredAsset.getMetadata?.() ??
27
+ parseAssetRecord(structuredAsset.metadata);
28
+ const nextMetadata = {
29
+ ...sidecar,
30
+ imageMetadata: metadata,
31
+ imageMetadataUpdatedAt: new Date().toISOString(),
32
+ };
33
+ if (structuredAsset.setMetadata) {
34
+ structuredAsset.setMetadata(nextMetadata);
35
+ }
36
+ else {
37
+ structuredAsset.metadata = JSON.stringify(nextMetadata);
38
+ }
39
+ if (!asset.mimeType && metadata.mimeType) {
40
+ asset.mimeType = metadata.mimeType;
41
+ }
42
+ await asset.save();
43
+ }
44
+ function resolveVariantRequest(request, presets) {
45
+ const preset = presets[request.variant];
46
+ const width = request.width ?? preset?.width;
47
+ const height = request.height ?? preset?.height;
48
+ const fit = request.fit ?? preset?.fit ?? 'cover';
49
+ if (!width || !height) {
50
+ throw new Error(`Unknown asset image variant "${request.variant}" requires width and height.`);
51
+ }
52
+ return {
53
+ variant: request.variant,
54
+ width,
55
+ height,
56
+ fit,
57
+ };
58
+ }
59
+ function variantExternalId(source, variant, quality) {
60
+ const sourceVersion = Number(source.version ?? 1) || 1;
61
+ return [
62
+ 'smrt-assets',
63
+ 'variant',
64
+ source.id,
65
+ sourceVersion,
66
+ `${variant.variant}-${variant.width}x${variant.height}-${variant.fit}-webp-q${quality}`,
67
+ ].join(':');
68
+ }
69
+ async function findCachedVariant(input, externalId) {
70
+ if (!input.asset.id)
71
+ return null;
72
+ const derivatives = await input.runtime.collection.getDerivatives(input.asset.id);
73
+ return derivatives.find((child) => child.externalId === externalId) ?? null;
74
+ }
75
+ export function createLocalAssetProcessor(options = {}) {
76
+ const variants = { ...DEFAULT_VARIANTS, ...(options.variants ?? {}) };
77
+ const quality = options.quality ?? 82;
78
+ async function ensureVariant(input) {
79
+ if (!input.asset.mimeType?.startsWith('image/')) {
80
+ throw new AssetCapabilitySkippedError('ensureVariant', 'Local processor only handles image assets.');
81
+ }
82
+ if (!input.asset.id) {
83
+ throw new Error('Local asset variants require a persisted source asset.');
84
+ }
85
+ const variant = resolveVariantRequest(input.request, variants);
86
+ const externalId = variantExternalId(input.asset, variant, quality);
87
+ const cached = await findCachedVariant(input, externalId);
88
+ if (cached) {
89
+ return {
90
+ asset: cached,
91
+ variant: variant.variant,
92
+ source: 'cached',
93
+ url: cached.sourceUri,
94
+ metadata: cached.getMetadata?.() ??
95
+ parseAssetRecord(cached.metadata),
96
+ };
97
+ }
98
+ const source = await input.runtime.store.read(input.asset);
99
+ const { data, info } = await sharp(source)
100
+ .rotate()
101
+ .resize({
102
+ width: variant.width,
103
+ height: variant.height,
104
+ fit: variant.fit,
105
+ withoutEnlargement: true,
106
+ })
107
+ .webp({ quality })
108
+ .toBuffer({ resolveWithObject: true });
109
+ const variantMetadata = {
110
+ assetVariant: {
111
+ sourceAssetId: input.asset.id,
112
+ sourceVersion: Number(input.asset.version ?? 1) || 1,
113
+ ...variant,
114
+ format: 'webp',
115
+ quality,
116
+ },
117
+ };
118
+ const derived = await input.runtime.storeDerivedAsset(input.asset, `${input.asset.name || input.asset.id} ${variant.variant}.webp`, data, {
119
+ mimeType: 'image/webp',
120
+ typeSlug: 'image-variant',
121
+ role: ASSET_ROLES.ASSET_VARIANT,
122
+ description: `Generated ${variant.variant} image variant for ${input.asset.name || input.asset.id}`,
123
+ externalId,
124
+ sourceType: 'smrt-assets-local',
125
+ metadata: variantMetadata,
126
+ });
127
+ return {
128
+ asset: derived,
129
+ variant: variant.variant,
130
+ source: 'generated',
131
+ url: derived.sourceUri,
132
+ metadata: {
133
+ width: info.width,
134
+ height: info.height,
135
+ mimeType: 'image/webp',
136
+ },
137
+ };
138
+ }
139
+ return {
140
+ name: 'smrt-assets-local',
141
+ async processAsset(input) {
142
+ if (!input.asset.mimeType?.startsWith('image/')) {
143
+ throw new AssetCapabilitySkippedError('processAsset', 'Local processor only handles image assets.');
144
+ }
145
+ const data = await input.runtime.store.read(input.asset);
146
+ const metadata = await extractAssetImageMetadataFromBuffer(data);
147
+ await writeImageMetadata(input.asset, metadata);
148
+ const requested = input.variants ?? Object.keys(variants).map((variant) => ({ variant }));
149
+ const generatedVariants = await Promise.all(requested.map((request) => ensureVariant({
150
+ runtime: input.runtime,
151
+ asset: input.asset,
152
+ request,
153
+ })));
154
+ return {
155
+ asset: input.asset,
156
+ metadata,
157
+ variants: generatedVariants,
158
+ };
159
+ },
160
+ ensureVariant,
161
+ };
162
+ }
163
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export interface NormalizedAssetImageMetadata {
2
+ [key: string]: unknown;
3
+ width: number | null;
4
+ height: number | null;
5
+ mimeType: string | null;
6
+ capturedAt: string | null;
7
+ gps: {
8
+ latitude: number;
9
+ longitude: number;
10
+ } | null;
11
+ }
12
+ export declare function normalizeAssetImageMetadata(input: {
13
+ width?: unknown;
14
+ height?: unknown;
15
+ mimeType?: unknown;
16
+ exif?: Record<string, unknown> | null;
17
+ }): NormalizedAssetImageMetadata;
18
+ export declare function extractAssetImageMetadataFromBuffer(data: Buffer): Promise<NormalizedAssetImageMetadata>;
19
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,4BAA4B;IAC3C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,EAAE;QACH,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;CACV;AAySD,wBAAgB,2BAA2B,CAAC,KAAK,EAAE;IACjD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACvC,GAAG,4BAA4B,CA+C/B;AAED,wBAAsB,mCAAmC,CACvD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,4BAA4B,CAAC,CAYvC"}
@@ -0,0 +1,257 @@
1
+ import sharp from 'sharp';
2
+ function numericValue(value) {
3
+ if (typeof value === 'number' && Number.isFinite(value)) {
4
+ return value;
5
+ }
6
+ if (typeof value === 'string') {
7
+ const parsed = Number(value.trim());
8
+ return Number.isFinite(parsed) ? parsed : null;
9
+ }
10
+ if (value && typeof value === 'object') {
11
+ const record = value;
12
+ if (typeof record.numerator === 'number' &&
13
+ typeof record.denominator === 'number' &&
14
+ record.denominator !== 0) {
15
+ return record.numerator / record.denominator;
16
+ }
17
+ if (typeof record.valueOf === 'function') {
18
+ const parsed = Number(record.valueOf());
19
+ return Number.isFinite(parsed) ? parsed : null;
20
+ }
21
+ }
22
+ return null;
23
+ }
24
+ function coordinatePart(value) {
25
+ const numeric = numericValue(value);
26
+ if (numeric !== null)
27
+ return numeric;
28
+ if (typeof value !== 'string')
29
+ return null;
30
+ const match = value.match(/-?\d+(?:\.\d+)?/);
31
+ return match ? Number(match[0]) : null;
32
+ }
33
+ function coordinateFromDms(value, ref) {
34
+ if (typeof value === 'number' && Number.isFinite(value)) {
35
+ return applyCoordinateRef(value, ref);
36
+ }
37
+ if (typeof value === 'string') {
38
+ const decimal = Number(value.trim());
39
+ if (Number.isFinite(decimal)) {
40
+ return applyCoordinateRef(decimal, ref);
41
+ }
42
+ const parts = value.match(/-?\d+(?:\.\d+)?/g)?.map(Number) ?? [];
43
+ if (parts.length > 0) {
44
+ return applyCoordinateRef(parts[0] + (parts[1] ?? 0) / 60 + (parts[2] ?? 0) / 3600, ref);
45
+ }
46
+ }
47
+ if (Array.isArray(value)) {
48
+ const parts = value
49
+ .map(coordinatePart)
50
+ .filter((part) => part !== null);
51
+ if (parts.length > 0) {
52
+ return applyCoordinateRef(parts[0] + (parts[1] ?? 0) / 60 + (parts[2] ?? 0) / 3600, ref);
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+ function applyCoordinateRef(value, ref) {
58
+ const direction = String(ref ?? '')
59
+ .trim()
60
+ .toUpperCase();
61
+ if (direction === 'S' ||
62
+ direction === 'W' ||
63
+ direction === 'SOUTH' ||
64
+ direction === 'WEST') {
65
+ return -Math.abs(value);
66
+ }
67
+ return value;
68
+ }
69
+ function dateFromExif(value) {
70
+ if (value instanceof Date && Number.isFinite(value.getTime())) {
71
+ return value.toISOString();
72
+ }
73
+ if (typeof value !== 'string' || value.trim().length === 0) {
74
+ return null;
75
+ }
76
+ const normalized = value
77
+ .trim()
78
+ .replace(/^(\d{4}):(\d{2}):(\d{2})/, '$1-$2-$3');
79
+ const parsed = new Date(normalized);
80
+ return Number.isFinite(parsed.getTime()) ? parsed.toISOString() : null;
81
+ }
82
+ function firstPresent(record, keys) {
83
+ for (const key of keys) {
84
+ if (record[key] !== undefined &&
85
+ record[key] !== null &&
86
+ record[key] !== '') {
87
+ return record[key];
88
+ }
89
+ }
90
+ return undefined;
91
+ }
92
+ const TIFF_TYPE_SIZE = {
93
+ 1: 1,
94
+ 2: 1,
95
+ 3: 2,
96
+ 4: 4,
97
+ 5: 8,
98
+ 7: 1,
99
+ 9: 4,
100
+ 10: 8,
101
+ };
102
+ function readExifTagsFromBuffer(data) {
103
+ const tiffStart = findExifTiffStart(data);
104
+ if (tiffStart === null)
105
+ return null;
106
+ const littleEndian = data.toString('ascii', tiffStart, tiffStart + 2) === 'II';
107
+ const readUInt16 = (offset) => littleEndian ? data.readUInt16LE(offset) : data.readUInt16BE(offset);
108
+ const readUInt32 = (offset) => littleEndian ? data.readUInt32LE(offset) : data.readUInt32BE(offset);
109
+ const readInt32 = (offset) => littleEndian ? data.readInt32LE(offset) : data.readInt32BE(offset);
110
+ if (readUInt16(tiffStart + 2) !== 42)
111
+ return null;
112
+ const ifd0Offset = tiffStart + readUInt32(tiffStart + 4);
113
+ const ifd0 = readIfdEntries(data, tiffStart, ifd0Offset, readUInt16, readUInt32, readInt32);
114
+ const exifOffset = numericValue(ifd0[0x8769]);
115
+ const gpsOffset = numericValue(ifd0[0x8825]);
116
+ const exif = exifOffset !== null
117
+ ? readIfdEntries(data, tiffStart, tiffStart + exifOffset, readUInt16, readUInt32, readInt32)
118
+ : {};
119
+ const gps = gpsOffset !== null
120
+ ? readIfdEntries(data, tiffStart, tiffStart + gpsOffset, readUInt16, readUInt32, readInt32)
121
+ : {};
122
+ return {
123
+ DateTimeOriginal: exif[0x9003],
124
+ CreateDate: exif[0x9004],
125
+ GPSLatitudeRef: gps[0x0001],
126
+ GPSLatitude: gps[0x0002],
127
+ GPSLongitudeRef: gps[0x0003],
128
+ GPSLongitude: gps[0x0004],
129
+ };
130
+ }
131
+ function findExifTiffStart(data) {
132
+ if (data.length < 4 || data[0] !== 0xff || data[1] !== 0xd8)
133
+ return null;
134
+ let offset = 2;
135
+ while (offset + 4 < data.length) {
136
+ if (data[offset] !== 0xff)
137
+ return null;
138
+ const marker = data[offset + 1];
139
+ offset += 2;
140
+ if (marker === 0xda || marker === 0xd9)
141
+ return null;
142
+ const segmentLength = data.readUInt16BE(offset);
143
+ const segmentStart = offset + 2;
144
+ if (marker === 0xe1 &&
145
+ data.toString('ascii', segmentStart, segmentStart + 6) === 'Exif\0\0') {
146
+ return segmentStart + 6;
147
+ }
148
+ offset += segmentLength;
149
+ }
150
+ return null;
151
+ }
152
+ function readIfdEntries(data, tiffStart, ifdOffset, readUInt16, readUInt32, readInt32) {
153
+ if (ifdOffset < 0 || ifdOffset + 2 > data.length)
154
+ return {};
155
+ const count = readUInt16(ifdOffset);
156
+ const entries = {};
157
+ for (let index = 0; index < count; index++) {
158
+ const entryOffset = ifdOffset + 2 + index * 12;
159
+ if (entryOffset + 12 > data.length)
160
+ break;
161
+ const tag = readUInt16(entryOffset);
162
+ const type = readUInt16(entryOffset + 2);
163
+ const valueCount = readUInt32(entryOffset + 4);
164
+ const value = readTiffValue(data, tiffStart, entryOffset, type, valueCount, readUInt16, readUInt32, readInt32);
165
+ if (value !== undefined)
166
+ entries[tag] = value;
167
+ }
168
+ return entries;
169
+ }
170
+ function readTiffValue(data, tiffStart, entryOffset, type, count, readUInt16, readUInt32, readInt32) {
171
+ const typeSize = TIFF_TYPE_SIZE[type];
172
+ if (!typeSize)
173
+ return undefined;
174
+ const byteLength = typeSize * count;
175
+ const valueOffset = byteLength <= 4 ? entryOffset + 8 : tiffStart + readUInt32(entryOffset + 8);
176
+ if (valueOffset < 0 || valueOffset + byteLength > data.length)
177
+ return undefined;
178
+ const values = Array.from({ length: count }, (_, index) => readTiffScalar(data, valueOffset + index * typeSize, type, readUInt16, readUInt32, readInt32));
179
+ if (type === 2) {
180
+ return data
181
+ .toString('ascii', valueOffset, valueOffset + byteLength)
182
+ .replace(/\0+$/, '')
183
+ .trim();
184
+ }
185
+ return count === 1 ? values[0] : values;
186
+ }
187
+ function readTiffScalar(data, offset, type, readUInt16, readUInt32, readInt32) {
188
+ if (type === 3)
189
+ return readUInt16(offset);
190
+ if (type === 4)
191
+ return readUInt32(offset);
192
+ if (type === 5) {
193
+ const denominator = readUInt32(offset + 4);
194
+ return denominator === 0 ? null : readUInt32(offset) / denominator;
195
+ }
196
+ if (type === 9)
197
+ return readInt32(offset);
198
+ if (type === 10) {
199
+ const denominator = readInt32(offset + 4);
200
+ return denominator === 0 ? null : readInt32(offset) / denominator;
201
+ }
202
+ return data[offset];
203
+ }
204
+ function mimeTypeFromSharpFormat(format) {
205
+ if (!format)
206
+ return null;
207
+ if (format === 'jpeg')
208
+ return 'image/jpeg';
209
+ if (format === 'svg')
210
+ return 'image/svg+xml';
211
+ return `image/${format}`;
212
+ }
213
+ export function normalizeAssetImageMetadata(input) {
214
+ const exif = input.exif ?? {};
215
+ const directLatitude = numericValue(firstPresent(exif, ['latitude', 'Latitude', 'GPSLatitudeDecimal']));
216
+ const directLongitude = numericValue(firstPresent(exif, ['longitude', 'Longitude', 'GPSLongitudeDecimal']));
217
+ const latitude = directLatitude ??
218
+ coordinateFromDms(firstPresent(exif, ['GPSLatitude', 'gpsLatitude']), firstPresent(exif, ['GPSLatitudeRef', 'gpsLatitudeRef']));
219
+ const longitude = directLongitude ??
220
+ coordinateFromDms(firstPresent(exif, ['GPSLongitude', 'gpsLongitude']), firstPresent(exif, ['GPSLongitudeRef', 'gpsLongitudeRef']));
221
+ const capturedAt = dateFromExif(firstPresent(exif, [
222
+ 'DateTimeOriginal',
223
+ 'CreateDate',
224
+ 'ModifyDate',
225
+ 'SubSecDateTimeOriginal',
226
+ 'dateTimeOriginal',
227
+ 'createDate',
228
+ ]));
229
+ return {
230
+ width: numericValue(input.width),
231
+ height: numericValue(input.height),
232
+ mimeType: typeof input.mimeType === 'string' && input.mimeType.trim()
233
+ ? input.mimeType.trim()
234
+ : null,
235
+ capturedAt,
236
+ gps: latitude !== null && longitude !== null
237
+ ? {
238
+ latitude,
239
+ longitude,
240
+ }
241
+ : null,
242
+ };
243
+ }
244
+ export async function extractAssetImageMetadataFromBuffer(data) {
245
+ const metadata = await sharp(data).metadata();
246
+ const exif = readExifTagsFromBuffer(data);
247
+ return normalizeAssetImageMetadata({
248
+ width: metadata.width,
249
+ height: metadata.height,
250
+ mimeType: mimeTypeFromSharpFormat(metadata.format),
251
+ exif: {
252
+ ...metadata,
253
+ ...(exif ?? {}),
254
+ },
255
+ });
256
+ }
257
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAc1B,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACpC,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACjD,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,KAAgC,CAAC;QAChD,IACE,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;YACpC,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ;YACtC,MAAM,CAAC,WAAW,KAAK,CAAC,EACxB,CAAC;YACD,OAAO,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC;QAC/C,CAAC;QACD,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;YACzC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;YACxC,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACjD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC7C,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAc,EAAE,GAAY;IACrD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,kBAAkB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACrC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,OAAO,kBAAkB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC1C,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACjE,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,kBAAkB,CACvB,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,EACxD,GAAG,CACJ,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,KAAK;aAChB,GAAG,CAAC,cAAc,CAAC;aACnB,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QACnD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,kBAAkB,CACvB,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,EACxD,GAAG,CACJ,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAa,EAAE,GAAY;IACrD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;SAChC,IAAI,EAAE;SACN,WAAW,EAAE,CAAC;IACjB,IACE,SAAS,KAAK,GAAG;QACjB,SAAS,KAAK,GAAG;QACjB,SAAS,KAAK,OAAO;QACrB,SAAS,KAAK,MAAM,EACpB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,YAAY,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC9D,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC;IAC7B,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,UAAU,GAAG,KAAK;SACrB,IAAI,EAAE;SACN,OAAO,CAAC,0BAA0B,EAAE,UAAU,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC;IACpC,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACzE,CAAC;AAED,SAAS,YAAY,CACnB,MAA+B,EAC/B,IAAc;IAEd,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IACE,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS;YACzB,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI;YACpB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,EAClB,CAAC;YACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,cAAc,GAA2B;IAC7C,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,EAAE,EAAE,CAAC;CACN,CAAC;AAEF,SAAS,sBAAsB,CAAC,IAAY;IAC1C,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,YAAY,GAChB,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC;IAC5D,MAAM,UAAU,GAAG,CAAC,MAAc,EAAE,EAAE,CACpC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACvE,MAAM,UAAU,GAAG,CAAC,MAAc,EAAE,EAAE,CACpC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACvE,MAAM,SAAS,GAAG,CAAC,MAAc,EAAE,EAAE,CACnC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACrE,IAAI,UAAU,CAAC,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAElD,MAAM,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,cAAc,CACzB,IAAI,EACJ,SAAS,EACT,UAAU,EACV,UAAU,EACV,UAAU,EACV,SAAS,CACV,CAAC;IACF,MAAM,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7C,MAAM,IAAI,GACR,UAAU,KAAK,IAAI;QACjB,CAAC,CAAC,cAAc,CACZ,IAAI,EACJ,SAAS,EACT,SAAS,GAAG,UAAU,EACtB,UAAU,EACV,UAAU,EACV,SAAS,CACV;QACH,CAAC,CAAC,EAAE,CAAC;IACT,MAAM,GAAG,GACP,SAAS,KAAK,IAAI;QAChB,CAAC,CAAC,cAAc,CACZ,IAAI,EACJ,SAAS,EACT,SAAS,GAAG,SAAS,EACrB,UAAU,EACV,UAAU,EACV,SAAS,CACV;QACH,CAAC,CAAC,EAAE,CAAC;IAET,OAAO;QACL,gBAAgB,EAAE,IAAI,CAAC,MAAM,CAAC;QAC9B,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;QACxB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC;QAC3B,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC;QACxB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC;QAC5B,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC;KAC1B,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY;IACrC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACzE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAChC,MAAM,IAAI,CAAC,CAAC;QACZ,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACpD,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,YAAY,GAAG,MAAM,GAAG,CAAC,CAAC;QAChC,IACE,MAAM,KAAK,IAAI;YACf,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,YAAY,GAAG,CAAC,CAAC,KAAK,UAAU,EACrE,CAAC;YACD,OAAO,YAAY,GAAG,CAAC,CAAC;QAC1B,CAAC;QACD,MAAM,IAAI,aAAa,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,cAAc,CACrB,IAAY,EACZ,SAAiB,EACjB,SAAiB,EACjB,UAAsC,EACtC,UAAsC,EACtC,SAAqC;IAErC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC5D,MAAM,KAAK,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IACpC,MAAM,OAAO,GAA4B,EAAE,CAAC;IAC5C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;QAC3C,MAAM,WAAW,GAAG,SAAS,GAAG,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;QAC/C,IAAI,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM;YAAE,MAAM;QAC1C,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;QACzC,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,aAAa,CACzB,IAAI,EACJ,SAAS,EACT,WAAW,EACX,IAAI,EACJ,UAAU,EACV,UAAU,EACV,UAAU,EACV,SAAS,CACV,CAAC;QACF,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAChD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,aAAa,CACpB,IAAY,EACZ,SAAiB,EACjB,WAAmB,EACnB,IAAY,EACZ,KAAa,EACb,UAAsC,EACtC,UAAsC,EACtC,SAAqC;IAErC,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,CAAC;IACpC,MAAM,WAAW,GACf,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,UAAU,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;IAC9E,IAAI,WAAW,GAAG,CAAC,IAAI,WAAW,GAAG,UAAU,GAAG,IAAI,CAAC,MAAM;QAC3D,OAAO,SAAS,CAAC;IACnB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CACxD,cAAc,CACZ,IAAI,EACJ,WAAW,GAAG,KAAK,GAAG,QAAQ,EAC9B,IAAI,EACJ,UAAU,EACV,UAAU,EACV,SAAS,CACV,CACF,CAAC;IACF,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;QACf,OAAO,IAAI;aACR,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,WAAW,GAAG,UAAU,CAAC;aACxD,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;aACnB,IAAI,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAC1C,CAAC;AAED,SAAS,cAAc,CACrB,IAAY,EACZ,MAAc,EACd,IAAY,EACZ,UAAsC,EACtC,UAAsC,EACtC,SAAqC;IAErC,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;QACf,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC3C,OAAO,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC;IACrE,CAAC;IACD,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC1C,OAAO,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC;IACpE,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,uBAAuB,CAAC,MAA0B;IACzD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,YAAY,CAAC;IAC3C,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,eAAe,CAAC;IAC7C,OAAO,SAAS,MAAM,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,KAK3C;IACC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;IAC9B,MAAM,cAAc,GAAG,YAAY,CACjC,YAAY,CAAC,IAAI,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,oBAAoB,CAAC,CAAC,CACnE,CAAC;IACF,MAAM,eAAe,GAAG,YAAY,CAClC,YAAY,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,qBAAqB,CAAC,CAAC,CACtE,CAAC;IACF,MAAM,QAAQ,GACZ,cAAc;QACd,iBAAiB,CACf,YAAY,CAAC,IAAI,EAAE,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,EAClD,YAAY,CAAC,IAAI,EAAE,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC,CACzD,CAAC;IACJ,MAAM,SAAS,GACb,eAAe;QACf,iBAAiB,CACf,YAAY,CAAC,IAAI,EAAE,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC,EACpD,YAAY,CAAC,IAAI,EAAE,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,CAAC,CAC3D,CAAC;IACJ,MAAM,UAAU,GAAG,YAAY,CAC7B,YAAY,CAAC,IAAI,EAAE;QACjB,kBAAkB;QAClB,YAAY;QACZ,YAAY;QACZ,wBAAwB;QACxB,kBAAkB;QAClB,YAAY;KACb,CAAC,CACH,CAAC;IAEF,OAAO;QACL,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC;QAChC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC;QAClC,QAAQ,EACN,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE;YACzD,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE;YACvB,CAAC,CAAC,IAAI;QACV,UAAU;QACV,GAAG,EACD,QAAQ,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI;YACrC,CAAC,CAAC;gBACE,QAAQ;gBACR,SAAS;aACV;YACH,CAAC,CAAC,IAAI;KACX,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,IAAY;IAEZ,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC9C,MAAM,IAAI,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC1C,OAAO,2BAA2B,CAAC;QACjC,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,QAAQ,EAAE,uBAAuB,CAAC,QAAQ,CAAC,MAAM,CAAC;QAClD,IAAI,EAAE;YACJ,GAAI,QAA+C;YACnD,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;SAChB;KACF,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@happyvertical/smrt-assets-local",
3
+ "version": "0.30.0",
4
+ "description": "Local image metadata and variant processor for SMRT assets",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "CLAUDE.md",
11
+ "AGENTS.md"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "dependencies": {
21
+ "sharp": "^0.34.5",
22
+ "@happyvertical/smrt-assets": "0.30.0"
23
+ },
24
+ "devDependencies": {
25
+ "@happyvertical/sql": "^0.74.7",
26
+ "@types/node": "25.0.9",
27
+ "typescript": "^5.9.3",
28
+ "vitest": "^4.0.17",
29
+ "@happyvertical/smrt-core": "0.30.0"
30
+ },
31
+ "publishConfig": {
32
+ "registry": "https://registry.npmjs.org",
33
+ "access": "public"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/happyvertical/smrt.git",
38
+ "directory": "packages/assets-local"
39
+ },
40
+ "author": "HappyVertical",
41
+ "license": "MIT",
42
+ "scripts": {
43
+ "build": "tsc -p tsconfig.json",
44
+ "check": "tsc -p tsconfig.json --noEmit",
45
+ "test": "vitest run",
46
+ "typecheck": "tsc --noEmit -p tsconfig.json"
47
+ }
48
+ }