docs: function publish(uri, data, locals) {
    const startTime = process.hrtime(),
      prefix = getPrefix(uri),
      site = getSite(prefix, locals),
      timeoutLimit = timeoutConstant * timeoutPublishCoefficient,
      user = locals && locals.user;
    var publishedMeta; // We need to store some meta a little later
  
    return getPublishData(uri, data)
      .then(publishService.resolvePublishUrl(uri, locals, site))
      .then(({ meta, data: pageData}) => {
        const published = 'published',
          dynamicPage = pageData._dynamic,
          componentList = references.getPageReferences(pageData);
  
        if (!references.isUrl(meta.url) && !dynamicPage) {
          throw new Error('Client: Page must have valid url to publish.');
        }
  
        return bluebird.map(componentList, getRecursivePublishedPutOperations(locals))
          .then(_.flatten) // only one level of flattening needed, because getPutOperations should have flattened its list already
          .then(ops => {
            // convert the data to all @published
            pageData = replacePageReferenceVersions(pageData, published);
  
            // Make public unless we're dealing with a `_dynamic` page
            if (!dynamicPage) {
              addOpToMakePublic(ops, prefix, meta.url, uri);
            }
  
            // Store the metadata if we're at this point
            publishedMeta = meta;
  
            // add page PUT operation last
            return addOp(replaceVersion(uri, published), pageData, ops);
          });
      })
      .then(applyBatch)
      .tap(() => {
        const ms = timer.getMillisecondsSince(startTime);
  
        if (ms > timeoutLimit * 0.5) {
          log('warn', `slow publish ${uri} ${ms}ms`);
        } else {
          log('info', `published ${replaceVersion(uri)} ${ms}ms`);
        }
      })
      .timeout(timeoutLimit, `Page publish exceeded ${timeoutLimit}ms: ${uri}`)
      .then(publishedData => {
        return meta.publishPage(uri, publishedMeta, user).then(() => {
          // Notify the bus
          bus.publish('publishPage', { uri, data: publishedData, user});
  
          notifications.notify(site, 'published', publishedData);
          // Update the meta object
          return publishedData;
        });
      });
  }