Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More โ†’
Socket
Sign inDemoInstall
Socket

@atproto/lexicon

Package Overview
Dependencies
Maintainers
3
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@atproto/lexicon - npm Package Compare versions

Comparing version 0.0.4 to 0.1.0

dist/blob-refs.d.ts

2

dist/index.d.ts
export * from './types';
export * from './lexicons';
export * from './blob-refs';
export * from './serialize';

10

dist/lexicons.d.ts

@@ -12,6 +12,8 @@ import { LexiconDoc, LexUserType, ValidationResult } from './types';

validate(lexUri: string, value: unknown): ValidationResult;
assertValidRecord(lexUri: string, value: unknown): void;
assertValidXrpcParams(lexUri: string, value: unknown): void;
assertValidXrpcInput(lexUri: string, value: unknown): void;
assertValidXrpcOutput(lexUri: string, value: unknown): void;
assertValidRecord(lexUri: string, value: unknown): unknown;
assertValidXrpcParams(lexUri: string, value: unknown): unknown;
assertValidXrpcInput(lexUri: string, value: unknown): unknown;
assertValidXrpcOutput(lexUri: string, value: unknown): unknown;
assertValidXrpcMessage<T = unknown>(lexUri: string, value: unknown): T;
resolveLexUri(lexUri: string, ref: string): string;
}

@@ -5,3 +5,3 @@ import { Lexicons } from './lexicons';

