@better-media/core 0.1.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/dist/index.mjs ADDED
@@ -0,0 +1,718 @@
1
+ import { z } from 'zod';
2
+
3
+ // src/plugin/verified-sources.ts
4
+ function markFileContentVerified(context) {
5
+ const c = context;
6
+ c._verifiedSources ??= /* @__PURE__ */ new Set();
7
+ c._verifiedSources.add("file:content");
8
+ }
9
+ var TrustedMetadataSchema = z.object({
10
+ file: z.object({
11
+ mimeType: z.string().optional(),
12
+ size: z.number().int().nonnegative().optional(),
13
+ originalName: z.string().optional(),
14
+ extension: z.string().optional()
15
+ }).strict().optional(),
16
+ checksums: z.object({
17
+ sha256: z.string().optional(),
18
+ md5: z.string().optional()
19
+ }).strict().optional(),
20
+ media: z.object({
21
+ width: z.number().int().nonnegative().optional(),
22
+ height: z.number().int().nonnegative().optional(),
23
+ duration: z.number().nonnegative().optional()
24
+ }).strict().optional()
25
+ }).strict();
26
+
27
+ // src/plugin/interfaces/hook-name.interface.ts
28
+ var HOOK_NAMES = [
29
+ "upload:init",
30
+ "validation:run",
31
+ "scan:run",
32
+ "process:run",
33
+ "upload:complete"
34
+ ];
35
+
36
+ // src/plugin/interfaces/hook-mode.interface.ts
37
+ var HOOK_MODE_CONSTRAINTS = {
38
+ "upload:init": "sync-only",
39
+ "validation:run": "sync-only",
40
+ "scan:run": "sync-only",
41
+ "process:run": "sync-or-background",
42
+ "upload:complete": "sync-or-background"
43
+ };
44
+ function resolveHookMode(hookName, requested) {
45
+ const constraint = HOOK_MODE_CONSTRAINTS[hookName];
46
+ if (constraint === "sync-only" && requested === "background") {
47
+ return { effective: "sync", overridden: true };
48
+ }
49
+ if (constraint === "background-only" && requested === "sync") {
50
+ return { effective: "background", overridden: true };
51
+ }
52
+ return { effective: requested, overridden: false };
53
+ }
54
+ var ValidationResultSchema = z.object({
55
+ valid: z.boolean(),
56
+ pluginId: z.string().optional(),
57
+ message: z.string().optional(),
58
+ errors: z.array(z.string()).optional()
59
+ }).strict();
60
+ var VirusScanResultSchema = z.object({
61
+ status: z.enum(["clean", "infected", "error"]),
62
+ threats: z.array(z.string()).optional(),
63
+ scanner: z.string().optional(),
64
+ scannedAt: z.string().datetime().optional()
65
+ }).strict();
66
+
67
+ // src/database/schema.ts
68
+ var schema = {
69
+ // Core media records
70
+ media: {
71
+ fields: {
72
+ id: { type: "string", primaryKey: true, required: true },
73
+ ownerId: { type: "string" },
74
+ filename: { type: "string" },
75
+ mimeType: { type: "string" },
76
+ size: { type: "number" },
77
+ storageProvider: { type: "string" },
78
+ storageKey: { type: "string" },
79
+ checksum: { type: "string" },
80
+ width: { type: "number" },
81
+ height: { type: "number" },
82
+ duration: { type: "number" },
83
+ context: { type: "json" },
84
+ status: { type: "string" },
85
+ visibility: { type: "string" },
86
+ createdAt: { type: "date" },
87
+ updatedAt: { type: "date" },
88
+ deletedAt: { type: "date" }
89
+ },
90
+ indexes: [{ fields: ["checksum", "storageKey"] }]
91
+ },
92
+ // Different versions of the media (thumbnails, previews, etc.)
93
+ media_versions: {
94
+ fields: {
95
+ id: { type: "string", primaryKey: true, required: true },
96
+ mediaId: {
97
+ type: "string",
98
+ references: {
99
+ model: "media",
100
+ field: "id",
101
+ onDelete: "cascade"
102
+ }
103
+ },
104
+ storageKey: { type: "string" },
105
+ checksum: { type: "string" },
106
+ isOriginal: { type: "boolean" },
107
+ type: { type: "string" },
108
+ // e.g., 'thumbnail', 'preview', 'compressed'
109
+ versionNumber: { type: "number" },
110
+ createdAt: { type: "date" }
111
+ },
112
+ indexes: [
113
+ {
114
+ fields: ["mediaId", "versionNumber"],
115
+ unique: true
116
+ }
117
+ ]
118
+ },
119
+ // Background job state for media processing
120
+ media_jobs: {
121
+ fields: {
122
+ id: { type: "string", primaryKey: true, required: true },
123
+ mediaId: {
124
+ type: "string",
125
+ references: {
126
+ model: "media",
127
+ field: "id",
128
+ onDelete: "cascade"
129
+ }
130
+ },
131
+ type: { type: "string" },
132
+ // e.g., 'virus-scan', 'thumbnail'
133
+ status: { type: "string" },
134
+ // e.g., 'pending', 'running', 'completed', 'failed'
135
+ attempts: { type: "number" },
136
+ maxAttempts: { type: "number" },
137
+ idempotencyKey: { type: "string", unique: true },
138
+ scheduledAt: { type: "date" },
139
+ startedAt: { type: "date" },
140
+ completedAt: { type: "date" },
141
+ error: { type: "string" },
142
+ createdAt: { type: "date" }
143
+ }
144
+ },
145
+ media_validation_results: {
146
+ fields: {
147
+ id: { type: "string", primaryKey: true, required: true },
148
+ mediaId: {
149
+ type: "string",
150
+ references: { model: "media", field: "id", onDelete: "cascade" }
151
+ },
152
+ valid: { type: "boolean" },
153
+ pluginId: { type: "string" },
154
+ errors: { type: "json" },
155
+ createdAt: { type: "date" }
156
+ }
157
+ },
158
+ media_virus_scan_results: {
159
+ fields: {
160
+ id: { type: "string", primaryKey: true, required: true },
161
+ mediaId: {
162
+ type: "string",
163
+ references: { model: "media", field: "id", onDelete: "cascade" }
164
+ },
165
+ status: { type: "string" },
166
+ // 'clean', 'infected', 'error'
167
+ threats: { type: "json" },
168
+ // List of detected threats
169
+ scanner: { type: "string" },
170
+ // clamd, etc.
171
+ createdAt: { type: "date" }
172
+ }
173
+ }
174
+ };
175
+
176
+ // src/database/naming.ts
177
+ function toSnakeCase(value) {
178
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
179
+ }
180
+ function toCamelCase(value) {
181
+ return value.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
182
+ }
183
+ function toDbFieldName(field) {
184
+ return toSnakeCase(field);
185
+ }
186
+
187
+ // src/database/fields.ts
188
+ var converters = {
189
+ json: {
190
+ serialize: (value) => value === void 0 ? void 0 : JSON.stringify(value),
191
+ deserialize: (value) => typeof value === "string" ? JSON.parse(value) : value
192
+ },
193
+ date: {
194
+ serialize: (value) => value instanceof Date ? value.toISOString() : typeof value === "string" ? value : value,
195
+ deserialize: (value) => typeof value === "string" ? new Date(value) : value
196
+ },
197
+ boolean: {
198
+ serialize: (value) => typeof value === "boolean" ? value ? 1 : 0 : value,
199
+ deserialize: (value) => typeof value === "number" ? value === 1 : Boolean(value)
200
+ },
201
+ string: {
202
+ serialize: (value) => value,
203
+ deserialize: (value) => typeof value === "number" ? String(value) : value
204
+ },
205
+ number: {
206
+ serialize: (value) => typeof value === "string" ? Number(value) : value,
207
+ deserialize: (value) => value
208
+ }
209
+ };
210
+ function serializeField(type, value) {
211
+ if (value === null || value === void 0) return value;
212
+ return converters[type]?.serialize(value) ?? value;
213
+ }
214
+ function deserializeField(type, value) {
215
+ if (value === null || value === void 0) return value;
216
+ return converters[type]?.deserialize(value) ?? value;
217
+ }
218
+ function serializeData(fields, data) {
219
+ const result = {};
220
+ for (const [key, value] of Object.entries(data)) {
221
+ if (fields[key]) {
222
+ result[key] = serializeField(fields[key].type, value);
223
+ } else {
224
+ result[key] = value;
225
+ }
226
+ }
227
+ return result;
228
+ }
229
+ function deserializeData(fields, data) {
230
+ const result = {};
231
+ for (const [key, value] of Object.entries(data)) {
232
+ if (fields[key]) {
233
+ result[key] = deserializeField(fields[key].type, value);
234
+ } else {
235
+ result[key] = value;
236
+ }
237
+ }
238
+ return result;
239
+ }
240
+
241
+ // src/database/plan.ts
242
+ var dialectMap = {
243
+ string: {
244
+ postgres: "text",
245
+ mysql: "varchar(255)",
246
+ sqlite: "text",
247
+ mssql: "varchar(255)"
248
+ },
249
+ number: {
250
+ postgres: "integer",
251
+ mysql: "integer",
252
+ sqlite: "integer",
253
+ mssql: "integer"
254
+ },
255
+ boolean: {
256
+ postgres: "boolean",
257
+ mysql: "boolean",
258
+ sqlite: "integer",
259
+ mssql: "bit"
260
+ },
261
+ date: {
262
+ postgres: "timestamp",
263
+ mysql: "datetime",
264
+ sqlite: "text",
265
+ mssql: "datetime2"
266
+ },
267
+ json: {
268
+ postgres: "jsonb",
269
+ mysql: "json",
270
+ sqlite: "text",
271
+ mssql: "nvarchar(max)"
272
+ }
273
+ };
274
+ function getColumnType(field, dialect) {
275
+ if (field.primaryKey && field.type === "string") {
276
+ if (dialect === "postgres") return "uuid";
277
+ return dialect === "mysql" || dialect === "mssql" ? "varchar(36)" : "text";
278
+ }
279
+ if (field.references && field.type === "string" && dialect === "postgres") {
280
+ return "uuid";
281
+ }
282
+ const types = dialectMap[field.type];
283
+ if (!types) return "text";
284
+ const type = types[dialect];
285
+ return type || "text";
286
+ }
287
+ function matchType(dbType, expectedType, dialect) {
288
+ const normalizedDb = (dbType.toLowerCase().split("(")[0] || "").trim();
289
+ const normalizedMap = {
290
+ postgres: {
291
+ string: ["character varying", "varchar", "text", "uuid"],
292
+ number: ["int4", "integer", "bigint", "smallint", "numeric", "real", "double precision"],
293
+ boolean: ["bool", "boolean"],
294
+ date: ["timestamptz", "timestamp", "date"],
295
+ json: ["json", "jsonb"]
296
+ },
297
+ mysql: {
298
+ string: ["varchar", "text", "uuid", "char"],
299
+ number: ["integer", "int", "bigint", "smallint", "decimal", "float", "double"],
300
+ boolean: ["boolean", "tinyint", "bit"],
301
+ date: ["timestamp", "datetime", "date"],
302
+ json: ["json", "text", "longtext"]
303
+ },
304
+ sqlite: {
305
+ string: ["text", "varchar"],
306
+ number: ["integer", "real", "numeric"],
307
+ boolean: ["integer", "boolean"],
308
+ date: ["date", "text", "integer"],
309
+ json: ["text"]
310
+ },
311
+ mssql: {
312
+ string: ["varchar", "nvarchar", "uniqueidentifier"],
313
+ number: ["int", "bigint", "smallint", "decimal", "float", "double", "numeric"],
314
+ boolean: ["bit", "smallint"],
315
+ date: ["datetime2", "date", "datetime", "timestamp"],
316
+ json: ["varchar", "nvarchar"]
317
+ }
318
+ };
319
+ return normalizedMap[dialect][expectedType].includes(normalizedDb);
320
+ }
321
+ var MigrationPlanner = class {
322
+ constructor(dialect) {
323
+ this.dialect = dialect;
324
+ }
325
+ plan(schema2, currentTables) {
326
+ const operations = [];
327
+ for (const [tableName, model] of Object.entries(schema2)) {
328
+ const existingTable = currentTables.find((t) => t.name === tableName);
329
+ if (!existingTable) {
330
+ operations.push({ type: "createTable", table: tableName, definition: model });
331
+ continue;
332
+ }
333
+ for (const [fieldName, fieldDef] of Object.entries(model.fields)) {
334
+ const existingColumn = existingTable.columns.find((c) => c.name === fieldName);
335
+ if (!existingColumn) {
336
+ operations.push({
337
+ type: "addColumn",
338
+ table: tableName,
339
+ field: fieldName,
340
+ definition: fieldDef
341
+ });
342
+ } else {
343
+ if (!matchType(existingColumn.dataType, fieldDef.type, this.dialect)) {
344
+ console.warn(
345
+ `[BetterMedia] Type mismatch for ${tableName}.${fieldName}: Expected ${fieldDef.type} but found ${existingColumn.dataType} in DB.`
346
+ );
347
+ }
348
+ }
349
+ }
350
+ }
351
+ return operations;
352
+ }
353
+ };
354
+ function applyOperationsToMetadata(currentMetadata, operations, dialect) {
355
+ const metadata = JSON.parse(JSON.stringify(currentMetadata));
356
+ for (const op of operations) {
357
+ if (op.type === "createTable") {
358
+ const columns = Object.entries(op.definition.fields).map(([name, field]) => ({
359
+ name,
360
+ dataType: getColumnType(field, dialect),
361
+ isNullable: !field.required && !field.primaryKey,
362
+ isUnique: field.unique || field.primaryKey
363
+ }));
364
+ metadata.push({ name: op.table, columns });
365
+ } else if (op.type === "addColumn") {
366
+ const table = metadata.find((t) => t.name === op.table);
367
+ if (table) {
368
+ table.columns.push({
369
+ name: op.field,
370
+ dataType: getColumnType(op.definition, dialect),
371
+ isNullable: !op.definition.required,
372
+ isUnique: !!op.definition.unique
373
+ });
374
+ }
375
+ }
376
+ }
377
+ return metadata;
378
+ }
379
+
380
+ // src/database/sql.ts
381
+ function quoteIdent(ident, dialect) {
382
+ if (dialect === "mysql") return `\`${ident}\``;
383
+ return `"${ident}"`;
384
+ }
385
+ function onDeleteSql(onDelete) {
386
+ if (!onDelete) return "";
387
+ const normalized = String(onDelete).toUpperCase();
388
+ if (normalized === "CASCADE") return " ON DELETE CASCADE";
389
+ if (normalized === "SET NULL") return " ON DELETE SET NULL";
390
+ if (normalized === "RESTRICT") return " ON DELETE RESTRICT";
391
+ return "";
392
+ }
393
+ function columnSql(table, name, field, dialect) {
394
+ const parts = [];
395
+ const dbName = toDbFieldName(name);
396
+ parts.push(quoteIdent(dbName, dialect));
397
+ parts.push(getColumnType(field, dialect));
398
+ if (field.primaryKey) parts.push("PRIMARY KEY");
399
+ if (field.required) parts.push("NOT NULL");
400
+ if (field.unique) parts.push("UNIQUE");
401
+ if (field.references) {
402
+ const ref = field.references;
403
+ const refTable = quoteIdent(ref.model, dialect);
404
+ const refField = quoteIdent(toDbFieldName(ref.field), dialect);
405
+ parts.push(`REFERENCES ${refTable}(${refField})${onDeleteSql(ref.onDelete)}`);
406
+ }
407
+ return parts.join(" ");
408
+ }
409
+ function createTableSql(table, definition, dialect) {
410
+ const tableName = quoteIdent(table, dialect);
411
+ const cols = Object.entries(definition.fields).map(
412
+ ([name, field]) => columnSql(table, name, field, dialect)
413
+ );
414
+ const ifNotExists = dialect === "mssql" ? "" : " IF NOT EXISTS";
415
+ return `CREATE TABLE${ifNotExists} ${tableName} (
416
+ ${cols.join(",\n ")}
417
+ );`;
418
+ }
419
+ function createIndexSql(table, indexName, fields, unique, dialect) {
420
+ const idx = quoteIdent(indexName, dialect);
421
+ const tbl = quoteIdent(table, dialect);
422
+ const cols = fields.map((f) => quoteIdent(toDbFieldName(f), dialect)).join(", ");
423
+ const uniqueSql = unique ? "UNIQUE " : "";
424
+ const ifNotExists = dialect === "mssql" ? "" : " IF NOT EXISTS";
425
+ return `CREATE ${uniqueSql}INDEX${ifNotExists} ${idx} ON ${tbl} (${cols});`;
426
+ }
427
+ function addColumnSql(table, field, definition, dialect) {
428
+ const tableName = quoteIdent(table, dialect);
429
+ const columnDef = columnSql(table, field, definition, dialect);
430
+ return `ALTER TABLE ${tableName} ADD COLUMN ${columnDef};`;
431
+ }
432
+ function compileMigrationOperationsSql(options) {
433
+ const { operations, dialect } = options;
434
+ const statements = [];
435
+ statements.push(`-- Better Media planned migrations (${dialect})`);
436
+ if (dialect === "sqlite") {
437
+ statements.push("PRAGMA foreign_keys = ON;");
438
+ }
439
+ for (const op of operations) {
440
+ if (op.type === "createTable") {
441
+ statements.push(createTableSql(op.table, op.definition, dialect));
442
+ if (op.definition.indexes) {
443
+ for (const index of op.definition.indexes) {
444
+ const indexName = `idx_${op.table}_${index.fields.map((f) => toDbFieldName(f)).join("_")}`;
445
+ statements.push(createIndexSql(op.table, indexName, index.fields, index.unique, dialect));
446
+ }
447
+ }
448
+ continue;
449
+ }
450
+ if (op.type === "addColumn") {
451
+ statements.push(addColumnSql(op.table, op.field, op.definition, dialect));
452
+ continue;
453
+ }
454
+ if (op.type === "createIndex") {
455
+ statements.push(createIndexSql(op.table, op.name, op.fields, op.unique, dialect));
456
+ }
457
+ }
458
+ return statements.join("\n\n") + "\n";
459
+ }
460
+ function generateCreateSchemaSql(options) {
461
+ const { schema: schema2, dialect } = options;
462
+ const statements = [];
463
+ statements.push(`-- Better Media schema (${dialect})`);
464
+ if (dialect === "sqlite") {
465
+ statements.push("PRAGMA foreign_keys = ON;");
466
+ }
467
+ for (const [table, def] of Object.entries(schema2)) {
468
+ statements.push(createTableSql(table, def, dialect));
469
+ }
470
+ for (const [table, def] of Object.entries(schema2)) {
471
+ if (!def.indexes) continue;
472
+ for (const index of def.indexes) {
473
+ const indexName = `idx_${table}_${index.fields.map((f) => toDbFieldName(f)).join("_")}`;
474
+ statements.push(createIndexSql(table, indexName, index.fields, index.unique, dialect));
475
+ }
476
+ }
477
+ return statements.join("\n\n") + "\n";
478
+ }
479
+
480
+ // src/database/migration.ts
481
+ async function getMigrations(adapter, options = {}) {
482
+ const migratable = adapter;
483
+ if (typeof migratable.__getMetadata !== "function" || typeof migratable.__executeMigration !== "function") {
484
+ return {
485
+ toBeCreated: [],
486
+ toBeAdded: [],
487
+ operations: [],
488
+ compileMigrations: () => "",
489
+ runMigrations: async () => {
490
+ throw new Error("[BetterMedia] Adapter does not support planned migrations.");
491
+ }
492
+ };
493
+ }
494
+ let dialect = options.dialect;
495
+ if (!dialect && typeof migratable.__getDialect === "function") {
496
+ dialect = migratable.__getDialect();
497
+ }
498
+ const resolvedDialect = dialect || "postgres";
499
+ const metadata = await migratable.__getMetadata();
500
+ const planner = new MigrationPlanner(resolvedDialect);
501
+ const operations = planner.plan(schema, metadata);
502
+ const toBeCreated = operations.filter(
503
+ (op) => op.type === "createTable"
504
+ ).map((op) => ({ table: op.table, fields: Object.keys(op.definition.fields) }));
505
+ const toBeAdded = operations.filter(
506
+ (op) => op.type === "addColumn"
507
+ ).reduce((acc, op) => {
508
+ const existing = acc.find((t) => t.table === op.table);
509
+ if (existing) existing.fields.push(op.field);
510
+ else acc.push({ table: op.table, fields: [op.field] });
511
+ return acc;
512
+ }, []);
513
+ return {
514
+ toBeCreated,
515
+ toBeAdded,
516
+ operations,
517
+ compileMigrations: () => compileMigrationOperationsSql({ operations, dialect: resolvedDialect }),
518
+ runMigrations: async () => {
519
+ for (const op of operations) {
520
+ await migratable.__executeMigration(op);
521
+ }
522
+ }
523
+ };
524
+ }
525
+ async function runMigrations(adapter, options = {}) {
526
+ const mode = options.mode ?? "safe";
527
+ const migratable = adapter;
528
+ if (typeof migratable.__getMetadata === "function" && typeof migratable.__executeMigration === "function") {
529
+ console.log("[BetterMedia] Starting planned migration...");
530
+ const { operations, runMigrations: runPlannedMigrations } = await getMigrations(
531
+ adapter,
532
+ options
533
+ );
534
+ if (operations.length === 0) {
535
+ console.log("[BetterMedia] Database is up to date.");
536
+ return;
537
+ }
538
+ console.log(`[BetterMedia] Plan: ${operations.length} operation(s) to execute.`);
539
+ await runPlannedMigrations();
540
+ console.log("[BetterMedia] Migration completed successfully.");
541
+ return;
542
+ }
543
+ if (typeof migratable.__createTable === "function") {
544
+ for (const [model, definition] of Object.entries(schema)) {
545
+ await migratable.__createTable(model, definition, { mode });
546
+ }
547
+ } else if (typeof migratable.__initCollection === "function") {
548
+ for (const [model, definition] of Object.entries(schema)) {
549
+ await migratable.__initCollection(model, definition, { mode });
550
+ }
551
+ } else {
552
+ console.warn("[BetterMedia] Adapter does not support automatic migrations.");
553
+ }
554
+ }
555
+
556
+ // src/database/hooks.ts
557
+ var runHooks = {
558
+ async beforeCreate(hooks, data, context) {
559
+ let currentData = data;
560
+ const handlers = hooks?.before?.create || [];
561
+ for (const handler of handlers) {
562
+ currentData = await handler(currentData, context);
563
+ }
564
+ return currentData;
565
+ },
566
+ async beforeUpdate(hooks, data, context) {
567
+ let currentData = data;
568
+ const handlers = hooks?.before?.update || [];
569
+ for (const handler of handlers) {
570
+ currentData = await handler(currentData, context);
571
+ }
572
+ return currentData;
573
+ },
574
+ async beforeDelete(hooks, where, context) {
575
+ const handlers = hooks?.before?.delete || [];
576
+ for (const handler of handlers) {
577
+ await handler(where, context);
578
+ }
579
+ },
580
+ async afterCreate(hooks, result, context) {
581
+ const handlers = hooks?.after?.create || [];
582
+ for (const handler of handlers) {
583
+ await handler(result, context);
584
+ }
585
+ },
586
+ async afterUpdate(hooks, result, context) {
587
+ const handlers = hooks?.after?.update || [];
588
+ for (const handler of handlers) {
589
+ await handler(result, context);
590
+ }
591
+ },
592
+ async afterDelete(hooks, where, context) {
593
+ const handlers = hooks?.after?.delete || [];
594
+ for (const handler of handlers) {
595
+ await handler(where, context);
596
+ }
597
+ }
598
+ };
599
+
600
+ // src/database/postgres-utils.ts
601
+ function isPgPoolLike(value) {
602
+ return Boolean(
603
+ value && typeof value === "object" && typeof value.query === "function"
604
+ );
605
+ }
606
+ function toDatabaseAdapter(database, postgresFactory) {
607
+ if (isPgPoolLike(database) && typeof database.create !== "function") {
608
+ if (postgresFactory) return postgresFactory(database);
609
+ throw new Error(
610
+ "[BetterMedia] Postgres pool detected but no adapter factory provided. This typically means you should use the main better-media package."
611
+ );
612
+ }
613
+ return database;
614
+ }
615
+
616
+ // src/adapter/get-adapter.ts
617
+ async function getAdapter(options) {
618
+ const database = options.database ?? await options.createDatabase?.();
619
+ const adapter = database ? toDatabaseAdapter(database) : void 0;
620
+ if (!adapter) {
621
+ throw new Error(
622
+ `[media] Failed to initialize database adapter. Provide "database" or "createDatabase()".`
623
+ );
624
+ }
625
+ return adapter;
626
+ }
627
+
628
+ // src/constants/mime-types.ts
629
+ var EXTENSION_TO_MIME_MAP = {
630
+ ".aac": ["audio/aac"],
631
+ ".abw": ["application/x-abiword"],
632
+ ".apng": ["image/apng"],
633
+ ".arc": ["application/x-freearc"],
634
+ ".avif": ["image/avif"],
635
+ ".avi": ["video/x-msvideo"],
636
+ ".azw": ["application/vnd.amazon.ebook"],
637
+ ".bin": ["application/octet-stream"],
638
+ ".bmp": ["image/bmp"],
639
+ ".bz": ["application/x-bzip"],
640
+ ".bz2": ["application/x-bzip2"],
641
+ ".cda": ["application/x-cdf"],
642
+ ".csh": ["application/x-csh"],
643
+ ".css": ["text/css"],
644
+ ".csv": ["text/csv"],
645
+ ".doc": ["application/msword"],
646
+ ".docx": ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
647
+ ".eot": ["application/vnd.ms-fontobject"],
648
+ ".epub": ["application/epub+zip"],
649
+ ".gz": ["application/gzip", "application/x-gzip"],
650
+ ".gif": ["image/gif"],
651
+ ".htm": ["text/html"],
652
+ ".html": ["text/html"],
653
+ ".ico": ["image/vnd.microsoft.icon"],
654
+ ".ics": ["text/calendar"],
655
+ ".jar": ["application/java-archive"],
656
+ ".jpeg": ["image/jpeg"],
657
+ ".jpg": ["image/jpeg"],
658
+ ".js": ["text/javascript"],
659
+ ".mjs": ["text/javascript"],
660
+ ".json": ["application/json"],
661
+ ".jsonld": ["application/ld+json"],
662
+ ".md": ["text/markdown"],
663
+ ".mid": ["audio/midi", "audio/x-midi"],
664
+ ".midi": ["audio/midi", "audio/x-midi"],
665
+ ".mp3": ["audio/mpeg"],
666
+ ".mp4": ["video/mp4"],
667
+ ".mpeg": ["video/mpeg"],
668
+ ".mpkg": ["application/vnd.apple.installer+xml"],
669
+ ".odp": ["application/vnd.oasis.opendocument.presentation"],
670
+ ".ods": ["application/vnd.oasis.opendocument.spreadsheet"],
671
+ ".odt": ["application/vnd.oasis.opendocument.text"],
672
+ ".oga": ["audio/ogg"],
673
+ ".ogv": ["video/ogg"],
674
+ ".ogx": ["application/ogg"],
675
+ ".opus": ["audio/ogg"],
676
+ ".otf": ["font/otf"],
677
+ ".png": ["image/png"],
678
+ ".pdf": ["application/pdf"],
679
+ ".php": ["application/x-httpd-php"],
680
+ ".ppt": ["application/vnd.ms-powerpoint"],
681
+ ".pptx": ["application/vnd.openxmlformats-officedocument.presentationml.presentation"],
682
+ ".rar": ["application/vnd.rar"],
683
+ ".rtf": ["application/rtf"],
684
+ ".sh": ["application/x-sh"],
685
+ ".svg": ["image/svg+xml"],
686
+ ".tar": ["application/x-tar"],
687
+ ".tif": ["image/tiff"],
688
+ ".tiff": ["image/tiff"],
689
+ ".ts": ["video/mp2t"],
690
+ ".ttf": ["font/ttf"],
691
+ ".txt": ["text/plain"],
692
+ ".vsd": ["application/vnd.visio"],
693
+ ".wav": ["audio/wav"],
694
+ ".weba": ["audio/webm"],
695
+ ".webm": ["video/webm"],
696
+ ".webmanifest": ["application/manifest+json"],
697
+ ".webp": ["image/webp"],
698
+ ".woff": ["font/woff"],
699
+ ".woff2": ["font/woff2"],
700
+ ".xhtml": ["application/xhtml+xml"],
701
+ ".xls": ["application/vnd.ms-excel"],
702
+ ".xlsx": ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
703
+ ".xml": ["application/xml", "text/xml", "application/atom+xml"],
704
+ ".xul": ["application/vnd.mozilla.xul+xml"],
705
+ ".zip": ["application/zip", "application/x-zip-compressed"],
706
+ ".3gp": ["video/3gpp", "audio/3gpp"],
707
+ ".3g2": ["video/3gpp2", "audio/3gpp2"],
708
+ ".7z": ["application/x-7z-compressed"]
709
+ };
710
+
711
+ // src/index.ts
712
+ function sleep(ms) {
713
+ return new Promise((resolve) => setTimeout(resolve, ms));
714
+ }
715
+
716
+ export { EXTENSION_TO_MIME_MAP, HOOK_MODE_CONSTRAINTS, HOOK_NAMES, MigrationPlanner, TrustedMetadataSchema, ValidationResultSchema, VirusScanResultSchema, applyOperationsToMetadata, compileMigrationOperationsSql, converters, deserializeData, deserializeField, generateCreateSchemaSql, getAdapter, getColumnType, getMigrations, isPgPoolLike, markFileContentVerified, matchType, resolveHookMode, runHooks, runMigrations, schema, serializeData, serializeField, sleep, toCamelCase, toDatabaseAdapter, toDbFieldName, toSnakeCase };
717
+ //# sourceMappingURL=index.mjs.map
718
+ //# sourceMappingURL=index.mjs.map