Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@prototypey/cli",
"version": "0.1.1",
"version": "0.2.0",
"description": "CLI tool for generating types from ATProto lexicon schemas",
"repository": {
"type": "git",
Expand Down
30 changes: 0 additions & 30 deletions packages/cli/tests/fixtures/schemas/app.bsky.actor.profile.json

This file was deleted.

43 changes: 0 additions & 43 deletions packages/cli/tests/fixtures/schemas/app.bsky.feed.post.json

This file was deleted.

47 changes: 0 additions & 47 deletions packages/cli/tests/fixtures/schemas/app.bsky.feed.searchPosts.json

This file was deleted.

5 changes: 3 additions & 2 deletions packages/prototypey/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,13 @@ npm run lexicon:emit
- CLI generates ts from json definitions
- Inferrance of valid type from full lexicon definition
- the really cool part of this is that it fills in the refs from the defs all at the type level
- `lx.lexicon(...).validate(data)` for validating data using `@atproto/lexicon` and your lexicon definitions

**TODO/In Progress:**

- Library art! Please reach out if you'd be willing to contribute some drawings or anything!
- Runtime validation using [@atproto/lexicon](https://www.npmjs.com/package/@atproto/lexicon)
- this will be hard to get correct, I'm weary of loading all of the json in a project's lexicons into js memory and would like to run benchmarks and find the best way to get this right.
- Runtime validation `assert*` api's with `@atproto/lexicon`
- Add CLI support for inferring and validating from json as the starting point
- The CLI needs more real world use and mileage. I expect bugs and weird behavior in this initial release (sorry).

## Disclaimer:
Expand Down
4 changes: 3 additions & 1 deletion packages/prototypey/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "prototypey",
"version": "0.1.2",
"version": "0.2.0",
"description": "atproto lexicon typescript toolkit",
"repository": {
"type": "git",
Expand Down Expand Up @@ -31,10 +31,12 @@
"lint": "eslint .",
"test": "vitest run",
"test:bench": "node tests/infer.bench.ts",
"test:bench:validation": "vitest bench --run tests/validation-baseline.bench.ts",
"test:update-snapshots": "vitest run -u",
"tsc": "tsc"
},
"dependencies": {
"@atproto/lexicon": "^0.5.1",
"@prototypey/cli": "workspace:*"
},
"devDependencies": {
Expand Down
40 changes: 37 additions & 3 deletions packages/prototypey/src/lib.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/* eslint-disable @typescript-eslint/no-empty-object-type */
import type { Infer } from "./infer.ts";
import type { UnionToTuple } from "./type-utils.ts";
import type { LexiconDoc, ValidationResult } from "@atproto/lexicon";
import { Lexicons } from "@atproto/lexicon";

/** @see https://atproto.com/specs/lexicon#overview-of-types */
type LexiconType =
Expand Down Expand Up @@ -327,12 +329,44 @@ interface SubscriptionOptions {
errors?: ErrorDef[];
}

class Namespace<T extends LexiconNamespace> {
/**
* Public interface for Lexicon to avoid exposing private implementation details
*/
export interface LexiconSchema<T extends LexiconNamespace> {
json: T;
infer: Infer<{ json: T }>;
validate(
data: unknown,
def?: keyof T["defs"],
): ValidationResult<Infer<{ json: T }>>;
}

class Lexicon<T extends LexiconNamespace> implements LexiconSchema<T> {
public json: T;
public infer: Infer<{ json: T }> = null as unknown as Infer<{ json: T }>;
private _validator: Lexicons;

constructor(json: T) {
this.json = json;
// Clone before passing to Lexicons to prevent mutation of this.json
this._validator = new Lexicons([
structuredClone(json) as unknown as LexiconDoc,
]);
}

/**
* Validate data against this lexicon's main definition.
* @param data - The data to validate
* @returns ValidationResult with success status and value or error
*/
validate(
data: unknown,
def: keyof T["defs"] = "main",
): ValidationResult<Infer<{ json: T }>> {
return this._validator.validate(
`${this.json.id}#${def as string}`,
data,
) as ValidationResult<Infer<{ json: T }>>;
}
}

Expand Down Expand Up @@ -569,8 +603,8 @@ export const lx = {
lexicon<ID extends string, D extends LexiconNamespace["defs"]>(
id: ID,
defs: D,
): Namespace<{ lexicon: 1; id: ID; defs: D }> {
return new Namespace({
): LexiconSchema<{ lexicon: 1; id: ID; defs: D }> {
return new Lexicon({
lexicon: 1,
id,
defs,
Expand Down
Loading