export declare function validateOneOf(lexicons: Lexicons, path: string, def: LexRefVariant | LexUserType, value: unknown, mustBeObj?: boolean): ValidationResult;
export declare function assertValidOneOf(lexicons: Lexicons, path: string, def: LexRefVariant | LexUserType, value: unknown, mustBeObj?: boolean): void;
export declare function assertValidOneOf(lexicons: Lexicons, path: string, def: LexRefVariant | LexUserType, value: unknown, mustBeObj?: boolean): unknown;
export declare function toConcreteTypes(lexicons: Lexicons, def: LexRefVariant | LexUserType): LexUserType[];
import { Lexicons } from './lexicons';
import { LexRecord, LexXrpcProcedure, LexXrpcQuery } from './types';
export declare function assertValidRecord(lexicons: Lexicons, def: LexRecord, value: unknown): void;
export declare function assertValidXrpcParams(lexicons: Lexicons, def: LexXrpcProcedure | LexXrpcQuery, value: unknown): void;
export declare function assertValidXrpcInput(lexicons: Lexicons, def: LexXrpcProcedure, value: unknown): void;
export declare function assertValidXrpcOutput(lexicons: Lexicons, def: LexXrpcProcedure | LexXrpcQuery, value: unknown): void;
import { LexRecord, LexXrpcProcedure, LexXrpcQuery, LexXrpcSubscription } from './types';
export declare function assertValidRecord(lexicons: Lexicons, def: LexRecord, value: unknown): unknown;
export declare function assertValidXrpcParams(lexicons: Lexicons, def: LexXrpcProcedure | LexXrpcQuery | LexXrpcSubscription, value: unknown): unknown;
export declare function assertValidXrpcInput(lexicons: Lexicons, def: LexXrpcProcedure, value: unknown): unknown;
export declare function assertValidXrpcOutput(lexicons: Lexicons, def: LexXrpcProcedure | LexXrpcQuery, value: unknown): unknown;
export declare function assertValidXrpcMessage(lexicons: Lexicons, def: LexXrpcSubscription, value: unknown): unknown;
import { Lexicons } from '../lexicons';
import { LexUserType, ValidationResult } from '../types';
export declare function blob(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function image(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function video(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function audio(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
import { Lexicons } from '../lexicons';
import { LexUserType, ValidationResult } from '../types';
import { LexArray, LexUserType, ValidationResult } from '../types';
export declare function validate(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function array(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function array(lexicons: Lexicons, path: string, def: LexArray, value: unknown): ValidationResult;
export declare function object(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;

@@ -5,6 +5,7 @@ import { Lexicons } from '../lexicons';

export declare function boolean(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function number(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function float(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function integer(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function string(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function datetime(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function bytes(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function cidLink(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
export declare function unknown(lexicons: Lexicons, path: string, def: LexUserType, value: unknown): ValidationResult;
import { Lexicons } from '../lexicons';
import { LexXrpcParameters, ValidationResult } from '../types';
export declare function params(lexicons: Lexicons, path: string, def: LexXrpcParameters, value: unknown): ValidationResult;
export declare function params(lexicons: Lexicons, path: string, def: LexXrpcParameters, val: unknown): ValidationResult;
{
"name": "@atproto/lexicon",
"version": "0.0.4",
"version": "0.1.0",
"main": "dist/index.js",

@@ -22,6 +22,10 @@ "scripts": {

"dependencies": {
"@atproto/common-web": "*",
"@atproto/identifier": "*",
"@atproto/nsid": "*",
"@atproto/uri": "*",
"iso-datestring-validator": "^2.2.2",
"multiformats": "^9.6.4",
"zod": "^3.14.2"
}
}
export * from './types'
export * from './lexicons'
export * from './blob-refs'
export * from './serialize'

@@ -16,2 +16,3 @@ import { ZodError } from 'zod'

hasProp,
LexXrpcSubscription,
} from './types'

@@ -23,2 +24,3 @@ import {

assertValidXrpcOutput,
assertValidXrpcMessage,
} from './validation'

@@ -163,3 +165,3 @@ import { toLexUri } from './util'

}
assertValidRecord(this, def as LexRecord, value)
return assertValidRecord(this, def as LexRecord, value)
}

@@ -172,4 +174,12 @@

lexUri = toLexUri(lexUri)
const def = this.getDefOrThrow(lexUri, ['query', 'procedure'])
assertValidXrpcParams(this, def as LexXrpcProcedure | LexXrpcQuery, value)
const def = this.getDefOrThrow(lexUri, [
'query',
'procedure',
'subscription',
])
return assertValidXrpcParams(
this,
def as LexXrpcProcedure | LexXrpcQuery | LexXrpcSubscription,
value,
)
}

@@ -183,3 +193,3 @@

const def = this.getDefOrThrow(lexUri, ['procedure'])
assertValidXrpcInput(this, def as LexXrpcProcedure, value)
return assertValidXrpcInput(this, def as LexXrpcProcedure, value)
}

@@ -193,4 +203,25 @@

const def = this.getDefOrThrow(lexUri, ['query', 'procedure'])
assertValidXrpcOutput(this, def as LexXrpcProcedure | LexXrpcQuery, value)
return assertValidXrpcOutput(
this,
def as LexXrpcProcedure | LexXrpcQuery,
value,
)
}
/**
* Validate xrpc subscription message and throw on any error.
*/
assertValidXrpcMessage<T = unknown>(lexUri: string, value: unknown): T {
lexUri = toLexUri(lexUri)
const def = this.getDefOrThrow(lexUri, ['subscription'])
return assertValidXrpcMessage(this, def as LexXrpcSubscription, value) as T
}
/**
* Resolve a lex uri given a ref
*/
resolveLexUri(lexUri: string, ref: string) {
lexUri = toLexUri(lexUri)
return toLexUri(ref, lexUri)
}
}

@@ -197,0 +228,0 @@

@@ -15,4 +15,4 @@ import { z } from 'zod'

export const lexNumber = z.object({
type: z.literal('number'),
export const lexFloat = z.object({
type: z.literal('float'),
description: z.string().optional(),

@@ -25,3 +25,3 @@ default: z.number().optional(),

})
export type LexNumber = z.infer<typeof lexNumber>
export type LexFloat = z.infer<typeof lexFloat>

@@ -39,4 +39,17 @@ export const lexInteger = z.object({

export const lexStringFormat = z.enum([
'datetime',
'uri',
'at-uri',
'did',
'handle',
'at-identifier',
'nsid',
'cid',
])
export type LexStringFormat = z.infer<typeof lexStringFormat>
export const lexString = z.object({
type: z.literal('string'),
format: lexStringFormat.optional(),
description: z.string().optional(),

@@ -46,2 +59,4 @@ default: z.string().optional(),

maxLength: z.number().int().optional(),
minGraphemes: z.number().int().optional(),
maxGraphemes: z.number().int().optional(),
enum: z.string().array().optional(),

@@ -53,8 +68,2 @@ const: z.string().optional(),

export const lexDatetime = z.object({
type: z.literal('datetime'),
description: z.string().optional(),
})
export type LexDatetime = z.infer<typeof lexDatetime>
export const lexUnknown = z.object({

@@ -68,6 +77,5 @@ type: z.literal('unknown'),

lexBoolean,
lexNumber,
lexFloat,
lexInteger,
lexString,
lexDatetime,
lexUnknown,

@@ -77,2 +85,22 @@ ])

// ipld types
// =
export const lexBytes = z.object({
type: z.literal('bytes'),
description: z.string().optional(),
maxLength: z.number().optional(),
minLength: z.number().optional(),
})
export type LexBytes = z.infer<typeof lexBytes>
export const lexCidLink = z.object({
type: z.literal('cid-link'),
description: z.string().optional(),
})
export type LexCidLink = z.infer<typeof lexCidLink>
export const lexIpldType = z.union([lexBytes, lexCidLink])
export type LexIpldType = z.infer<typeof lexIpldType>
// references

@@ -110,35 +138,2 @@ // =

export const lexImage = z.object({
type: z.literal('image'),
description: z.string().optional(),
accept: z.string().array().optional(),
maxSize: z.number().optional(),
maxWidth: z.number().int().optional(),
maxHeight: z.number().int().optional(),
})
export type LexImage = z.infer<typeof lexImage>
export const lexVideo = z.object({
type: z.literal('video'),
description: z.string().optional(),
accept: z.string().array().optional(),
maxSize: z.number().optional(),
maxWidth: z.number().int().optional(),
maxHeight: z.number().int().optional(),
maxLength: z.number().int().optional(),
})
export type LexVideo = z.infer<typeof lexVideo>
export const lexAudio = z.object({
type: z.literal('audio'),
description: z.string().optional(),
accept: z.string().array().optional(),
maxSize: z.number().optional(),
maxLength: z.number().int().optional(),
})
export type LexAudio = z.infer<typeof lexAudio>
export const lexBlobVariant = z.union([lexBlob, lexImage, lexVideo, lexAudio])
export type LexBlobVariant = z.infer<typeof lexBlobVariant>
// complex types

@@ -150,3 +145,3 @@ // =

description: z.string().optional(),
items: z.union([lexPrimitive, lexBlobVariant, lexRefVariant]),
items: z.union([lexPrimitive, lexIpldType, lexBlob, lexRefVariant]),
minLength: z.number().int().optional(),

@@ -157,2 +152,9 @@ maxLength: z.number().int().optional(),

export const lexPrimitiveArray = lexArray.merge(
z.object({
items: lexPrimitive,
}),
)
export type LexPrimitiveArray = z.infer<typeof lexPrimitiveArray>
export const lexToken = z.object({

@@ -168,4 +170,7 @@ type: z.literal('token'),

required: z.string().array().optional(),
nullable: z.string().array().optional(),
properties: z
.record(z.union([lexRefVariant, lexArray, lexBlobVariant, lexPrimitive]))
.record(
z.union([lexRefVariant, lexIpldType, lexArray, lexBlob, lexPrimitive]),
)
.optional(),

@@ -182,3 +187,3 @@ })

required: z.string().array().optional(),
properties: z.record(lexPrimitive),
properties: z.record(z.union([lexPrimitive, lexPrimitiveArray])),
})

@@ -194,2 +199,10 @@ export type LexXrpcParameters = z.infer<typeof lexXrpcParameters>

export const lexXrpcSubscriptionMessage = z.object({
description: z.string().optional(),
schema: z.union([lexRefVariant, lexObject]).optional(),
})
export type LexXrpcSubscriptionMessage = z.infer<
typeof lexXrpcSubscriptionMessage
>
export const lexXrpcError = z.object({

@@ -220,2 +233,12 @@ name: z.string(),

export const lexXrpcSubscription = z.object({
type: z.literal('subscription'),
description: z.string().optional(),
parameters: lexXrpcParameters.optional(),
message: lexXrpcSubscriptionMessage.optional(),
infos: lexXrpcError.array().optional(),
errors: lexXrpcError.array().optional(),
})
export type LexXrpcSubscription = z.infer<typeof lexXrpcSubscription>
// database

@@ -240,7 +263,5 @@ // =

lexXrpcProcedure,
lexXrpcSubscription,
lexBlob,
lexImage,
lexVideo,
lexAudio,

@@ -252,6 +273,7 @@ lexArray,

lexBoolean,
lexNumber,
lexFloat,
lexInteger,
lexString,
lexDatetime,
lexBytes,
lexCidLink,
lexUnknown,

@@ -278,7 +300,8 @@ ])

def.type === 'procedure' ||
def.type === 'query')
def.type === 'query' ||
def.type === 'subscription')
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Records, procedures, and queries must be the main definition.`,
message: `Records, procedures, queries, and subscriptions must be the main definition.`,
})

@@ -328,6 +351,11 @@ }

export interface ValidationResult {
success: boolean
error?: ValidationError
}
export type ValidationResult =
| {
success: true
value: unknown
}
| {
success: false
error: ValidationError
}

@@ -334,0 +362,0 @@ export class ValidationError extends Error {}

@@ -52,3 +52,3 @@ import { Lexicons } from './lexicons'

}
return { success: true }
return { success: true, value }
} else {

@@ -92,5 +92,4 @@ concreteDefs = toConcreteTypes(lexicons, {

const res = validateOneOf(lexicons, path, def, value, mustBeObj)
if (!res.success) {
throw res.error
}
if (!res.success) throw res.error
return res.value
}

@@ -97,0 +96,0 @@

import { Lexicons } from './lexicons'
import { LexRecord, LexXrpcProcedure, LexXrpcQuery } from './types'
import {
LexRecord,
LexXrpcProcedure,
LexXrpcQuery,
LexXrpcSubscription,
} from './types'
import { assertValidOneOf } from './util'

@@ -15,2 +20,3 @@

if (!res.success) throw res.error
return res.value
}

@@ -20,3 +26,3 @@

lexicons: Lexicons,
def: LexXrpcProcedure | LexXrpcQuery,
def: LexXrpcProcedure | LexXrpcQuery | LexXrpcSubscription,
value: unknown,

@@ -27,2 +33,3 @@ ) {

if (!res.success) throw res.error
return res.value
}

@@ -38,3 +45,3 @@ }

// loop: all input schema definitions
assertValidOneOf(lexicons, 'Input', def.input.schema, value, true)
return assertValidOneOf(lexicons, 'Input', def.input.schema, value, true)
}

@@ -50,4 +57,21 @@ }

// loop: all output schema definitions
assertValidOneOf(lexicons, 'Output', def.output.schema, value, true)
return assertValidOneOf(lexicons, 'Output', def.output.schema, value, true)
}
}
export function assertValidXrpcMessage(
lexicons: Lexicons,
def: LexXrpcSubscription,
value: unknown,
) {
if (def.message?.schema) {
// loop: all output schema definitions
return assertValidOneOf(
lexicons,
'Message',
def.message.schema,
value,
true,
)
}
}

@@ -0,4 +1,4 @@

import { BlobRef } from '../blob-refs'
import { Lexicons } from '../lexicons'
import { LexUserType, ValidationResult, ValidationError } from '../types'
import { isObj, hasProp } from '../types'

@@ -11,48 +11,10 @@ export function blob(

): ValidationResult {
if (!isObj(value)) {
// check
if (!value || !(value instanceof BlobRef)) {
return {
success: false,
error: new ValidationError(`${path} should be an object`),
error: new ValidationError(`${path} should be a blob ref`),
}
}
if (!hasProp(value, 'cid') || typeof value.cid !== 'string') {
return {
success: false,
error: new ValidationError(`${path}/cid should be a string`),
}
}
if (!hasProp(value, 'mimeType') || typeof value.mimeType !== 'string') {
return {
success: false,
error: new ValidationError(`${path}/mimeType should be a string`),
}
}
return { success: true }
return { success: true, value }
}
export function image(
lexicons: Lexicons,
path: string,
def: LexUserType,
value: unknown,
): ValidationResult {
return blob(lexicons, path, def, value)
}
export function video(
lexicons: Lexicons,
path: string,
def: LexUserType,
value: unknown,
): ValidationResult {
return blob(lexicons, path, def, value)
}
export function audio(
lexicons: Lexicons,
path: string,
def: LexUserType,
value: unknown,
): ValidationResult {
return blob(lexicons, path, def, value)
}

@@ -23,4 +23,4 @@ import { Lexicons } from '../lexicons'

return Primitives.boolean(lexicons, path, def, value)
case 'number':
return Primitives.number(lexicons, path, def, value)
case 'float':
return Primitives.float(lexicons, path, def, value)
case 'integer':

@@ -30,4 +30,6 @@ return Primitives.integer(lexicons, path, def, value)

return Primitives.string(lexicons, path, def, value)
case 'datetime':
return Primitives.datetime(lexicons, path, def, value)
case 'bytes':
return Primitives.bytes(lexicons, path, def, value)
case 'cid-link':
return Primitives.cidLink(lexicons, path, def, value)
case 'unknown':

@@ -41,8 +43,2 @@ return Primitives.unknown(lexicons, path, def, value)

return Blob.blob(lexicons, path, def, value)
case 'image':
return Blob.image(lexicons, path, def, value)
case 'video':
return Blob.video(lexicons, path, def, value)
case 'audio':
return Blob.audio(lexicons, path, def, value)
default:

@@ -59,7 +55,5 @@ return {

path: string,
def: LexUserType,
def: LexArray,
value: unknown,
): ValidationResult {
def = def as LexArray
// type

@@ -108,3 +102,3 @@ if (!Array.isArray(value)) {

return { success: true }
return { success: true, value }
}

@@ -128,31 +122,38 @@

// required
if (Array.isArray(def.required)) {
for (const key of def.required) {
if (!(key in value)) {
return {
success: false,
error: new ValidationError(`${path} must have the property "${key}"`),
}
}
}
}
const requiredProps = new Set(def.required)
const nullableProps = new Set(def.nullable)
// properties
let resultValue = value
if (typeof def.properties === 'object') {
for (const key in def.properties) {
const propValue = value[key]
if (typeof propValue === 'undefined') {
continue // skip- if required, will have already failed
if (value[key] === null && nullableProps.has(key)) {
continue
}
const propDef = def.properties[key]
const propPath = `${path}/${key}`
const res = validateOneOf(lexicons, propPath, propDef, propValue)
if (!res.success) {
return res
const validated = validateOneOf(lexicons, propPath, propDef, value[key])
const propValue = validated.success ? validated.value : value[key]
const propIsUndefined = typeof propValue === 'undefined'
// Return error for bad validation, giving required rule precedence
if (propIsUndefined && requiredProps.has(key)) {
return {
success: false,
error: new ValidationError(`${path} must have the property "${key}"`),
}
} else if (!propIsUndefined && !validated.success) {
return validated
}
// Adjust value based on e.g. applied defaults, cloning shallowly if there was a changed value
if (propValue !== value[key]) {
if (resultValue === value) {
// Lazy shallow clone
resultValue = { ...value }
}
resultValue[key] = propValue
}
}
}
return { success: true }
return { success: true, value: resultValue }
}

@@ -1,12 +0,14 @@

import { isValidISODateString } from 'iso-datestring-validator'
import { utf8Len, graphemeLen } from '@atproto/common-web'
import { CID } from 'multiformats/cid'
import { Lexicons } from '../lexicons'
import * as formats from './formats'
import {
LexUserType,
LexBoolean,
LexNumber,
LexFloat,
LexInteger,
LexString,
LexDatetime,
ValidationResult,
ValidationError,
LexBytes,
} from '../types'

@@ -23,4 +25,4 @@

return boolean(lexicons, path, def, value)
case 'number':
return number(lexicons, path, def, value)
case 'float':
return float(lexicons, path, def, value)
case 'integer':

@@ -30,4 +32,6 @@ return integer(lexicons, path, def, value)

return string(lexicons, path, def, value)
case 'datetime':
return datetime(lexicons, path, def, value)
case 'bytes':
return bytes(lexicons, path, def, value)
case 'cid-link':
return cidLink(lexicons, path, def, value)
case 'unknown':

@@ -53,5 +57,5 @@ return unknown(lexicons, path, def, value)

const type = typeof value
if (type == 'undefined') {
if (type === 'undefined') {
if (typeof def.default === 'boolean') {
return { success: true }
return { success: true, value: def.default }
}

@@ -79,6 +83,6 @@ return {

return { success: true }
return { success: true, value }
}
export function number(
export function float(
lexicons: Lexicons,

@@ -89,9 +93,9 @@ path: string,

): ValidationResult {
def = def as LexNumber
def = def as LexFloat
// type
const type = typeof value
if (type == 'undefined') {
if (type === 'undefined') {
if (typeof def.default === 'number') {
return { success: true }
return { success: true, value: def.default }
}

@@ -155,3 +159,3 @@ return {

return { success: true }
return { success: true, value }
}

@@ -168,5 +172,7 @@

// run number validation
const numRes = number(lexicons, path, def, value)
const numRes = float(lexicons, path, def, value)
if (!numRes.success) {
return numRes
} else {
value = numRes.value
}

@@ -182,3 +188,3 @@

return { success: true }
return { success: true, value }
}

@@ -195,6 +201,5 @@

// type
const type = typeof value
if (type == 'undefined') {
if (typeof value === 'undefined') {
if (typeof def.default === 'string') {
return { success: true }
return { success: true, value: def.default }
}

@@ -205,3 +210,3 @@ return {

}
} else if (type !== 'string') {
} else if (typeof value !== 'string') {
return {

@@ -237,3 +242,3 @@ success: false,

if (typeof def.maxLength === 'number') {
if ((value as string).length > def.maxLength) {
if (utf8Len(value) > def.maxLength) {
return {

@@ -250,3 +255,3 @@ success: false,

if (typeof def.minLength === 'number') {
if ((value as string).length < def.minLength) {
if (utf8Len(value) < def.minLength) {
return {

@@ -261,6 +266,51 @@ success: false,

return { success: true }
// maxGraphemes
if (typeof def.maxGraphemes === 'number') {
if (graphemeLen(value) > def.maxGraphemes) {
return {
success: false,
error: new ValidationError(
`${path} must not be longer than ${def.maxGraphemes} graphemes`,
),
}
}
}
// minGraphemes
if (typeof def.minGraphemes === 'number') {
if (graphemeLen(value) < def.minGraphemes) {
return {
success: false,
error: new ValidationError(
`${path} must not be shorter than ${def.minGraphemes} graphemes`,
),
}
}
}
if (typeof def.format === 'string') {
switch (def.format) {
case 'datetime':
return formats.datetime(path, value)
case 'uri':
return formats.uri(path, value)
case 'at-uri':
return formats.atUri(path, value)
case 'did':
return formats.did(path, value)
case 'handle':
return formats.handle(path, value)
case 'at-identifier':
return formats.atIdentifier(path, value)
case 'nsid':
return formats.nsid(path, value)
case 'cid':
return formats.cid(path, value)
}
}
return { success: true, value }
}
export function datetime(
export function bytes(
lexicons: Lexicons,

@@ -271,29 +321,54 @@ path: string,

): ValidationResult {
def = def as LexDatetime
def = def as LexBytes
// type
const type = typeof value
if (type !== 'string') {
if (!value || !(value instanceof Uint8Array)) {
return {
success: false,
error: new ValidationError(`${path} must be a string`),
error: new ValidationError(`${path} must be a byte array`),
}
}
// valid iso-8601
{
try {
if (typeof value !== 'string' || !isValidISODateString(value)) {
throw new ValidationError(
`${path} must be an iso8601 formatted datetime`,
)
// maxLength
if (typeof def.maxLength === 'number') {
if (value.byteLength > def.maxLength) {
return {
success: false,
error: new ValidationError(
`${path} must not be larger than ${def.maxLength} bytes`,
),
}
} catch {
throw new ValidationError(`${path} must be an iso8601 formatted datetime`)
}
}
return { success: true }
// minLength
if (typeof def.minLength === 'number') {
if (value.byteLength < def.minLength) {
return {
success: false,
error: new ValidationError(
`${path} must not be smaller than ${def.minLength} bytes`,
),
}
}
}
return { success: true, value }
}
export function cidLink(
lexicons: Lexicons,
path: string,
def: LexUserType,
value: unknown,
): ValidationResult {
if (CID.asCID(value) === null) {
return {
success: false,
error: new ValidationError(`${path} must be a CID`),
}
}
return { success: true, value }
}
export function unknown(

@@ -313,3 +388,3 @@ lexicons: Lexicons,

return { success: true }
return { success: true, value }
}

@@ -5,2 +5,3 @@ import { Lexicons } from '../lexicons'

import * as PrimitiveValidators from './primitives'
import { array } from './complex'

@@ -11,16 +12,22 @@ export function params(

def: LexXrpcParameters,
value: unknown,
val: unknown,
): ValidationResult {
def = def as LexXrpcParameters
// type
if (!value || typeof value !== 'object') {
// in this case, we just fall back to an object
value = {}
}
const value = val && typeof val === 'object' ? val : {}
// required
if (Array.isArray(def.required)) {
for (const key of def.required) {
if (!(key in (value as Record<string, unknown>))) {
const requiredProps = new Set(def.required ?? [])
// properties
let resultValue = value
if (typeof def.properties === 'object') {
for (const key in def.properties) {
const propDef = def.properties[key]
const validated =
propDef.type === 'array'
? array(lexicons, key, propDef, value[key])
: PrimitiveValidators.validate(lexicons, key, propDef, value[key])
const propValue = validated.success ? validated.value : value[key]
const propIsUndefined = typeof propValue === 'undefined'
// Return error for bad validation, giving required rule precedence
if (propIsUndefined && requiredProps.has(key)) {
return {

@@ -30,24 +37,17 @@ success: false,

}
} else if (!propIsUndefined && !validated.success) {
return validated
}
// Adjust value based on e.g. applied defaults, cloning shallowly if there was a changed value
if (propValue !== value[key]) {
if (resultValue === value) {
// Lazy shallow clone
resultValue = { ...value }
}
resultValue[key] = propValue
}
}
}
// properties
for (const key in def.properties) {
if (typeof (value as Record<string, unknown>)[key] === 'undefined') {
continue // skip- if required, will have already failed
}
const paramDef = def.properties[key]
const res = PrimitiveValidators.validate(
lexicons,
key,
paramDef,
(value as Record<string, unknown>)[key],
)
if (!res.success) {
return res
}
}
return { success: true }
return { success: true, value: resultValue }
}

@@ -16,6 +16,7 @@ export default [

'boolean',
'number',
'float',
'integer',
'string',
'datetime',
'bytes',
'cidLink',
],

@@ -26,6 +27,7 @@ properties: {

boolean: { type: 'boolean' },
number: { type: 'number' },
float: { type: 'float' },
integer: { type: 'integer' },
string: { type: 'string' },
datetime: { type: 'datetime' },
bytes: { type: 'bytes' },
cidLink: { type: 'cid-link' },
},

@@ -36,3 +38,3 @@ },

type: 'object',
required: ['object', 'array', 'boolean', 'number', 'integer', 'string'],
required: ['object', 'array', 'boolean', 'float', 'integer', 'string'],
properties: {

@@ -42,3 +44,3 @@ object: { type: 'ref', ref: '#subobject' },

boolean: { type: 'boolean' },
number: { type: 'number' },
float: { type: 'float' },
integer: { type: 'integer' },

@@ -66,8 +68,10 @@ string: { type: 'string' },

type: 'params',
required: ['boolean', 'number', 'integer'],
required: ['boolean', 'float', 'integer'],
properties: {
boolean: { type: 'boolean' },
number: { type: 'number' },
float: { type: 'float' },
integer: { type: 'integer' },
string: { type: 'string' },
array: { type: 'array', items: { type: 'string' } },
def: { type: 'integer', default: 0 },
},

@@ -91,8 +95,9 @@ },

type: 'params',
required: ['boolean', 'number', 'integer'],
required: ['boolean', 'float', 'integer'],
properties: {
boolean: { type: 'boolean' },
number: { type: 'number' },
float: { type: 'float' },
integer: { type: 'integer' },
string: { type: 'string' },
array: { type: 'array', items: { type: 'string' } },
},

@@ -123,3 +128,3 @@ },

boolean: { type: 'boolean' },
number: { type: 'number' },
float: { type: 'float' },
integer: { type: 'integer' },

@@ -134,2 +139,31 @@ string: { type: 'string' },

lexicon: 1,
id: 'com.example.default',
defs: {
main: {
type: 'record',
record: {
type: 'object',
required: ['boolean'],
properties: {
boolean: { type: 'boolean', default: false },
float: { type: 'float', default: 0 },
integer: { type: 'integer', default: 0 },
string: { type: 'string', default: '' },
object: { type: 'ref', ref: '#object' },
},
},
},
object: {
type: 'object',
properties: {
boolean: { type: 'boolean', default: true },
float: { type: 'float', default: 1.5 },
integer: { type: 'integer', default: 1 },
string: { type: 'string', default: 'x' },
},
},
},
},
{
lexicon: 1,
id: 'com.example.union',

@@ -197,3 +231,3 @@ defs: {

maxLength: 4,
items: { type: 'number' },
items: { type: 'float' },
},

@@ -225,3 +259,3 @@ },

lexicon: 1,
id: 'com.example.numberRange',
id: 'com.example.floatRange',
defs: {

@@ -233,4 +267,4 @@ main: {

properties: {
number: {
type: 'number',
float: {
type: 'float',
minimum: 2,

@@ -246,3 +280,3 @@ maximum: 4,

lexicon: 1,
id: 'com.example.numberEnum',
id: 'com.example.floatEnum',
defs: {

@@ -254,4 +288,4 @@ main: {

properties: {
number: {
type: 'number',
float: {
type: 'float',
enum: [1, 1.5, 2],

@@ -266,3 +300,3 @@ },

lexicon: 1,
id: 'com.example.numberConst',
id: 'com.example.floatConst',
defs: {

@@ -274,4 +308,4 @@ main: {

properties: {
number: {
type: 'number',
float: {
type: 'float',
const: 0,

@@ -360,2 +394,21 @@ },

lexicon: 1,
id: 'com.example.stringLengthGrapheme',
defs: {
main: {
type: 'record',
record: {
type: 'object',
properties: {
string: {
type: 'string',
minGraphemes: 2,
maxGraphemes: 4,
},
},
},
},
},
},
{
lexicon: 1,
id: 'com.example.stringEnum',

@@ -404,4 +457,126 @@ defs: {

properties: {
datetime: {
type: 'datetime',
datetime: { type: 'string', format: 'datetime' },
},
},
},
},
},
{
lexicon: 1,
id: 'com.example.uri',
defs: {
main: {
type: 'record',
record: {
type: 'object',
properties: {
uri: { type: 'string', format: 'uri' },
},
},
},
},
},
{
lexicon: 1,
id: 'com.example.atUri',
defs: {
main: {
type: 'record',
record: {
type: 'object',
properties: {
atUri: { type: 'string', format: 'at-uri' },
},
},
},
},
},
{
lexicon: 1,
id: 'com.example.did',
defs: {
main: {
type: 'record',
record: {
type: 'object',
properties: {
did: { type: 'string', format: 'did' },
},
},
},
},
},
{
lexicon: 1,
id: 'com.example.handle',
defs: {
main: {
type: 'record',
record: {
type: 'object',
properties: {
handle: { type: 'string', format: 'handle' },
},
},
},
},
},
{
lexicon: 1,
id: 'com.example.atIdentifier',
defs: {
main: {
type: 'record',
record: {
type: 'object',
properties: {
atIdentifier: { type: 'string', format: 'at-identifier' },
},
},
},
},
},
{
lexicon: 1,
id: 'com.example.nsid',
defs: {
main: {
type: 'record',
record: {
type: 'object',
properties: {
nsid: { type: 'string', format: 'nsid' },
},
},
},
},
},
{
lexicon: 1,
id: 'com.example.cid',
defs: {
main: {
type: 'record',
record: {
type: 'object',
properties: {
cid: { type: 'string', format: 'cid' },
},
},
},
},
},
{
lexicon: 1,
id: 'com.example.byteLength',
defs: {
main: {
type: 'record',
record: {
type: 'object',
properties: {
bytes: {
type: 'bytes',
minLength: 2,
maxLength: 4,
},

@@ -408,0 +583,0 @@ },

@@ -0,1 +1,2 @@

import { CID } from 'multiformats/cid'
import { Lexicons } from '../src/index'

@@ -43,3 +44,3 @@ import LexiconDocs from './_scaffolds/lexicons'

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,

@@ -50,6 +51,13 @@ string: 'string',

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,
string: 'string',
datetime: new Date().toISOString(),
atUri: 'at://did:web:example.com/com.example.test/self',
did: 'did:web:example.com',
cid: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
bytes: new Uint8Array([0, 1, 2, 3]),
cidLink: CID.parse(
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
),
})

@@ -61,2 +69,3 @@ expect(res.success).toBe(true)

expect(res.success).toBe(false)
if (res.success) throw new Error('Asserted')
expect(res.error?.message).toBe('Record must have the property "object"')

@@ -71,3 +80,3 @@ }

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,

@@ -81,2 +90,3 @@ string: 'string',

expect(res.success).toBe(false)
if (res.success) throw new Error('Asserted')
expect(res.error?.message).toBe('Object must have the property "object"')

@@ -90,20 +100,25 @@ }

it('Passes valid schemas', () => {
lex.assertValidRecord('com.example.kitchenSink', {
$type: 'com.example.kitchenSink',
object: {
object: { boolean: true },
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
},
const passingSink = {
$type: 'com.example.kitchenSink',
object: {
object: { boolean: true },
array: ['one', 'two'],
boolean: true,
number: 123.45,
float: 123.45,
integer: 123,
string: 'string',
datetime: new Date().toISOString(),
})
},
array: ['one', 'two'],
boolean: true,
float: 123.45,
integer: 123,
string: 'string',
bytes: new Uint8Array([0, 1, 2, 3]),
cidLink: CID.parse(
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
),
}
it('Passes valid schemas', () => {
lex.assertValidRecord('com.example.kitchenSink', passingSink)
})

@@ -138,8 +153,21 @@

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,
string: 'string',
datetime: new Date().toISOString(),
atUri: 'at://did:web:example.com/com.example.test/self',
did: 'did:web:example.com',
cid: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
bytes: new Uint8Array([0, 1, 2, 3]),
cidLink: CID.parse(
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
),
}),
).toThrow('Record must have the property "object"')
expect(() =>
lex.assertValidRecord('com.example.kitchenSink', {
...passingSink,
object: undefined,
}),
).toThrow('Record must have the property "object"')
})

@@ -150,17 +178,7 @@

lex.assertValidRecord('com.example.kitchenSink', {
$type: 'com.example.kitchenSink',
...passingSink,
object: {
...passingSink.object,
object: { boolean: '1234' },
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
},
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
datetime: new Date().toISOString(),
}),

@@ -170,10 +188,4 @@ ).toThrow('Record/object/object/boolean must be a boolean')

lex.assertValidRecord('com.example.kitchenSink', {
$type: 'com.example.kitchenSink',
...passingSink,
object: true,
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
datetime: new Date().toISOString(),
}),

@@ -183,17 +195,4 @@ ).toThrow('Record/object must be an object')

lex.assertValidRecord('com.example.kitchenSink', {
$type: 'com.example.kitchenSink',
object: {
object: { boolean: true },
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
},
...passingSink,
array: 1234,
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
datetime: new Date().toISOString(),
}),

@@ -203,36 +202,10 @@ ).toThrow('Record/array must be an array')

lex.assertValidRecord('com.example.kitchenSink', {
$type: 'com.example.kitchenSink',
object: {
object: { boolean: true },
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
},
array: ['one', 'two'],
boolean: true,
number: 'string',
integer: 123,
string: 'string',
datetime: new Date().toISOString(),
...passingSink,
float: 'string',
}),
).toThrow('Record/number must be a number')
).toThrow('Record/float must be a number')
expect(() =>
lex.assertValidRecord('com.example.kitchenSink', {
$type: 'com.example.kitchenSink',
object: {
object: { boolean: true },
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
},
array: ['one', 'two'],
boolean: true,
number: 123.45,
...passingSink,
integer: true,
string: 'string',
datetime: new Date().toISOString(),
}),

@@ -242,17 +215,4 @@ ).toThrow('Record/integer must be a number')

lex.assertValidRecord('com.example.kitchenSink', {
$type: 'com.example.kitchenSink',
object: {
object: { boolean: true },
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
},
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
...passingSink,
string: {},
datetime: new Date().toISOString(),
}),

@@ -262,19 +222,12 @@ ).toThrow('Record/string must be a string')

lex.assertValidRecord('com.example.kitchenSink', {
$type: 'com.example.kitchenSink',
object: {
object: { boolean: true },
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
},
array: ['one', 'two'],
boolean: true,
number: 123.45,
integer: 123,
string: 'string',
datetime: 1234,
...passingSink,
bytes: 1234,
}),
).toThrow('Record/datetime must be a string')
).toThrow('Record/bytes must be a byte array')
expect(() =>
lex.assertValidRecord('com.example.kitchenSink', {
...passingSink,
cidLink: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
}),
).toThrow('Record/cidLink must be a CID')
})

@@ -288,2 +241,23 @@

it('Handles default properties correctly', () => {
const result = lex.assertValidRecord('com.example.default', {
$type: 'com.example.default',
object: {},
})
expect(result).toEqual({
$type: 'com.example.default',
boolean: false,
integer: 0,
float: 0,
string: '',
object: {
boolean: true,
integer: 1,
float: 1.5,
string: 'x',
},
})
expect(result).not.toHaveProperty('datetime')
})
it('Handles unions correctly', () => {

@@ -297,3 +271,3 @@ lex.assertValidRecord('com.example.union', {

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,

@@ -373,2 +347,17 @@ string: 'string',

it('Applies array item constraints', () => {
expect(() =>
lex.assertValidRecord('com.example.arrayLength', {
$type: 'com.example.arrayLength',
array: [1, '2', 3],
}),
).toThrow('Record/array/1 must be a number')
expect(() =>
lex.assertValidRecord('com.example.arrayLength', {
$type: 'com.example.arrayLength',
array: [1, undefined, 3],
}),
).toThrow('Record/array/1 must be a number')
})
it('Applies boolean const constraint', () => {

@@ -387,45 +376,45 @@ lex.assertValidRecord('com.example.boolConst', {

it('Applies number range constraint', () => {
lex.assertValidRecord('com.example.numberRange', {
$type: 'com.example.numberRange',
number: 2.5,
it('Applies float range constraint', () => {
lex.assertValidRecord('com.example.floatRange', {
$type: 'com.example.floatRange',
float: 2.5,
})
expect(() =>
lex.assertValidRecord('com.example.numberRange', {
$type: 'com.example.numberRange',
number: 1,
lex.assertValidRecord('com.example.floatRange', {
$type: 'com.example.floatRange',
float: 1,
}),
).toThrow('Record/number can not be less than 2')
).toThrow('Record/float can not be less than 2')
expect(() =>
lex.assertValidRecord('com.example.numberRange', {
$type: 'com.example.numberRange',
number: 5,
lex.assertValidRecord('com.example.floatRange', {
$type: 'com.example.floatRange',
float: 5,
}),
).toThrow('Record/number can not be greater than 4')
).toThrow('Record/float can not be greater than 4')
})
it('Applies number enum constraint', () => {
lex.assertValidRecord('com.example.numberEnum', {
$type: 'com.example.numberEnum',
number: 1.5,
it('Applies float enum constraint', () => {
lex.assertValidRecord('com.example.floatEnum', {
$type: 'com.example.floatEnum',
float: 1.5,
})
expect(() =>
lex.assertValidRecord('com.example.numberEnum', {
$type: 'com.example.numberEnum',
number: 0,
lex.assertValidRecord('com.example.floatEnum', {
$type: 'com.example.floatEnum',
float: 0,
}),
).toThrow('Record/number must be one of (1|1.5|2)')
).toThrow('Record/float must be one of (1|1.5|2)')
})
it('Applies number const constraint', () => {
lex.assertValidRecord('com.example.numberConst', {
$type: 'com.example.numberConst',
number: 0,
it('Applies float const constraint', () => {
lex.assertValidRecord('com.example.floatConst', {
$type: 'com.example.floatConst',
float: 0,
})
expect(() =>
lex.assertValidRecord('com.example.numberConst', {
$type: 'com.example.numberConst',
number: 1,
lex.assertValidRecord('com.example.floatConst', {
$type: 'com.example.floatConst',
float: 1,
}),
).toThrow('Record/number must be 0')
).toThrow('Record/float must be 0')
})

@@ -504,4 +493,29 @@

).toThrow('Record/string must not be longer than 4 characters')
expect(() =>
lex.assertValidRecord('com.example.stringLength', {
$type: 'com.example.stringLength',
string: '๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง',
}),
).toThrow('Record/string must not be longer than 4 characters')
})
it('Applies grapheme string length constraint', () => {
lex.assertValidRecord('com.example.stringLengthGrapheme', {
$type: 'com.example.stringLengthGrapheme',
string: '12๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง',
})
expect(() =>
lex.assertValidRecord('com.example.stringLengthGrapheme', {
$type: 'com.example.stringLengthGrapheme',
string: '๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง',
}),
).toThrow('Record/string must not be shorter than 2 graphemes')
expect(() =>
lex.assertValidRecord('com.example.stringLengthGrapheme', {
$type: 'com.example.stringLengthGrapheme',
string: '12345',
}),
).toThrow('Record/string must not be longer than 4 graphemes')
})
it('Applies string enum constraint', () => {

@@ -558,2 +572,164 @@ lex.assertValidRecord('com.example.stringEnum', {

})
it('Applies uri formatting constraint', () => {
for (const uri of [
'https://example.com',
'https://example.com/with/path',
'https://example.com/with/path?and=query',
'at://bsky.social',
'did:example:test',
]) {
lex.assertValidRecord('com.example.uri', {
$type: 'com.example.uri',
uri,
})
}
expect(() =>
lex.assertValidRecord('com.example.uri', {
$type: 'com.example.uri',
uri: 'not a uri',
}),
).toThrow('Record/uri must be a uri')
})
it('Applies at-uri formatting constraint', () => {
lex.assertValidRecord('com.example.atUri', {
$type: 'com.example.atUri',
atUri: 'at://did:web:example.com/com.example.test/self',
})
expect(() =>
lex.assertValidRecord('com.example.atUri', {
$type: 'com.example.atUri',
atUri: 'http://not-atproto.com',
}),
).toThrow('Record/atUri must be a valid at-uri')
})
it('Applies did formatting constraint', () => {
lex.assertValidRecord('com.example.did', {
$type: 'com.example.did',
did: 'did:web:example.com',
})
lex.assertValidRecord('com.example.did', {
$type: 'com.example.did',
did: 'did:plc:12345678abcdefghijklmnop',
})
expect(() =>
lex.assertValidRecord('com.example.did', {
$type: 'com.example.did',
did: 'bad did',
}),
).toThrow('Record/did must be a valid did')
expect(() =>
lex.assertValidRecord('com.example.did', {
$type: 'com.example.did',
did: 'did:short',
}),
).toThrow('Record/did must be a valid did')
})
it('Applies handle formatting constraint', () => {
lex.assertValidRecord('com.example.handle', {
$type: 'com.example.handle',
handle: 'test.bsky.social',
})
lex.assertValidRecord('com.example.handle', {
$type: 'com.example.handle',
handle: 'bsky.test',
})
expect(() =>
lex.assertValidRecord('com.example.handle', {
$type: 'com.example.handle',
handle: 'bad handle',
}),
).toThrow('Record/handle must be a valid handle')
expect(() =>
lex.assertValidRecord('com.example.handle', {
$type: 'com.example.handle',
handle: '-bad-.test',
}),
).toThrow('Record/handle must be a valid handle')
})
it('Applies at-identifier formatting constraint', () => {
lex.assertValidRecord('com.example.atIdentifier', {
$type: 'com.example.atIdentifier',
atIdentifier: 'bsky.test',
})
lex.assertValidRecord('com.example.atIdentifier', {
$type: 'com.example.atIdentifier',
atIdentifier: 'did:plc:12345678abcdefghijklmnop',
})
expect(() =>
lex.assertValidRecord('com.example.atIdentifier', {
$type: 'com.example.atIdentifier',
atIdentifier: 'bad id',
}),
).toThrow('Record/atIdentifier must be a valid did or a handle')
expect(() =>
lex.assertValidRecord('com.example.atIdentifier', {
$type: 'com.example.atIdentifier',
atIdentifier: '-bad-.test',
}),
).toThrow('Record/atIdentifier must be a valid did or a handle')
})
it('Applies nsid formatting constraint', () => {
lex.assertValidRecord('com.example.nsid', {
$type: 'com.example.nsid',
nsid: 'com.atproto.test',
})
lex.assertValidRecord('com.example.nsid', {
$type: 'com.example.nsid',
nsid: 'app.bsky.nested.test',
})
expect(() =>
lex.assertValidRecord('com.example.nsid', {
$type: 'com.example.nsid',
nsid: 'bad nsid',
}),
).toThrow('Record/nsid must be a valid nsid')
expect(() =>
lex.assertValidRecord('com.example.nsid', {
$type: 'com.example.nsid',
nsid: 'com.bad-.foo',
}),
).toThrow('Record/nsid must be a valid nsid')
})
it('Applies cid formatting constraint', () => {
lex.assertValidRecord('com.example.cid', {
$type: 'com.example.cid',
cid: 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
})
expect(() =>
lex.assertValidRecord('com.example.cid', {
$type: 'com.example.cid',
cid: 'abapsdofiuwrpoiasdfuaspdfoiu',
}),
).toThrow('Record/cid must be a cid string')
})
it('Applies bytes length constraints', () => {
lex.assertValidRecord('com.example.byteLength', {
$type: 'com.example.byteLength',
bytes: new Uint8Array([1, 2, 3]),
})
expect(() =>
lex.assertValidRecord('com.example.byteLength', {
$type: 'com.example.byteLength',
bytes: new Uint8Array([1]),
}),
).toThrow('Record/bytes must not be smaller than 2 bytes')
expect(() =>
lex.assertValidRecord('com.example.byteLength', {
$type: 'com.example.byteLength',
bytes: new Uint8Array([1, 2, 3, 4, 5]),
}),
).toThrow('Record/bytes must not be larger than 4 bytes')
})
})

@@ -565,14 +741,33 @@

it('Passes valid parameters', () => {
lex.assertValidXrpcParams('com.example.query', {
const queryResult = lex.assertValidXrpcParams('com.example.query', {
boolean: true,
number: 123.45,
float: 123.45,
integer: 123,
string: 'string',
array: ['x', 'y'],
})
lex.assertValidXrpcParams('com.example.procedure', {
expect(queryResult).toEqual({
boolean: true,
number: 123.45,
float: 123.45,
integer: 123,
string: 'string',
array: ['x', 'y'],
def: 0,
})
const paramResult = lex.assertValidXrpcParams('com.example.procedure', {
boolean: true,
float: 123.45,
integer: 123,
string: 'string',
array: ['x', 'y'],
def: 1,
})
expect(paramResult).toEqual({
boolean: true,
float: 123.45,
integer: 123,
string: 'string',
array: ['x', 'y'],
def: 1,
})
})

@@ -583,3 +778,3 @@

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,

@@ -590,5 +785,12 @@ })

boolean: true,
number: 123.45,
float: 123.45,
}),
).toThrow('Params must have the property "integer"')
expect(() =>
lex.assertValidXrpcParams('com.example.query', {
boolean: true,
float: 123.45,
integer: undefined,
}),
).toThrow('Params must have the property "integer"')
})

@@ -600,3 +802,3 @@

boolean: 'string',
number: 123.45,
float: 123.45,
integer: 123,

@@ -609,7 +811,16 @@ string: 'string',

boolean: true,
number: true,
float: true,
integer: 123,
string: 'string',
}),
).toThrow('number must be a number')
).toThrow('float must be a number')
expect(() =>
lex.assertValidXrpcParams('com.example.query', {
boolean: true,
float: 123.45,
integer: 123,
string: 'string',
array: 'x',
}),
).toThrow('array must be an array')
})

@@ -626,3 +837,3 @@ })

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,

@@ -640,3 +851,3 @@ string: 'string',

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,

@@ -660,3 +871,3 @@ string: 'string',

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,

@@ -669,3 +880,3 @@ string: 'string',

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,

@@ -683,3 +894,3 @@ string: 'string',

boolean: true,
number: 123.45,
float: 123.45,
integer: 123,

@@ -686,0 +897,0 @@ string: 'string',

@@ -10,4 +10,6 @@ {

"references": [
{ "path": "../nsid/tsconfig.build.json" }
{ "path": "../common/tsconfig.build.json" },
{ "path": "../nsid/tsconfig.build.json" },
{ "path": "../uri/tsconfig.build.json" }
]
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with โšก๏ธ by Socket Inc