Ctrl K
ComunidadGuías…

lib/clay-project-records.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
var crypto = require('crypto');
var authority = require('./canonical-clay-record-authority');
var input = require('./clay-project-record-input');
function fingerprint(value) { return crypto.createHash('sha256').update(JSON.stringify(value)).digest('hex'); }
function text(value, max) { return typeof value === 'string' && value.trim() && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value); }
function attachClayProjectRecords(ctx) {
  var state = ctx.state;
  // This closure is created only after the canonical source query authorizes.
  // Every service call rechecks it, including after commit-verification awaits.
  var token = authority.issue(function (slug) {
    var live = ctx.resolve(slug);
    return Object.assign({}, live, { actor: ctx.actor() });
  });
  function resolve(slug, area) {
    var live = authority.resolve(token, slug), services = ctx.services();
    var service = services && services[area];
    var bound = service && service.bindCanonicalClay({ projectSlug: slug, authority: token });
    if (!bound) throw new Error('Selected project record service is unavailable.');
    return { project: live.project, status: live.status, bound: bound };
  }
  function parse(args, write) {
    ctx.authorize();
    if (!args || Object.keys(args).some(function (key) { return key !== 'projectSlug' && key !== 'operation'; }) || !text(args.projectSlug, 200)) throw new Error('Choose an exact ordinary project and typed record operation.');
    return JSON.parse(JSON.stringify((write ? input.writeSchema : input.readSchema).parse(args.operation)));
  }
  function snapshot(live, op) {
    return op.ref ? live.bound[op.area === 'issues' ? 'readIssue' : 'readLog']({ ref: op.ref }) : null;
  }
  function check(target, exact) {
    var live = resolve(target.slug, target.op.area);
    if (live.project !== target.project || fingerprint([live.status.path, live.status.projectKnowledgeId, live.status.changeSetId, live.status.parentSlug, live.status.branch, live.status.baseCommit, live.status.headCommit]) !== target.context) throw new Error('Selected project records context changed.');
    if (exact) {
      if (Date.now() - target.created > 300000) throw new Error('Record inspection expired.');
      if (fingerprint(snapshot(live, target.op)) !== target.snapshot) throw new Error('Record or comment revision changed. Inspect and review again.');
    }
    return live;
  }
  async function read(args) {
    var op = parse(args, false), live = resolve(args.projectSlug, op.area);
    var result = await live.bound[input.READS[op.area + ':' + op.action]](input.parameters(op));
    var after = resolve(args.projectSlug, op.area);
    if (after.project !== live.project || fingerprint(after.status) !== fingerprint(live.status)) throw new Error('Selected project changed during read.');
    return result;
  }
  function inspect(args) {
    var op = parse(args, true), live = resolve(args.projectSlug, op.area), entry = snapshot(live, op);
    if (entry && (op.area === 'issues' ? entry.revision : entry.revisions) !== op.expectedRevision) throw new Error('Record revision conflict.');
    if (entry && entry.scheduledResult) throw new Error('Scheduled results are server-owned records.');
    if (JSON.stringify(op).length > 1400) throw new Error('The exact record change must fit the existing owner review; use a smaller change.');
    if (state.targets.size >= 100) state.targets.delete(state.targets.keys().next().value);
    var id = 'records-control:' + crypto.randomUUID();
    var target = { slug: args.projectSlug, op: op, project: live.project, context: fingerprint([live.status.path, live.status.projectKnowledgeId, live.status.changeSetId, live.status.parentSlug, live.status.branch, live.status.baseCommit, live.status.headCommit]), snapshot: fingerprint(entry), created: Date.now() };
    target.presentationValid = function () { check(target, true); }; state.targets.set(id, target);
    return { targetToken: id, projectSlug: args.projectSlug, operation: op, expiresInSeconds: 300 };
  }
  function act(args) {
    ctx.authorize();
    if (!args || Object.keys(args).some(function (key) { return ['targetToken', 'requestId', 'why'].indexOf(key) < 0; }) || !text(args.targetToken, 100) || !text(args.requestId, 128) || !text(args.why, 300)) throw new Error('Use an exact record token, requestId and reason.');
    args = Object.assign({}, args);
    var target = state.targets.get(args.targetToken), key = fingerprint(args), previous = state.requests.get(args.requestId);
    if (!target) throw new Error('Inspect the exact record change first.');
    if (previous) { if (previous.key !== key) throw new Error('requestId already names another record change.'); return previous.promise.then(function (result) { check(target, false); return result; }); }
    if (state.requests.size >= 100) throw new Error('Too many record requests in this query.');
    var persisted = false;
    var promise = Promise.resolve().then(async function () {
      check(target, true);
      var question = 'Apply this exact project record change:\n' + JSON.stringify(target.op) + '\nProject: ' + target.slug + '\nWhy: ' + args.why;
      if (question.length > 2000) throw new Error('The exact change must fit the existing owner review.');
      var response = await ctx.review(question, function () { check(target, true); });
      if (response.status !== 'submitted' || !response.answers.control || response.answers.control.length !== 1 || response.answers.control[0] !== 'Apply exact change') return { status: 'cancelled', persisted: false };
      var live = check(target, true);
      persisted = 'unknown';
      var result = await live.bound[input.WRITES[target.op.area + ':' + target.op.action]](input.parameters(target.op));
      persisted = true; check(target, false);
      if (target.op.area === 'issues' && live.project.notifyIssueUpdate) live.project.notifyIssueUpdate(result, target.op.action === 'delete' ? 'issue_deleted' : undefined);
      if (target.op.area === 'logs' && live.project.notifyLogUpdate) live.project.notifyLogUpdate(result, target.op.action === 'review' ? target.op.reviewAction : target.op.action);
      return { status: 'completed', persisted: true, result: result };
    }).catch(function (error) { return { status: 'failed', persisted: persisted, retrySafe: false, error: error.message }; });
    state.requests.set(args.requestId, { key: key, promise: promise });
    return promise;
  }
  return { readProjectRecords: read, inspectProjectRecordChange: inspect, actProjectRecords: act };
}
module.exports = { attachClayProjectRecords: attachClayProjectRecords };