sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
281 lines (280 loc) • 10.7 kB
JavaScript
import chalk from "chalk";
import { MANIFEST_FILENAME, extractManifestSafe } from "./extractManifestAction.js";
import { defineTrace } from "@sanity/telemetry";
import { uniqBy } from "lodash-es";
import { stat, readFile } from "node:fs/promises";
import path, { resolve, join } from "node:path";
const SANITY_WORKSPACE_SCHEMA_ID_PREFIX = "_.schemas", CURRENT_WORKSPACE_SCHEMA_VERSION = "2025-05-01", GenerateManifest = defineTrace({
name: "Manifest generation executed",
version: 1,
description: "Manifest generation was executed"
}), SchemaDeploy = defineTrace({
name: "Schema deploy action executed",
version: 1,
description: "Schema deploy action was executed, either via sanity schema deploy or as sanity deploy"
});
function isDefined(value) {
return value != null;
}
const createManifestReader = ({
manifestDir,
output,
jsonReader = parseJsonFile
}) => {
let parsedManifest;
const parsedWorkspaces = {}, getManifest = async () => {
if (parsedManifest)
return parsedManifest?.parsedJson;
const manifestFile = path.join(manifestDir, MANIFEST_FILENAME), result = await jsonReader(manifestFile);
if (!result)
throw new Error(`Manifest does not exist at ${manifestFile}. To create the manifest file, omit --no-extract-manifest or run "sanity manifest extract" first.`);
return output.print(chalk.gray(`\u21B3 Read manifest from ${manifestFile} (last modified: ${result.lastModified})`)), parsedManifest = result, result.parsedJson;
};
return {
getManifest,
getWorkspaceSchema: async (workspaceName) => {
if (parsedWorkspaces[workspaceName])
return parsedWorkspaces[workspaceName]?.parsedJson;
const manifest = await getManifest();
if (!manifest)
throw Error("Manifest is required to read workspace schema.");
const workspaceManifest = manifest.workspaces.find((workspace) => workspace.name === workspaceName);
if (!workspaceManifest)
throw Error(`No workspace named "${workspaceName}" found in manifest.`);
const workspaceSchemaFile = path.join(manifestDir, workspaceManifest.schema ?? ""), result = await jsonReader(workspaceSchemaFile);
if (!result)
throw Error(`Workspace schema file at "${workspaceSchemaFile}" does not exist.`);
return parsedWorkspaces[workspaceName] = result, result.parsedJson;
}
};
};
function resolveManifestDirectory(workDir, customPath) {
const defaultOutputDir = resolve(join(workDir, "dist")), outputDir = resolve(defaultOutputDir), defaultStaticPath = join(outputDir, "static"), staticPath = customPath ?? defaultStaticPath;
return path.resolve(process.cwd(), staticPath);
}
async function parseJsonFile(filePath) {
let stats;
try {
stats = await stat(filePath);
} catch {
return;
}
const content = await readFile(filePath, "utf-8"), lastModified = stats.mtime.toISOString(), json = JSON.parse(content);
if (!json)
throw new Error(`JSON file "${filePath}" was empty.`);
return {
parsedJson: json,
path: filePath,
lastModified
};
}
const validForIdChars = "a-zA-Z0-9._-", validForIdPattern = new RegExp(`^[${validForIdChars}]+$`, "g"), validForNamesChars = "a-zA-Z0-9_-", validForNamesPattern = new RegExp(`^[${validForNamesChars}]+$`, "g"), requiredInId = SANITY_WORKSPACE_SCHEMA_ID_PREFIX.replace(/[.]/g, "\\."), idIdPatternString = `^${requiredInId}\\.([${validForNamesChars}]+)`, baseIdPattern = new RegExp(`${idIdPatternString}$`), taggedIdIdPattern = new RegExp(`${idIdPatternString}\\.tag\\.([${validForNamesChars}]+)$`);
class FlagValidationError extends Error {
constructor(message) {
super(message), this.name = "FlagValidationError";
}
}
function parseCommonFlags(flags, context, errors) {
const manifestDir = parseManifestDir(flags, errors), verbose = !!flags.verbose, extractManifest = flags["extract-manifest"] ?? !0;
return {
manifestDir: resolveManifestDirectory(context.workDir, manifestDir),
verbose,
extractManifest
};
}
function parseDeploySchemasConfig(flags, context) {
const errors = [], commonFlags = parseCommonFlags(flags, context, errors), workspaceName = parseWorkspace(flags, errors), tag = parseTag(flags, errors), schemaRequired = !!flags["schema-required"];
return assertNoErrors(errors), {
...commonFlags,
workspaceName,
tag,
schemaRequired
};
}
function parseListSchemasConfig(flags, context) {
const errors = [], commonFlags = parseCommonFlags(flags, context, errors), id = parseId(flags, errors), json = !!flags.json;
return assertNoErrors(errors), {
...commonFlags,
json,
id
};
}
function parseDeleteSchemasConfig(flags, context) {
const errors = [], commonFlags = parseCommonFlags(flags, context, errors), ids = parseIds(flags, errors), dataset = parseDataset(flags, errors);
return assertNoErrors(errors), {
...commonFlags,
dataset,
ids
};
}
function assertNoErrors(errors) {
if (errors.length)
throw new FlagValidationError(`Invalid arguments:
${errors.map((error) => ` - ${error}`).join(`
`)}`);
}
function parseIds(flags, errors) {
const parsedIds = parseNonEmptyString(flags, "ids", errors);
if (errors.length)
return [];
const ids = parsedIds.split(",").map((id) => id.trim()).filter((id) => !!id).map((id) => parseWorkspaceSchemaId(id, errors)).filter(isDefined), uniqueIds = uniqBy(ids, "schemaId");
return uniqueIds.length < ids.length && errors.push("ids contains duplicates"), !errors.length && !uniqueIds.length && errors.push("ids contains no valid id strings"), uniqueIds;
}
function parseId(flags, errors) {
const id = flags.id === void 0 ? void 0 : parseNonEmptyString(flags, "id", errors);
if (id)
return parseWorkspaceSchemaId(id, errors)?.schemaId;
}
function parseWorkspaceSchemaId(id, errors) {
const trimmedId = id.trim();
if (!trimmedId.match(validForIdPattern)) {
errors.push(`id can only contain characters in [${validForIdChars}] but found: "${trimmedId}"`);
return;
}
if (trimmedId.startsWith("-")) {
errors.push(`id cannot start with - (dash) but found: "${trimmedId}"`);
return;
}
if (trimmedId.match(/\.\./g)) {
errors.push(`id cannot have consecutive . (period) characters, but found: "${trimmedId}"`);
return;
}
const [fullMatch, workspace] = trimmedId.match(taggedIdIdPattern) ?? trimmedId.match(baseIdPattern) ?? [];
if (!workspace) {
errors.push([`id must either match ${SANITY_WORKSPACE_SCHEMA_ID_PREFIX}.<workspaceName> `, `or ${SANITY_WORKSPACE_SCHEMA_ID_PREFIX}.<workspaceName>.tag.<tag> but found: "${trimmedId}". `, `Note that workspace name characters not in [${validForNamesChars}] has to be replaced with _ for schema id.`].join(""));
return;
}
return {
schemaId: trimmedId,
workspace
};
}
function parseDataset(flags, errors) {
return flags.dataset === void 0 ? void 0 : parseNonEmptyString(flags, "dataset", errors);
}
function parseWorkspace(flags, errors) {
return flags.workspace === void 0 ? void 0 : parseNonEmptyString(flags, "workspace", errors);
}
function parseManifestDir(flags, errors) {
return flags["manifest-dir"] === void 0 ? void 0 : parseNonEmptyString(flags, "manifest-dir", errors);
}
function parseTag(flags, errors) {
if (flags.tag === void 0)
return;
const tag = parseNonEmptyString(flags, "tag", errors);
if (!errors.length) {
if (tag.includes(".")) {
errors.push(`tag cannot contain . (period), but was: "${tag}"`);
return;
}
if (!tag.match(validForNamesPattern)) {
errors.push(`tag can only contain characters in [${validForNamesChars}], but was: "${tag}"`);
return;
}
if (tag.startsWith("-")) {
errors.push(`tag cannot start with - (dash) but was: "${tag}"`);
return;
}
return tag;
}
}
function parseNonEmptyString(flags, flagName, errors) {
const flag = flags[flagName];
return !isString(flag) || !flag ? (errors.push(`${flagName} argument is empty`), "") : flag;
}
function isString(flag) {
return typeof flag == "string";
}
const SCHEMA_PERMISSION_HELP_TEXT = "For multi-project workspaces, set SANITY_AUTH_TOKEN environment variable to a token with access to the workspace projects.";
async function ensureManifestExtractSatisfied(args) {
const {
schemaRequired,
extractManifest,
manifestDir,
manifestExtractor,
output,
telemetry
} = args;
if (!extractManifest)
return !0;
const trace = telemetry.trace(GenerateManifest, {
manifestDir,
schemaRequired
});
try {
return trace.start(), await manifestExtractor(manifestDir), trace.complete(), !0;
} catch (err) {
if (trace.error(err), schemaRequired || err instanceof FlagValidationError)
throw err;
return output.print(chalk.gray(`\u21B3 Failed to extract manifest:
${err.message}`)), !1;
}
}
function createManifestExtractor(context) {
return async (manifestDir) => {
const error = await extractManifestSafe({
extOptions: {
path: manifestDir
},
groupOrCommand: "extract",
argv: [],
argsWithoutOptions: [],
extraArguments: []
}, context);
if (!context.safe && error)
throw error;
};
}
function createSchemaApiClient(apiClient) {
const client = apiClient({
requireUser: !0,
requireProject: !0
}).withConfig({
apiVersion: "v2025-03-01",
useCdn: !1
}), projectId = client.config().projectId, dataset = client.config().dataset;
if (!projectId) throw new Error("Project ID is not defined");
if (!dataset) throw new Error("Dataset is not defined");
return {
client,
projectId,
dataset
};
}
function getProjectIdDatasetsOutString(projectIdDatasets) {
return projectIdDatasets.length === 1 ? `${projectIdDatasetPair(projectIdDatasets[0])}` : `${getStringArrayOutString(projectIdDatasets.map(projectIdDatasetPair))}`;
}
function projectIdDatasetPair(pair) {
return JSON.stringify({
projectId: pair.projectId,
dataset: pair.dataset
});
}
function getStringArrayOutString(array) {
return `[${array.map((d) => `"${d}"`).join(",")}]`;
}
function getStringList(array) {
return array.map((s) => `- ${s}`).join(`
`);
}
export {
CURRENT_WORKSPACE_SCHEMA_VERSION,
FlagValidationError,
SANITY_WORKSPACE_SCHEMA_ID_PREFIX,
SCHEMA_PERMISSION_HELP_TEXT,
SchemaDeploy,
createManifestExtractor,
createManifestReader,
createSchemaApiClient,
ensureManifestExtractSatisfied,
getProjectIdDatasetsOutString,
getStringList,
isDefined,
parseDeleteSchemasConfig,
parseDeploySchemasConfig,
parseListSchemasConfig,
projectIdDatasetPair,
validForNamesChars,
validForNamesPattern
};
//# sourceMappingURL=schemaStoreOutStrings.js.map