@better-media/framework 0.1.0 → 0.2.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/README.md +1 -1
- package/dist/index.js +82 -83
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +82 -83
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -1,29 +1,14 @@
|
|
|
1
|
-
import { isPgPoolLike, TrustedMetadataSchema, HOOK_NAMES, schema, serializeData, toDbFieldName, deserializeData, toCamelCase, getColumnType, markFileContentVerified, resolveHookMode } from '@better-media/core';
|
|
2
|
-
export { HOOK_NAMES, MigrationPlanner, applyOperationsToMetadata, compileMigrationOperationsSql, deserializeData, getAdapter, getColumnType, getMigrations, isPgPoolLike, runHooks, runMigrations, schema, serializeData, toCamelCase, toDbFieldName } from '@better-media/core';
|
|
3
1
|
import fs2 from 'fs/promises';
|
|
4
2
|
import { randomUUID } from 'crypto';
|
|
5
3
|
import { memoryJobAdapter } from '@better-media/adapter-jobs';
|
|
6
|
-
import
|
|
4
|
+
import { isPgPoolLike, HOOK_NAMES, schema, serializeData, toDbFieldName, deserializeData, toCamelCase, getColumnType, TrustedMetadataSchema, markFileContentVerified, resolveHookMode } from '@better-media/core';
|
|
5
|
+
export { HOOK_NAMES, MigrationPlanner, applyOperationsToMetadata, compileMigrationOperationsSql, deserializeData, getAdapter, getColumnType, getMigrations, isPgPoolLike, runHooks, runMigrations, schema, serializeData, toCamelCase, toDbFieldName } from '@better-media/core';
|
|
6
|
+
import path2 from 'path';
|
|
7
7
|
import { createWriteStream } from 'fs';
|
|
8
8
|
import os from 'os';
|
|
9
9
|
import { Readable } from 'stream';
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
-
var __esm = (fn, res) => function __init() {
|
|
14
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
15
|
-
};
|
|
16
|
-
var __export = (target, all) => {
|
|
17
|
-
for (var name in all)
|
|
18
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// src/core/lifecycle-engine.ts
|
|
22
|
-
var lifecycle_engine_exports = {};
|
|
23
|
-
__export(lifecycle_engine_exports, {
|
|
24
|
-
LifecycleEngine: () => LifecycleEngine,
|
|
25
|
-
createSecureContext: () => createSecureContext
|
|
26
|
-
});
|
|
11
|
+
// src/index.ts
|
|
27
12
|
function createSecureContext(context, pluginName, namespace, trustLevel, capabilities) {
|
|
28
13
|
const api = {
|
|
29
14
|
emitMetadata(patch) {
|
|
@@ -120,52 +105,48 @@ function createSecureContext(context, pluginName, namespace, trustLevel, capabil
|
|
|
120
105
|
});
|
|
121
106
|
return { proxy, api };
|
|
122
107
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (result.valid === false) return result;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
for (const { name, manifest } of backgroundHandlers) {
|
|
150
|
-
const payload = {
|
|
151
|
-
recordId: context.recordId,
|
|
152
|
-
metadata: JSON.parse(JSON.stringify(context.metadata)),
|
|
153
|
-
file: JSON.parse(JSON.stringify(context.file)),
|
|
154
|
-
storageLocation: JSON.parse(JSON.stringify(context.storageLocation)),
|
|
155
|
-
processing: JSON.parse(JSON.stringify(context.processing)),
|
|
156
|
-
hookName,
|
|
157
|
-
pluginName: name,
|
|
158
|
-
manifest: JSON.parse(JSON.stringify(manifest))
|
|
159
|
-
};
|
|
160
|
-
await this.jobAdapter.enqueue(JOB_QUEUE_NAME, payload);
|
|
161
|
-
}
|
|
108
|
+
|
|
109
|
+
// src/core/lifecycle-engine.ts
|
|
110
|
+
var JOB_QUEUE_NAME = "better-media:background";
|
|
111
|
+
var LifecycleEngine = class {
|
|
112
|
+
constructor(registry, jobAdapter) {
|
|
113
|
+
this.registry = registry;
|
|
114
|
+
this.jobAdapter = jobAdapter;
|
|
115
|
+
}
|
|
116
|
+
async trigger(hookName, context) {
|
|
117
|
+
const handlers = this.registry.get(hookName) ?? [];
|
|
118
|
+
const syncHandlers = handlers.filter((h) => h.mode === "sync");
|
|
119
|
+
const backgroundHandlers = handlers.filter((h) => h.mode === "background");
|
|
120
|
+
for (const { name, fn, manifest } of syncHandlers) {
|
|
121
|
+
const { proxy, api } = createSecureContext(
|
|
122
|
+
context,
|
|
123
|
+
name,
|
|
124
|
+
manifest.namespace,
|
|
125
|
+
manifest.trustLevel,
|
|
126
|
+
manifest.capabilities
|
|
127
|
+
);
|
|
128
|
+
const result = await fn(proxy, api);
|
|
129
|
+
if (result !== void 0 && typeof result === "object" && "valid" in result) {
|
|
130
|
+
if (result.valid === false) return result;
|
|
162
131
|
}
|
|
163
|
-
}
|
|
132
|
+
}
|
|
133
|
+
for (const { name, manifest } of backgroundHandlers) {
|
|
134
|
+
const payload = {
|
|
135
|
+
recordId: context.recordId,
|
|
136
|
+
metadata: JSON.parse(JSON.stringify(context.metadata)),
|
|
137
|
+
file: JSON.parse(JSON.stringify(context.file)),
|
|
138
|
+
storageLocation: JSON.parse(JSON.stringify(context.storageLocation)),
|
|
139
|
+
processing: JSON.parse(JSON.stringify(context.processing)),
|
|
140
|
+
hookName,
|
|
141
|
+
pluginName: name,
|
|
142
|
+
manifest: JSON.parse(JSON.stringify(manifest))
|
|
143
|
+
};
|
|
144
|
+
await this.jobAdapter.enqueue(JOB_QUEUE_NAME, payload);
|
|
145
|
+
}
|
|
164
146
|
}
|
|
165
|
-
}
|
|
147
|
+
};
|
|
166
148
|
|
|
167
149
|
// src/plugins/plugin-registry.ts
|
|
168
|
-
init_lifecycle_engine();
|
|
169
150
|
function createEmptyRegistry() {
|
|
170
151
|
const reg = /* @__PURE__ */ new Map();
|
|
171
152
|
for (const name of HOOK_NAMES) {
|
|
@@ -329,9 +310,6 @@ function hasBackgroundHandlers(registry) {
|
|
|
329
310
|
}
|
|
330
311
|
return false;
|
|
331
312
|
}
|
|
332
|
-
|
|
333
|
-
// src/index.ts
|
|
334
|
-
init_lifecycle_engine();
|
|
335
313
|
async function loadTrustedFromDb(database, recordId) {
|
|
336
314
|
const record = await database.findOne({
|
|
337
315
|
model: "media",
|
|
@@ -400,8 +378,8 @@ async function saveTrustedToDb(database, recordId, fileKey, trusted, initialArgs
|
|
|
400
378
|
}
|
|
401
379
|
}
|
|
402
380
|
async function streamToTempFile(stream, fileKey) {
|
|
403
|
-
const ext =
|
|
404
|
-
const tmpPath =
|
|
381
|
+
const ext = path2.extname(fileKey) || ".bin";
|
|
382
|
+
const tmpPath = path2.join(os.tmpdir(), `better-media-${randomUUID()}${ext}`);
|
|
405
383
|
const nodeStream = stream instanceof Readable ? stream : Readable.fromWeb(stream);
|
|
406
384
|
const writeStream = createWriteStream(tmpPath);
|
|
407
385
|
await new Promise((resolve, reject) => {
|
|
@@ -419,6 +397,10 @@ async function loadFileIntoContext(context, fileHandling) {
|
|
|
419
397
|
const storageWithExtras = storage;
|
|
420
398
|
if (!context.utilities) context.utilities = {};
|
|
421
399
|
const fileContent = {};
|
|
400
|
+
if (context.storageLocation.url && context.storageLocation.url === context.file.key) {
|
|
401
|
+
context.utilities.fileContent = fileContent;
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
422
404
|
let useStream = false;
|
|
423
405
|
if (maxBufferBytes != null && typeof storageWithExtras.getSize === "function" && typeof storageWithExtras.getStream === "function") {
|
|
424
406
|
const size = await storageWithExtras.getSize(fileKey);
|
|
@@ -459,8 +441,22 @@ async function cleanupTempFile(context) {
|
|
|
459
441
|
function buildFileInfo(fileKey, metadata) {
|
|
460
442
|
const mime = metadata.contentType ?? metadata.mimeType ?? metadata["content-type"];
|
|
461
443
|
const size = typeof metadata.size === "number" ? metadata.size : void 0;
|
|
462
|
-
const originalName = metadata.originalName ?? metadata.originalname
|
|
463
|
-
|
|
444
|
+
const originalName = metadata.originalName ?? metadata.originalname ?? (() => {
|
|
445
|
+
try {
|
|
446
|
+
const url = new URL(fileKey);
|
|
447
|
+
return path2.basename(url.pathname);
|
|
448
|
+
} catch {
|
|
449
|
+
return void 0;
|
|
450
|
+
}
|
|
451
|
+
})();
|
|
452
|
+
const ext = originalName ? path2.extname(originalName).toLowerCase() : (() => {
|
|
453
|
+
try {
|
|
454
|
+
const url = new URL(fileKey);
|
|
455
|
+
return path2.extname(url.pathname).toLowerCase();
|
|
456
|
+
} catch {
|
|
457
|
+
return path2.extname(fileKey).toLowerCase();
|
|
458
|
+
}
|
|
459
|
+
})();
|
|
464
460
|
return {
|
|
465
461
|
key: fileKey,
|
|
466
462
|
size,
|
|
@@ -470,12 +466,12 @@ function buildFileInfo(fileKey, metadata) {
|
|
|
470
466
|
checksums: void 0
|
|
471
467
|
};
|
|
472
468
|
}
|
|
473
|
-
function buildStorageLocation(fileKey) {
|
|
469
|
+
function buildStorageLocation(fileKey, referenceUrl) {
|
|
474
470
|
return {
|
|
475
471
|
key: fileKey,
|
|
476
472
|
bucket: void 0,
|
|
477
473
|
region: void 0,
|
|
478
|
-
url:
|
|
474
|
+
url: referenceUrl
|
|
479
475
|
};
|
|
480
476
|
}
|
|
481
477
|
function syncTrustedToFile(context) {
|
|
@@ -508,7 +504,7 @@ var PipelineExecutor = class {
|
|
|
508
504
|
const context = {
|
|
509
505
|
recordId,
|
|
510
506
|
file: buildFileInfo(fileKey, meta),
|
|
511
|
-
storageLocation: buildStorageLocation(fileKey),
|
|
507
|
+
storageLocation: buildStorageLocation(fileKey, appContext.referenceUrl),
|
|
512
508
|
processing: {},
|
|
513
509
|
metadata: { ...meta, ...appContext },
|
|
514
510
|
// Merge for plugins to read backwards-compatibly
|
|
@@ -573,7 +569,7 @@ async function runBackgroundJob(payload, registry, storage, database, jobs, file
|
|
|
573
569
|
size: typeof meta.size === "number" ? meta.size : void 0,
|
|
574
570
|
mimeType: typeof (meta.contentType ?? meta.mimeType ?? meta["content-type"]) === "string" ? meta.contentType ?? meta.mimeType ?? meta["content-type"] : void 0,
|
|
575
571
|
originalName: typeof (meta.originalName ?? meta.originalname) === "string" ? meta.originalName ?? meta.originalname : void 0,
|
|
576
|
-
extension:
|
|
572
|
+
extension: path2.extname(legacyKey).toLowerCase() || void 0
|
|
577
573
|
} : { key: "" });
|
|
578
574
|
const recordId = payloadRecordId ?? file.key ?? "unknown";
|
|
579
575
|
const storageLocation = payloadStorage ?? { key: file.key };
|
|
@@ -602,8 +598,7 @@ async function runBackgroundJob(payload, registry, storage, database, jobs, file
|
|
|
602
598
|
throw new Error(`Handler not found: ${hookName}/${pluginName}`);
|
|
603
599
|
}
|
|
604
600
|
const manifest = handler.manifest;
|
|
605
|
-
const {
|
|
606
|
-
const { proxy, api } = createSecureContext2(
|
|
601
|
+
const { proxy, api } = createSecureContext(
|
|
607
602
|
context,
|
|
608
603
|
pluginName,
|
|
609
604
|
manifest.namespace,
|
|
@@ -905,7 +900,7 @@ async function normalizeInput(input, fileHandling) {
|
|
|
905
900
|
data = Buffer.concat(chunks);
|
|
906
901
|
} else if ("url" in file && file.url) {
|
|
907
902
|
if (file.mode === "reference") {
|
|
908
|
-
|
|
903
|
+
return { metadata, shouldDeleteSource: false, isReference: true, referenceUrl: file.url };
|
|
909
904
|
}
|
|
910
905
|
const response = await fetch(file.url);
|
|
911
906
|
if (!response.ok) throw new Error(`Failed to fetch URL: ${response.statusText}`);
|
|
@@ -941,15 +936,19 @@ function createBetterMedia(config) {
|
|
|
941
936
|
async ingest(input) {
|
|
942
937
|
const normalized = await normalizeInput(input, fileHandling);
|
|
943
938
|
const recordId = randomUUID();
|
|
944
|
-
const finalKey = input.key ?? normalized.metadata.filename ?? recordId;
|
|
939
|
+
const finalKey = input.key ?? normalized.metadata.filename ?? (normalized.isReference ? normalized.referenceUrl : void 0) ?? recordId;
|
|
945
940
|
try {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
finalKey,
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
941
|
+
if (normalized.isReference && normalized.referenceUrl) {
|
|
942
|
+
normalized.metadata.referenceUrl = normalized.referenceUrl;
|
|
943
|
+
} else if (normalized.data) {
|
|
944
|
+
await storage.put(finalKey, normalized.data);
|
|
945
|
+
} else {
|
|
946
|
+
throw new Error("Ingest failed: No data and not a reference.");
|
|
947
|
+
}
|
|
948
|
+
await runPipeline(recordId, finalKey, normalized.metadata, {
|
|
949
|
+
...normalized.metadata.context ?? {},
|
|
950
|
+
referenceUrl: normalized.referenceUrl
|
|
951
|
+
});
|
|
953
952
|
return {
|
|
954
953
|
id: recordId,
|
|
955
954
|
key: finalKey,
|