UNPKG

@atproto/lexicon

Version:

atproto Lexicon schema language library

372 lines 11.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LexiconDefNotFoundError = exports.InvalidLexiconError = exports.ValidationError = exports.lexiconDoc = exports.lexUserType = exports.lexRecord = exports.lexXrpcSubscription = exports.lexXrpcProcedure = exports.lexXrpcQuery = exports.lexXrpcError = exports.lexXrpcSubscriptionMessage = exports.lexXrpcBody = exports.lexXrpcParameters = exports.lexObject = exports.lexToken = exports.lexPrimitiveArray = exports.lexArray = exports.lexBlob = exports.lexRefVariant = exports.lexRefUnion = exports.lexRef = exports.lexIpldType = exports.lexCidLink = exports.lexBytes = exports.lexPrimitive = exports.lexUnknown = exports.lexString = exports.lexStringFormat = exports.lexInteger = exports.lexBoolean = void 0; exports.isValidLexiconDoc = isValidLexiconDoc; exports.isObj = isObj; exports.isDiscriminatedObject = isDiscriminatedObject; exports.parseLexiconDoc = parseLexiconDoc; const zod_1 = require("zod"); const syntax_1 = require("@atproto/syntax"); const util_1 = require("./util"); // primitives // = exports.lexBoolean = zod_1.z .object({ type: zod_1.z.literal('boolean'), description: zod_1.z.string().optional(), default: zod_1.z.boolean().optional(), const: zod_1.z.boolean().optional(), }) .strict(); exports.lexInteger = zod_1.z .object({ type: zod_1.z.literal('integer'), description: zod_1.z.string().optional(), default: zod_1.z.number().int().optional(), minimum: zod_1.z.number().int().optional(), maximum: zod_1.z.number().int().optional(), enum: zod_1.z.number().int().array().optional(), const: zod_1.z.number().int().optional(), }) .strict(); exports.lexStringFormat = zod_1.z.enum([ 'datetime', 'uri', 'at-uri', 'did', 'handle', 'at-identifier', 'nsid', 'cid', 'language', 'tid', 'record-key', ]); exports.lexString = zod_1.z .object({ type: zod_1.z.literal('string'), format: exports.lexStringFormat.optional(), description: zod_1.z.string().optional(), default: zod_1.z.string().optional(), minLength: zod_1.z.number().int().optional(), maxLength: zod_1.z.number().int().optional(), minGraphemes: zod_1.z.number().int().optional(), maxGraphemes: zod_1.z.number().int().optional(), enum: zod_1.z.string().array().optional(), const: zod_1.z.string().optional(), knownValues: zod_1.z.string().array().optional(), }) .strict(); exports.lexUnknown = zod_1.z .object({ type: zod_1.z.literal('unknown'), description: zod_1.z.string().optional(), }) .strict(); exports.lexPrimitive = zod_1.z.discriminatedUnion('type', [ exports.lexBoolean, exports.lexInteger, exports.lexString, exports.lexUnknown, ]); // ipld types // = exports.lexBytes = zod_1.z .object({ type: zod_1.z.literal('bytes'), description: zod_1.z.string().optional(), maxLength: zod_1.z.number().optional(), minLength: zod_1.z.number().optional(), }) .strict(); exports.lexCidLink = zod_1.z .object({ type: zod_1.z.literal('cid-link'), description: zod_1.z.string().optional(), }) .strict(); exports.lexIpldType = zod_1.z.discriminatedUnion('type', [exports.lexBytes, exports.lexCidLink]); // references // = exports.lexRef = zod_1.z .object({ type: zod_1.z.literal('ref'), description: zod_1.z.string().optional(), ref: zod_1.z.string(), }) .strict(); exports.lexRefUnion = zod_1.z .object({ type: zod_1.z.literal('union'), description: zod_1.z.string().optional(), refs: zod_1.z.string().array(), closed: zod_1.z.boolean().optional(), }) .strict(); exports.lexRefVariant = zod_1.z.discriminatedUnion('type', [exports.lexRef, exports.lexRefUnion]); // blobs // = exports.lexBlob = zod_1.z .object({ type: zod_1.z.literal('blob'), description: zod_1.z.string().optional(), accept: zod_1.z.string().array().optional(), maxSize: zod_1.z.number().optional(), }) .strict(); // complex types // = exports.lexArray = zod_1.z .object({ type: zod_1.z.literal('array'), description: zod_1.z.string().optional(), items: zod_1.z.discriminatedUnion('type', [ // lexPrimitive exports.lexBoolean, exports.lexInteger, exports.lexString, exports.lexUnknown, // lexIpldType exports.lexBytes, exports.lexCidLink, // lexRefVariant exports.lexRef, exports.lexRefUnion, // other exports.lexBlob, ]), minLength: zod_1.z.number().int().optional(), maxLength: zod_1.z.number().int().optional(), }) .strict(); exports.lexPrimitiveArray = exports.lexArray.merge(zod_1.z .object({ items: exports.lexPrimitive, }) .strict()); exports.lexToken = zod_1.z .object({ type: zod_1.z.literal('token'), description: zod_1.z.string().optional(), }) .strict(); exports.lexObject = zod_1.z .object({ type: zod_1.z.literal('object'), description: zod_1.z.string().optional(), required: zod_1.z.string().array().optional(), nullable: zod_1.z.string().array().optional(), properties: zod_1.z.record(zod_1.z.discriminatedUnion('type', [ exports.lexArray, // lexPrimitive exports.lexBoolean, exports.lexInteger, exports.lexString, exports.lexUnknown, // lexIpldType exports.lexBytes, exports.lexCidLink, // lexRefVariant exports.lexRef, exports.lexRefUnion, // other exports.lexBlob, ])), }) .strict() .superRefine(util_1.requiredPropertiesRefinement); // xrpc // = exports.lexXrpcParameters = zod_1.z .object({ type: zod_1.z.literal('params'), description: zod_1.z.string().optional(), required: zod_1.z.string().array().optional(), properties: zod_1.z.record(zod_1.z.discriminatedUnion('type', [ exports.lexPrimitiveArray, // lexPrimitive exports.lexBoolean, exports.lexInteger, exports.lexString, exports.lexUnknown, ])), }) .strict() .superRefine(util_1.requiredPropertiesRefinement); exports.lexXrpcBody = zod_1.z .object({ description: zod_1.z.string().optional(), encoding: zod_1.z.string(), // @NOTE using discriminatedUnion with a refined schema requires zod >= 4 schema: zod_1.z.union([exports.lexRefVariant, exports.lexObject]).optional(), }) .strict(); exports.lexXrpcSubscriptionMessage = zod_1.z .object({ description: zod_1.z.string().optional(), // @NOTE using discriminatedUnion with a refined schema requires zod >= 4 schema: zod_1.z.union([exports.lexRefVariant, exports.lexObject]).optional(), }) .strict(); exports.lexXrpcError = zod_1.z .object({ name: zod_1.z.string(), description: zod_1.z.string().optional(), }) .strict(); exports.lexXrpcQuery = zod_1.z .object({ type: zod_1.z.literal('query'), description: zod_1.z.string().optional(), parameters: exports.lexXrpcParameters.optional(), output: exports.lexXrpcBody.optional(), errors: exports.lexXrpcError.array().optional(), }) .strict(); exports.lexXrpcProcedure = zod_1.z .object({ type: zod_1.z.literal('procedure'), description: zod_1.z.string().optional(), parameters: exports.lexXrpcParameters.optional(), input: exports.lexXrpcBody.optional(), output: exports.lexXrpcBody.optional(), errors: exports.lexXrpcError.array().optional(), }) .strict(); exports.lexXrpcSubscription = zod_1.z .object({ type: zod_1.z.literal('subscription'), description: zod_1.z.string().optional(), parameters: exports.lexXrpcParameters.optional(), message: exports.lexXrpcSubscriptionMessage.optional(), errors: exports.lexXrpcError.array().optional(), }) .strict(); // database // = exports.lexRecord = zod_1.z .object({ type: zod_1.z.literal('record'), description: zod_1.z.string().optional(), key: zod_1.z.string().optional(), record: exports.lexObject, }) .strict(); // core // = // We need to use `z.custom` here because // lexXrpcProperty and lexObject are refined // `z.union` would work, but it's too slow // see #915 for details exports.lexUserType = zod_1.z.custom((val) => { if (!val || typeof val !== 'object') { return; } if (val['type'] === undefined) { return; } switch (val['type']) { case 'record': return exports.lexRecord.parse(val); case 'query': return exports.lexXrpcQuery.parse(val); case 'procedure': return exports.lexXrpcProcedure.parse(val); case 'subscription': return exports.lexXrpcSubscription.parse(val); case 'blob': return exports.lexBlob.parse(val); case 'array': return exports.lexArray.parse(val); case 'token': return exports.lexToken.parse(val); case 'object': return exports.lexObject.parse(val); case 'boolean': return exports.lexBoolean.parse(val); case 'integer': return exports.lexInteger.parse(val); case 'string': return exports.lexString.parse(val); case 'bytes': return exports.lexBytes.parse(val); case 'cid-link': return exports.lexCidLink.parse(val); case 'unknown': return exports.lexUnknown.parse(val); } }, (val) => { if (!val || typeof val !== 'object') { return { message: 'Must be an object', fatal: true, }; } if (val['type'] === undefined) { return { message: 'Must have a type', fatal: true, }; } if (typeof val['type'] !== 'string') { return { message: 'Type property must be a string', fatal: true, }; } return { message: `Invalid type: ${val['type']} must be one of: record, query, procedure, subscription, blob, array, token, object, boolean, integer, string, bytes, cid-link, unknown`, fatal: true, }; }); exports.lexiconDoc = zod_1.z .object({ lexicon: zod_1.z.literal(1), id: zod_1.z.string().refine((v) => syntax_1.NSID.isValid(v), { message: 'Must be a valid NSID', }), revision: zod_1.z.number().optional(), description: zod_1.z.string().optional(), defs: zod_1.z.record(exports.lexUserType), }) .strict() .superRefine((doc, ctx) => { for (const defId in doc.defs) { const def = doc.defs[defId]; if (defId !== 'main' && (def.type === 'record' || def.type === 'procedure' || def.type === 'query' || def.type === 'subscription')) { ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, message: `Records, procedures, queries, and subscriptions must be the main definition.`, }); } } }); // helpers // = function isValidLexiconDoc(v) { return exports.lexiconDoc.safeParse(v).success; } function isObj(v) { return v != null && typeof v === 'object'; } function isDiscriminatedObject(v) { return isObj(v) && '$type' in v && typeof v.$type === 'string'; } function parseLexiconDoc(v) { exports.lexiconDoc.parse(v); return v; } class ValidationError extends Error { } exports.ValidationError = ValidationError; class InvalidLexiconError extends Error { } exports.InvalidLexiconError = InvalidLexiconError; class LexiconDefNotFoundError extends Error { } exports.LexiconDefNotFoundError = LexiconDefNotFoundError; //# sourceMappingURL=types.js.map