@atproto/lex-data 0.0.11 → 0.0.12

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atproto/lex-data
2
2
 
3
+ ## 0.0.12
4
+
5
+ ### Patch Changes
6
+
7
+ - [#4660](https://github.com/bluesky-social/atproto/pull/4660) [`ea5df64`](https://github.com/bluesky-social/atproto/commit/ea5df64db9e408d2b320f5f75eb2878aef6bc6df) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Minor code simplification
8
+
3
9
  ## 0.0.11
4
10
 
5
11
  ### Patch Changes
package/dist/lex.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lex.d.ts","sourceRoot":"","sources":["../src/lex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAS,MAAM,UAAU,CAAA;AAGrC;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,GAAG,GAAG,UAAU,CAAA;AAE3E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,QAAQ,EAAE,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,QAAQ;CAAE,CAAA;AAE5E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,MAAM,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,QAAQ;CAAE,CAAA;AAEjD;;;;;;;;;GASG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,EAAE,CAAA;AAEjC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAa9D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CA8C5D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG;IAAE,KAAK,EAAE,CAAC,CAAA;CAAE,CAAA;AAE1E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK,IAAI,WAAW,CAInE"}
1
+ {"version":3,"file":"lex.d.ts","sourceRoot":"","sources":["../src/lex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAS,MAAM,UAAU,CAAA;AAGrC;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,GAAG,GAAG,UAAU,CAAA;AAE3E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,QAAQ,EAAE,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,QAAQ;CAAE,CAAA;AAE5E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,MAAM,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,QAAQ;CAAE,CAAA;AAEjD;;;;;;;;;GASG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,EAAE,CAAA;AAEjC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAa9D;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CA4B5D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG;IAAE,KAAK,EAAE,CAAC,CAAA;CAAE,CAAA;AAE1E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK,IAAI,WAAW,CAInE"}
package/dist/lex.js CHANGED
@@ -116,40 +116,20 @@ function isLexValue(value) {
116
116
  const visited = new Set();
117
117
  do {
118
118
  const value = stack.pop();
119
- // Optimization: we are not using `isLexScalar` here to avoid extra function
120
- // calls, and to avoid computing `typeof value` multiple times.
121
- switch (typeof value) {
122
- case 'object':
123
- if (value === null) {
124
- // LexScalar
125
- }
126
- else if ((0, object_js_1.isPlainProto)(value)) {
127
- if (visited.has(value))
128
- return false;
129
- visited.add(value);
130
- stack.push(...Object.values(value));
131
- }
132
- else if (Array.isArray(value)) {
133
- if (visited.has(value))
134
- return false;
135
- visited.add(value);
136
- stack.push(...value);
137
- }
138
- else if (value instanceof Uint8Array || (0, cid_js_1.isCid)(value)) {
139
- // LexScalar
140
- }
141
- else {
142
- return false;
143
- }
144
- break;
145
- case 'string':
146
- case 'boolean':
147
- break;
148
- case 'number':
149
- if (Number.isInteger(value))
150
- break;
151
- // fallthrough
152
- default:
119
+ if ((0, object_js_1.isPlainObject)(value)) {
120
+ if (visited.has(value))
121
+ return false;
122
+ visited.add(value);
123
+ stack.push(...Object.values(value));
124
+ }
125
+ else if (Array.isArray(value)) {
126
+ if (visited.has(value))
127
+ return false;
128
+ visited.add(value);
129
+ stack.push(...value);
130
+ }
131
+ else {
132
+ if (!isLexScalar(value))
153
133
  return false;
154
134
  }
155
135
  } while (stack.length > 0);
package/dist/lex.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lex.js","sourceRoot":"","sources":["../src/lex.ts"],"names":[],"mappings":"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","sourcesContent":["import { Cid, isCid } from './cid.js'\nimport { isPlainObject, isPlainProto } from './object.js'\n\n/**\n * Primitive values in the Lexicon data model.\n *\n * Represents the basic scalar types that can appear in AT Protocol data:\n * - `number` - Integer values only (no floats)\n * - `string` - UTF-8 text\n * - `boolean` - true or false\n * - `null` - Explicit null value\n * - `Cid` - Content Identifier (link by hash)\n * - `Uint8Array` - Binary data (bytes)\n *\n * @see {@link LexValue} for the complete recursive value type\n */\nexport type LexScalar = number | string | boolean | null | Cid | Uint8Array\n\n/**\n * Any valid Lexicon value (recursive type).\n *\n * This is the union of all types that can appear in AT Protocol Lexicon data:\n * - {@link LexScalar} - Primitive values (number, string, boolean, null, Cid, Uint8Array)\n * - `LexValue[]` - Arrays of LexValues\n * - `{ [key: string]?: LexValue }` - Objects with string keys and LexValue values\n *\n * @example\n * ```typescript\n * import type { LexValue } from '@atproto/lex'\n *\n * const scalar: LexValue = 'hello'\n * const array: LexValue = [1, 2, 3]\n * const object: LexValue = { name: 'Alice', age: 30 }\n * ```\n *\n * @see {@link LexScalar} for primitive value types\n * @see {@link LexMap} for object types\n * @see {@link LexArray} for array types\n */\nexport type LexValue = LexScalar | LexValue[] | { [_ in string]?: LexValue }\n\n/**\n * Object with string keys and LexValue values.\n *\n * Represents a plain object in the Lexicon data model where all values\n * must be valid {@link LexValue} types.\n *\n * @example\n * ```typescript\n * import type { LexMap } from '@atproto/lex'\n *\n * const user: LexMap = {\n * name: 'Alice',\n * age: 30,\n * tags: ['admin', 'user']\n * }\n * ```\n *\n * @see {@link TypedLexMap} for objects with a required `$type` property\n */\nexport type LexMap = { [_ in string]?: LexValue }\n\n/**\n * Array of {@link LexValue} elements.\n *\n * @example\n * ```typescript\n * import type { LexArray } from '@atproto/lex'\n *\n * const items: LexArray = [1, 'two', { three: 3 }]\n * ```\n */\nexport type LexArray = LexValue[]\n\n/**\n * Type guard to check if a value is a valid {@link LexMap}.\n *\n * Returns true if the value is a plain object where all values are valid\n * {@link LexValue} types.\n *\n * @param value - The value to check\n * @returns `true` if the value is a valid LexMap\n *\n * @example\n * ```typescript\n * import { isLexMap } from '@atproto/lex'\n *\n * if (isLexMap(data)) {\n * // data is narrowed to LexMap\n * console.log(Object.keys(data))\n * }\n * ```\n */\nexport function isLexMap(value: unknown): value is LexMap {\n return isPlainObject(value) && Object.values(value).every(isLexValue)\n}\n\n/**\n * Type guard to check if a value is a valid {@link LexArray}.\n *\n * Returns true if the value is an array where all elements are valid\n * {@link LexValue} types.\n *\n * @param value - The value to check\n * @returns `true` if the value is a valid LexArray\n *\n * @example\n * ```typescript\n * import { isLexArray } from '@atproto/lex'\n *\n * if (isLexArray(data)) {\n * // data is narrowed to LexArray\n * data.forEach(item => console.log(item))\n * }\n * ```\n */\nexport function isLexArray(value: unknown): value is LexArray {\n return Array.isArray(value) && value.every(isLexValue)\n}\n\n/**\n * Type guard to check if a value is a valid {@link LexScalar}.\n *\n * Returns true if the value is one of the primitive Lexicon types:\n * number (integer only), string, boolean, null, Cid, or Uint8Array.\n *\n * @param value - The value to check\n * @returns `true` if the value is a valid LexScalar\n *\n * @example\n * ```typescript\n * import { isLexScalar } from '@atproto/lex'\n *\n * isLexScalar('hello') // true\n * isLexScalar(42) // true\n * isLexScalar(3.14) // false (floats not allowed)\n * isLexScalar([1, 2]) // false (arrays are not scalars)\n * ```\n */\nexport function isLexScalar(value: unknown): value is LexScalar {\n switch (typeof value) {\n case 'object':\n return value === null || value instanceof Uint8Array || isCid(value)\n case 'string':\n case 'boolean':\n return true\n case 'number':\n if (Number.isInteger(value)) return true\n // fallthrough\n default:\n return false\n }\n}\n\n/**\n * Type guard to check if a value is a valid {@link LexValue}.\n *\n * Performs a deep check to validate that the value (and all nested values)\n * conform to the Lexicon data model. This includes checking for:\n * - Valid scalar types (number, string, boolean, null, Cid, Uint8Array)\n * - Arrays containing only valid LexValues\n * - Plain objects with string keys and valid LexValue values\n * - No cyclic references (which cannot be serialized to JSON or CBOR)\n *\n * @param value - The value to check\n * @returns `true` if the value is a valid LexValue\n *\n * @example\n * ```typescript\n * import { isLexValue } from '@atproto/lex'\n *\n * isLexValue({ name: 'Alice', tags: ['admin'] }) // true\n * isLexValue(new Date()) // false (not a plain object)\n * isLexValue({ fn: () => {} }) // false (functions not allowed)\n * ```\n */\nexport function isLexValue(value: unknown): value is LexValue {\n // Using a stack to avoid recursion depth issues.\n const stack: unknown[] = [value]\n // Cyclic structures are not valid LexValues as they cannot be serialized to\n // JSON or CBOR. This also allows us to avoid infinite loops when traversing\n // the structure.\n const visited = new Set<object>()\n\n do {\n const value = stack.pop()!\n\n // Optimization: we are not using `isLexScalar` here to avoid extra function\n // calls, and to avoid computing `typeof value` multiple times.\n switch (typeof value) {\n case 'object':\n if (value === null) {\n // LexScalar\n } else if (isPlainProto(value)) {\n if (visited.has(value)) return false\n visited.add(value)\n stack.push(...Object.values(value))\n } else if (Array.isArray(value)) {\n if (visited.has(value)) return false\n visited.add(value)\n stack.push(...value)\n } else if (value instanceof Uint8Array || isCid(value)) {\n // LexScalar\n } else {\n return false\n }\n break\n case 'string':\n case 'boolean':\n break\n case 'number':\n if (Number.isInteger(value)) break\n // fallthrough\n default:\n return false\n }\n } while (stack.length > 0)\n\n // Optimization: ease GC's work\n visited.clear()\n\n return true\n}\n\n/**\n * A {@link LexMap} with a required `$type` property.\n *\n * Used to represent typed objects in the Lexicon data model, where the\n * `$type` property identifies the Lexicon schema that defines the object's\n * structure.\n *\n * @example\n * ```typescript\n * import type { TypedLexMap } from '@atproto/lex'\n *\n * const post: TypedLexMap = {\n * $type: 'app.bsky.feed.post',\n * text: 'Hello world!',\n * createdAt: '2024-01-01T00:00:00Z'\n * }\n * ```\n *\n * @see {@link isTypedLexMap} to check if a value is a TypedLexMap\n */\nexport type TypedLexMap<T extends string = string> = LexMap & { $type: T }\n\n/**\n * Type guard to check if a value is a {@link TypedLexMap}.\n *\n * Returns true if the value is a valid {@link LexMap} with a non-empty\n * `$type` string property.\n *\n * @param value - The LexValue to check\n * @returns `true` if the value is a TypedLexMap\n *\n * @example\n * ```typescript\n * import { isTypedLexMap } from '@atproto/lex'\n *\n * const data = { $type: 'app.bsky.feed.post', text: 'Hello' }\n *\n * if (isTypedLexMap(data)) {\n * console.log(data.$type) // 'app.bsky.feed.post'\n * }\n * ```\n */\nexport function isTypedLexMap(value: LexValue): value is TypedLexMap {\n return (\n isLexMap(value) && typeof value.$type === 'string' && value.$type.length > 0\n )\n}\n"]}
1
+ {"version":3,"file":"lex.js","sourceRoot":"","sources":["../src/lex.ts"],"names":[],"mappings":";;AA6FA,4BAEC;AAqBD,gCAEC;AAqBD,kCAaC;AAwBD,gCA4BC;AA4CD,sCAIC;AA5PD,qCAAqC;AACrC,2CAA2C;AAyE3C;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,QAAQ,CAAC,KAAc;IACrC,OAAO,IAAA,yBAAa,EAAC,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;AACvE,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,UAAU,CAAC,KAAc;IACvC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;AACxD,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,WAAW,CAAC,KAAc;IACxC,QAAQ,OAAO,KAAK,EAAE,CAAC;QACrB,KAAK,QAAQ;YACX,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,YAAY,UAAU,IAAI,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;QACtE,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS;YACZ,OAAO,IAAI,CAAA;QACb,KAAK,QAAQ;YACX,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAA;QAC1C,cAAc;QACd;YACE,OAAO,KAAK,CAAA;IAChB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,UAAU,CAAC,KAAc;IACvC,iDAAiD;IACjD,MAAM,KAAK,GAAc,CAAC,KAAK,CAAC,CAAA;IAChC,4EAA4E;IAC5E,4EAA4E;IAC5E,iBAAiB;IACjB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAA;IAEjC,GAAG,CAAC;QACF,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAG,CAAA;QAE1B,IAAI,IAAA,yBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAA;YACpC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YAClB,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACrC,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAA;YACpC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YAClB,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAA;QACtB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAA;QACvC,CAAC;IACH,CAAC,QAAQ,KAAK,CAAC,MAAM,GAAG,CAAC,EAAC;IAE1B,+BAA+B;IAC/B,OAAO,CAAC,KAAK,EAAE,CAAA;IAEf,OAAO,IAAI,CAAA;AACb,CAAC;AAwBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,aAAa,CAAC,KAAe;IAC3C,OAAO,CACL,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAC7E,CAAA;AACH,CAAC","sourcesContent":["import { Cid, isCid } from './cid.js'\nimport { isPlainObject } from './object.js'\n\n/**\n * Primitive values in the Lexicon data model.\n *\n * Represents the basic scalar types that can appear in AT Protocol data:\n * - `number` - Integer values only (no floats)\n * - `string` - UTF-8 text\n * - `boolean` - true or false\n * - `null` - Explicit null value\n * - `Cid` - Content Identifier (link by hash)\n * - `Uint8Array` - Binary data (bytes)\n *\n * @see {@link LexValue} for the complete recursive value type\n */\nexport type LexScalar = number | string | boolean | null | Cid | Uint8Array\n\n/**\n * Any valid Lexicon value (recursive type).\n *\n * This is the union of all types that can appear in AT Protocol Lexicon data:\n * - {@link LexScalar} - Primitive values (number, string, boolean, null, Cid, Uint8Array)\n * - `LexValue[]` - Arrays of LexValues\n * - `{ [key: string]?: LexValue }` - Objects with string keys and LexValue values\n *\n * @example\n * ```typescript\n * import type { LexValue } from '@atproto/lex'\n *\n * const scalar: LexValue = 'hello'\n * const array: LexValue = [1, 2, 3]\n * const object: LexValue = { name: 'Alice', age: 30 }\n * ```\n *\n * @see {@link LexScalar} for primitive value types\n * @see {@link LexMap} for object types\n * @see {@link LexArray} for array types\n */\nexport type LexValue = LexScalar | LexValue[] | { [_ in string]?: LexValue }\n\n/**\n * Object with string keys and LexValue values.\n *\n * Represents a plain object in the Lexicon data model where all values\n * must be valid {@link LexValue} types.\n *\n * @example\n * ```typescript\n * import type { LexMap } from '@atproto/lex'\n *\n * const user: LexMap = {\n * name: 'Alice',\n * age: 30,\n * tags: ['admin', 'user']\n * }\n * ```\n *\n * @see {@link TypedLexMap} for objects with a required `$type` property\n */\nexport type LexMap = { [_ in string]?: LexValue }\n\n/**\n * Array of {@link LexValue} elements.\n *\n * @example\n * ```typescript\n * import type { LexArray } from '@atproto/lex'\n *\n * const items: LexArray = [1, 'two', { three: 3 }]\n * ```\n */\nexport type LexArray = LexValue[]\n\n/**\n * Type guard to check if a value is a valid {@link LexMap}.\n *\n * Returns true if the value is a plain object where all values are valid\n * {@link LexValue} types.\n *\n * @param value - The value to check\n * @returns `true` if the value is a valid LexMap\n *\n * @example\n * ```typescript\n * import { isLexMap } from '@atproto/lex'\n *\n * if (isLexMap(data)) {\n * // data is narrowed to LexMap\n * console.log(Object.keys(data))\n * }\n * ```\n */\nexport function isLexMap(value: unknown): value is LexMap {\n return isPlainObject(value) && Object.values(value).every(isLexValue)\n}\n\n/**\n * Type guard to check if a value is a valid {@link LexArray}.\n *\n * Returns true if the value is an array where all elements are valid\n * {@link LexValue} types.\n *\n * @param value - The value to check\n * @returns `true` if the value is a valid LexArray\n *\n * @example\n * ```typescript\n * import { isLexArray } from '@atproto/lex'\n *\n * if (isLexArray(data)) {\n * // data is narrowed to LexArray\n * data.forEach(item => console.log(item))\n * }\n * ```\n */\nexport function isLexArray(value: unknown): value is LexArray {\n return Array.isArray(value) && value.every(isLexValue)\n}\n\n/**\n * Type guard to check if a value is a valid {@link LexScalar}.\n *\n * Returns true if the value is one of the primitive Lexicon types:\n * number (integer only), string, boolean, null, Cid, or Uint8Array.\n *\n * @param value - The value to check\n * @returns `true` if the value is a valid LexScalar\n *\n * @example\n * ```typescript\n * import { isLexScalar } from '@atproto/lex'\n *\n * isLexScalar('hello') // true\n * isLexScalar(42) // true\n * isLexScalar(3.14) // false (floats not allowed)\n * isLexScalar([1, 2]) // false (arrays are not scalars)\n * ```\n */\nexport function isLexScalar(value: unknown): value is LexScalar {\n switch (typeof value) {\n case 'object':\n return value === null || value instanceof Uint8Array || isCid(value)\n case 'string':\n case 'boolean':\n return true\n case 'number':\n if (Number.isInteger(value)) return true\n // fallthrough\n default:\n return false\n }\n}\n\n/**\n * Type guard to check if a value is a valid {@link LexValue}.\n *\n * Performs a deep check to validate that the value (and all nested values)\n * conform to the Lexicon data model. This includes checking for:\n * - Valid scalar types (number, string, boolean, null, Cid, Uint8Array)\n * - Arrays containing only valid LexValues\n * - Plain objects with string keys and valid LexValue values\n * - No cyclic references (which cannot be serialized to JSON or CBOR)\n *\n * @param value - The value to check\n * @returns `true` if the value is a valid LexValue\n *\n * @example\n * ```typescript\n * import { isLexValue } from '@atproto/lex'\n *\n * isLexValue({ name: 'Alice', tags: ['admin'] }) // true\n * isLexValue(new Date()) // false (not a plain object)\n * isLexValue({ fn: () => {} }) // false (functions not allowed)\n * ```\n */\nexport function isLexValue(value: unknown): value is LexValue {\n // Using a stack to avoid recursion depth issues.\n const stack: unknown[] = [value]\n // Cyclic structures are not valid LexValues as they cannot be serialized to\n // JSON or CBOR. This also allows us to avoid infinite loops when traversing\n // the structure.\n const visited = new Set<object>()\n\n do {\n const value = stack.pop()!\n\n if (isPlainObject(value)) {\n if (visited.has(value)) return false\n visited.add(value)\n stack.push(...Object.values(value))\n } else if (Array.isArray(value)) {\n if (visited.has(value)) return false\n visited.add(value)\n stack.push(...value)\n } else {\n if (!isLexScalar(value)) return false\n }\n } while (stack.length > 0)\n\n // Optimization: ease GC's work\n visited.clear()\n\n return true\n}\n\n/**\n * A {@link LexMap} with a required `$type` property.\n *\n * Used to represent typed objects in the Lexicon data model, where the\n * `$type` property identifies the Lexicon schema that defines the object's\n * structure.\n *\n * @example\n * ```typescript\n * import type { TypedLexMap } from '@atproto/lex'\n *\n * const post: TypedLexMap = {\n * $type: 'app.bsky.feed.post',\n * text: 'Hello world!',\n * createdAt: '2024-01-01T00:00:00Z'\n * }\n * ```\n *\n * @see {@link isTypedLexMap} to check if a value is a TypedLexMap\n */\nexport type TypedLexMap<T extends string = string> = LexMap & { $type: T }\n\n/**\n * Type guard to check if a value is a {@link TypedLexMap}.\n *\n * Returns true if the value is a valid {@link LexMap} with a non-empty\n * `$type` string property.\n *\n * @param value - The LexValue to check\n * @returns `true` if the value is a TypedLexMap\n *\n * @example\n * ```typescript\n * import { isTypedLexMap } from '@atproto/lex'\n *\n * const data = { $type: 'app.bsky.feed.post', text: 'Hello' }\n *\n * if (isTypedLexMap(data)) {\n * console.log(data.$type) // 'app.bsky.feed.post'\n * }\n * ```\n */\nexport function isTypedLexMap(value: LexValue): value is TypedLexMap {\n return (\n isLexMap(value) && typeof value.$type === 'string' && value.$type.length > 0\n )\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/lex-data",
3
- "version": "0.0.11",
3
+ "version": "0.0.12",
4
4
  "license": "MIT",
5
5
  "description": "Core utilities for AT Lexicons",
6
6
  "keywords": [
package/src/lex.test.ts CHANGED
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'
2
2
  import { parseCid } from './cid.js'
3
3
  import { isLexArray, isLexScalar, isLexValue, isTypedLexMap } from './lex.js'
4
4
 
5
- describe('isLexScalar', () => {
5
+ describe(isLexScalar, () => {
6
6
  for (const { note, value, expected } of [
7
7
  { note: 'string', value: 'hello', expected: true },
8
8
  { note: 'boolean', value: true, expected: true },
@@ -29,7 +29,7 @@ describe('isLexScalar', () => {
29
29
  }
30
30
  })
31
31
 
32
- describe('isLexArray', () => {
32
+ describe(isLexArray, () => {
33
33
  it('returns true for valid LexArray', () => {
34
34
  const list = [123, 'blah', true, null, new Uint8Array([1, 2, 3]), { a: 1 }]
35
35
  expect(isLexArray(list)).toBe(true)
@@ -55,7 +55,7 @@ describe('isLexArray', () => {
55
55
  })
56
56
  })
57
57
 
58
- describe('isLexValue', () => {
58
+ describe(isLexValue, () => {
59
59
  describe('valid values', () => {
60
60
  for (const { note, value } of [
61
61
  { note: 'string', value: 'hello' },
package/src/lex.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Cid, isCid } from './cid.js'
2
- import { isPlainObject, isPlainProto } from './object.js'
2
+ import { isPlainObject } from './object.js'
3
3
 
4
4
  /**
5
5
  * Primitive values in the Lexicon data model.
@@ -185,34 +185,16 @@ export function isLexValue(value: unknown): value is LexValue {
185
185
  do {
186
186
  const value = stack.pop()!
187
187
 
188
- // Optimization: we are not using `isLexScalar` here to avoid extra function
189
- // calls, and to avoid computing `typeof value` multiple times.
190
- switch (typeof value) {
191
- case 'object':
192
- if (value === null) {
193
- // LexScalar
194
- } else if (isPlainProto(value)) {
195
- if (visited.has(value)) return false
196
- visited.add(value)
197
- stack.push(...Object.values(value))
198
- } else if (Array.isArray(value)) {
199
- if (visited.has(value)) return false
200
- visited.add(value)
201
- stack.push(...value)
202
- } else if (value instanceof Uint8Array || isCid(value)) {
203
- // LexScalar
204
- } else {
205
- return false
206
- }
207
- break
208
- case 'string':
209
- case 'boolean':
210
- break
211
- case 'number':
212
- if (Number.isInteger(value)) break
213
- // fallthrough
214
- default:
215
- return false
188
+ if (isPlainObject(value)) {
189
+ if (visited.has(value)) return false
190
+ visited.add(value)
191
+ stack.push(...Object.values(value))
192
+ } else if (Array.isArray(value)) {
193
+ if (visited.has(value)) return false
194
+ visited.add(value)
195
+ stack.push(...value)
196
+ } else {
197
+ if (!isLexScalar(value)) return false
216
198
  }
217
199
  } while (stack.length > 0)
218
200