UNPKG

@sap/cf-tools

Version:
628 lines 27.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.cfApi = exports.cfGetApps = exports.cfGetServiceInstancesList = exports.cfGetInstanceKeyParameters = exports.cfGetInstanceCredentials = exports.cfGetServiceKeys = exports.cfLogout = exports.cfGetServicePlans = exports.cfGetTarget = exports.cfGetAuthToken = exports.cfGetInstanceMetadata = exports.cfBindLocalUps = exports.cfBindLocalServices = exports.cfGetSpaceServices = exports.cfGetServices = exports.cfGetTargets = exports.cfSetOrgSpace = exports.cfGetManagedServiceInstances = exports.cfGetServiceInstances = exports.cfGetServicePlansList = exports.cfGetAvailableSpaces = exports.cfGetAvailableOrgs = exports.cfLogin = exports.cfCreateUpsInstance = exports.cfCreateService = exports.cfGetUpsInstances = exports.clearCacheServiceInstances = exports.resolveEndpoint = void 0; const comment_json_1 = require("comment-json"); const _ = require("lodash"); const cli_1 = require("./cli"); const messages_1 = require("./messages"); const types_1 = require("./types"); const utils_1 = require("./utils"); const url_1 = require("url"); const baseParams = [ types_1.eFilters.page, types_1.eFilters.per_page, types_1.eFilters.oder_by, types_1.eFilters.label_selector, types_1.eFilters.created_ats, types_1.eFilters.updated_ats, ]; const resourceServiceInstances = { name: "service_instances", params: _.uniq(_.concat(baseParams, [ types_1.eFilters.names, types_1.eFilters.type, types_1.eFilters.space_guids, types_1.eFilters.organization_guids, types_1.eFilters.service_plan_guids, types_1.eFilters.service_plan, types_1.eFilters.service_plan_names, ])), }; const resourceOrganizations = { name: "organizations", params: _.uniq(_.concat(baseParams, [types_1.eFilters.names, types_1.eFilters.guids])), }; const resourceSpaces = { name: "spaces", params: _.uniq(_.concat(baseParams, [types_1.eFilters.names, types_1.eFilters.guids, types_1.eFilters.organization_guids, types_1.eFilters.include])), }; const resourceServicePlan = { name: "service_plan", params: _.uniq(_.concat(baseParams, [ types_1.eFilters.names, types_1.eFilters.guids, types_1.eFilters.available, types_1.eFilters.broker_catalog_ids, types_1.eFilters.space_guids, types_1.eFilters.organization_guids, types_1.eFilters.service_broker_guids, types_1.eFilters.service_broker_names, types_1.eFilters.service_offering_guids, types_1.eFilters.service_offering_names, types_1.eFilters.service_instance_guids, types_1.eFilters.include, ])), }; const resourceServiceOfferings = { name: "service_offerings", params: _.uniq(_.concat(baseParams, [ types_1.eFilters.names, types_1.eFilters.available, types_1.eFilters.service_broker_guids, types_1.eFilters.service_broker_names, types_1.eFilters.space_guids, types_1.eFilters.organization_guids, ])), }; const resourceServiceCredentialsBinding = { name: "service_credential_bindings", params: _.uniq(_.concat(baseParams, [ types_1.eFilters.names, types_1.eFilters.guids, types_1.eFilters.include, types_1.eFilters.service_instance_guids, types_1.eFilters.broker_catalog_ids, types_1.eFilters.space_guids, types_1.eFilters.service_instance_names, types_1.eFilters.app_guids, types_1.eFilters.app_names, types_1.eFilters.service_plan_names, types_1.eFilters.service_offering_guids, types_1.eFilters.service_offering_names, types_1.eFilters.type, ])), }; const resourceApps = { name: "apps", params: _.uniq(_.concat(baseParams, [ types_1.eFilters.names, types_1.eFilters.space_guids, types_1.eFilters.organization_guids, types_1.eFilters.guids, types_1.eFilters.include, ])), }; function evaluateResponse(data) { if (_.size(_.get(data, "errors"))) { throw new Error(`${_.get(data, ["errors", "0", "detail"])} [code: ${_.get(data, ["errors", "0", "code"])} title: ${_.get(data, [ "errors", "0", "title", ])}]`); } return data; } async function resolveEndpoint(query) { try { return /http/.test(new url_1.URL(query).protocol) ? _.replace(query, (await cfGetTarget(true))["api endpoint"], "") : query; } catch (e) { return query; } } exports.resolveEndpoint = resolveEndpoint; let cacheServiceInstanceTypes = {}; function clearCacheServiceInstances() { cacheServiceInstanceTypes = {}; } exports.clearCacheServiceInstances = clearCacheServiceInstances; function evaluateQueryFilters(query, resource) { _.each(query === null || query === void 0 ? void 0 : query.filters, (filter) => { if (!resource.params.includes(filter.key)) { throw new Error(messages_1.messages.not_allowed_filter(filter.key, resource.name)); } }); } const ENTITY_STATE_INPROGRESS = "in progress"; const ENTITY_STATE_FAILED = "failed"; function composeQuery(query) { query = (0, utils_1.ensureQuery)(query); function _generate_statement(filter) { const value = _.get(filter, "value"); if (value) { return filter.op === types_1.eOperation.fields ? `${filter.op}[${filter.key}]=${value}` : `${filter.key}` + (filter.op ? `[${filter.op}]` : ``) + `=${value}`; } } function _queryFilters(filters) { return _.compact(_.values(_.map(filters, _generate_statement))); } function _queryParams(object) { return _.compact(_.map(_.keys(object), (key) => { const value = _.get(object, key); if (value) { return `${key}=${value}`; } })); } return _.compact(_.concat(_queryFilters(query.filters).join("&"), _queryParams(_.omit(query, "filters")))).join("&"); } function waitForEntity(resolve, reject, resource, attempt, maxNumberOfAttemps, jobFunction, progress) { if (_.size(_.get(resource, "errors"))) { reject(new Error(messages_1.messages.service_creation_failed(_.get(resource, ["errors", "0", "detail"])))); return; } if (attempt < maxNumberOfAttemps) { if (progress.cancelToken.isCancellationRequested) { reject(new Error(messages_1.messages.create_service_canceled_by_requester)); return; } const state = _.get(resource, "last_operation.state", ENTITY_STATE_INPROGRESS); if (state === ENTITY_STATE_INPROGRESS) { progress.progress.report({ message: `\n${messages_1.messages.service_creation_started}`, increment: Math.floor((1 / maxNumberOfAttemps) * 100), }); setTimeout(() => { jobFunction() .then((retriedResource) => { waitForEntity(resolve, reject, retriedResource, attempt + 1, maxNumberOfAttemps, jobFunction, progress); }) .catch((error) => { reject(error); }); }, 2000); } else if (state === ENTITY_STATE_FAILED) { reject(new Error(messages_1.messages.failed_creating_entity(_.get(resource, "last_operation.description"), (0, utils_1.getName)(resource)))); } else { progress.progress.report({ message: `\n${messages_1.messages.service_creation_started}`, increment: 100 }); resolve(resource); } } else { reject(new Error(messages_1.messages.exceed_number_of_attempts((0, utils_1.getName)(resource)))); } } async function execQuery(args, fncParse, reverseErrorOrder) { const cliResult = await cli_1.Cli.execute(args.query, args.options, args.token); if (cliResult.exitCode !== 0) { throw new Error(reverseErrorOrder ? cliResult.stdout || cliResult.stderr || cliResult.error : cliResult.error || cliResult.stderr || cliResult.stdout); } return fncParse ? await fncParse(evaluateResponse((0, comment_json_1.parse)(cliResult.stdout))) : cliResult.stdout || cliResult.stderr; } async function execTotal(args, fncParse) { const collection = []; let query = args.query; while (query) { const result = (0, comment_json_1.parse)(await execQuery({ query: ["curl", await resolveEndpoint(query)], options: args.options, token: args.token })); for (const resource of _.get(result, "resources", [])) { collection.push(fncParse ? await fncParse(resource, _.get(result, "included")) : resource); } query = _.get(result, ["pagination", "next", "href"]); } return _.compact(collection); } async function getServiceInstance(query, token) { evaluateQueryFilters(query, resourceServiceInstances); query = await (0, utils_1.padQuerySpace)(query, [{ key: types_1.eFilters.type, value: types_1.eServiceTypes.managed }]); const result = await execTotal({ query: `/v3/service_instances?${composeQuery(query)}`, token }); if (_.size(result) >= 1) { return _.head(result); } throw new Error(messages_1.messages.service_not_found(decodeURIComponent(_.get(_.find(query.filters, ["key", types_1.eFilters.names]), "value")) || "unknown")); } async function getUpsCredentials(instanceGuid, token) { return execQuery({ query: ["curl", `/v3/service_instances/${instanceGuid}/credentials`], token }, (data) => data); } function resolveCfResource(data, service) { return _.merge({ name: (0, utils_1.getName)(data), guid: (0, utils_1.getGuid)(data), description: (0, utils_1.getDescription)(data), }, service ? { service_offering: { guid: (0, utils_1.getGuid)(service), description: (0, utils_1.getDescription)(service), name: (0, utils_1.getName)(service), }, } : {}); } function getCachedServicePlan(plan) { if (!cacheServiceInstanceTypes[plan.guid]) { cacheServiceInstanceTypes[plan.guid] = execQuery({ query: ["curl", `/v3/service_plans/${plan.guid}?include=service_offering`] }, (data) => { return Promise.resolve(resolveCfResource(data, _.find(_.get(data, ["included", "service_offerings"]), [ "guid", _.get(data, ["relationships", "service_offering", "data", "guid"]), ]))); }); } return cacheServiceInstanceTypes[plan.guid]; } function getServiceInstanceItem(item) { const planGuid = _.get(item, ["relationships", "service_plan", "data", "guid"]); return Promise.resolve({ guid: (0, utils_1.getGuid)(item), label: (0, utils_1.getName)(item), tags: (0, utils_1.getTags)(item), serviceName: (0, utils_1.isUpsType)(item) ? Promise.resolve({ service_offering: { name: types_1.eServiceTypes.user_provided }, name: "" }) : getCachedServicePlan({ guid: planGuid }) .then((plan) => plan) .catch(() => { return {}; }), plan_guid: planGuid, credentials: (0, utils_1.isUpsType)(item) ? getUpsCredentials((0, utils_1.getGuid)(item)) .then((data) => data) .catch(() => { return {}; }) : Promise.resolve(), }); } async function resolveServiceInstances(results) { const queries = _.concat(_.map(results, "serviceName"), _.map(results, "credentials")); if (!_.size(queries)) { return []; } return Promise.all(queries).then(async () => { const instances = []; for (const result of results) { const serviceName = await _.get(result, "serviceName"); instances.push({ guid: (0, utils_1.getGuid)(result), label: (0, utils_1.getLabel)(result), serviceName: _.get(serviceName, ["service_offering", "name"], "unknown"), plan_guid: _.get(result, "plan_guid"), plan: _.get(serviceName, "name", "unknown"), tags: _.get(result, "tags"), credentials: await result.credentials, }); } return _.compact(instances); }); } async function cfGetUpsInstances(query, token) { evaluateQueryFilters(query, resourceServiceInstances); query = await (0, utils_1.padQuerySpace)(query, [{ key: types_1.eFilters.type, value: types_1.eServiceTypes.user_provided }]); return resolveServiceInstances(await execTotal({ query: `/v3/service_instances?${composeQuery(query)}`, token }, async (info) => getServiceInstanceItem(info))); } exports.cfGetUpsInstances = cfGetUpsInstances; async function cfCreateService(planGuid, instanceName, params, tags, progress, maxNumberOfAttemps) { const spaceGuid = await (0, utils_1.getSpaceGuidThrowIfUndefined)(); maxNumberOfAttemps = _.isNil(maxNumberOfAttemps) ? 45 : maxNumberOfAttemps; progress = _.defaults(progress, { progress: { report: () => "" } }, { cancelToken: { isCancellationRequested: false, onCancellationRequested: () => "" } }); const request = { type: types_1.eServiceTypes.managed, name: instanceName, relationships: { space: { data: { guid: spaceGuid } }, service_plan: { data: { guid: planGuid } }, }, parameters: params, tags, }; const result = await execQuery({ query: ["curl", "/v3/service_instances", "-d", (0, comment_json_1.stringify)(request), "-X", "POST"], token: progress.cancelToken, }); progress.progress.report({ message: `\n${messages_1.messages.service_creation_started}`, increment: 1 }); const query = { filters: [ { key: types_1.eFilters.names, value: encodeURIComponent(instanceName) }, { key: types_1.eFilters.space_guids, value: spaceGuid }, ], }; return new Promise((resolve, reject) => { waitForEntity(resolve, reject, !_.isEmpty(_.replace(result, "\n", "")) ? (0, comment_json_1.parse)(result) : result, 0, maxNumberOfAttemps, () => getServiceInstance(query, progress.cancelToken), progress); }); } exports.cfCreateService = cfCreateService; async function cfCreateUpsInstance(info) { let spaceGuid = info.space_guid; if (!spaceGuid) { spaceGuid = await (0, utils_1.getSpaceGuidThrowIfUndefined)(); } return evaluateResponse((0, comment_json_1.parse)(await execQuery({ query: [ "curl", `/v3/service_instances`, "-d", (0, comment_json_1.stringify)(_.merge({ name: info.instanceName, type: types_1.eServiceTypes.user_provided, relationships: { space: { data: { guid: spaceGuid } } }, }, info.credentials ? { credentials: info.credentials } : {}, info.route_service_url ? { route_service_url: info.route_service_url } : {}, info.syslog_drain_url ? { syslog_drain_url: info.syslog_drain_url } : {}, info.tags ? { tags: info.tags } : {})), "-X", "POST", ], }))); } exports.cfCreateUpsInstance = cfCreateUpsInstance; async function cfLogin(options) { let result; try { let query = ["login", "-a"]; query = "ssoPasscode" in options ? _.concat(query, [ options.endpoint, "--sso-passcode", options.ssoPasscode, "-o", "no-org-for-now", "-s", "no-space-for-now", ]) : _.concat(query, [ options.endpoint, "-u", options.user, "-p", options.password, "-o", "no-org-for-now", "-s", "no-space-for-now", ]); query = _.concat(query, options.origin ? ["--origin", options.origin] : []); result = await execQuery({ query, options: { env: { CF_COLOR: "false" } }, }, undefined, true); } catch (e) { result = _.get(e, "message", ""); } return result.includes(`Authenticating...${types_1.NEW_LINE}${types_1.OK}`) ? types_1.OK : result; } exports.cfLogin = cfLogin; async function cfGetAvailableOrgs(query) { evaluateQueryFilters(query, resourceOrganizations); const ret = execTotal({ query: `/v3/organizations?${composeQuery(query)}` }, (resource) => { return Promise.resolve({ label: (0, utils_1.getName)(resource), guid: (0, utils_1.getGuid)(resource), }); }); return ret; } exports.cfGetAvailableOrgs = cfGetAvailableOrgs; async function cfGetAvailableSpaces(orgGuid) { const query = (0, utils_1.ensureQuery)(); if (orgGuid) { _.merge(query.filters, [{ key: types_1.eFilters.organization_guids, value: orgGuid }]); } evaluateQueryFilters(query, resourceSpaces); const ret = execTotal({ query: `/v3/spaces?${composeQuery(query)}` }, (resource) => { return Promise.resolve({ label: (0, utils_1.getName)(resource), guid: (0, utils_1.getGuid)(resource), orgGUID: (0, utils_1.getOrgGUID)(resource), }); }); return ret; } exports.cfGetAvailableSpaces = cfGetAvailableSpaces; function resolvePlanInfo(data, service) { return _.merge({ label: (0, utils_1.getName)(data), guid: (0, utils_1.getGuid)(data), description: (0, utils_1.getDescription)(data), }, service ? { service_offering: { guid: (0, utils_1.getGuid)(service), description: (0, utils_1.getDescription)(service), name: (0, utils_1.getName)(service), }, } : {}); } async function cfGetServicePlansList(query, token) { query = await (0, utils_1.padQuerySpace)(query, [{ key: types_1.eFilters.include, value: "service_offering" }]); evaluateQueryFilters(query, resourceServicePlan); return execTotal({ query: `/v3/service_plans?${composeQuery(query)}`, token }, (data, included) => { return Promise.resolve(resolvePlanInfo(data, _.find(_.get(included, "service_offerings"), [ "guid", _.get(data, ["relationships", "service_offering", "data", "guid"]), ]))); }); } exports.cfGetServicePlansList = cfGetServicePlansList; async function cfGetServiceInstances(query, token) { query = await (0, utils_1.padQuerySpace)(query, [ { key: types_1.eFilters.service_plan, value: "guid,name", op: types_1.eOperation.fields }, { key: types_1.eFilters.type, value: types_1.eServiceTypes.managed }, ]); evaluateQueryFilters(query, resourceServiceInstances); return resolveServiceInstances(await execTotal({ query: `/v3/service_instances?${composeQuery(query)}`, token }, (info) => getServiceInstanceItem(info))); } exports.cfGetServiceInstances = cfGetServiceInstances; async function cfGetManagedServiceInstances(query, token) { return cfGetServiceInstances(query, token); } exports.cfGetManagedServiceInstances = cfGetManagedServiceInstances; async function cfSetOrgSpace(org, space) { await execQuery({ query: _.concat(["target", "-o", org], space ? ["-s", space] : []) }); clearCacheServiceInstances(); void cfGetManagedServiceInstances(); } exports.cfSetOrgSpace = cfSetOrgSpace; async function cfGetTargets() { const targets = (await execQuery({ query: ["targets"] })); if (_.includes(targets, "No targets have been saved yet") || _.includes(targets, "is not a registered command")) { return [{ label: types_1.DEFAULT_TARGET, isCurrent: true, isDirty: false }]; } const targetSubstrings = _.compact(_.map(targets.split(types_1.NEW_LINE), (targetSubstring) => targetSubstring.trim())); return _.map(targetSubstrings, (targetSubstring) => { const parentthesisPos = targetSubstring.indexOf("(current"); if (parentthesisPos > 0) { targetSubstring = targetSubstring.substring(0, parentthesisPos); return { label: targetSubstring.trim(), isCurrent: true, isDirty: targetSubstring.includes("modified") }; } return { label: targetSubstring, isCurrent: false, isDirty: false }; }); } exports.cfGetTargets = cfGetTargets; async function cfGetServices(query, cancellationToken) { evaluateQueryFilters(query, resourceServiceOfferings); return execTotal({ query: `/v3/service_offerings?${composeQuery(await (0, utils_1.padQuerySpace)(query))}`, token: cancellationToken }, (service) => { return Promise.resolve({ label: (0, utils_1.getName)(service), service_plans_url: _.get(service, ["links", "service_plans", "href"]), guid: (0, utils_1.getGuid)(service), description: (0, utils_1.getDescription)(service), }); }); } exports.cfGetServices = cfGetServices; async function cfGetSpaceServices(query, spaceGUID, cancellationToken) { return cfGetServices((0, utils_1.padQuery)(query, [{ key: types_1.eFilters.space_guids, value: spaceGUID }]), cancellationToken); } exports.cfGetSpaceServices = cfGetSpaceServices; async function cfBindLocalServices(filePath, instanceNames, tags, serviceKeyNames, serviceKeyParams, quoteVcap) { await execQuery({ query: [ "bind-local", "-path", filePath, "-service-names", ...instanceNames, ...(_.size(tags) ? _.concat(["-tags"], tags) : []), ...(_.size(serviceKeyNames) ? _.concat(["-service-keys"], serviceKeyNames) : []), ...(_.size(serviceKeyParams) ? _.concat(["-params"], _.map(serviceKeyParams, (param) => { return (0, comment_json_1.stringify)(param); })) : []), ...(quoteVcap ? ["-quote-vcap"] : []), ], }); } exports.cfBindLocalServices = cfBindLocalServices; async function cfBindLocalUps(filePath, instanceNames, tags, quoteVcap) { await execQuery({ query: _.concat(["bind-local-ups", "-path", filePath], _.reduce(instanceNames, (result, instanceName) => { result = _.concat(result, [`-service-names`, `${instanceName}`]); return result; }, []), _.reduce(tags, (result, tag) => { result = _.concat(result, [`-tags`, `${tag}`]); return result; }, []), quoteVcap ? ["-quote-vcap"] : []), }); } exports.cfBindLocalUps = cfBindLocalUps; async function cfGetInstanceMetadata(instanceName) { const result = await cfGetServiceInstances(await (0, utils_1.padQuerySpace)({ filters: [ { key: types_1.eFilters.names, value: encodeURIComponent(instanceName) }, { key: types_1.eFilters.type, value: types_1.eServiceTypes.managed }, ], })); if (!_.size(result)) { throw new Error(messages_1.messages.service_not_found(instanceName)); } const serviceInstance = _.head(result); return { serviceName: (0, utils_1.getLabel)(serviceInstance), plan: _.get(serviceInstance, "plan"), plan_guid: _.get(serviceInstance, "plan_guid"), service: _.get(serviceInstance, "serviceName"), }; } exports.cfGetInstanceMetadata = cfGetInstanceMetadata; async function cfGetAuthToken() { return await execQuery({ query: ["oauth-token"] }); } exports.cfGetAuthToken = cfGetAuthToken; async function cfGetTarget(weak) { if (!weak) { await cfGetAuthToken(); } return (0, utils_1.parseRawDictData)(await execQuery({ query: ["target"], options: { env: { CF_COLOR: "false" } } })); } exports.cfGetTarget = cfGetTarget; async function cfGetServicePlans(servicePlansUrl) { return execTotal({ query: servicePlansUrl }, (data) => { return Promise.resolve({ label: (0, utils_1.getName)(data), guid: (0, utils_1.getGuid)(data), description: (0, utils_1.getDescription)(data) }); }); } exports.cfGetServicePlans = cfGetServicePlans; async function cfLogout() { await execQuery({ query: ["logout"] }); } exports.cfLogout = cfLogout; async function cfGetServiceKeys(query, token) { evaluateQueryFilters(query, resourceServiceCredentialsBinding); return execTotal({ query: `/v3/service_credential_bindings?${composeQuery((0, utils_1.padQuery)(query, [{ key: types_1.eFilters.type, value: "key" }]))}`, token, }); } exports.cfGetServiceKeys = cfGetServiceKeys; async function cfGetInstanceCredentials(query, token) { const results = _.map(await cfGetServiceKeys(query, token), (resource) => { return (execQuery({ query: ["curl", `/v3/service_credential_bindings/${(0, utils_1.getGuid)(resource)}/details`], token }, (data) => data) .then((data) => data) .catch(() => { return {}; })); }); return Promise.all(_.compact(results)); } exports.cfGetInstanceCredentials = cfGetInstanceCredentials; async function cfGetInstanceKeyParameters(instanceName) { const instance = await getServiceInstance({ filters: [{ key: types_1.eFilters.names, value: encodeURIComponent(instanceName) }], }); const query = { filters: [{ key: types_1.eFilters.service_instance_guids, value: (0, utils_1.getGuid)(instance) }] }; let keys = await cfGetServiceKeys(query); if (!_.size(keys)) { await cli_1.Cli.execute(["create-service-key", encodeURIComponent(instanceName), "key", "--wait"]); keys = await cfGetServiceKeys((0, utils_1.padQuery)(query, [{ key: types_1.eFilters.names, value: "key" }])); } return (execQuery({ query: ["curl", `/v3/service_credential_bindings/${(0, utils_1.getGuid)(_.head(keys))}/details`] }, (data) => data) .then((data) => data) .catch(() => { return {}; })); } exports.cfGetInstanceKeyParameters = cfGetInstanceKeyParameters; async function cfGetServiceInstancesList(query, token) { query = await (0, utils_1.padQuerySpace)(query, [{ key: types_1.eFilters.service_plan, value: "guid,name", op: types_1.eOperation.fields }]); evaluateQueryFilters(query, resourceServiceInstances); return resolveServiceInstances(await execTotal({ query: `/v3/service_instances?${composeQuery(query)}`, token }, (info) => getServiceInstanceItem(info))); } exports.cfGetServiceInstancesList = cfGetServiceInstancesList; async function cfGetApps(query, token) { evaluateQueryFilters(query, resourceApps); return execTotal({ query: `/v3/apps?${composeQuery(await (0, utils_1.padQuerySpace)(query))}`, token }); } exports.cfGetApps = cfGetApps; async function cfApi(params) { const query = ["api"]; if (params === null || params === void 0 ? void 0 : params.url) { query.push(params.url); } if (params === null || params === void 0 ? void 0 : params.skip_ssl_validation) { query.push("--skip-ssl-validation"); } if (params === null || params === void 0 ? void 0 : params.unset) { query.push("--unset"); } return (0, utils_1.parseRawDictData)(await execQuery({ query: [...query] })); } exports.cfApi = cfApi; //# sourceMappingURL=cf-local.js.map