@atproto/lexicon
Version:
atproto Lexicon schema language library
372 lines • 11.7 kB
JavaScript
"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