lib/clay-control-presentation-binding.js
Rama main · réplica de lectura del repositorio original.
var path = require('path');
var presentation = require('./clay-control-presentation');
function bindPresentation(ctx) {
function stamp(mate) { return mate && JSON.stringify([mate.id, mate.createdBy || null, mate.createdAt || null, mate.builtinKey || null]); }
var sourceRoot, sourceIdentity;
var sourceOrigin = ctx.session.sessionOriginId, sourceGeneration = ctx.session._sdkQueryGeneration;
var sourceStamp = stamp(ctx.resolveMate(ctx.ownerId, ctx.mateId));
function sourceValid() {
ctx.authorize();
if (!sourceOrigin || ctx.session.sessionOriginId !== sourceOrigin || ctx.session._sdkQueryGeneration !== sourceGeneration || stamp(ctx.resolveMate(ctx.ownerId, ctx.mateId)) !== sourceStamp) throw new Error('The exact presentation source changed.');
if (ctx.session._pendingMessageDrainPaused) throw new Error('The source was stopped.');
var project = ctx.getProjects().get(ctx.sourceSlug), status = project.getStatus();
if (ctx.getMateDir && path.resolve(status.path) !== path.resolve(ctx.getMateDir(ctx.ownerId || 'default', ctx.mateId))) throw new Error('Source directory changed.');
var identity = project.getMateChatControl && project.getMateChatControl().validate(ctx.session);
if (sourceRoot !== undefined && (sourceRoot !== status.path || sourceIdentity !== identity)) throw new Error('Source runtime identity changed.');
sourceRoot = status.path; sourceIdentity = identity;
}
var source = { sourceSessionOriginId: sourceOrigin, sourceMateStamp: sourceStamp, sourceQueryGeneration: sourceGeneration || 0 };
var engine = presentation.attachControlPresentation({ session: ctx.session, authorize: ctx.authorize,
open: function (description) {
var service = ctx.getOwnerUi && ctx.getOwnerUi();
var principal = ctx.authorize();
return service && service.activity && service.activity(principal.userId || null, ctx.sourceSlug, ctx.session.localId, ctx.mateId, description.viewer, source, sourceValid, description.navigationTarget || description.target);
}
});
function describe(domain, bucket, args) {
var entry = bucket && bucket.targets.get(args.targetToken);
if (!entry) return null;
var op = entry.op || entry.intent || {}, live = entry.live || entry.initial || entry.snapshot && entry.snapshot.live || {};
var project = entry.project || entry.runtime || live.project || entry.snapshot && entry.snapshot.project;
var session = entry.session || live.session;
var slug = entry.slug || live.slug || op.projectSlug || entry.intent && entry.intent.projectSlug;
if (!slug && project) ctx.getProjects().forEach(function (item, key) { if (item === project) slug = key; });
var initialStatus = project && project.getStatus(), root = initialStatus && initialStatus.path;
var action = args.action || op.action;
// These are fixed adapter identities, never selectors supplied by a tool.
var target = require('./clay-control-target-projection').describe(domain, entry, project, slug, session);
function targetValid(exact, removed) {
sourceValid();
var principal = ctx.authorize();
if (exact && entry.mate && JSON.stringify(ctx.resolveMate(ctx.ownerId, entry.mate.id)) !== JSON.stringify(entry.mate)) throw new Error('Target Mate revision changed.');
if (exact && entry.presentationValid) entry.presentationValid();
if (!project) return;
if (removed && !ctx.getProjects().has(slug)) return;
if (ctx.getProjects().get(slug) !== project || project.getStatus().path !== root) throw new Error('Target runtime changed.');
var current = project.getStatus();
if (current.isMate) {
var mate = ctx.resolveMate(ctx.ownerId, current.mateId);
if (!mate || mate.lifecycleDeletion || current.projectOwnerId !== ctx.ownerId || ctx.multiUser() && mate.createdBy !== ctx.ownerId || ctx.getMateDir && path.resolve(current.path) !== path.resolve(ctx.getMateDir(ctx.ownerId || 'default', mate.id))) throw new Error('Target Mate revoked.');
} else {
var decision = ctx.access.evaluate(principal, current);
if (!decision.owned || !decision.accessible || ctx.canUsePath && !ctx.canUsePath(principal.userId, current.path)) throw new Error('Target project access changed.');
if (current.isWorktree) {
var parent = ctx.getProjects().get(current.parentSlug), parentStatus = parent && parent.getStatus();
if (!parentStatus || !ctx.access.evaluate(principal, parentStatus).owned || !ctx.access.evaluate(principal, parentStatus).accessible) throw new Error('Parent access changed.');
}
}
if (session && !(removed && !project.getSessionManager().sessions.has(session.localId)) && (project.getSessionManager().sessions.get(session.localId) !== session || !removed && (session.hidden || session.destroying) || (session.ownerId || null) !== (principal.userId || null))) throw new Error('Target conversation changed.');
}
var viewSession = domain === 'screen' && op.action === 'split_view' && entry.snapshot.view && entry.snapshot.view.sessions.find(function (item) { return item.sessionOriginId === entry.snapshot.view.activeSessionOriginId; });
return { domain: domain, action: action, target: target,
navigationTarget: viewSession ? { projectSlug: op.projectSlug, sessionId: viewSession.id } : null,
viewer: domain === 'screen' && (op.action === 'navigate' || op.action === 'split_view') ? entry.snapshot.viewer : null,
validSource: sourceValid, removesTarget: action === 'delete' || action === 'remove',
valid: function () { targetValid(true); },
validTerminal: function (removed) { targetValid(false, removed); }
};
}
function track(domain, action, target, valid, phase) {
try {
sourceValid();
var observer, release, description;
var pending = new Promise(function (resolve) { release = resolve; });
var expiry;
engine.wrap(function () { observer = engine.current(); observer.start(phase || 'running'); return pending; }, function () {
description = { domain: domain, action: action, target: target || null, validSource: sourceValid, valid: function () { sourceValid(); if (valid) valid(); }, validTerminal: function () { sourceValid(); if (valid) valid(); } }; return description;
})({});
expiry = setTimeout(function () { release({ outcome: 'uncertain' }); }, 300000); expiry.unref();
return { id: observer.id, guard: function () { sourceValid(); if (valid) valid(); }, focus: observer.focus, target: function (value, check) { description.target = value; if (check) valid = check; }, phase: observer.update, finish: function (result) { clearTimeout(expiry); release(result); }, cancel: function () { clearTimeout(expiry); observer.cancel(); release({ status: 'cancelled' }); } };
} catch (error) { return null; }
}
function decorate(api) {
var methods = { act: ['session', null], actProject: ['project', 'lifecycle'], actMate: ['mate', 'mates'],
actMateKnowledge: ['knowledge', 'knowledge'], actProjectRecords: ['records', 'records'],
actSchedules: ['schedule', 'scheduling'], actMateChat: ['conversation', 'mateChat'],
actWorkspace: ['workspace', 'workspace'], actScreen: ['screen', 'screen'] };
Object.keys(methods).forEach(function (name) {
if (!api[name]) return;
var adapter = methods[name];
api[name] = engine.wrap(api[name], function (args) { return describe(adapter[0], adapter[1] ? ctx.state[adapter[1]] : ctx.state, args); });
});
return api;
}
return { decorate: decorate, review: engine.review, current: engine.current, track: track, source: function () { sourceValid(); return Object.assign({ ownerId: ctx.session.ownerId || null, sourceSlug: ctx.sourceSlug, sourceSessionId: ctx.session.localId, mateId: ctx.mateId, runtimeId: require('./clay-control-target-projection').runtime(ctx.getProjects().get(ctx.sourceSlug).getSessionManager()) }, source); } };
}
module.exports = { bindPresentation: bindPresentation };