Files
memind/mindspace-service.mjs

121 lines
4.0 KiB
JavaScript

import {
buildConversationPackageManifest,
buildConversationStoragePrefix,
createConversationArtifact,
createConversationPackageRecord,
} from './mindspace-conversation-package.mjs';
import {
createMindSpacePublicPageUrl,
createMindSpacePublicUrl,
normalizeMindSpacePublicRelativePath,
} from './mindspace-canonical-url.mjs';
import { normalizeMindSpaceStorageKey } from './mindspace-storage-adapter.mjs';
function serviceError(message, code, details = {}) {
return Object.assign(new Error(message), { code, ...details });
}
function requireStorageAdapter(storageAdapter) {
if (!storageAdapter || typeof storageAdapter.putObject !== 'function') {
throw serviceError('MindSpace storage adapter is required', 'missing_mindspace_storage_adapter');
}
return storageAdapter;
}
function packageStorageKey(packageRecord, relativePath) {
const record = createConversationPackageRecord(packageRecord);
const relative = normalizeMindSpaceStorageKey(relativePath);
return `${record.storagePrefix}/${relative}`;
}
export function createMindSpaceServiceFacade({
storageAdapter,
publicBaseUrl,
conversationPackageBackfill,
conversationPackagePublicHtmlHydrator,
logger = console,
} = {}) {
const storage = requireStorageAdapter(storageAdapter);
return {
createConversationPackage(input) {
return createConversationPackageRecord(input);
},
createConversationArtifact(input) {
return createConversationArtifact(input);
},
buildConversationManifest(input) {
return buildConversationPackageManifest(input);
},
async putConversationManifest({ packageRecord, artifacts = [] }) {
const manifest = buildConversationPackageManifest({ packageRecord, artifacts });
const body = `${JSON.stringify(manifest, null, 2)}\n`;
const writeResult = await this.putPackageObject({
packageRecord: manifest,
relativePath: 'manifest.json',
body,
});
return {
manifest,
storageKey: writeResult.key,
writeResult,
};
},
buildConversationStoragePrefix(input) {
return buildConversationStoragePrefix(input);
},
createPublicPageUrl({ ownerKey, filename }) {
return createMindSpacePublicPageUrl({ publicBaseUrl, ownerKey, filename });
},
createPublicObjectUrl({ ownerKey, relativePath }) {
return createMindSpacePublicUrl({ publicBaseUrl, ownerKey, relativePath });
},
resolvePackageStorageKey({ packageRecord, relativePath }) {
return packageStorageKey(packageRecord, relativePath);
},
async putPackageObject({ packageRecord, relativePath, body }) {
return storage.putObject(packageStorageKey(packageRecord, relativePath), body);
},
async getPackageObject({ packageRecord, relativePath }) {
return storage.getObject(packageStorageKey(packageRecord, relativePath));
},
async statPackageObject({ packageRecord, relativePath }) {
return storage.statObject(packageStorageKey(packageRecord, relativePath));
},
async listPackageObjects({ packageRecord, prefix = '' }) {
const record = createConversationPackageRecord(packageRecord);
const cleanPrefix = prefix ? normalizeMindSpacePublicRelativePath(prefix) : '';
const storagePrefix = cleanPrefix ? `${record.storagePrefix}/${cleanPrefix}` : record.storagePrefix;
return storage.listObjects(storagePrefix);
},
async prepareConversationPackageRead({ user, sessionId } = {}) {
if (typeof conversationPackageBackfill === 'function') {
await conversationPackageBackfill({ user, sessionId }).catch((error) => {
logger?.warn?.('[MindSpace] conversation package DB backfill failed:', error?.message ?? error);
});
}
if (typeof conversationPackagePublicHtmlHydrator === 'function') {
return conversationPackagePublicHtmlHydrator({ user, sessionId });
}
return [];
},
};
}
export const mindspaceServiceInternals = {
serviceError,
packageStorageKey,
};