Capture bounded source resource evidence and expose navigation gap
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m36s
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 6m36s
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
This commit is contained in:
parent
6e07f572a8
commit
0ee31b596e
8 changed files with 373 additions and 3 deletions
53
scripts/bubble-resource-utils.mjs
Normal file
53
scripts/bubble-resource-utils.mjs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/** 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);
|
||||
|
||||
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. */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue