Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m29s
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a192e9-c28b-7533-bf25-e2341364c22a
78 lines
3.9 KiB
JavaScript
78 lines
3.9 KiB
JavaScript
/** Select typed content resources; never retain user/session envelopes or raw responses. */
|
|
import {normalizeBubbleRef} from './bubble-corpus-utils.mjs';
|
|
|
|
export const RESOURCE_TYPES = new Set(['custom.scrap', 'custom.relattachment', 'custom.attachment']);
|
|
const validId = value => typeof value === 'string' && /^[0-9]+x[0-9]+$/.test(value);
|
|
|
|
/** Bind observed attachment relations to child articles and their original files. */
|
|
export function attachmentRows(parent, resources, articles) {
|
|
const rows=[];
|
|
for (const [key,versions] of Object.entries(resources.records)) {
|
|
if (!key.startsWith('custom.relattachment:')) continue;
|
|
if (!versions.some(r=>normalizeBubbleRef(r.to_custom_article)===parent)) continue;
|
|
if (versions.length!==1) throw new Error('attachment_relation_drift');
|
|
const relation=versions[0], id=normalizeBubbleRef(relation._chunk_custom_article);
|
|
const child=articles[id];
|
|
if (!validId(id)||!child||normalizeBubbleRef(child.context_custom_article)!==parent)
|
|
throw new Error('attachment_child_binding');
|
|
const source=child.logo_image;
|
|
if (typeof source!=='string') throw new Error('attachment_file_missing');
|
|
const url=new URL(source.startsWith('//')?'https:'+source:source);
|
|
if (url.protocol!=='https:'||url.username||url.password||url.port||url.hash||
|
|
!/^[a-f0-9]{32}\.cdn\.bubble\.io$/.test(url.hostname))
|
|
throw new Error('attachment_file_origin');
|
|
rows.push({relation_id:normalizeBubbleRef(relation._id),article_id:id,
|
|
url:url.href,name:child.nomer_text||'',created_at:child['Created Date'],
|
|
modified_at:child['Modified Date']});
|
|
}
|
|
if (rows.length>100) throw new Error('attachment_count_bound');
|
|
return rows;
|
|
}
|
|
|
|
export function resourceParents(articles) {
|
|
return Object.entries(articles).filter(([,a]) => a.hasattachments_boolean === true ||
|
|
(Object.hasOwn(a, '_list_custom_scrap') &&
|
|
!(Array.isArray(a._list_custom_scrap) && a._list_custom_scrap.length === 0)))
|
|
.map(([id]) => normalizeBubbleRef(id)).filter(validId).sort();
|
|
}
|
|
|
|
export function ingestResourceBody(text, output) {
|
|
const visit = obj => {
|
|
if (Array.isArray(obj)) { obj.forEach(visit); return; }
|
|
if (!obj || typeof obj !== 'object') return;
|
|
// Typed initialization is a terminal envelope. Never walk into unrelated
|
|
// user/session data, even if it contains something resembling a resource.
|
|
if (typeof obj.type === 'string' && Object.hasOwn(obj, 'data')) {
|
|
const id = normalizeBubbleRef(obj.id), data = obj.data;
|
|
if (!RESOURCE_TYPES.has(obj.type) || !validId(id)) return;
|
|
if (data && typeof data === 'object' && !Array.isArray(data) &&
|
|
Object.keys(data).length === 0) {
|
|
output.empty[`${obj.type}:${id}`] = {type:obj.type,id};
|
|
} else if (data && data._type === obj.type && normalizeBubbleRef(data._id) === id) {
|
|
visit({_type:obj.type,_id:id,_source:data});
|
|
}
|
|
return;
|
|
}
|
|
if (RESOURCE_TYPES.has(obj._type)) {
|
|
const id = normalizeBubbleRef(obj._id), data = obj._source;
|
|
if (validId(id) && data && typeof data === 'object' && !Array.isArray(data) &&
|
|
(!data._id || normalizeBubbleRef(data._id) === id) &&
|
|
(!data._type || data._type === obj._type)) {
|
|
const key = `${obj._type}:${id}`;
|
|
// Keep source observations, not a guessed union of different versions.
|
|
const observed = {_type:obj._type,_id:id,...data};
|
|
const versions = output.records[key] ||= [];
|
|
if (!versions.some(v => JSON.stringify(v) === JSON.stringify(observed))) versions.push(observed);
|
|
}
|
|
return;
|
|
}
|
|
if (Array.isArray(obj.responses)) obj.responses.forEach(visit);
|
|
if (obj.hits && Array.isArray(obj.hits.hits)) obj.hits.hits.forEach(visit);
|
|
};
|
|
try { visit(JSON.parse(text)); }
|
|
catch {
|
|
for (const line of text.split('\n')) {
|
|
try { visit(JSON.parse(line)); } catch { /* Not a JSON record. */ }
|
|
}
|
|
}
|
|
}
|