@atproto/lex-schema 0.0.19 → 0.1.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/core/$type.js +2 -6
- package/dist/core/$type.js.map +1 -1
- package/dist/core/record-key.js +4 -8
- package/dist/core/record-key.js.map +1 -1
- package/dist/core/result.js +6 -14
- package/dist/core/result.js.map +1 -1
- package/dist/core/schema.js +16 -20
- package/dist/core/schema.js.map +1 -1
- package/dist/core/standard-schema.js +5 -10
- package/dist/core/standard-schema.js.map +1 -1
- package/dist/core/string-format.d.ts.map +1 -1
- package/dist/core/string-format.js +37 -61
- package/dist/core/string-format.js.map +1 -1
- package/dist/core/types.js +1 -2
- package/dist/core/validation-error.js +14 -25
- package/dist/core/validation-error.js.map +1 -1
- package/dist/core/validation-issue.js +13 -41
- package/dist/core/validation-issue.js.map +1 -1
- package/dist/core/validator.js +19 -28
- package/dist/core/validator.js.map +1 -1
- package/dist/core.js +9 -12
- package/dist/core.js.map +1 -1
- package/dist/external.js +3 -6
- package/dist/external.js.map +1 -1
- package/dist/helpers.js +5 -9
- package/dist/helpers.js.map +1 -1
- package/dist/index.js +3 -7
- package/dist/index.js.map +1 -1
- package/dist/schema/array.js +5 -11
- package/dist/schema/array.js.map +1 -1
- package/dist/schema/blob.js +10 -17
- package/dist/schema/blob.js.map +1 -1
- package/dist/schema/boolean.js +8 -9
- package/dist/schema/boolean.js.map +1 -1
- package/dist/schema/bytes.js +7 -12
- package/dist/schema/bytes.js.map +1 -1
- package/dist/schema/cid.js +7 -12
- package/dist/schema/cid.js.map +1 -1
- package/dist/schema/custom.js +5 -13
- package/dist/schema/custom.js.map +1 -1
- package/dist/schema/dict.js +6 -13
- package/dist/schema/dict.js.map +1 -1
- package/dist/schema/discriminated-union.js +10 -18
- package/dist/schema/discriminated-union.js.map +1 -1
- package/dist/schema/enum.js +6 -11
- package/dist/schema/enum.js.map +1 -1
- package/dist/schema/integer.js +5 -10
- package/dist/schema/integer.js.map +1 -1
- package/dist/schema/intersection.js +4 -11
- package/dist/schema/intersection.js.map +1 -1
- package/dist/schema/lex-map.js +13 -14
- package/dist/schema/lex-map.js.map +1 -1
- package/dist/schema/lex-value.js +11 -12
- package/dist/schema/lex-value.js.map +1 -1
- package/dist/schema/literal.js +4 -10
- package/dist/schema/literal.js.map +1 -1
- package/dist/schema/never.js +8 -9
- package/dist/schema/never.js.map +1 -1
- package/dist/schema/null.js +9 -10
- package/dist/schema/null.js.map +1 -1
- package/dist/schema/nullable.js +5 -10
- package/dist/schema/nullable.js.map +1 -1
- package/dist/schema/object.js +8 -14
- package/dist/schema/object.js.map +1 -1
- package/dist/schema/optional.js +5 -10
- package/dist/schema/optional.js.map +1 -1
- package/dist/schema/params.js +39 -44
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/payload.js +5 -13
- package/dist/schema/payload.js.map +1 -1
- package/dist/schema/permission-set.js +2 -10
- package/dist/schema/permission-set.js.map +1 -1
- package/dist/schema/permission.js +2 -9
- package/dist/schema/permission.js.map +1 -1
- package/dist/schema/procedure.js +3 -13
- package/dist/schema/procedure.js.map +1 -1
- package/dist/schema/query.js +3 -12
- package/dist/schema/query.js.map +1 -1
- package/dist/schema/record.js +15 -24
- package/dist/schema/record.js.map +1 -1
- package/dist/schema/ref.js +4 -9
- package/dist/schema/ref.js.map +1 -1
- package/dist/schema/refine.js +3 -6
- package/dist/schema/refine.js.map +1 -1
- package/dist/schema/regexp.js +4 -11
- package/dist/schema/regexp.js.map +1 -1
- package/dist/schema/string.js +15 -26
- package/dist/schema/string.js.map +1 -1
- package/dist/schema/subscription.js +3 -12
- package/dist/schema/subscription.js.map +1 -1
- package/dist/schema/token.js +5 -11
- package/dist/schema/token.js.map +1 -1
- package/dist/schema/typed-object.js +11 -18
- package/dist/schema/typed-object.js.map +1 -1
- package/dist/schema/typed-ref.js +4 -9
- package/dist/schema/typed-ref.js.map +1 -1
- package/dist/schema/typed-union.js +8 -15
- package/dist/schema/typed-union.js.map +1 -1
- package/dist/schema/union.js +5 -11
- package/dist/schema/union.js.map +1 -1
- package/dist/schema/unknown.js +8 -9
- package/dist/schema/unknown.js.map +1 -1
- package/dist/schema/with-default.js +4 -11
- package/dist/schema/with-default.js.map +1 -1
- package/dist/schema.js +38 -41
- package/dist/schema.js.map +1 -1
- package/dist/util/array-agg.js +1 -4
- package/dist/util/array-agg.js.map +1 -1
- package/dist/util/assertion-util.js +1 -2
- package/dist/util/if-any.js +1 -2
- package/dist/util/lazy-property.js +1 -4
- package/dist/util/lazy-property.js.map +1 -1
- package/dist/util/memoize.js +2 -6
- package/dist/util/memoize.js.map +1 -1
- package/package.json +7 -8
- package/src/core/string-format.ts +2 -1
- package/src/core/validation-issue.ts +1 -1
- package/src/helpers.test.ts +52 -52
- package/src/schema/record.test.ts +2 -2
- package/src/schema/ref.test.ts +1 -1
- package/src/schema/string.test.ts +6 -6
- package/src/schema/subscription.test.ts +4 -4
- package/src/schema/typed-object.test.ts +2 -2
package/dist/schema/record.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const core_js_1 = require("../core.js");
|
|
6
|
-
const lazy_property_js_1 = require("../util/lazy-property.js");
|
|
7
|
-
const literal_js_1 = require("./literal.js");
|
|
8
|
-
const string_js_1 = require("./string.js");
|
|
1
|
+
import { $typed, Schema, } from '../core.js';
|
|
2
|
+
import { lazyProperty } from '../util/lazy-property.js';
|
|
3
|
+
import { literal } from './literal.js';
|
|
4
|
+
import { string } from './string.js';
|
|
9
5
|
/**
|
|
10
6
|
* Schema for AT Protocol records with a type identifier and key constraints.
|
|
11
7
|
*
|
|
@@ -26,17 +22,13 @@ const string_js_1 = require("./string.js");
|
|
|
26
22
|
* )
|
|
27
23
|
* ```
|
|
28
24
|
*/
|
|
29
|
-
class RecordSchema extends
|
|
30
|
-
key;
|
|
31
|
-
$type;
|
|
32
|
-
schema;
|
|
33
|
-
type = 'record';
|
|
34
|
-
keySchema;
|
|
25
|
+
export class RecordSchema extends Schema {
|
|
35
26
|
constructor(key, $type, schema) {
|
|
36
27
|
super();
|
|
37
28
|
this.key = key;
|
|
38
29
|
this.$type = $type;
|
|
39
30
|
this.schema = schema;
|
|
31
|
+
this.type = 'record';
|
|
40
32
|
this.keySchema = recordKey(key);
|
|
41
33
|
}
|
|
42
34
|
validateInContext(input, ctx) {
|
|
@@ -50,7 +42,7 @@ class RecordSchema extends core_js_1.Schema {
|
|
|
50
42
|
return result;
|
|
51
43
|
}
|
|
52
44
|
build(input) {
|
|
53
|
-
return
|
|
45
|
+
return $typed(input, this.$type);
|
|
54
46
|
}
|
|
55
47
|
isTypeOf(value) {
|
|
56
48
|
return value.$type === this.$type;
|
|
@@ -60,21 +52,20 @@ class RecordSchema extends core_js_1.Schema {
|
|
|
60
52
|
* @see {@link build}
|
|
61
53
|
*/
|
|
62
54
|
get $build() {
|
|
63
|
-
return
|
|
55
|
+
return lazyProperty(this, '$build', this.build.bind(this));
|
|
64
56
|
}
|
|
65
57
|
/**
|
|
66
58
|
* Bound alias for {@link isTypeOf} for compatibility with generated utilities.
|
|
67
59
|
* @see {@link isTypeOf}
|
|
68
60
|
*/
|
|
69
61
|
get $isTypeOf() {
|
|
70
|
-
return
|
|
62
|
+
return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this));
|
|
71
63
|
}
|
|
72
64
|
}
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const selfLiteralSchema = (0, literal_js_1.literal)('self');
|
|
65
|
+
const keySchema = string({ minLength: 1 });
|
|
66
|
+
const tidSchema = string({ format: 'tid' });
|
|
67
|
+
const nsidSchema = string({ format: 'nsid' });
|
|
68
|
+
const selfLiteralSchema = literal('self');
|
|
78
69
|
function recordKey(key) {
|
|
79
70
|
// @NOTE Use cached instances for common schemas
|
|
80
71
|
if (key === 'any')
|
|
@@ -87,12 +78,12 @@ function recordKey(key) {
|
|
|
87
78
|
const value = key.slice(8);
|
|
88
79
|
if (value === 'self')
|
|
89
80
|
return selfLiteralSchema;
|
|
90
|
-
return
|
|
81
|
+
return literal(value);
|
|
91
82
|
}
|
|
92
83
|
throw new Error(`Unsupported record key type: ${key}`);
|
|
93
84
|
}
|
|
94
85
|
/*@__NO_SIDE_EFFECTS__*/
|
|
95
|
-
function record(key, type, validator) {
|
|
86
|
+
export function record(key, type, validator) {
|
|
96
87
|
return new RecordSchema(key, type, validator);
|
|
97
88
|
}
|
|
98
89
|
//# sourceMappingURL=record.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/schema/record.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/schema/record.ts"],"names":[],"mappings":"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","sourcesContent":["import { LexMap } from '@atproto/lex-data'\nimport {\n $Typed,\n $typed,\n InferInput,\n InferOutput,\n LexiconRecordKey,\n NsidString,\n Schema,\n TidString,\n Unknown$TypedObject,\n ValidationContext,\n Validator,\n} from '../core.js'\nimport { lazyProperty } from '../util/lazy-property.js'\nimport { literal } from './literal.js'\nimport { string } from './string.js'\n\n/**\n * Infers the record key type from a RecordSchema.\n *\n * @template R - The RecordSchema type\n */\nexport type InferRecordKey<R extends RecordSchema> =\n R extends RecordSchema<infer TKey> ? RecordKeySchemaOutput<TKey> : never\n\nexport type TypedRecord<\n TType extends NsidString,\n TValue extends { $type?: unknown } = { $type?: unknown },\n> = TValue extends { $type: TType }\n ? TValue\n : $Typed<Exclude<TValue, Unknown$TypedObject>, TType>\n\n/**\n * Schema for AT Protocol records with a type identifier and key constraints.\n *\n * Records are the primary data unit in AT Protocol. Each record has a `$type`\n * field identifying its Lexicon schema, and is stored at a specific key\n * (TID, NSID, or other format) in a repository.\n *\n * @template TKey - The record key type ('tid', 'nsid', 'any', or 'literal:...')\n * @template TType - The NSID string identifying this record type\n * @template TShape - The validator type for the record's data shape\n *\n * @example\n * ```ts\n * const postSchema = new RecordSchema(\n * 'tid',\n * 'app.bsky.feed.post',\n * l.object({ text: l.string(), createdAt: l.string() })\n * )\n * ```\n */\nexport class RecordSchema<\n const TKey extends LexiconRecordKey = LexiconRecordKey,\n const TType extends NsidString = NsidString,\n const TShape extends Validator<LexMap> = Validator<LexMap>,\n> extends Schema<\n $Typed<InferInput<TShape>, TType>,\n $Typed<InferOutput<TShape>, TType>\n> {\n readonly type = 'record' as const\n\n keySchema: RecordKeySchema<TKey>\n\n constructor(\n readonly key: TKey,\n readonly $type: TType,\n readonly schema: TShape,\n ) {\n super()\n this.keySchema = recordKey(key)\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const result = ctx.validate(input, this.schema)\n\n if (!result.success) {\n return result\n }\n\n if (result.value.$type !== this.$type) {\n return ctx.issueInvalidPropertyValue(result.value, '$type', [this.$type])\n }\n\n return result\n }\n\n build(\n input: Omit<InferOutput<TShape>, '$type'>,\n ): $Typed<InferOutput<TShape>, TType>\n build(\n input: Omit<InferInput<TShape>, '$type'>,\n ): $Typed<InferInput<TShape>, TType>\n build(input: Record<string, unknown>) {\n return $typed(input, this.$type)\n }\n\n isTypeOf<TValue extends { $type?: unknown }>(\n value: TValue,\n ): value is TypedRecord<TType, TValue> {\n return value.$type === this.$type\n }\n\n /**\n * Bound alias for {@link build} for compatibility with generated utilities.\n * @see {@link build}\n */\n get $build(): typeof this.build {\n return lazyProperty(this, '$build', this.build.bind(this))\n }\n\n /**\n * Bound alias for {@link isTypeOf} for compatibility with generated utilities.\n * @see {@link isTypeOf}\n */\n get $isTypeOf(): typeof this.isTypeOf {\n return lazyProperty(this, '$isTypeOf', this.isTypeOf.bind(this))\n }\n}\n\nexport type RecordKeySchemaOutput<Key extends LexiconRecordKey> =\n Key extends 'any'\n ? string\n : Key extends 'tid'\n ? TidString\n : Key extends 'nsid'\n ? NsidString\n : Key extends `literal:${infer L extends string}`\n ? L\n : never\n\nexport type RecordKeySchema<Key extends LexiconRecordKey> = Schema<\n RecordKeySchemaOutput<Key>\n>\n\nconst keySchema = string({ minLength: 1 })\nconst tidSchema = string({ format: 'tid' })\nconst nsidSchema = string({ format: 'nsid' })\nconst selfLiteralSchema = literal('self')\n\nfunction recordKey<Key extends LexiconRecordKey>(\n key: Key,\n): RecordKeySchema<Key> {\n // @NOTE Use cached instances for common schemas\n if (key === 'any') return keySchema as any\n if (key === 'tid') return tidSchema as any\n if (key === 'nsid') return nsidSchema as any\n if (key.startsWith('literal:')) {\n const value = key.slice(8) as RecordKeySchemaOutput<Key>\n if (value === 'self') return selfLiteralSchema as any\n return literal(value)\n }\n\n throw new Error(`Unsupported record key type: ${key}`)\n}\n\n/**\n * Ensures that a `$type` used in a record is a valid NSID (i.e. no fragment).\n */\ntype AsNsid<T> = T extends `${string}#${string}` ? never : T\n\n/**\n * Creates a record schema for AT Protocol records.\n *\n * Records are the primary data unit in AT Protocol repositories. They have\n * a `$type` field identifying their Lexicon schema, and are stored at keys\n * following a specific format (TID, NSID, etc.).\n *\n * This function offers two overloads:\n * - One that infers the output type from the provided arguments (does not\n * support circular references)\n * - One with an explicitly defined interface for use with codegen and\n * circular references\n *\n * @param key - The record key type: 'tid', 'nsid', 'any', or 'literal:value'\n * @param type - The NSID identifying this record type (e.g., 'app.bsky.feed.post')\n * @param validator - Schema validator for the record's properties\n * @returns A new {@link RecordSchema} instance\n *\n * @example\n * ```ts\n * // Post record with TID key\n * const postSchema = l.record('tid', 'app.bsky.feed.post', l.object({\n * text: l.string({ maxGraphemes: 300 }),\n * createdAt: l.string({ format: 'datetime' }),\n * reply: l.optional(l.object({\n * root: l.ref(() => strongRefSchema),\n * parent: l.ref(() => strongRefSchema),\n * })),\n * }))\n *\n * // Profile record with literal 'self' key\n * const profileSchema = l.record('literal:self', 'app.bsky.actor.profile', l.object({\n * displayName: l.optional(l.string({ maxGraphemes: 64 })),\n * description: l.optional(l.string({ maxGraphemes: 256 })),\n * avatar: l.optional(l.blob({ accept: ['image/*'] })),\n * }))\n *\n * // Build a record with automatic $type injection\n * const post = postSchema.build({ text: 'Hello!', createdAt: new Date().toISOString() })\n * ```\n */\nexport function record<\n const K extends LexiconRecordKey,\n const T extends NsidString,\n const S extends Validator<LexMap>,\n>(key: K, type: AsNsid<T>, validator: S): RecordSchema<K, T, S>\nexport function record<\n const K extends LexiconRecordKey,\n const V extends LexMap & { $type: NsidString },\n>(\n key: K,\n type: AsNsid<V['$type']>,\n validator: Validator<Omit<V, '$type'>>,\n): RecordSchema<K, V['$type'], Validator<Omit<V, '$type'>>>\n/*@__NO_SIDE_EFFECTS__*/\nexport function record<\n const K extends LexiconRecordKey,\n const T extends NsidString,\n const S extends Validator<LexMap>,\n>(key: K, type: T, validator: S) {\n return new RecordSchema<K, T, S>(key, type, validator)\n}\n"]}
|
package/dist/schema/ref.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RefSchema = void 0;
|
|
4
|
-
exports.ref = ref;
|
|
5
|
-
const core_js_1 = require("../core.js");
|
|
1
|
+
import { Schema, } from '../core.js';
|
|
6
2
|
/**
|
|
7
3
|
* Schema for creating references to other schemas with lazy resolution.
|
|
8
4
|
*
|
|
@@ -20,13 +16,13 @@ const core_js_1 = require("../core.js");
|
|
|
20
16
|
* })
|
|
21
17
|
* ```
|
|
22
18
|
*/
|
|
23
|
-
class RefSchema extends
|
|
24
|
-
type = 'ref';
|
|
19
|
+
export class RefSchema extends Schema {
|
|
25
20
|
#getter;
|
|
26
21
|
constructor(getter) {
|
|
27
22
|
// @NOTE In order to avoid circular dependency issues, we don't resolve
|
|
28
23
|
// the schema here. Instead, we resolve it lazily when first accessed.
|
|
29
24
|
super();
|
|
25
|
+
this.type = 'ref';
|
|
30
26
|
this.#getter = getter;
|
|
31
27
|
}
|
|
32
28
|
get validator() {
|
|
@@ -39,8 +35,7 @@ class RefSchema extends core_js_1.Schema {
|
|
|
39
35
|
return ctx.validate(input, this.validator);
|
|
40
36
|
}
|
|
41
37
|
}
|
|
42
|
-
|
|
43
|
-
function ref(get) {
|
|
38
|
+
export function ref(get) {
|
|
44
39
|
return new RefSchema(get);
|
|
45
40
|
}
|
|
46
41
|
//# sourceMappingURL=ref.js.map
|
package/dist/schema/ref.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ref.js","sourceRoot":"","sources":["../../src/schema/ref.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ref.js","sourceRoot":"","sources":["../../src/schema/ref.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,MAAM,GAIP,MAAM,YAAY,CAAA;AASnB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,SACX,SAAQ,MAAuD;IAK/D,OAAO,CAA6B;IAEpC,YAAY,MAAmC;QAC7C,uEAAuE;QACvE,sEAAsE;QAEtE,KAAK,EAAE,CAAA;QARA,SAAI,GAAG,KAAc,CAAA;QAU5B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;IACvB,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChC,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAC,SAAS,CAAA;IACvB,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IAC5C,CAAC;CACF;AAqCD,MAAM,UAAU,GAAG,CACjB,GAAgC;IAEhC,OAAO,IAAI,SAAS,CAAa,GAAG,CAAC,CAAA;AACvC,CAAC","sourcesContent":["import {\n InferInput,\n InferOutput,\n Schema,\n ValidationContext,\n Validator,\n WrappedValidator,\n} from '../core.js'\n\n/**\n * Function type that returns a validator, used for lazy schema resolution.\n *\n * @template TValidator - The validator type that will be returned\n */\nexport type RefSchemaGetter<out TValidator extends Validator> = () => TValidator\n\n/**\n * Schema for creating references to other schemas with lazy resolution.\n *\n * Useful for handling circular references or breaking module dependency cycles.\n * The referenced schema is resolved lazily when first needed for validation.\n *\n * @template TValidator - The referenced validator type\n *\n * @example\n * ```ts\n * // Self-referential schema for tree structure\n * const nodeSchema = l.object({\n * value: l.string(),\n * children: l.array(l.ref(() => nodeSchema)),\n * })\n * ```\n */\nexport class RefSchema<const TValidator extends Validator>\n extends Schema<InferInput<TValidator>, InferOutput<TValidator>>\n implements WrappedValidator<TValidator>\n{\n readonly type = 'ref' as const\n\n #getter: RefSchemaGetter<TValidator>\n\n constructor(getter: RefSchemaGetter<TValidator>) {\n // @NOTE In order to avoid circular dependency issues, we don't resolve\n // the schema here. Instead, we resolve it lazily when first accessed.\n\n super()\n\n this.#getter = getter\n }\n\n get validator(): TValidator {\n return this.#getter.call(null)\n }\n\n unwrap(): TValidator {\n return this.validator\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n return ctx.validate(input, this.validator)\n }\n}\n\n/**\n * Creates a reference schema with lazy resolution.\n *\n * Allows referencing schemas that may not be defined yet, enabling\n * circular references and breaking dependency cycles. The getter function\n * is called lazily when validation is first performed.\n *\n * @param get - Function that returns the referenced validator\n * @returns A new {@link RefSchema} instance\n *\n * @example\n * ```ts\n * // Circular reference - tree node that contains children of the same type\n * const treeNodeSchema = l.object({\n * name: l.string(),\n * children: l.optional(l.array(l.ref(() => treeNodeSchema))),\n * })\n *\n * // Cross-module reference\n * const commentSchema = l.object({\n * text: l.string(),\n * author: l.ref(() => userSchema), // userSchema defined elsewhere\n * })\n *\n * // Explicitly typed reference\n * const itemSchema = l.ref<Item>(() => complexItemSchema)\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function ref<const TValidator extends Validator>(\n get: RefSchemaGetter<TValidator>,\n): RefSchema<TValidator>\nexport function ref<TInput, TOutput extends TInput = TInput>(\n get: RefSchemaGetter<Validator<TInput, TOutput>>,\n): RefSchema<Validator<TInput, TOutput>>\nexport function ref<const TValidator extends Validator>(\n get: RefSchemaGetter<TValidator>,\n) {\n return new RefSchema<TValidator>(get)\n}\n"]}
|
package/dist/schema/refine.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.refine = refine;
|
|
4
|
-
const core_js_1 = require("../core.js");
|
|
1
|
+
import { IssueCustom, } from '../core.js';
|
|
5
2
|
/*@__NO_SIDE_EFFECTS__*/
|
|
6
|
-
function refine(schema, refinement) {
|
|
3
|
+
export function refine(schema, refinement) {
|
|
7
4
|
// This is basically the same as monkey patching the "validateInContext"
|
|
8
5
|
// method to the schema, but done in a way that does not mutate the original
|
|
9
6
|
// schema. This is safe to do because Validators don't update their internal
|
|
@@ -26,7 +23,7 @@ function validateInContextUnbound(input, ctx) {
|
|
|
26
23
|
const checkResult = this.refinement.check.call(null, result.value, ctx);
|
|
27
24
|
if (!checkResult) {
|
|
28
25
|
const path = ctx.concatPath(this.refinement.path);
|
|
29
|
-
return ctx.issue(new
|
|
26
|
+
return ctx.issue(new IssueCustom(path, input, this.refinement.message));
|
|
30
27
|
}
|
|
31
28
|
return result;
|
|
32
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refine.js","sourceRoot":"","sources":["../../src/schema/refine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"refine.js","sourceRoot":"","sources":["../../src/schema/refine.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,GAIZ,MAAM,YAAY,CAAA;AA2GnB,wBAAwB;AACxB,MAAM,UAAU,MAAM,CACpB,MAAkB,EAClB,UAA+B;IAE/B,wEAAwE;IACxE,4EAA4E;IAC5E,4EAA4E;IAC5E,6BAA6B;IAC7B,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE;QAC3B,iBAAiB,EAAE;YACjB,8DAA8D;YAC9D,KAAK,EAAE,wBAAwB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YAC5D,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,IAAI;SACnB;KACF,CAAC,CAAA;AACJ,CAAC;AAED,wBAAwB;AACxB,SAAS,wBAAwB,CAK/B,KAAc,EACd,GAAsB;IAEtB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IAC/C,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAA;IAElC,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IACvE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QACjD,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;IACzE,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import {\n InferInput,\n IssueCustom,\n ValidationContext,\n ValidationResult,\n Validator,\n} from '../core.js'\nimport { CustomAssertionContext } from './custom.js'\n\n/**\n * Configuration for a refinement check that validates a condition.\n *\n * @template T - The type being validated\n * @property check - Function that returns true if the value passes the check\n * @property message - Error message when the check fails\n * @property path - Optional path to associate with the error\n */\nexport type RefinementCheck<T> = {\n check: (value: T, ctx: CustomAssertionContext) => boolean\n message: string\n path?: PropertyKey | readonly PropertyKey[]\n}\n\n/**\n * Configuration for a refinement assertion that narrows the type.\n *\n * @template T - The input type being validated\n * @template Out - The narrowed output type\n * @property check - Type guard function that narrows the type\n * @property message - Error message when the assertion fails\n * @property path - Optional path to associate with the error\n */\nexport type RefinementAssertion<T, Out extends T> = {\n check: (this: null, value: T, ctx: CustomAssertionContext) => value is Out\n message: string\n path?: PropertyKey | readonly PropertyKey[]\n}\n\n/**\n * Infers the input type from a refinement configuration.\n *\n * @template R - The refinement type\n */\nexport type InferRefinement<R> =\n R extends RefinementCheck<infer T>\n ? T\n : R extends RefinementAssertion<infer T, any>\n ? T\n : never\n\n/**\n * Union type of refinement check or assertion.\n *\n * @template T - The input type being validated\n * @template Out - The output type (same as T for checks, narrowed for assertions)\n */\nexport type Refinement<T = any, Out extends T = T> =\n | RefinementCheck<T>\n | RefinementAssertion<T, Out>\n\n/**\n * Creates a refined schema by adding additional validation constraints.\n *\n * Wraps an existing schema with an additional check function. The base schema\n * is validated first, then the refinement check is applied to the result.\n *\n * @param schema - The base schema to refine\n * @param refinement - The refinement check or assertion to apply\n * @returns A new schema that includes the refinement\n *\n * @example\n * ```ts\n * // Simple check refinement\n * const positiveInt = l.refine(l.integer(), {\n * check: (value) => value > 0,\n * message: 'Value must be positive',\n * })\n *\n * positiveInt.parse(5) // 5\n * positiveInt.parse(-1) // throws\n *\n * // Type-narrowing assertion\n * const nonEmptyString = l.refine(l.string(), {\n * check: (value): value is string & { length: number } => value.length > 0,\n * message: 'String must not be empty',\n * })\n *\n * // With custom path for nested errors\n * const validDateRange = l.refine(\n * l.object({ start: l.string(), end: l.string() }),\n * {\n * check: (v) => new Date(v.start) < new Date(v.end),\n * message: 'Start date must be before end date',\n * path: ['end'],\n * }\n * )\n * ```\n */\nexport function refine<\n const TValidator extends Validator,\n TInput extends InferInput<TValidator>,\n>(\n schema: TValidator,\n refinement: RefinementAssertion<InferInput<TValidator>, TInput>,\n): TValidator & Validator<TInput>\nexport function refine<const TValidator extends Validator>(\n schema: TValidator,\n refinement: RefinementCheck<InferInput<TValidator>>,\n): TValidator\nexport function refine<\n TRefinement extends Refinement,\n const TValidator extends Validator<InferRefinement<TRefinement>>,\n>(schema: TValidator, refinement: TRefinement): TValidator\n/*@__NO_SIDE_EFFECTS__*/\nexport function refine<const TValidator extends Validator>(\n schema: TValidator,\n refinement: Refinement<unknown>,\n): TValidator {\n // This is basically the same as monkey patching the \"validateInContext\"\n // method to the schema, but done in a way that does not mutate the original\n // schema. This is safe to do because Validators don't update their internal\n // state over their lifetime.\n return Object.create(schema, {\n validateInContext: {\n // We do not use an arrow function to avoid creating a closure\n value: validateInContextUnbound.bind({ schema, refinement }),\n enumerable: false,\n writable: false,\n configurable: true,\n },\n })\n}\n\n/*@__NO_SIDE_EFFECTS__*/\nfunction validateInContextUnbound<S extends Validator>(\n this: {\n schema: S\n refinement: Refinement<InferInput<S>>\n },\n input: unknown,\n ctx: ValidationContext,\n): ValidationResult<InferInput<S>> {\n const result = ctx.validate(input, this.schema)\n if (!result.success) return result\n\n const checkResult = this.refinement.check.call(null, result.value, ctx)\n if (!checkResult) {\n const path = ctx.concatPath(this.refinement.path)\n return ctx.issue(new IssueCustom(path, input, this.refinement.message))\n }\n\n return result\n}\n"]}
|
package/dist/schema/regexp.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RegexpSchema = void 0;
|
|
4
|
-
exports.regexp = regexp;
|
|
5
|
-
const core_js_1 = require("../core.js");
|
|
1
|
+
import { Schema } from '../core.js';
|
|
6
2
|
/**
|
|
7
3
|
* Schema for validating strings against a regular expression pattern.
|
|
8
4
|
*
|
|
@@ -18,14 +14,12 @@ const core_js_1 = require("../core.js");
|
|
|
18
14
|
* schema.validate('Hello') // fails - uppercase not allowed
|
|
19
15
|
* ```
|
|
20
16
|
*/
|
|
21
|
-
class RegexpSchema extends
|
|
22
|
-
pattern;
|
|
23
|
-
message;
|
|
24
|
-
type = 'regexp';
|
|
17
|
+
export class RegexpSchema extends Schema {
|
|
25
18
|
constructor(pattern, message) {
|
|
26
19
|
super();
|
|
27
20
|
this.pattern = pattern;
|
|
28
21
|
this.message = message;
|
|
22
|
+
this.type = 'regexp';
|
|
29
23
|
}
|
|
30
24
|
validateInContext(input, ctx) {
|
|
31
25
|
if (typeof input !== 'string') {
|
|
@@ -37,7 +31,6 @@ class RegexpSchema extends core_js_1.Schema {
|
|
|
37
31
|
return ctx.success(input);
|
|
38
32
|
}
|
|
39
33
|
}
|
|
40
|
-
exports.RegexpSchema = RegexpSchema;
|
|
41
34
|
/**
|
|
42
35
|
* Creates a regexp schema that validates strings against a pattern.
|
|
43
36
|
*
|
|
@@ -68,7 +61,7 @@ exports.RegexpSchema = RegexpSchema;
|
|
|
68
61
|
* ```
|
|
69
62
|
*/
|
|
70
63
|
/*@__NO_SIDE_EFFECTS__*/
|
|
71
|
-
function regexp(pattern, message) {
|
|
64
|
+
export function regexp(pattern, message) {
|
|
72
65
|
return new RegexpSchema(pattern, message);
|
|
73
66
|
}
|
|
74
67
|
//# sourceMappingURL=regexp.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"regexp.js","sourceRoot":"","sources":["../../src/schema/regexp.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"regexp.js","sourceRoot":"","sources":["../../src/schema/regexp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,YAAY,CAAA;AAEtD;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,YAEX,SAAQ,MAAc;IAGtB,YACkB,OAAe,EACf,OAAgB;QAEhC,KAAK,EAAE,CAAA;QAHS,YAAO,GAAP,OAAO,CAAQ;QACf,YAAO,GAAP,OAAO,CAAS;QAJzB,SAAI,GAAG,QAAiB,CAAA;IAOjC,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,GAAG,CAAC,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;QACjD,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,GAAG,CAAC,kBAAkB,CAC3B,KAAK,EACL,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EACvB,IAAI,CAAC,OAAO,CACb,CAAA;QACH,CAAC;QAED,OAAO,GAAG,CAAC,OAAO,CAAC,KAAe,CAAC,CAAA;IACrC,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAwB;AACxB,MAAM,UAAU,MAAM,CACpB,OAAe,EACf,OAAgB;IAEhB,OAAO,IAAI,YAAY,CAAS,OAAO,EAAE,OAAO,CAAC,CAAA;AACnD,CAAC","sourcesContent":["import { Schema, ValidationContext } from '../core.js'\n\n/**\n * Schema for validating strings against a regular expression pattern.\n *\n * Validates that the input is a string and matches the provided pattern.\n * The pattern is tested using RegExp.test().\n *\n * @template TValue - The string type (can be narrowed with branded types)\n *\n * @example\n * ```ts\n * const schema = new RegexpSchema(/^[a-z]+$/)\n * schema.validate('hello') // success\n * schema.validate('Hello') // fails - uppercase not allowed\n * ```\n */\nexport class RegexpSchema<\n TValue extends string = string,\n> extends Schema<TValue> {\n readonly type = 'regexp' as const\n\n constructor(\n public readonly pattern: RegExp,\n public readonly message?: string,\n ) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n if (typeof input !== 'string') {\n return ctx.issueUnexpectedType(input, 'string')\n }\n\n if (!this.pattern.test(input)) {\n return ctx.issueInvalidFormat(\n input,\n this.pattern.toString(),\n this.message,\n )\n }\n\n return ctx.success(input as TValue)\n }\n}\n\n/**\n * Creates a regexp schema that validates strings against a pattern.\n *\n * Useful for custom string formats not covered by the built-in format\n * validators.\n *\n * @param pattern - Regular expression pattern to match against\n * @returns A new {@link RegexpSchema} instance\n *\n * @example\n * ```ts\n * // Simple pattern\n * const slugSchema = l.regexp(/^[a-z0-9-]+$/)\n *\n * // With anchors for exact match\n * const uuidSchema = l.regexp(\n * /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n * )\n *\n * // Semantic versioning\n * const semverSchema = l.regexp(/^\\d+\\.\\d+\\.\\d+(-[\\w.]+)?(\\+[\\w.]+)?$/)\n *\n * // Use in object\n * const configSchema = l.object({\n * name: l.regexp(/^[a-z][a-z0-9-]*$/), // kebab-case identifier\n * version: semverSchema,\n * })\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function regexp<TInput extends string = string>(\n pattern: RegExp,\n message?: string,\n): RegexpSchema<TInput> {\n return new RegexpSchema<TInput>(pattern, message)\n}\n"]}
|
package/dist/schema/string.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const lex_data_1 = require("@atproto/lex-data");
|
|
6
|
-
const core_js_1 = require("../core.js");
|
|
7
|
-
const memoize_js_1 = require("../util/memoize.js");
|
|
8
|
-
const token_js_1 = require("./token.js");
|
|
1
|
+
import { graphemeLen, ifCid, utf8Len } from '@atproto/lex-data';
|
|
2
|
+
import { Schema, isStringFormat, } from '../core.js';
|
|
3
|
+
import { memoizedOptions } from '../util/memoize.js';
|
|
4
|
+
import { TokenSchema } from './token.js';
|
|
9
5
|
/**
|
|
10
6
|
* Schema for validating string values with optional format and length constraints.
|
|
11
7
|
*
|
|
@@ -20,16 +16,10 @@ const token_js_1 = require("./token.js");
|
|
|
20
16
|
* const result = schema.validate('2024-01-15T10:30:00Z')
|
|
21
17
|
* ```
|
|
22
18
|
*/
|
|
23
|
-
class StringSchema extends
|
|
24
|
-
type = 'string';
|
|
25
|
-
// @NOTE since the _string utility allows omitting knownValues when TOptions
|
|
26
|
-
// *does* include it (since it's only used for typing), we cannot type options
|
|
27
|
-
// as TOptions directly since it may not actually include knownValues at
|
|
28
|
-
// runtime, making schema.options.knownValues potentially undefined even when
|
|
29
|
-
// TOptions includes it.
|
|
30
|
-
options;
|
|
19
|
+
export class StringSchema extends Schema {
|
|
31
20
|
constructor(options) {
|
|
32
21
|
super();
|
|
22
|
+
this.type = 'string';
|
|
33
23
|
this.options = options;
|
|
34
24
|
}
|
|
35
25
|
validateInContext(input, ctx) {
|
|
@@ -40,7 +30,7 @@ class StringSchema extends core_js_1.Schema {
|
|
|
40
30
|
let lazyUtf8Len;
|
|
41
31
|
const minLength = this.options.minLength;
|
|
42
32
|
if (minLength != null) {
|
|
43
|
-
if ((lazyUtf8Len ??=
|
|
33
|
+
if ((lazyUtf8Len ??= utf8Len(str)) < minLength) {
|
|
44
34
|
return ctx.issueTooSmall(str, 'string', minLength, lazyUtf8Len);
|
|
45
35
|
}
|
|
46
36
|
}
|
|
@@ -52,7 +42,7 @@ class StringSchema extends core_js_1.Schema {
|
|
|
52
42
|
if (str.length * 3 <= maxLength) {
|
|
53
43
|
// Input string so small it can't possibly exceed maxLength
|
|
54
44
|
}
|
|
55
|
-
else if ((lazyUtf8Len ??=
|
|
45
|
+
else if ((lazyUtf8Len ??= utf8Len(str)) > maxLength) {
|
|
56
46
|
return ctx.issueTooBig(str, 'string', maxLength, lazyUtf8Len);
|
|
57
47
|
}
|
|
58
48
|
}
|
|
@@ -66,7 +56,7 @@ class StringSchema extends core_js_1.Schema {
|
|
|
66
56
|
// Fail early.
|
|
67
57
|
return ctx.issueTooSmall(str, 'grapheme', minGraphemes, str.length);
|
|
68
58
|
}
|
|
69
|
-
else if ((lazyGraphLen ??=
|
|
59
|
+
else if ((lazyGraphLen ??= graphemeLen(str)) < minGraphemes) {
|
|
70
60
|
return ctx.issueTooSmall(str, 'grapheme', minGraphemes, lazyGraphLen);
|
|
71
61
|
}
|
|
72
62
|
}
|
|
@@ -76,19 +66,18 @@ class StringSchema extends core_js_1.Schema {
|
|
|
76
66
|
// If the JavaScript string length (UTF-16) is within the maximum limit,
|
|
77
67
|
// its grapheme length (which <= .length) will also be within.
|
|
78
68
|
}
|
|
79
|
-
else if ((lazyGraphLen ??=
|
|
69
|
+
else if ((lazyGraphLen ??= graphemeLen(str)) > maxGraphemes) {
|
|
80
70
|
return ctx.issueTooBig(str, 'grapheme', maxGraphemes, lazyGraphLen);
|
|
81
71
|
}
|
|
82
72
|
}
|
|
83
73
|
const format = this.options.format;
|
|
84
|
-
if (format != null && !
|
|
74
|
+
if (format != null && !isStringFormat(str, format, ctx.options)) {
|
|
85
75
|
return ctx.issueInvalidFormat(str, format);
|
|
86
76
|
}
|
|
87
77
|
return ctx.success(str);
|
|
88
78
|
}
|
|
89
79
|
}
|
|
90
|
-
|
|
91
|
-
function coerceToString(input) {
|
|
80
|
+
export function coerceToString(input) {
|
|
92
81
|
switch (typeof input) {
|
|
93
82
|
// @NOTE We do *not* coerce numbers/booleans to strings because that can
|
|
94
83
|
// lead to them being accepted as string instead of being coerced to
|
|
@@ -101,7 +90,7 @@ function coerceToString(input) {
|
|
|
101
90
|
return null;
|
|
102
91
|
// @NOTE Allow using TokenSchema instances in places expecting strings,
|
|
103
92
|
// converting them to their string value.
|
|
104
|
-
if (input instanceof
|
|
93
|
+
if (input instanceof TokenSchema) {
|
|
105
94
|
return input.toString();
|
|
106
95
|
}
|
|
107
96
|
if (input instanceof Date) {
|
|
@@ -112,7 +101,7 @@ function coerceToString(input) {
|
|
|
112
101
|
if (input instanceof URL) {
|
|
113
102
|
return input.toString();
|
|
114
103
|
}
|
|
115
|
-
const cid =
|
|
104
|
+
const cid = ifCid(input);
|
|
116
105
|
if (cid)
|
|
117
106
|
return cid.toString();
|
|
118
107
|
if (input instanceof String) {
|
|
@@ -154,5 +143,5 @@ function _string(options = {}) {
|
|
|
154
143
|
* const handleSchema = l.string({ format: 'handle', minLength: 3, maxLength: 253 })
|
|
155
144
|
* ```
|
|
156
145
|
*/
|
|
157
|
-
|
|
146
|
+
export const string = /*#__PURE__*/ memoizedOptions(_string);
|
|
158
147
|
//# sourceMappingURL=string.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"string.js","sourceRoot":"","sources":["../../src/schema/string.ts"],"names":[],"mappings":"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","sourcesContent":["import { graphemeLen, ifCid, utf8Len } from '@atproto/lex-data'\nimport {\n InferStringFormat,\n Restricted,\n Schema,\n StringFormat,\n UnknownString,\n ValidationContext,\n isStringFormat,\n} from '../core.js'\nimport { IfAny } from '../util/if-any.js'\nimport { memoizedOptions } from '../util/memoize.js'\nimport { TokenSchema } from './token.js'\n\n/**\n * Configuration options for string schema validation.\n *\n * @property format - Expected string format (e.g., 'datetime', 'uri', 'at-uri', 'did', 'handle', 'nsid', 'cid', 'tid', 'record-key', 'at-identifier', 'language')\n * @property knownValues - Known string literal values for type narrowing\n * @property minLength - Minimum length in UTF-8 bytes\n * @property maxLength - Maximum length in UTF-8 bytes\n * @property minGraphemes - Minimum number of grapheme clusters\n * @property maxGraphemes - Maximum number of grapheme clusters\n */\nexport type StringSchemaOptions = {\n format?: StringFormat\n knownValues?: readonly string[]\n minLength?: number\n maxLength?: number\n minGraphemes?: number\n maxGraphemes?: number\n}\n\n/**\n * Schema for validating string values with optional format and length constraints.\n *\n * Supports various string formats defined in the Lexicon specification, as well as\n * length constraints measured in UTF-8 bytes or grapheme clusters.\n *\n * @template TOptions - The configuration options type\n *\n * @example\n * ```ts\n * const schema = new StringSchema({ format: 'datetime', maxLength: 64 })\n * const result = schema.validate('2024-01-15T10:30:00Z')\n * ```\n */\nexport class StringSchema<\n const TOptions extends StringSchemaOptions = StringSchemaOptions,\n> extends Schema<\n IfAny<\n TOptions,\n string,\n TOptions extends { format: infer F extends StringFormat }\n ? InferStringFormat<F>\n : TOptions extends { knownValues: readonly (infer V extends string)[] }\n ? V | UnknownString\n : string\n >\n> {\n readonly type = 'string' as const\n\n // @NOTE since the _string utility allows omitting knownValues when TOptions\n // *does* include it (since it's only used for typing), we cannot type options\n // as TOptions directly since it may not actually include knownValues at\n // runtime, making schema.options.knownValues potentially undefined even when\n // TOptions includes it.\n readonly options: StringSchemaOptions\n\n constructor(options: TOptions) {\n super()\n this.options = options\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const str = coerceToString(input)\n if (str == null) {\n return ctx.issueUnexpectedType(input, 'string')\n }\n\n let lazyUtf8Len: number\n\n const minLength = this.options.minLength\n if (minLength != null) {\n if ((lazyUtf8Len ??= utf8Len(str)) < minLength) {\n return ctx.issueTooSmall(str, 'string', minLength, lazyUtf8Len)\n }\n }\n\n const maxLength = this.options.maxLength\n if (maxLength != null) {\n // Optimization: we can avoid computing the UTF-8 length if the maximum\n // possible length, in bytes, of the input JS string is smaller than the\n // maxLength (in UTF-8 string bytes).\n if (str.length * 3 <= maxLength) {\n // Input string so small it can't possibly exceed maxLength\n } else if ((lazyUtf8Len ??= utf8Len(str)) > maxLength) {\n return ctx.issueTooBig(str, 'string', maxLength, lazyUtf8Len)\n }\n }\n\n // Optimization: count graphemes once\n let lazyGraphLen: number\n\n const minGraphemes = this.options.minGraphemes\n if (minGraphemes != null) {\n if (str.length < minGraphemes) {\n // If the JavaScript string length (UTF-16) is below the minimal limit,\n // its grapheme length (which <= .length) will also be below.\n // Fail early.\n return ctx.issueTooSmall(str, 'grapheme', minGraphemes, str.length)\n } else if ((lazyGraphLen ??= graphemeLen(str)) < minGraphemes) {\n return ctx.issueTooSmall(str, 'grapheme', minGraphemes, lazyGraphLen)\n }\n }\n\n const maxGraphemes = this.options.maxGraphemes\n if (maxGraphemes != null) {\n if (str.length <= maxGraphemes) {\n // If the JavaScript string length (UTF-16) is within the maximum limit,\n // its grapheme length (which <= .length) will also be within.\n } else if ((lazyGraphLen ??= graphemeLen(str)) > maxGraphemes) {\n return ctx.issueTooBig(str, 'grapheme', maxGraphemes, lazyGraphLen)\n }\n }\n\n const format = this.options.format\n if (format != null && !isStringFormat(str, format, ctx.options)) {\n return ctx.issueInvalidFormat(str, format)\n }\n\n return ctx.success(str)\n }\n}\n\nexport function coerceToString(input: unknown): string | null {\n switch (typeof input) {\n // @NOTE We do *not* coerce numbers/booleans to strings because that can\n // lead to them being accepted as string instead of being coerced to\n // number/boolean when the input is a string and the expected result is\n // number/boolean (e.g. in params).\n case 'string':\n return input\n case 'object': {\n if (input == null) return null\n\n // @NOTE Allow using TokenSchema instances in places expecting strings,\n // converting them to their string value.\n if (input instanceof TokenSchema) {\n return input.toString()\n }\n\n if (input instanceof Date) {\n if (Number.isNaN(input.getTime())) return null\n return input.toISOString()\n }\n\n if (input instanceof URL) {\n return input.toString()\n }\n\n const cid = ifCid(input)\n if (cid) return cid.toString()\n\n if (input instanceof String) {\n return input.valueOf()\n }\n }\n\n // falls through\n default:\n return null\n }\n}\n\nfunction _string(): StringSchema<NonNullable<unknown>>\nfunction _string<\n // Allow calling `string<{ knownValues: [...] }>()` without passing an options\n // object, since knownValues is only used for typing and has no runtime\n // effect, so it can be safely omitted at runtime.\n const TOptions extends {\n knownValues: StringSchemaOptions['knownValues']\n } & {\n [K in Exclude<\n keyof StringSchemaOptions,\n 'knownValues'\n >]?: Restricted<`An options argument is required when using the \"${K}\" option`>\n },\n>(): StringSchema<\n IfAny<TOptions, any, { knownValues: TOptions['knownValues'] }>\n>\nfunction _string<const TOptions extends StringSchemaOptions>(\n // If TOptions is explicitly provided (e.g. `string<{ ... }>({ ... })`), we\n // allow the actual options argument to omit the \"knownValues\" property since\n // it's only used for inferring the type and has no runtime effect.\n options: TOptions | Omit<TOptions, 'knownValues'>,\n): StringSchema<TOptions>\nfunction _string(options: StringSchemaOptions = {}) {\n return new StringSchema(options)\n}\n\n/**\n * Creates a string schema with optional format and length constraints.\n *\n * Strings can be validated against various formats (datetime, uri, did, handle, etc.)\n * and constrained by length in UTF-8 bytes or grapheme clusters.\n *\n * @param options - Optional configuration for format and length constraints\n * @returns A new {@link StringSchema} instance\n *\n * @example\n * ```ts\n * // Basic string\n * const nameSchema = l.string()\n *\n * // With format validation\n * const dateSchema = l.string({ format: 'datetime' })\n *\n * // With length constraints (UTF-8 bytes)\n * const bioSchema = l.string({ maxLength: 256 })\n *\n * // With grapheme constraints (user-perceived characters)\n * const displayNameSchema = l.string({ maxGraphemes: 64 })\n *\n * // Combining constraints\n * const handleSchema = l.string({ format: 'handle', minLength: 3, maxLength: 253 })\n * ```\n */\nexport const string = /*#__PURE__*/ memoizedOptions(_string)\n"]}
|
|
1
|
+
{"version":3,"file":"string.js","sourceRoot":"","sources":["../../src/schema/string.ts"],"names":[],"mappings":"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","sourcesContent":["import { graphemeLen, ifCid, utf8Len } from '@atproto/lex-data'\nimport {\n InferStringFormat,\n Restricted,\n Schema,\n StringFormat,\n UnknownString,\n ValidationContext,\n isStringFormat,\n} from '../core.js'\nimport { IfAny } from '../util/if-any.js'\nimport { memoizedOptions } from '../util/memoize.js'\nimport { TokenSchema } from './token.js'\n\n/**\n * Configuration options for string schema validation.\n *\n * @property format - Expected string format (e.g., 'datetime', 'uri', 'at-uri', 'did', 'handle', 'nsid', 'cid', 'tid', 'record-key', 'at-identifier', 'language')\n * @property knownValues - Known string literal values for type narrowing\n * @property minLength - Minimum length in UTF-8 bytes\n * @property maxLength - Maximum length in UTF-8 bytes\n * @property minGraphemes - Minimum number of grapheme clusters\n * @property maxGraphemes - Maximum number of grapheme clusters\n */\nexport type StringSchemaOptions = {\n format?: StringFormat\n knownValues?: readonly string[]\n minLength?: number\n maxLength?: number\n minGraphemes?: number\n maxGraphemes?: number\n}\n\n/**\n * Schema for validating string values with optional format and length constraints.\n *\n * Supports various string formats defined in the Lexicon specification, as well as\n * length constraints measured in UTF-8 bytes or grapheme clusters.\n *\n * @template TOptions - The configuration options type\n *\n * @example\n * ```ts\n * const schema = new StringSchema({ format: 'datetime', maxLength: 64 })\n * const result = schema.validate('2024-01-15T10:30:00Z')\n * ```\n */\nexport class StringSchema<\n const TOptions extends StringSchemaOptions = StringSchemaOptions,\n> extends Schema<\n IfAny<\n TOptions,\n string,\n TOptions extends { format: infer F extends StringFormat }\n ? InferStringFormat<F>\n : TOptions extends { knownValues: readonly (infer V extends string)[] }\n ? V | UnknownString\n : string\n >\n> {\n readonly type = 'string' as const\n\n // @NOTE since the _string utility allows omitting knownValues when TOptions\n // *does* include it (since it's only used for typing), we cannot type options\n // as TOptions directly since it may not actually include knownValues at\n // runtime, making schema.options.knownValues potentially undefined even when\n // TOptions includes it.\n readonly options: StringSchemaOptions\n\n constructor(options: TOptions) {\n super()\n this.options = options\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n const str = coerceToString(input)\n if (str == null) {\n return ctx.issueUnexpectedType(input, 'string')\n }\n\n let lazyUtf8Len: number\n\n const minLength = this.options.minLength\n if (minLength != null) {\n if ((lazyUtf8Len ??= utf8Len(str)) < minLength) {\n return ctx.issueTooSmall(str, 'string', minLength, lazyUtf8Len)\n }\n }\n\n const maxLength = this.options.maxLength\n if (maxLength != null) {\n // Optimization: we can avoid computing the UTF-8 length if the maximum\n // possible length, in bytes, of the input JS string is smaller than the\n // maxLength (in UTF-8 string bytes).\n if (str.length * 3 <= maxLength) {\n // Input string so small it can't possibly exceed maxLength\n } else if ((lazyUtf8Len ??= utf8Len(str)) > maxLength) {\n return ctx.issueTooBig(str, 'string', maxLength, lazyUtf8Len)\n }\n }\n\n // Optimization: count graphemes once\n let lazyGraphLen: number\n\n const minGraphemes = this.options.minGraphemes\n if (minGraphemes != null) {\n if (str.length < minGraphemes) {\n // If the JavaScript string length (UTF-16) is below the minimal limit,\n // its grapheme length (which <= .length) will also be below.\n // Fail early.\n return ctx.issueTooSmall(str, 'grapheme', minGraphemes, str.length)\n } else if ((lazyGraphLen ??= graphemeLen(str)) < minGraphemes) {\n return ctx.issueTooSmall(str, 'grapheme', minGraphemes, lazyGraphLen)\n }\n }\n\n const maxGraphemes = this.options.maxGraphemes\n if (maxGraphemes != null) {\n if (str.length <= maxGraphemes) {\n // If the JavaScript string length (UTF-16) is within the maximum limit,\n // its grapheme length (which <= .length) will also be within.\n } else if ((lazyGraphLen ??= graphemeLen(str)) > maxGraphemes) {\n return ctx.issueTooBig(str, 'grapheme', maxGraphemes, lazyGraphLen)\n }\n }\n\n const format = this.options.format\n if (format != null && !isStringFormat(str, format, ctx.options)) {\n return ctx.issueInvalidFormat(str, format)\n }\n\n return ctx.success(str)\n }\n}\n\nexport function coerceToString(input: unknown): string | null {\n switch (typeof input) {\n // @NOTE We do *not* coerce numbers/booleans to strings because that can\n // lead to them being accepted as string instead of being coerced to\n // number/boolean when the input is a string and the expected result is\n // number/boolean (e.g. in params).\n case 'string':\n return input\n case 'object': {\n if (input == null) return null\n\n // @NOTE Allow using TokenSchema instances in places expecting strings,\n // converting them to their string value.\n if (input instanceof TokenSchema) {\n return input.toString()\n }\n\n if (input instanceof Date) {\n if (Number.isNaN(input.getTime())) return null\n return input.toISOString()\n }\n\n if (input instanceof URL) {\n return input.toString()\n }\n\n const cid = ifCid(input)\n if (cid) return cid.toString()\n\n if (input instanceof String) {\n return input.valueOf()\n }\n }\n\n // falls through\n default:\n return null\n }\n}\n\nfunction _string(): StringSchema<NonNullable<unknown>>\nfunction _string<\n // Allow calling `string<{ knownValues: [...] }>()` without passing an options\n // object, since knownValues is only used for typing and has no runtime\n // effect, so it can be safely omitted at runtime.\n const TOptions extends {\n knownValues: StringSchemaOptions['knownValues']\n } & {\n [K in Exclude<\n keyof StringSchemaOptions,\n 'knownValues'\n >]?: Restricted<`An options argument is required when using the \"${K}\" option`>\n },\n>(): StringSchema<\n IfAny<TOptions, any, { knownValues: TOptions['knownValues'] }>\n>\nfunction _string<const TOptions extends StringSchemaOptions>(\n // If TOptions is explicitly provided (e.g. `string<{ ... }>({ ... })`), we\n // allow the actual options argument to omit the \"knownValues\" property since\n // it's only used for inferring the type and has no runtime effect.\n options: TOptions | Omit<TOptions, 'knownValues'>,\n): StringSchema<TOptions>\nfunction _string(options: StringSchemaOptions = {}) {\n return new StringSchema(options)\n}\n\n/**\n * Creates a string schema with optional format and length constraints.\n *\n * Strings can be validated against various formats (datetime, uri, did, handle, etc.)\n * and constrained by length in UTF-8 bytes or grapheme clusters.\n *\n * @param options - Optional configuration for format and length constraints\n * @returns A new {@link StringSchema} instance\n *\n * @example\n * ```ts\n * // Basic string\n * const nameSchema = l.string()\n *\n * // With format validation\n * const dateSchema = l.string({ format: 'datetime' })\n *\n * // With length constraints (UTF-8 bytes)\n * const bioSchema = l.string({ maxLength: 256 })\n *\n * // With grapheme constraints (user-perceived characters)\n * const displayNameSchema = l.string({ maxGraphemes: 64 })\n *\n * // Combining constraints\n * const handleSchema = l.string({ format: 'handle', minLength: 3, maxLength: 253 })\n * ```\n */\nexport const string = /*#__PURE__*/ memoizedOptions(_string)\n"]}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Subscription = void 0;
|
|
4
|
-
exports.subscription = subscription;
|
|
5
1
|
/**
|
|
6
2
|
* Represents a Lexicon subscription (WebSocket) endpoint definition.
|
|
7
3
|
*
|
|
@@ -24,20 +20,15 @@ exports.subscription = subscription;
|
|
|
24
20
|
* )
|
|
25
21
|
* ```
|
|
26
22
|
*/
|
|
27
|
-
class Subscription {
|
|
28
|
-
nsid;
|
|
29
|
-
parameters;
|
|
30
|
-
message;
|
|
31
|
-
errors;
|
|
32
|
-
type = 'subscription';
|
|
23
|
+
export class Subscription {
|
|
33
24
|
constructor(nsid, parameters, message, errors) {
|
|
34
25
|
this.nsid = nsid;
|
|
35
26
|
this.parameters = parameters;
|
|
36
27
|
this.message = message;
|
|
37
28
|
this.errors = errors;
|
|
29
|
+
this.type = 'subscription';
|
|
38
30
|
}
|
|
39
31
|
}
|
|
40
|
-
exports.Subscription = Subscription;
|
|
41
32
|
/**
|
|
42
33
|
* Creates a subscription definition for a Lexicon WebSocket endpoint.
|
|
43
34
|
*
|
|
@@ -76,7 +67,7 @@ exports.Subscription = Subscription;
|
|
|
76
67
|
* ```
|
|
77
68
|
*/
|
|
78
69
|
/*@__NO_SIDE_EFFECTS__*/
|
|
79
|
-
function subscription(nsid, parameters, message, errors = undefined) {
|
|
70
|
+
export function subscription(nsid, parameters, message, errors = undefined) {
|
|
80
71
|
return new Subscription(nsid, parameters, message, errors);
|
|
81
72
|
}
|
|
82
73
|
//# sourceMappingURL=subscription.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../src/schema/subscription.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../src/schema/subscription.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,YAAY;IAUvB,YACW,IAAW,EACX,UAAuB,EACvB,OAAiB,EACjB,MAAe;QAHf,SAAI,GAAJ,IAAI,CAAO;QACX,eAAU,GAAV,UAAU,CAAa;QACvB,YAAO,GAAP,OAAO,CAAU;QACjB,WAAM,GAAN,MAAM,CAAS;QANjB,SAAI,GAAG,cAAuB,CAAA;IAOpC,CAAC;CACL;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAwB;AACxB,MAAM,UAAU,YAAY,CAK1B,IAAO,EAAE,UAAa,EAAE,OAAU,EAAE,SAAY,SAAc;IAC9D,OAAO,IAAI,YAAY,CAAa,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;AACxE,CAAC","sourcesContent":["import { LexValue } from '@atproto/lex-data'\nimport { Infer, NsidString, Schema } from '../core.js'\nimport { ParamsSchema } from './params.js'\n\n/**\n * Infers the parameters type from a Subscription definition.\n *\n * @template S - The Subscription type\n */\nexport type InferSubscriptionParameters<S extends Subscription> = Infer<\n S['parameters']\n>\n\n/**\n * Infers the message type from a Subscription definition.\n *\n * @template S - The Subscription type\n */\nexport type InferSubscriptionMessage<S extends Subscription> = Infer<\n S['message']\n>\n\n/**\n * Represents a Lexicon subscription (WebSocket) endpoint definition.\n *\n * Subscriptions are real-time event streams delivered over WebSocket.\n * They have parameters for initializing the connection and a message\n * schema for validating incoming events.\n *\n * @template TNsid - The NSID identifying this subscription\n * @template TParameters - The connection parameters schema type\n * @template TMessage - The message schema type\n * @template TErrors - Array of error type strings, or undefined\n *\n * @example\n * ```ts\n * const firehose = new Subscription(\n * 'com.atproto.sync.subscribeRepos',\n * l.params({ cursor: l.optional(l.integer()) }),\n * repoEventSchema,\n * ['FutureCursor']\n * )\n * ```\n */\nexport class Subscription<\n const TNsid extends NsidString = NsidString,\n const TParameters extends ParamsSchema = ParamsSchema,\n const TMessage extends Schema<LexValue> = Schema<LexValue>,\n const TErrors extends undefined | readonly string[] =\n | undefined\n | readonly string[],\n> {\n readonly type = 'subscription' as const\n\n constructor(\n readonly nsid: TNsid,\n readonly parameters: TParameters,\n readonly message: TMessage,\n readonly errors: TErrors,\n ) {}\n}\n\n/**\n * Creates a subscription definition for a Lexicon WebSocket endpoint.\n *\n * Subscriptions enable real-time event streaming. The connection is\n * initialized with parameters, and the server sends messages matching\n * the message schema.\n *\n * @param nsid - The NSID identifying this subscription endpoint\n * @param parameters - Schema for connection parameters\n * @param message - Schema for validating incoming messages\n * @param errors - Optional array of error type strings\n * @returns A new {@link Subscription} instance\n *\n * @example\n * ```ts\n * // Repository event stream\n * const subscribeRepos = l.subscription(\n * 'com.atproto.sync.subscribeRepos',\n * l.params({\n * cursor: l.optional(l.integer()),\n * }),\n * l.typedUnion([\n * l.typedRef(() => commitEventSchema),\n * l.typedRef(() => handleEventSchema),\n * l.typedRef(() => identityEventSchema),\n * ], false),\n * ['FutureCursor', 'ConsumerTooSlow'],\n * )\n *\n * // Label stream\n * const subscribeLabels = l.subscription(\n * 'com.atproto.label.subscribeLabels',\n * l.params({ cursor: l.optional(l.integer()) }),\n * labelEventSchema,\n * )\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function subscription<\n const N extends NsidString,\n const P extends ParamsSchema,\n const M extends Schema<LexValue>,\n const E extends undefined | readonly string[] = undefined,\n>(nsid: N, parameters: P, message: M, errors: E = undefined as E) {\n return new Subscription<N, P, M, E>(nsid, parameters, message, errors)\n}\n"]}
|
package/dist/schema/token.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TokenSchema = void 0;
|
|
4
|
-
exports.token = token;
|
|
5
|
-
const core_js_1 = require("../core.js");
|
|
1
|
+
import { $type, Schema } from '../core.js';
|
|
6
2
|
/**
|
|
7
3
|
* Schema for Lexicon token values.
|
|
8
4
|
*
|
|
@@ -18,12 +14,11 @@ const core_js_1 = require("../core.js");
|
|
|
18
14
|
* schema.validate('app.bsky.feed.defs#requestLess') // success
|
|
19
15
|
* ```
|
|
20
16
|
*/
|
|
21
|
-
class TokenSchema extends
|
|
22
|
-
value;
|
|
23
|
-
type = 'token';
|
|
17
|
+
export class TokenSchema extends Schema {
|
|
24
18
|
constructor(value) {
|
|
25
19
|
super();
|
|
26
20
|
this.value = value;
|
|
21
|
+
this.type = 'token';
|
|
27
22
|
}
|
|
28
23
|
validateInContext(input, ctx) {
|
|
29
24
|
if (input === this.value) {
|
|
@@ -48,7 +43,6 @@ class TokenSchema extends core_js_1.Schema {
|
|
|
48
43
|
return this.value;
|
|
49
44
|
}
|
|
50
45
|
}
|
|
51
|
-
exports.TokenSchema = TokenSchema;
|
|
52
46
|
/**
|
|
53
47
|
* Creates a token schema for Lexicon named constants.
|
|
54
48
|
*
|
|
@@ -82,7 +76,7 @@ exports.TokenSchema = TokenSchema;
|
|
|
82
76
|
* ```
|
|
83
77
|
*/
|
|
84
78
|
/*@__NO_SIDE_EFFECTS__*/
|
|
85
|
-
function token(nsid, hash = 'main') {
|
|
86
|
-
return new TokenSchema(
|
|
79
|
+
export function token(nsid, hash = 'main') {
|
|
80
|
+
return new TokenSchema($type(nsid, hash));
|
|
87
81
|
}
|
|
88
82
|
//# sourceMappingURL=token.js.map
|
package/dist/schema/token.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/schema/token.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/schema/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAc,MAAM,EAAqB,MAAM,YAAY,CAAA;AAEzE;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,WAEX,SAAQ,MAAc;IAGtB,YAAqB,KAAa;QAChC,KAAK,EAAE,CAAA;QADY,UAAK,GAAL,KAAK,CAAQ;QAFzB,SAAI,GAAG,OAAgB,CAAA;IAIhC,CAAC;IAED,iBAAiB,CAAC,KAAc,EAAE,GAAsB;QACtD,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YACzB,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAChC,CAAC;QAED,0EAA0E;QAC1E,eAAe;QACf,IAAI,KAAK,YAAY,WAAW,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YAC/D,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAChC,CAAC;QAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,GAAG,CAAC,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAChD,CAAC;QAED,OAAO,GAAG,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IACnD,CAAC;IAED,yEAAyE;IACzE,cAAc;IAEd,MAAM;QACJ,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAA;IACnB,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAwB;AACxB,MAAM,UAAU,KAAK,CAGnB,IAAO,EAAE,OAAU,MAAW;IAC9B,OAAO,IAAI,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;AAC3C,CAAC","sourcesContent":["import { $type, NsidString, Schema, ValidationContext } from '../core.js'\n\n/**\n * Schema for Lexicon token values.\n *\n * Tokens are named constants in Lexicon, identified by their NSID and hash.\n * They validate to their string value (e.g., 'app.bsky.feed.defs#requestLess').\n * TokenSchema instances can also be used as values themselves.\n *\n * @template TValue - The token string literal type\n *\n * @example\n * ```ts\n * const schema = new TokenSchema('app.bsky.feed.defs#requestLess')\n * schema.validate('app.bsky.feed.defs#requestLess') // success\n * ```\n */\nexport class TokenSchema<\n const TValue extends string = string,\n> extends Schema<TValue> {\n readonly type = 'token' as const\n\n constructor(readonly value: TValue) {\n super()\n }\n\n validateInContext(input: unknown, ctx: ValidationContext) {\n if (input === this.value) {\n return ctx.success(this.value)\n }\n\n // @NOTE: allow using the token instance itself (but convert to the actual\n // token value)\n if (input instanceof TokenSchema && input.value === this.value) {\n return ctx.success(this.value)\n }\n\n if (typeof input !== 'string') {\n return ctx.issueUnexpectedType(input, 'token')\n }\n\n return ctx.issueInvalidValue(input, [this.value])\n }\n\n // When using the TokenSchema instance as data, let's serialize it to the\n // token value\n\n toJSON(): string {\n return this.value\n }\n\n toString(): string {\n return this.value\n }\n}\n\n/**\n * Creates a token schema for Lexicon named constants.\n *\n * Tokens are used in Lexicon as named constants or enum-like values.\n * The token instance can be used both as a schema validator and as\n * the token value itself (it serializes to its string value).\n *\n * @param nsid - The NSID part of the token\n * @param hash - The hash part of the token (defaults to 'main')\n * @returns A new {@link TokenSchema} instance\n *\n * @example\n * ```ts\n * // Define tokens\n * const requestLess = l.token('app.bsky.feed.defs', 'requestLess')\n * const requestMore = l.token('app.bsky.feed.defs', 'requestMore')\n *\n * // Use as a value\n * console.log(requestLess.toString()) // 'app.bsky.feed.defs#requestLess'\n *\n * // Use in union for validation\n * const feedbackSchema = l.union([requestLess, requestMore])\n *\n * // Validate\n * feedbackSchema.parse('app.bsky.feed.defs#requestLess') // success\n *\n * // Token instances can be used as values in other schemas\n * const feedbackRequest = l.object({\n * feedback: requestLess, // Accepts the token value\n * })\n * ```\n */\n/*@__NO_SIDE_EFFECTS__*/\nexport function token<\n const N extends NsidString,\n const H extends string = 'main',\n>(nsid: N, hash: H = 'main' as H) {\n return new TokenSchema($type(nsid, hash))\n}\n"]}
|