]?: InferType | null } & { -readonly [K in NullableAndRequired]: InferType
| null } : {}>;
+type InferArray = T extends {
+ items: infer Items;
+} ? InferType[] : never[];
+type InferUnion = T extends {
+ refs: readonly (infer R)[];
+} ? R extends string ? {
+ $type: R;
+ [key: string]: unknown;
+} : never : never;
+type InferRef = T extends {
+ ref: infer R;
+} ? R extends string ? {
+ $type: R;
+ [key: string]: unknown;
+} : unknown : unknown;
+type InferParams = InferObject;
+type InferRecord = T extends {
+ record: infer R;
+} ? R extends {
+ type: "object";
+} ? InferObject : R extends {
+ type: "union";
+} ? InferUnion : unknown : unknown;
+/**
+ * Recursively replaces stub references in a type with their actual definitions.
+ * Detects circular references and missing references, returning string literal error messages.
+ */
+type ReplaceRefsInType = T extends {
+ $type: `#${infer DefName}`;
+} ? DefName extends keyof Defs ? DefName extends Visited ? `[Circular reference detected: #${DefName}]` : Prettify & {
+ $type: T["$type"];
+}> : `[Reference not found: #${DefName}]` : T extends Uint8Array | Blob ? T : T extends readonly (infer Item)[] ? ReplaceRefsInType- [] : T extends object ? T extends ((...args: unknown[]) => unknown) ? T : { [K in keyof T]: ReplaceRefsInType } : T;
+/**
+ * Infers the TypeScript type for a lexicon namespace, returning only the 'main' definition
+ * with all local refs (#user, #post, etc.) resolved to their actual types.
+ */
+type Infer;
+}> = Prettify<"main" extends keyof T["defs"] ? {
+ $type: T["id"];
+} & ReplaceRefsInType, { [K in keyof T["defs"]]: InferType }> : never>;
+//#endregion
+export { GetNullable, GetRequired, Infer };
+//# sourceMappingURL=infer.d.ts.map
\ No newline at end of file
diff --git a/packages/site/public/types/lib.d.ts b/packages/site/public/types/lib.d.ts
new file mode 100644
index 0000000..0db0dfb
--- /dev/null
+++ b/packages/site/public/types/lib.d.ts
@@ -0,0 +1,420 @@
+import { UnionToTuple } from "./type-utils.js";
+import { Infer } from "./infer.js";
+
+//#region src/lib.d.ts
+/** @see https://atproto.com/specs/lexicon#overview-of-types */
+type LexiconType = "null" | "boolean" | "integer" | "string" | "bytes" | "cid-link" | "blob" | "array" | "object" | "params" | "token" | "ref" | "union" | "unknown" | "record" | "query" | "procedure" | "subscription";
+/**
+ * Common options available for lexicon items.
+ * @see https://atproto.com/specs/lexicon#string-formats
+ */
+interface LexiconItemCommonOptions {
+ /** Indicates this field must be provided */
+ required?: boolean;
+ /** Indicates this field can be explicitly set to null */
+ nullable?: boolean;
+}
+/**
+ * Base interface for all lexicon items.
+ * @see https://atproto.com/specs/lexicon#overview-of-types
+ */
+interface LexiconItem extends LexiconItemCommonOptions {
+ type: LexiconType;
+}
+/**
+ * Definition in a lexicon namespace.
+ * @see https://atproto.com/specs/lexicon#lexicon-document
+ */
+interface Def {
+ type: LexiconType;
+}
+/**
+ * Lexicon namespace document structure.
+ * @see https://atproto.com/specs/lexicon#lexicon-document
+ */
+interface LexiconNamespace {
+ /** Namespaced identifier (NSID) for this lexicon */
+ id: string;
+ /** Named definitions within this namespace */
+ defs: Record;
+}
+/**
+ * String type options.
+ * @see https://atproto.com/specs/lexicon#string
+ */
+interface StringOptions extends LexiconItemCommonOptions {
+ /**
+ * Semantic string format constraint.
+ * @see https://atproto.com/specs/lexicon#string-formats
+ */
+ format?: "at-identifier" | "at-uri" | "cid" | "datetime" | "did" | "handle" | "nsid" | "tid" | "record-key" | "uri" | "language";
+ /** Maximum string length in bytes */
+ maxLength?: number;
+ /** Minimum string length in bytes */
+ minLength?: number;
+ /** Maximum string length in Unicode graphemes */
+ maxGraphemes?: number;
+ /** Minimum string length in Unicode graphemes */
+ minGraphemes?: number;
+ /** Hints at expected values, not enforced */
+ knownValues?: string[];
+ /** Restricts to an exact set of string values */
+ enum?: string[];
+ /** Default value if not provided */
+ default?: string;
+ /** Fixed, unchangeable value */
+ const?: string;
+}
+/**
+ * Boolean type options.
+ * @see https://atproto.com/specs/lexicon#boolean
+ */
+interface BooleanOptions extends LexiconItemCommonOptions {
+ /** Default value if not provided */
+ default?: boolean;
+ /** Fixed, unchangeable value */
+ const?: boolean;
+}
+/**
+ * Integer type options.
+ * @see https://atproto.com/specs/lexicon#integer
+ */
+interface IntegerOptions extends LexiconItemCommonOptions {
+ /** Minimum allowed value (inclusive) */
+ minimum?: number;
+ /** Maximum allowed value (inclusive) */
+ maximum?: number;
+ /** Restricts to an exact set of integer values */
+ enum?: number[];
+ /** Default value if not provided */
+ default?: number;
+ /** Fixed, unchangeable value */
+ const?: number;
+}
+/**
+ * Bytes type options for arbitrary byte arrays.
+ * @see https://atproto.com/specs/lexicon#bytes
+ */
+interface BytesOptions extends LexiconItemCommonOptions {
+ /** Minimum byte array length */
+ minLength?: number;
+ /** Maximum byte array length */
+ maxLength?: number;
+}
+/**
+ * Blob type options for binary data with MIME types.
+ * @see https://atproto.com/specs/lexicon#blob
+ */
+interface BlobOptions extends LexiconItemCommonOptions {
+ /** Allowed MIME types (e.g., ["image/png", "image/jpeg"]) */
+ accept?: string[];
+ /** Maximum blob size in bytes */
+ maxSize?: number;
+}
+/**
+ * Array type options.
+ * @see https://atproto.com/specs/lexicon#array
+ */
+interface ArrayOptions extends LexiconItemCommonOptions {
+ /** Minimum array length */
+ minLength?: number;
+ /** Maximum array length */
+ maxLength?: number;
+}
+/**
+ * Record type options for repository records.
+ * @see https://atproto.com/specs/lexicon#record
+ */
+interface RecordOptions {
+ /** Record key strategy: "self" for self-describing or "tid" for timestamp IDs */
+ key: "self" | "tid";
+ /** Object schema defining the record structure */
+ record: {
+ type: "object";
+ };
+ /** Human-readable description */
+ description?: string;
+}
+/**
+ * Union type options for multiple possible types.
+ * @see https://atproto.com/specs/lexicon#union
+ */
+interface UnionOptions extends LexiconItemCommonOptions {
+ /** If true, only listed refs are allowed; if false, additional types may be added */
+ closed?: boolean;
+}
+/**
+ * Map of property names to their lexicon item definitions.
+ * @see https://atproto.com/specs/lexicon#object
+ */
+type ObjectProperties = Record;
+type RequiredKeys = { [K in keyof T]: T[K] extends {
+ required: true;
+} ? K : never }[keyof T];
+type NullableKeys = { [K in keyof T]: T[K] extends {
+ nullable: true;
+} ? K : never }[keyof T];
+/**
+ * Resulting object schema with required and nullable fields extracted.
+ * @see https://atproto.com/specs/lexicon#object
+ */
+type ObjectResult = {
+ type: "object";
+ /** Property definitions */
+ properties: { [K in keyof T]: T[K] extends {
+ type: "object";
+ } ? T[K] : Omit };
+} & ([RequiredKeys] extends [never] ? {} : {
+ required: UnionToTuple>;
+}) & ([NullableKeys] extends [never] ? {} : {
+ nullable: UnionToTuple>;
+});
+/**
+ * Map of parameter names to their lexicon item definitions.
+ * @see https://atproto.com/specs/lexicon#params
+ */
+type ParamsProperties = Record;
+/**
+ * Resulting params schema with required fields extracted.
+ * @see https://atproto.com/specs/lexicon#params
+ */
+type ParamsResult = {
+ type: "params";
+ /** Parameter definitions */
+ properties: { [K in keyof T]: Omit };
+} & ([RequiredKeys] extends [never] ? {} : {
+ required: UnionToTuple>;
+});
+/**
+ * HTTP request or response body schema.
+ * @see https://atproto.com/specs/lexicon#http-endpoints
+ */
+interface BodySchema {
+ /** MIME type encoding (typically "application/json") */
+ encoding: "application/json" | (string & {});
+ /** Human-readable description */
+ description?: string;
+ /** Object schema defining the body structure */
+ schema?: ObjectResult;
+}
+/**
+ * Error definition for HTTP endpoints.
+ * @see https://atproto.com/specs/lexicon#http-endpoints
+ */
+interface ErrorDef {
+ /** Error name/code */
+ name: string;
+ /** Human-readable error description */
+ description?: string;
+}
+/**
+ * Query endpoint options (HTTP GET).
+ * @see https://atproto.com/specs/lexicon#query
+ */
+interface QueryOptions {
+ /** Human-readable description */
+ description?: string;
+ /** Query string parameters */
+ parameters?: ParamsResult;
+ /** Response body schema */
+ output?: BodySchema;
+ /** Possible error responses */
+ errors?: ErrorDef[];
+}
+/**
+ * Procedure endpoint options (HTTP POST).
+ * @see https://atproto.com/specs/lexicon#procedure
+ */
+interface ProcedureOptions {
+ /** Human-readable description */
+ description?: string;
+ /** Query string parameters */
+ parameters?: ParamsResult;
+ /** Request body schema */
+ input?: BodySchema;
+ /** Response body schema */
+ output?: BodySchema;
+ /** Possible error responses */
+ errors?: ErrorDef[];
+}
+/**
+ * WebSocket message schema for subscriptions.
+ * @see https://atproto.com/specs/lexicon#subscription
+ */
+interface MessageSchema {
+ /** Human-readable description */
+ description?: string;
+ /** Union of possible message types */
+ schema: {
+ type: "union";
+ refs: readonly string[];
+ };
+}
+/**
+ * Subscription endpoint options (WebSocket).
+ * @see https://atproto.com/specs/lexicon#subscription
+ */
+interface SubscriptionOptions {
+ /** Human-readable description */
+ description?: string;
+ /** Query string parameters */
+ parameters?: ParamsResult;
+ /** Message schema for events */
+ message?: MessageSchema;
+ /** Possible error responses */
+ errors?: ErrorDef[];
+}
+declare class Namespace {
+ json: T;
+ infer: Infer;
+ constructor(json: T);
+}
+/**
+ * Main API for creating lexicon schemas.
+ * @see https://atproto.com/specs/lexicon
+ */
+declare const lx: {
+ /**
+ * Creates a null type.
+ * @see https://atproto.com/specs/lexicon#null
+ */
+ null(options?: LexiconItemCommonOptions): {
+ type: "null";
+ } & LexiconItemCommonOptions;
+ /**
+ * Creates a boolean type with optional constraints.
+ * @see https://atproto.com/specs/lexicon#boolean
+ */
+ boolean(options?: T): T & {
+ type: "boolean";
+ };
+ /**
+ * Creates an integer type with optional min/max and enum constraints.
+ * @see https://atproto.com/specs/lexicon#integer
+ */
+ integer(options?: T): T & {
+ type: "integer";
+ };
+ /**
+ * Creates a string type with optional format, length, and value constraints.
+ * @see https://atproto.com/specs/lexicon#string
+ */
+ string(options?: T): T & {
+ type: "string";
+ };
+ /**
+ * Creates an unknown type for flexible, unvalidated objects.
+ * @see https://atproto.com/specs/lexicon#unknown
+ */
+ unknown(options?: LexiconItemCommonOptions): {
+ type: "unknown";
+ } & LexiconItemCommonOptions;
+ /**
+ * Creates a bytes type for arbitrary byte arrays.
+ * @see https://atproto.com/specs/lexicon#bytes
+ */
+ bytes(options?: T): T & {
+ type: "bytes";
+ };
+ /**
+ * Creates a CID link reference to content-addressed data.
+ * @see https://atproto.com/specs/lexicon#cid-link
+ */
+ cidLink(link: Link): {
+ type: "cid-link";
+ $link: Link;
+ };
+ /**
+ * Creates a blob type for binary data with MIME type constraints.
+ * @see https://atproto.com/specs/lexicon#blob
+ */
+ blob(options?: T): T & {
+ type: "blob";
+ };
+ /**
+ * Creates an array type with item schema and length constraints.
+ * @see https://atproto.com/specs/lexicon#array
+ */
+ array(items: Items, options?: Options): Options & {
+ type: "array";
+ items: Items;
+ };
+ /**
+ * Creates a token type for symbolic values in unions.
+ * @see https://atproto.com/specs/lexicon#token
+ */
+ token(description: Description): {
+ type: "token";
+ description: Description;
+ };
+ /**
+ * Creates a reference to another schema definition.
+ * @see https://atproto.com/specs/lexicon#ref
+ */
+ ref
[(ref: Ref, options?: LexiconItemCommonOptions): LexiconItemCommonOptions & {
+ type: "ref";
+ ref: Ref;
+ };
+ /**
+ * Creates a union type for multiple possible type variants.
+ * @see https://atproto.com/specs/lexicon#union
+ */
+ union(refs: Refs, options?: Options): Options & {
+ type: "union";
+ refs: Refs;
+ };
+ /**
+ * Creates a record type for repository records.
+ * @see https://atproto.com/specs/lexicon#record
+ */
+ record(options: T): T & {
+ type: "record";
+ };
+ /**
+ * Creates an object type with defined properties.
+ * @see https://atproto.com/specs/lexicon#object
+ */
+ object(options: T): ObjectResult;
+ /**
+ * Creates a params type for query string parameters.
+ * @see https://atproto.com/specs/lexicon#params
+ */
+ params(properties: Properties): ParamsResult;
+ /**
+ * Creates a query endpoint definition (HTTP GET).
+ * @see https://atproto.com/specs/lexicon#query
+ */
+ query(options?: T): T & {
+ type: "query";
+ };
+ /**
+ * Creates a procedure endpoint definition (HTTP POST).
+ * @see https://atproto.com/specs/lexicon#procedure
+ */
+ procedure(options?: T): T & {
+ type: "procedure";
+ };
+ /**
+ * Creates a subscription endpoint definition (WebSocket).
+ * @see https://atproto.com/specs/lexicon#subscription
+ */
+ subscription(options?: T): T & {
+ type: "subscription";
+ };
+ /**
+ * Creates a lexicon namespace document.
+ * @see https://atproto.com/specs/lexicon#lexicon-document
+ */
+ namespace(id: ID, defs: D): Namespace<{
+ lexicon: 1;
+ id: ID;
+ defs: D;
+ }>;
+};
+//#endregion
+export { lx };
+//# sourceMappingURL=lib.d.ts.map
\ No newline at end of file
diff --git a/packages/site/public/types/type-utils.d.ts b/packages/site/public/types/type-utils.d.ts
new file mode 100644
index 0000000..48f1d28
--- /dev/null
+++ b/packages/site/public/types/type-utils.d.ts
@@ -0,0 +1,14 @@
+//#region src/type-utils.d.ts
+/**
+ * Converts a string union type to a tuple type
+ * @example
+ * type Colors = "red" | "green" | "blue";
+ * type ColorTuple = UnionToTuple; // ["red", "green", "blue"]
+ */
+type UnionToTuple = ((T extends unknown ? (x: () => T) => void : never) extends ((x: infer I) => void) ? I : never) extends (() => infer R) ? [...UnionToTuple>, R] : [];
+type Prettify = { [K in keyof T]: T[K] } & {};
+//# sourceMappingURL=type-utils.d.ts.map
+
+//#endregion
+export { Prettify, UnionToTuple };
+//# sourceMappingURL=type-utils.d.ts.map
\ No newline at end of file
diff --git a/packages/site/src/components/Editor.tsx b/packages/site/src/components/Editor.tsx
index ad99671..dd99cd2 100644
--- a/packages/site/src/components/Editor.tsx
+++ b/packages/site/src/components/Editor.tsx
@@ -6,7 +6,9 @@ interface EditorProps {
}
export function Editor({ value, onChange }: EditorProps) {
- const handleEditorWillMount = (monaco: typeof import("monaco-editor")) => {
+ const handleEditorWillMount = async (
+ monaco: typeof import("monaco-editor"),
+ ) => {
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
noSemanticValidation: false,
noSyntaxValidation: false,
@@ -15,43 +17,44 @@ export function Editor({ value, onChange }: EditorProps) {
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true,
- moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
+ moduleResolution:
+ monaco.languages.typescript.ModuleResolutionKind.NodeJs,
module: monaco.languages.typescript.ModuleKind.ESNext,
noEmit: true,
esModuleInterop: true,
allowSyntheticDefaultImports: true,
});
- monaco.languages.typescript.typescriptDefaults.addExtraLib(
- `declare module "prototypekit" {
- type Prettify = { [K in keyof T]: T[K] } & {};
-
- type UnionToTuple = ((T extends unknown ? (x: () => T) => void : never) extends ((x: infer I) => void) ? I : never) extends (() => infer R) ? [...UnionToTuple>, R] : [];
+ try {
+ const [typeUtilsDts, inferDts, libDts, indexDts] = await Promise.all([
+ fetch("/types/type-utils.d.ts").then((r) => r.text()),
+ fetch("/types/infer.d.ts").then((r) => r.text()),
+ fetch("/types/lib.d.ts").then((r) => r.text()),
+ fetch("/types/index.d.ts").then((r) => r.text()),
+ ]);
- export const lx: {
- null(options?: { required?: boolean; nullable?: boolean }): { type: "null" } & { required?: boolean; nullable?: boolean };
- boolean(options?: T): T & { type: "boolean" };
- integer(options?: T): T & { type: "integer" };
- string(options?: T): T & { type: "string" };
- unknown(options?: { required?: boolean; nullable?: boolean }): { type: "unknown" } & { required?: boolean; nullable?: boolean };
- bytes(options?: T): T & { type: "bytes" };
- cidLink(link: Link): { type: "cid-link"; $link: Link };
- blob(options?: T): T & { type: "blob" };
- array(items: Items, options?: Options): Options & { type: "array"; items: Items };
- token(description: Description): { type: "token"; description: Description };
- ref][(ref: Ref, options?: { required?: boolean; nullable?: boolean }): { required?: boolean; nullable?: boolean } & { type: "ref"; ref: Ref };
- union(refs: Refs, options?: Options): Options & { type: "union"; refs: Refs };
- record(options: T): T & { type: "record" };
- object>(options: T): any;
- params>(properties: Properties): any;
- query(options?: T): T & { type: "query" };
- procedure(options?: T): T & { type: "procedure" };
- subscription(options?: T): T & { type: "subscription" };
- namespace>(id: ID, defs: D): { json: { lexicon: 1; id: ID; defs: D }; infer: any };
- };
-}`,
- "file:///node_modules/prototypekit/index.d.ts",
- );
+ monaco.languages.typescript.typescriptDefaults.addExtraLib(
+ typeUtilsDts,
+ "file:///node_modules/prototypekit/type-utils.d.ts",
+ );
+
+ monaco.languages.typescript.typescriptDefaults.addExtraLib(
+ inferDts,
+ "file:///node_modules/prototypekit/infer.d.ts",
+ );
+
+ monaco.languages.typescript.typescriptDefaults.addExtraLib(
+ libDts,
+ "file:///node_modules/prototypekit/lib.d.ts",
+ );
+
+ monaco.languages.typescript.typescriptDefaults.addExtraLib(
+ indexDts,
+ "file:///node_modules/prototypekit/index.d.ts",
+ );
+ } catch (error) {
+ console.error("Failed to load prototypekit types:", error);
+ }
};
return (
diff --git a/packages/site/vite.config.ts b/packages/site/vite.config.ts
index f9f0d5e..d48be7c 100644
--- a/packages/site/vite.config.ts
+++ b/packages/site/vite.config.ts
@@ -1,6 +1,37 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
+import { readFileSync } from "fs";
+import { resolve } from "path";
+
+function servePrototypeKitTypes() {
+ return {
+ name: "serve-prototypekit-types",
+ configureServer(server) {
+ server.middlewares.use((req, res, next) => {
+ if (req.url?.startsWith("/types/") && req.url.endsWith(".d.ts")) {
+ const fileName = req.url.slice(7);
+ try {
+ const filePath = resolve(
+ __dirname,
+ "../prototypekit/lib",
+ fileName,
+ );
+ const content = readFileSync(filePath, "utf-8");
+ res.setHeader("Content-Type", "application/typescript");
+ res.end(content);
+ return;
+ } catch (e) {
+ res.statusCode = 404;
+ res.end("Type file not found");
+ return;
+ }
+ }
+ next();
+ });
+ },
+ };
+}
export default defineConfig({
- plugins: [react()],
+ plugins: [react(), servePrototypeKitTypes()],
});
From 35e5d72435f922fad0836b4fcff131410b61a042 Mon Sep 17 00:00:00 2001
From: Tyler <26290074+tylersayshi@users.noreply.github.com>
Date: Sat, 18 Oct 2025 23:28:06 -0700
Subject: [PATCH 07/23] working json output
---
packages/site/src/components/Playground.tsx | 28 +++++++++------------
1 file changed, 12 insertions(+), 16 deletions(-)
diff --git a/packages/site/src/components/Playground.tsx b/packages/site/src/components/Playground.tsx
index 18c20d6..8dc0d7a 100644
--- a/packages/site/src/components/Playground.tsx
+++ b/packages/site/src/components/Playground.tsx
@@ -14,24 +14,20 @@ export function Playground() {
useEffect(() => {
const timeoutId = setTimeout(() => {
try {
- const AsyncFunction = async function () {}.constructor as new (
- ...args: string[]
- ) => (...args: unknown[]) => Promise;
+ const cleanedCode = code.replace(
+ /import\s+{[^}]*}\s+from\s+['"][^'"]+['"]\s*;?\s*/g,
+ "",
+ );
- const wrappedCode = `
- const { lx } = arguments[0];
- ${code}
- const exports = {};
- for (const key in this) {
- if (this.hasOwnProperty(key) && key !== 'lx') {
- exports[key] = this[key];
- }
- }
- return Object.values(exports)[0];
- `;
+ const lastVarMatch = cleanedCode.match(/(?:const|let|var)\s+(\w+)\s*=/);
+ const lastVarName = lastVarMatch ? lastVarMatch[1] : null;
- const fn = new AsyncFunction(wrappedCode);
- const result = fn.call({}, { lx });
+ const wrappedCode = lastVarName
+ ? `${cleanedCode}\nreturn ${lastVarName};`
+ : cleanedCode;
+
+ const fn = new Function("lx", wrappedCode);
+ const result = fn(lx);
if (result && typeof result === "object" && "json" in result) {
const jsonOutput = (result as { json: unknown }).json;
From 1b1dc6321ae2210936e9c50633ac06f716218126 Mon Sep 17 00:00:00 2001
From: Tyler <26290074+tylersayshi@users.noreply.github.com>
Date: Sat, 18 Oct 2025 23:36:48 -0700
Subject: [PATCH 08/23] types working fr
---
packages/site/public/types/index.d.ts | 5 +-
packages/site/src/components/Editor.tsx | 119 +++++++++++++++---------
2 files changed, 79 insertions(+), 45 deletions(-)
diff --git a/packages/site/public/types/index.d.ts b/packages/site/public/types/index.d.ts
index 2fa64d9..881584c 100644
--- a/packages/site/public/types/index.d.ts
+++ b/packages/site/public/types/index.d.ts
@@ -1,3 +1,2 @@
-import { GetNullable, GetRequired, Infer } from "./infer.js";
-import { lx } from "./lib.js";
-export { GetNullable, GetRequired, Infer, lx };
\ No newline at end of file
+export { GetNullable, GetRequired, Infer } from "./infer";
+export { lx } from "./lib";
diff --git a/packages/site/src/components/Editor.tsx b/packages/site/src/components/Editor.tsx
index dd99cd2..a1444f5 100644
--- a/packages/site/src/components/Editor.tsx
+++ b/packages/site/src/components/Editor.tsx
@@ -1,4 +1,6 @@
import MonacoEditor from "@monaco-editor/react";
+import { loader } from "@monaco-editor/react";
+import { useEffect, useState } from "react";
interface EditorProps {
value: string;
@@ -6,56 +8,90 @@ interface EditorProps {
}
export function Editor({ value, onChange }: EditorProps) {
- const handleEditorWillMount = async (
- monaco: typeof import("monaco-editor"),
- ) => {
- monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
- noSemanticValidation: false,
- noSyntaxValidation: false,
- });
+ const [isReady, setIsReady] = useState(false);
- monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
- target: monaco.languages.typescript.ScriptTarget.ES2020,
- allowNonTsExtensions: true,
- moduleResolution:
- monaco.languages.typescript.ModuleResolutionKind.NodeJs,
- module: monaco.languages.typescript.ModuleKind.ESNext,
- noEmit: true,
- esModuleInterop: true,
- allowSyntheticDefaultImports: true,
- });
+ useEffect(() => {
+ loader.init().then((monaco) => {
+ monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
+ target: monaco.languages.typescript.ScriptTarget.ES2020,
+ allowNonTsExtensions: true,
+ moduleResolution:
+ monaco.languages.typescript.ModuleResolutionKind.NodeJs,
+ module: monaco.languages.typescript.ModuleKind.ESNext,
+ noEmit: true,
+ esModuleInterop: true,
+ allowSyntheticDefaultImports: true,
+ strict: false,
+ });
- try {
- const [typeUtilsDts, inferDts, libDts, indexDts] = await Promise.all([
+ monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
+ noSemanticValidation: false,
+ noSyntaxValidation: false,
+ });
+
+ Promise.all([
fetch("/types/type-utils.d.ts").then((r) => r.text()),
fetch("/types/infer.d.ts").then((r) => r.text()),
fetch("/types/lib.d.ts").then((r) => r.text()),
- fetch("/types/index.d.ts").then((r) => r.text()),
- ]);
+ ]).then(([typeUtilsDts, inferDts, libDts]) => {
+ const stripImportsExports = (content: string) =>
+ content
+ .replace(/import\s+{[^}]*}\s+from\s+['""][^'"]*['""];?\s*/g, "")
+ .replace(/import\s+.*\s+from\s+['""][^'"]*['""];?\s*/g, "")
+ .replace(/^export\s+{[^}]*};?\s*/gm, "")
+ .replace(/^export\s+/gm, "")
+ .replace(/\/\/# sourceMappingURL=.*/g, "")
+ .replace(/\/\/#region.*\n?/g, "")
+ .replace(/\/\/#endregion.*\n?/g, "");
+
+ const combinedTypes = `
+${stripImportsExports(typeUtilsDts)}
+${stripImportsExports(inferDts)}
+${stripImportsExports(libDts)}
+`;
- monaco.languages.typescript.typescriptDefaults.addExtraLib(
- typeUtilsDts,
- "file:///node_modules/prototypekit/type-utils.d.ts",
- );
+ const moduleDeclaration = `declare module "prototypekit" {
+${combinedTypes}
+}`;
- monaco.languages.typescript.typescriptDefaults.addExtraLib(
- inferDts,
- "file:///node_modules/prototypekit/infer.d.ts",
- );
+ monaco.languages.typescript.typescriptDefaults.addExtraLib(
+ moduleDeclaration,
+ "prototypekit.d.ts",
+ );
- monaco.languages.typescript.typescriptDefaults.addExtraLib(
- libDts,
- "file:///node_modules/prototypekit/lib.d.ts",
- );
+ setIsReady(true);
+ });
+ });
+ }, []);
- monaco.languages.typescript.typescriptDefaults.addExtraLib(
- indexDts,
- "file:///node_modules/prototypekit/index.d.ts",
- );
- } catch (error) {
- console.error("Failed to load prototypekit types:", error);
- }
- };
+ if (!isReady) {
+ return (
+ ]
+
+ Input
+
+
+ Loading...
+
+
+ );
+ }
return (
@@ -78,7 +114,6 @@ export function Editor({ value, onChange }: EditorProps) {
value={value}
onChange={(value) => onChange(value || "")}
theme="vs-light"
- beforeMount={handleEditorWillMount}
options={{
minimap: { enabled: false },
fontSize: 14,
From ce4128f76879e6a377cadb815673700e545fb2d9 Mon Sep 17 00:00:00 2001
From: Tyler <26290074+tylersayshi@users.noreply.github.com>
Date: Sun, 19 Oct 2025 00:01:21 -0700
Subject: [PATCH 09/23] more site readiness
---
packages/prototypekit/src/infer.ts | 19 +--
packages/site/src/components/Editor.tsx | 7 +-
packages/site/src/components/OutputPanel.tsx | 49 +------
packages/site/src/components/Playground.tsx | 128 ++++++++++++++++--
.../site/tests/components/Playground.test.tsx | 34 +++++
packages/site/tests/setup.ts | 8 ++
packages/site/vitest.config.ts | 2 +-
7 files changed, 181 insertions(+), 66 deletions(-)
create mode 100644 packages/site/tests/setup.ts
diff --git a/packages/prototypekit/src/infer.ts b/packages/prototypekit/src/infer.ts
index 8d7037a..3741133 100644
--- a/packages/prototypekit/src/infer.ts
+++ b/packages/prototypekit/src/infer.ts
@@ -130,12 +130,13 @@ type ReplaceRefsInType
=
* Infers the TypeScript type for a lexicon namespace, returning only the 'main' definition
* with all local refs (#user, #post, etc.) resolved to their actual types.
*/
-export type Infer }> =
- Prettify<
- "main" extends keyof T["defs"]
- ? { $type: T["id"] } & ReplaceRefsInType<
- InferType,
- { [K in keyof T["defs"]]: InferType }
- >
- : never
- >;
+export type Infer<
+ T extends { json: { id: string; defs: Record } },
+> = Prettify<
+ "main" extends keyof T["json"]["defs"]
+ ? { $type: T["json"]["id"] } & ReplaceRefsInType<
+ InferType,
+ { [K in keyof T["json"]["defs"]]: InferType }
+ >
+ : never
+>;
diff --git a/packages/site/src/components/Editor.tsx b/packages/site/src/components/Editor.tsx
index a1444f5..0286385 100644
--- a/packages/site/src/components/Editor.tsx
+++ b/packages/site/src/components/Editor.tsx
@@ -5,9 +5,10 @@ import { useEffect, useState } from "react";
interface EditorProps {
value: string;
onChange: (value: string) => void;
+ onReady?: () => void;
}
-export function Editor({ value, onChange }: EditorProps) {
+export function Editor({ value, onChange, onReady }: EditorProps) {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
@@ -60,9 +61,10 @@ ${combinedTypes}
);
setIsReady(true);
+ onReady?.();
});
});
- }, []);
+ }, [onReady]);
if (!isReady) {
return (
@@ -111,6 +113,7 @@ ${combinedTypes}
onChange(value || "")}
theme="vs-light"
diff --git a/packages/site/src/components/OutputPanel.tsx b/packages/site/src/components/OutputPanel.tsx
index b944d40..a771226 100644
--- a/packages/site/src/components/OutputPanel.tsx
+++ b/packages/site/src/components/OutputPanel.tsx
@@ -1,4 +1,3 @@
-import { useState } from "react";
import MonacoEditor from "@monaco-editor/react";
interface OutputPanelProps {
@@ -10,53 +9,19 @@ interface OutputPanelProps {
}
export function OutputPanel({ output }: OutputPanelProps) {
- const [activeTab, setActiveTab] = useState<"json" | "types">("json");
-
return (
-
-
+ Output
{output.error ? (
@@ -74,8 +39,8 @@ export function OutputPanel({ output }: OutputPanelProps) {
) : (
(null);
const handleCodeChange = (newCode: string) => {
setCode(newCode);
};
+ const handleEditorReady = () => {
+ setEditorReady(true);
+ };
+
+ useEffect(() => {
+ if (monaco && editorReady && !tsWorkerRef.current && !tsWorkerInstance) {
+ const initWorker = async () => {
+ try {
+ await new Promise((resolve) => setTimeout(resolve, 200));
+ const worker =
+ await monaco.languages.typescript.getTypeScriptWorker();
+ const uri = monaco.Uri.parse("file:///main.ts");
+ const client = await worker(uri);
+ tsWorkerRef.current = client;
+ tsWorkerInstance = client;
+ } catch (err) {
+ console.error("Failed to initialize TypeScript worker:", err);
+ }
+ };
+ initWorker();
+ }
+ }, [monaco, editorReady]);
+
useEffect(() => {
- const timeoutId = setTimeout(() => {
+ const timeoutId = setTimeout(async () => {
try {
- const cleanedCode = code.replace(
- /import\s+{[^}]*}\s+from\s+['"][^'"]+['"]\s*;?\s*/g,
- "",
- );
+ const cleanedCode = code
+ .replace(/import\s+{[^}]*}\s+from\s+['"][^'"]+['"]\s*;?\s*/g, "")
+ .replace(/^type\s+\w+\s*=\s*[^;]+;?\s*$/gm, "");
const lastVarMatch = cleanedCode.match(/(?:const|let|var)\s+(\w+)\s*=/);
const lastVarName = lastVarMatch ? lastVarMatch[1] : null;
@@ -28,18 +59,55 @@ export function Playground() {
const fn = new Function("lx", wrappedCode);
const result = fn(lx);
+ let typeInfo = "// Hover over .infer in the editor to see the type";
+
+ if (lastVarName && monaco && tsWorkerRef.current) {
+ try {
+ const uri = monaco.Uri.parse("file:///main.ts");
+ const existingModel = monaco.editor.getModel(uri);
+
+ if (existingModel) {
+ const inferPosition = code.indexOf(`${lastVarName}.infer`);
+ if (inferPosition !== -1) {
+ const offset =
+ inferPosition + `${lastVarName}.infer`.length - 1;
+
+ const quickInfo =
+ await tsWorkerRef.current.getQuickInfoAtPosition(
+ uri.toString(),
+ offset,
+ );
+
+ if (quickInfo?.displayParts) {
+ const typeText = quickInfo.displayParts
+ .map((part: { text: string }) => part.text)
+ .join("");
+
+ const propertyMatch = typeText.match(
+ /\(property\)\s+.*?\.infer:\s*([\s\S]+?)$/,
+ );
+ if (propertyMatch) {
+ typeInfo = formatTypeString(propertyMatch[1]);
+ }
+ }
+ }
+ }
+ } catch (err) {
+ console.error("Type extraction error:", err);
+ }
+ }
if (result && typeof result === "object" && "json" in result) {
const jsonOutput = (result as { json: unknown }).json;
setOutput({
json: JSON.stringify(jsonOutput, null, 2),
- typeInfo: "// Type inference not yet implemented in playground",
+ typeInfo,
error: "",
});
} else {
setOutput({
json: JSON.stringify(result, null, 2),
- typeInfo: "// Type inference not yet implemented in playground",
+ typeInfo,
error: "",
});
}
@@ -53,7 +121,7 @@ export function Playground() {
}, 500);
return () => clearTimeout(timeoutId);
- }, [code]);
+ }, [code, monaco]);
return (
-
+
@@ -79,7 +151,37 @@ export function Playground() {
);
}
-const DEFAULT_CODE = `import { lx } from "prototypekit";
+function formatTypeString(typeStr: string): string {
+ let formatted = typeStr.trim();
+
+ formatted = formatted.replace(/\s+/g, " ");
+ formatted = formatted.replace(/;\s*/g, "\n");
+ formatted = formatted.replace(/{\s*/g, "{\n");
+ formatted = formatted.replace(/\s*}/g, "\n}");
+
+ const lines = formatted.split("\n");
+ let indentLevel = 0;
+ const indentedLines: string[] = [];
+
+ for (const line of lines) {
+ const trimmed = line.trim();
+ if (!trimmed) continue;
+
+ if (trimmed.startsWith("}")) {
+ indentLevel = Math.max(0, indentLevel - 1);
+ }
+
+ indentedLines.push(" ".repeat(indentLevel) + trimmed);
+
+ if (trimmed.endsWith("{") && !trimmed.includes("}")) {
+ indentLevel++;
+ }
+ }
+
+ return indentedLines.join("\n");
+}
+
+const DEFAULT_CODE = `import { lx, type Infer } from "prototypekit";
const profileNamespace = lx.namespace("app.bsky.actor.profile", {
main: lx.record({
@@ -89,4 +191,6 @@ const profileNamespace = lx.namespace("app.bsky.actor.profile", {
description: lx.string({ maxLength: 256, maxGraphemes: 256 }),
}),
}),
-});`;
+});
+
+type ProfileInferred = Infer
;`;
diff --git a/packages/site/tests/components/Playground.test.tsx b/packages/site/tests/components/Playground.test.tsx
index caf1a9c..6c71602 100644
--- a/packages/site/tests/components/Playground.test.tsx
+++ b/packages/site/tests/components/Playground.test.tsx
@@ -10,6 +10,40 @@ vi.mock("@monaco-editor/react", () => ({
onChange={(e) => onChange(e.target.value)}
/>
),
+ useMonaco: () => null,
+ loader: {
+ init: vi.fn(() =>
+ Promise.resolve({
+ languages: {
+ typescript: {
+ typescriptDefaults: {
+ setCompilerOptions: vi.fn(),
+ setDiagnosticsOptions: vi.fn(),
+ addExtraLib: vi.fn(),
+ },
+ ScriptTarget: { ES2020: 5 },
+ ModuleResolutionKind: { NodeJs: 2 },
+ ModuleKind: { ESNext: 99 },
+ getTypeScriptWorker: vi.fn(() =>
+ Promise.resolve(() =>
+ Promise.resolve({
+ getQuickInfoAtPosition: vi.fn(() => Promise.resolve(null)),
+ }),
+ ),
+ ),
+ },
+ },
+ editor: {
+ defineTheme: vi.fn(),
+ createModel: vi.fn(() => ({ dispose: vi.fn() })),
+ getModel: vi.fn(() => null),
+ },
+ Uri: {
+ parse: vi.fn((uri: string) => ({ toString: () => uri })),
+ },
+ }),
+ ),
+ },
}));
describe("Playground", () => {
diff --git a/packages/site/tests/setup.ts b/packages/site/tests/setup.ts
new file mode 100644
index 0000000..fab9e47
--- /dev/null
+++ b/packages/site/tests/setup.ts
@@ -0,0 +1,8 @@
+import { vi } from "vitest";
+
+global.fetch = vi.fn(
+ () =>
+ Promise.resolve({
+ text: () => Promise.resolve(""),
+ }) as any,
+);
diff --git a/packages/site/vitest.config.ts b/packages/site/vitest.config.ts
index f52fa1e..3d5c116 100644
--- a/packages/site/vitest.config.ts
+++ b/packages/site/vitest.config.ts
@@ -6,6 +6,6 @@ export default defineConfig({
test: {
environment: "jsdom",
globals: true,
- setupFiles: [],
+ setupFiles: ["./tests/setup.ts"],
},
});
From 1f280a0d8b5a754fbda80bd2c8fea3cc18d2ec93 Mon Sep 17 00:00:00 2001
From: Tyler <26290074+tylersayshi@users.noreply.github.com>
Date: Sun, 19 Oct 2025 00:04:40 -0700
Subject: [PATCH 10/23] Revert "more site readiness"
This reverts commit ce4128f76879e6a377cadb815673700e545fb2d9.
---
packages/prototypekit/src/infer.ts | 19 ++-
packages/site/src/components/Editor.tsx | 7 +-
packages/site/src/components/OutputPanel.tsx | 49 ++++++-
packages/site/src/components/Playground.tsx | 128 ++----------------
.../site/tests/components/Playground.test.tsx | 34 -----
packages/site/tests/setup.ts | 8 --
packages/site/vitest.config.ts | 2 +-
7 files changed, 66 insertions(+), 181 deletions(-)
delete mode 100644 packages/site/tests/setup.ts
diff --git a/packages/prototypekit/src/infer.ts b/packages/prototypekit/src/infer.ts
index 3741133..8d7037a 100644
--- a/packages/prototypekit/src/infer.ts
+++ b/packages/prototypekit/src/infer.ts
@@ -130,13 +130,12 @@ type ReplaceRefsInType =
* Infers the TypeScript type for a lexicon namespace, returning only the 'main' definition
* with all local refs (#user, #post, etc.) resolved to their actual types.
*/
-export type Infer<
- T extends { json: { id: string; defs: Record } },
-> = Prettify<
- "main" extends keyof T["json"]["defs"]
- ? { $type: T["json"]["id"] } & ReplaceRefsInType<
- InferType,
- { [K in keyof T["json"]["defs"]]: InferType }
- >
- : never
->;
+export type Infer }> =
+ Prettify<
+ "main" extends keyof T["defs"]
+ ? { $type: T["id"] } & ReplaceRefsInType<
+ InferType,
+ { [K in keyof T["defs"]]: InferType }
+ >
+ : never
+ >;
diff --git a/packages/site/src/components/Editor.tsx b/packages/site/src/components/Editor.tsx
index 0286385..a1444f5 100644
--- a/packages/site/src/components/Editor.tsx
+++ b/packages/site/src/components/Editor.tsx
@@ -5,10 +5,9 @@ import { useEffect, useState } from "react";
interface EditorProps {
value: string;
onChange: (value: string) => void;
- onReady?: () => void;
}
-export function Editor({ value, onChange, onReady }: EditorProps) {
+export function Editor({ value, onChange }: EditorProps) {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
@@ -61,10 +60,9 @@ ${combinedTypes}
);
setIsReady(true);
- onReady?.();
});
});
- }, [onReady]);
+ }, []);
if (!isReady) {
return (
@@ -113,7 +111,6 @@ ${combinedTypes}
onChange(value || "")}
theme="vs-light"
diff --git a/packages/site/src/components/OutputPanel.tsx b/packages/site/src/components/OutputPanel.tsx
index a771226..b944d40 100644
--- a/packages/site/src/components/OutputPanel.tsx
+++ b/packages/site/src/components/OutputPanel.tsx
@@ -1,3 +1,4 @@
+import { useState } from "react";
import MonacoEditor from "@monaco-editor/react";
interface OutputPanelProps {
@@ -9,19 +10,53 @@ interface OutputPanelProps {
}
export function OutputPanel({ output }: OutputPanelProps) {
+ const [activeTab, setActiveTab] = useState<"json" | "types">("json");
+
return (
- Output
+
+
{output.error ? (
@@ -39,8 +74,8 @@ export function OutputPanel({ output }: OutputPanelProps) {
) : (
(null);
const handleCodeChange = (newCode: string) => {
setCode(newCode);
};
- const handleEditorReady = () => {
- setEditorReady(true);
- };
-
- useEffect(() => {
- if (monaco && editorReady && !tsWorkerRef.current && !tsWorkerInstance) {
- const initWorker = async () => {
- try {
- await new Promise((resolve) => setTimeout(resolve, 200));
- const worker =
- await monaco.languages.typescript.getTypeScriptWorker();
- const uri = monaco.Uri.parse("file:///main.ts");
- const client = await worker(uri);
- tsWorkerRef.current = client;
- tsWorkerInstance = client;
- } catch (err) {
- console.error("Failed to initialize TypeScript worker:", err);
- }
- };
- initWorker();
- }
- }, [monaco, editorReady]);
-
useEffect(() => {
- const timeoutId = setTimeout(async () => {
+ const timeoutId = setTimeout(() => {
try {
- const cleanedCode = code
- .replace(/import\s+{[^}]*}\s+from\s+['"][^'"]+['"]\s*;?\s*/g, "")
- .replace(/^type\s+\w+\s*=\s*[^;]+;?\s*$/gm, "");
+ const cleanedCode = code.replace(
+ /import\s+{[^}]*}\s+from\s+['"][^'"]+['"]\s*;?\s*/g,
+ "",
+ );
const lastVarMatch = cleanedCode.match(/(?:const|let|var)\s+(\w+)\s*=/);
const lastVarName = lastVarMatch ? lastVarMatch[1] : null;
@@ -59,55 +28,18 @@ export function Playground() {
const fn = new Function("lx", wrappedCode);
const result = fn(lx);
- let typeInfo = "// Hover over .infer in the editor to see the type";
-
- if (lastVarName && monaco && tsWorkerRef.current) {
- try {
- const uri = monaco.Uri.parse("file:///main.ts");
- const existingModel = monaco.editor.getModel(uri);
-
- if (existingModel) {
- const inferPosition = code.indexOf(`${lastVarName}.infer`);
- if (inferPosition !== -1) {
- const offset =
- inferPosition + `${lastVarName}.infer`.length - 1;
-
- const quickInfo =
- await tsWorkerRef.current.getQuickInfoAtPosition(
- uri.toString(),
- offset,
- );
-
- if (quickInfo?.displayParts) {
- const typeText = quickInfo.displayParts
- .map((part: { text: string }) => part.text)
- .join("");
-
- const propertyMatch = typeText.match(
- /\(property\)\s+.*?\.infer:\s*([\s\S]+?)$/,
- );
- if (propertyMatch) {
- typeInfo = formatTypeString(propertyMatch[1]);
- }
- }
- }
- }
- } catch (err) {
- console.error("Type extraction error:", err);
- }
- }
if (result && typeof result === "object" && "json" in result) {
const jsonOutput = (result as { json: unknown }).json;
setOutput({
json: JSON.stringify(jsonOutput, null, 2),
- typeInfo,
+ typeInfo: "// Type inference not yet implemented in playground",
error: "",
});
} else {
setOutput({
json: JSON.stringify(result, null, 2),
- typeInfo,
+ typeInfo: "// Type inference not yet implemented in playground",
error: "",
});
}
@@ -121,7 +53,7 @@ export function Playground() {
}, 500);
return () => clearTimeout(timeoutId);
- }, [code, monaco]);
+ }, [code]);
return (
-
+
@@ -151,37 +79,7 @@ export function Playground() {
);
}
-function formatTypeString(typeStr: string): string {
- let formatted = typeStr.trim();
-
- formatted = formatted.replace(/\s+/g, " ");
- formatted = formatted.replace(/;\s*/g, "\n");
- formatted = formatted.replace(/{\s*/g, "{\n");
- formatted = formatted.replace(/\s*}/g, "\n}");
-
- const lines = formatted.split("\n");
- let indentLevel = 0;
- const indentedLines: string[] = [];
-
- for (const line of lines) {
- const trimmed = line.trim();
- if (!trimmed) continue;
-
- if (trimmed.startsWith("}")) {
- indentLevel = Math.max(0, indentLevel - 1);
- }
-
- indentedLines.push(" ".repeat(indentLevel) + trimmed);
-
- if (trimmed.endsWith("{") && !trimmed.includes("}")) {
- indentLevel++;
- }
- }
-
- return indentedLines.join("\n");
-}
-
-const DEFAULT_CODE = `import { lx, type Infer } from "prototypekit";
+const DEFAULT_CODE = `import { lx } from "prototypekit";
const profileNamespace = lx.namespace("app.bsky.actor.profile", {
main: lx.record({
@@ -191,6 +89,4 @@ const profileNamespace = lx.namespace("app.bsky.actor.profile", {
description: lx.string({ maxLength: 256, maxGraphemes: 256 }),
}),
}),
-});
-
-type ProfileInferred = Infer
;`;
+});`;
diff --git a/packages/site/tests/components/Playground.test.tsx b/packages/site/tests/components/Playground.test.tsx
index 6c71602..caf1a9c 100644
--- a/packages/site/tests/components/Playground.test.tsx
+++ b/packages/site/tests/components/Playground.test.tsx
@@ -10,40 +10,6 @@ vi.mock("@monaco-editor/react", () => ({
onChange={(e) => onChange(e.target.value)}
/>
),
- useMonaco: () => null,
- loader: {
- init: vi.fn(() =>
- Promise.resolve({
- languages: {
- typescript: {
- typescriptDefaults: {
- setCompilerOptions: vi.fn(),
- setDiagnosticsOptions: vi.fn(),
- addExtraLib: vi.fn(),
- },
- ScriptTarget: { ES2020: 5 },
- ModuleResolutionKind: { NodeJs: 2 },
- ModuleKind: { ESNext: 99 },
- getTypeScriptWorker: vi.fn(() =>
- Promise.resolve(() =>
- Promise.resolve({
- getQuickInfoAtPosition: vi.fn(() => Promise.resolve(null)),
- }),
- ),
- ),
- },
- },
- editor: {
- defineTheme: vi.fn(),
- createModel: vi.fn(() => ({ dispose: vi.fn() })),
- getModel: vi.fn(() => null),
- },
- Uri: {
- parse: vi.fn((uri: string) => ({ toString: () => uri })),
- },
- }),
- ),
- },
}));
describe("Playground", () => {
diff --git a/packages/site/tests/setup.ts b/packages/site/tests/setup.ts
deleted file mode 100644
index fab9e47..0000000
--- a/packages/site/tests/setup.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { vi } from "vitest";
-
-global.fetch = vi.fn(
- () =>
- Promise.resolve({
- text: () => Promise.resolve(""),
- }) as any,
-);
diff --git a/packages/site/vitest.config.ts b/packages/site/vitest.config.ts
index 3d5c116..f52fa1e 100644
--- a/packages/site/vitest.config.ts
+++ b/packages/site/vitest.config.ts
@@ -6,6 +6,6 @@ export default defineConfig({
test: {
environment: "jsdom",
globals: true,
- setupFiles: ["./tests/setup.ts"],
+ setupFiles: [],
},
});
From 00b8f3066b325d9d2e09182ab63b01243bfe0ea0 Mon Sep 17 00:00:00 2001
From: Tyler <26290074+tylersayshi@users.noreply.github.com>
Date: Sun, 19 Oct 2025 00:06:21 -0700
Subject: [PATCH 11/23] Revert "rename prototypey to prototypekit across entire
project"
This reverts commit 21ba4996abe4b4ee9a3b76f82881f9cc97c13e33.
---
.github/workflows/ci.yml | 4 ++--
README.md | 4 ++--
package.json | 4 ++--
packages/cli/README.md | 20 +++++++++----------
packages/cli/package.json | 8 ++++----
packages/cli/src/index.ts | 2 +-
packages/cli/src/templates/inferred.ts | 4 ++--
packages/cli/tests/commands/gen-emit.test.ts | 20 +++++++++----------
.../cli/tests/commands/gen-inferred.test.ts | 10 +++++-----
packages/cli/tests/fixtures/simple-lexicon.ts | 2 +-
.../{prototypekit => prototypey}/package.json | 6 +++---
.../setup-vitest.ts | 0
.../{prototypekit => prototypey}/src/index.ts | 0
.../{prototypekit => prototypey}/src/infer.ts | 0
.../{prototypekit => prototypey}/src/lib.ts | 0
.../src/type-utils.ts | 0
.../tests/base-case.test.ts | 0
.../tests/bsky-actor.test.ts | 0
.../tests/bsky-feed.test.ts | 0
.../tests/infer.bench.ts | 0
.../tests/infer.test.ts | 0
.../tests/primitives.test.ts | 0
.../tsconfig.json | 0
.../tsdown.config.ts | 0
.../vitest.config.ts | 0
pnpm-lock.yaml | 6 +++---
26 files changed, 45 insertions(+), 45 deletions(-)
rename packages/{prototypekit => prototypey}/package.json (85%)
rename packages/{prototypekit => prototypey}/setup-vitest.ts (100%)
rename packages/{prototypekit => prototypey}/src/index.ts (100%)
rename packages/{prototypekit => prototypey}/src/infer.ts (100%)
rename packages/{prototypekit => prototypey}/src/lib.ts (100%)
rename packages/{prototypekit => prototypey}/src/type-utils.ts (100%)
rename packages/{prototypekit => prototypey}/tests/base-case.test.ts (100%)
rename packages/{prototypekit => prototypey}/tests/bsky-actor.test.ts (100%)
rename packages/{prototypekit => prototypey}/tests/bsky-feed.test.ts (100%)
rename packages/{prototypekit => prototypey}/tests/infer.bench.ts (100%)
rename packages/{prototypekit => prototypey}/tests/infer.test.ts (100%)
rename packages/{prototypekit => prototypey}/tests/primitives.test.ts (100%)
rename packages/{prototypekit => prototypey}/tsconfig.json (100%)
rename packages/{prototypekit => prototypey}/tsdown.config.ts (100%)
rename packages/{prototypekit => prototypey}/vitest.config.ts (100%)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 26fed7d..8ce63e1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,7 +6,7 @@ jobs:
- uses: actions/checkout@v4
- uses: ./.github/actions/prepare
- run: pnpm build
- - run: node packages/prototypekit/lib/index.js
+ - run: node packages/prototypey/lib/index.js
lint:
name: Lint
runs-on: ubuntu-latest
@@ -44,7 +44,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/prepare
- - run: pnpm -F prototypekit test:bench
+ - run: pnpm -F prototypey test:bench
name: CI
diff --git a/README.md b/README.md
index f946db3..ae82064 100644
--- a/README.md
+++ b/README.md
@@ -73,8 +73,8 @@ const profileNamespace = lx.namespace("app.bsky.actor.profile", {
---
-
-
+
+
diff --git a/package.json b/package.json
index b143add..41b5205 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,11 @@
{
- "name": "prototypekit-monorepo",
+ "name": "prototypey-monorepo",
"version": "0.0.0",
"private": true,
"description": "Type-safe lexicon inference for ATProto schemas",
"repository": {
"type": "git",
- "url": "git+https://github.com/tylersayshi/prototypekit.git"
+ "url": "git+https://github.com/tylersayshi/prototypey.git"
},
"license": "MIT",
"author": {
diff --git a/packages/cli/README.md b/packages/cli/README.md
index 7fa65e1..4b4c31c 100644
--- a/packages/cli/README.md
+++ b/packages/cli/README.md
@@ -1,17 +1,17 @@
-# @prototypekit/cli
+# @prototypey/cli
CLI tool for generating types from ATProto lexicon schemas.
## Installation
```bash
-npm install -g @prototypekit/cli
+npm install -g @prototypey/cli
```
Or use directly with npx:
```bash
-npx @prototypekit/cli
+npx @prototypey/cli
```
## Commands
@@ -23,7 +23,7 @@ Generate type-inferred TypeScript code from JSON lexicon schemas.
**Usage:**
```bash
-prototypekit gen-inferred
+prototypey gen-inferred
```
**Arguments:**
@@ -34,7 +34,7 @@ prototypekit gen-inferred
**Example:**
```bash
-prototypekit gen-inferred ./generated/inferred ./lexicons/**/*.json
+prototypey gen-inferred ./generated/inferred ./lexicons/**/*.json
```
**What it does:**
@@ -51,7 +51,7 @@ Emit JSON lexicon schemas from authored TypeScript files.
**Usage:**
```bash
-prototypekit gen-emit
+prototypey gen-emit
```
**Arguments:**
@@ -62,7 +62,7 @@ prototypekit gen-emit
**Example:**
```bash
-prototypekit gen-emit ./lexicons ./src/lexicons/**/*.ts
+prototypey gen-emit ./lexicons ./src/lexicons/**/*.ts
```
**What it does:**
@@ -76,7 +76,7 @@ prototypekit gen-emit ./lexicons ./src/lexicons/**/*.ts
The typical workflow combines both commands for bidirectional type safety:
-1. **Author lexicons in TypeScript** using the `prototypekit` library
+1. **Author lexicons in TypeScript** using the `prototypey` library
2. **Emit to JSON** with `gen-emit` for runtime validation and API contracts
3. **Generate inferred types** with `gen-inferred` for consuming code
@@ -85,10 +85,10 @@ The typical workflow combines both commands for bidirectional type safety:
# src/lexicons/app.bsky.actor.profile.ts
# Emit JSON schemas
-prototypekit gen-emit ./schemas ./src/lexicons/**/*.ts
+prototypey gen-emit ./schemas ./src/lexicons/**/*.ts
# Generate TypeScript types from schemas
-prototypekit gen-inferred ./generated ./schemas/**/*.json
+prototypey gen-inferred ./generated ./schemas/**/*.json
```
## Requirements
diff --git a/packages/cli/package.json b/packages/cli/package.json
index de53923..62b107a 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,10 +1,10 @@
{
- "name": "@prototypekit/cli",
+ "name": "@prototypey/cli",
"version": "0.0.0",
"description": "CLI tool for generating types from ATProto lexicon schemas",
"repository": {
"type": "git",
- "url": "git+https://github.com/tylersayshi/prototypekit.git",
+ "url": "git+https://github.com/tylersayshi/prototypey.git",
"directory": "packages/cli"
},
"license": "MIT",
@@ -14,7 +14,7 @@
},
"type": "module",
"bin": {
- "prototypekit": "./lib/index.js"
+ "prototypey": "./lib/index.js"
},
"files": [
"lib/",
@@ -26,7 +26,7 @@
"tsc": "tsc"
},
"dependencies": {
- "prototypekit": "workspace:*",
+ "prototypey": "workspace:*",
"sade": "^1.8.1",
"tinyglobby": "^0.2.15"
},
diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts
index 950b091..4cf303b 100644
--- a/packages/cli/src/index.ts
+++ b/packages/cli/src/index.ts
@@ -7,7 +7,7 @@ const pkg = JSON.parse(
await readFile(new URL("../package.json", import.meta.url), "utf-8"),
) as { version: string };
-const prog = sade("prototypekit");
+const prog = sade("prototypey");
prog
.version(pkg.version)
diff --git a/packages/cli/src/templates/inferred.ts b/packages/cli/src/templates/inferred.ts
index fa80d17..23a4970 100644
--- a/packages/cli/src/templates/inferred.ts
+++ b/packages/cli/src/templates/inferred.ts
@@ -22,9 +22,9 @@ export function generateInferredCode(
// Generate a clean type name from the NSID
const typeName = generateTypeName(id);
- return `// Generated by prototypekit - DO NOT EDIT
+ return `// Generated by prototypey - DO NOT EDIT
// Source: ${id}
-import type { Infer } from "prototypekit";
+import type { Infer } from "prototypey";
import schema from "${relativeSchemaPath}" with { type: "json" };
/**
diff --git a/packages/cli/tests/commands/gen-emit.test.ts b/packages/cli/tests/commands/gen-emit.test.ts
index d4169a4..135628a 100644
--- a/packages/cli/tests/commands/gen-emit.test.ts
+++ b/packages/cli/tests/commands/gen-emit.test.ts
@@ -10,7 +10,7 @@ describe("genEmit", () => {
beforeEach(async () => {
// Create a temporary directory for test files
- testDir = join(tmpdir(), `prototypekit-test-${Date.now()}`);
+ testDir = join(tmpdir(), `prototypey-test-${Date.now()}`);
outDir = join(testDir, "output");
await mkdir(testDir, { recursive: true });
await mkdir(outDir, { recursive: true });
@@ -27,7 +27,7 @@ describe("genEmit", () => {
await writeFile(
lexiconFile,
`
-import { lx } from "prototypekit";
+import { lx } from "prototypey";
export const profileNamespace = lx.namespace("app.bsky.actor.profile", {
main: lx.record({
@@ -83,7 +83,7 @@ export const profileNamespace = lx.namespace("app.bsky.actor.profile", {
await writeFile(
lexiconFile,
`
-import { lx } from "prototypekit";
+import { lx } from "prototypey";
export const profile = lx.namespace("app.bsky.actor.profile", {
main: lx.record({
@@ -128,7 +128,7 @@ export const post = lx.namespace("app.bsky.feed.post", {
await writeFile(
join(lexicons, "profile.ts"),
`
-import { lx } from "prototypekit";
+import { lx } from "prototypey";
export const schema = lx.namespace("app.bsky.actor.profile", {
main: lx.record({ key: "self", record: lx.object({}) }),
});
@@ -138,7 +138,7 @@ export const schema = lx.namespace("app.bsky.actor.profile", {
await writeFile(
join(lexicons, "post.ts"),
`
-import { lx } from "prototypekit";
+import { lx } from "prototypey";
export const schema = lx.namespace("app.bsky.feed.post", {
main: lx.record({ key: "tid", record: lx.object({}) }),
});
@@ -167,7 +167,7 @@ export const schema = lx.namespace("app.bsky.feed.post", {
await writeFile(
lexiconFile,
`
-import { lx } from "prototypekit";
+import { lx } from "prototypey";
export const searchPosts = lx.namespace("app.bsky.feed.searchPosts", {
main: lx.query({
@@ -236,7 +236,7 @@ export const searchPosts = lx.namespace("app.bsky.feed.searchPosts", {
await writeFile(
lexiconFile,
`
-import { lx } from "prototypekit";
+import { lx } from "prototypey";
export const createPost = lx.namespace("com.atproto.repo.createRecord", {
main: lx.procedure({
@@ -307,7 +307,7 @@ export const createPost = lx.namespace("com.atproto.repo.createRecord", {
await writeFile(
lexiconFile,
`
-import { lx } from "prototypekit";
+import { lx } from "prototypey";
export const subscribeRepos = lx.namespace("com.atproto.sync.subscribeRepos", {
main: lx.subscription({
@@ -394,7 +394,7 @@ export const subscribeRepos = lx.namespace("com.atproto.sync.subscribeRepos", {
await writeFile(
lexiconFile,
`
-import { lx } from "prototypekit";
+import { lx } from "prototypey";
export const feedDefs = lx.namespace("app.bsky.feed.defs", {
postView: lx.object({
@@ -458,7 +458,7 @@ export const feedDefs = lx.namespace("app.bsky.feed.defs", {
await writeFile(
lexiconFile,
`
-import { lx } from "prototypekit";
+import { lx } from "prototypey";
export const imagePost = lx.namespace("app.example.imagePost", {
main: lx.record({
diff --git a/packages/cli/tests/commands/gen-inferred.test.ts b/packages/cli/tests/commands/gen-inferred.test.ts
index 5fc9649..3af2790 100644
--- a/packages/cli/tests/commands/gen-inferred.test.ts
+++ b/packages/cli/tests/commands/gen-inferred.test.ts
@@ -11,7 +11,7 @@ describe("genInferred", () => {
beforeEach(async () => {
// Create a temporary directory for test files
- testDir = join(tmpdir(), `prototypekit-inferred-test-${Date.now()}`);
+ testDir = join(tmpdir(), `prototypey-inferred-test-${Date.now()}`);
outDir = join(testDir, "output");
schemasDir = join(testDir, "schemas");
await mkdir(testDir, { recursive: true });
@@ -68,9 +68,9 @@ describe("genInferred", () => {
const content = await readFile(outputFile, "utf-8");
// Verify the generated code structure
- expect(content).toContain("// Generated by prototypekit - DO NOT EDIT");
+ expect(content).toContain("// Generated by prototypey - DO NOT EDIT");
expect(content).toContain("// Source: app.bsky.actor.profile");
- expect(content).toContain('import type { Infer } from "prototypekit"');
+ expect(content).toContain('import type { Infer } from "prototypey"');
expect(content).toContain('with { type: "json" }');
expect(content).toContain("export type Profile = Infer");
expect(content).toContain("export const ProfileSchema = schema");
@@ -321,7 +321,7 @@ describe("genInferred", () => {
const content = await readFile(outputFile, "utf-8");
// Check all required exports
- expect(content).toContain('import type { Infer } from "prototypekit"');
+ expect(content).toContain('import type { Infer } from "prototypey"');
expect(content).toContain("export type Complete = Infer");
expect(content).toContain("export const CompleteSchema = schema");
expect(content).toContain(
@@ -335,7 +335,7 @@ describe("genInferred", () => {
expect(content).toContain('v.$type === "app.test.complete"');
// Check comments
- expect(content).toContain("// Generated by prototypekit - DO NOT EDIT");
+ expect(content).toContain("// Generated by prototypey - DO NOT EDIT");
expect(content).toContain("// Source: app.test.complete");
expect(content).toContain(
"* Type-inferred from lexicon schema: app.test.complete",
diff --git a/packages/cli/tests/fixtures/simple-lexicon.ts b/packages/cli/tests/fixtures/simple-lexicon.ts
index 6e1259e..0842706 100644
--- a/packages/cli/tests/fixtures/simple-lexicon.ts
+++ b/packages/cli/tests/fixtures/simple-lexicon.ts
@@ -1,4 +1,4 @@
-import { lx } from "prototypekit";
+import { lx } from "prototypey";
export const profileNamespace = lx.namespace("app.bsky.actor.profile", {
main: lx.record({
diff --git a/packages/prototypekit/package.json b/packages/prototypey/package.json
similarity index 85%
rename from packages/prototypekit/package.json
rename to packages/prototypey/package.json
index 02f2478..bfc9572 100644
--- a/packages/prototypekit/package.json
+++ b/packages/prototypey/package.json
@@ -1,11 +1,11 @@
{
- "name": "prototypekit",
+ "name": "prototypey",
"version": "0.0.0",
"description": "Type-safe lexicon inference for ATProto schemas",
"repository": {
"type": "git",
- "url": "git+https://github.com/tylersayshi/prototypekit.git",
- "directory": "packages/prototypekit"
+ "url": "git+https://github.com/tylersayshi/prototypey.git",
+ "directory": "packages/prototypey"
},
"license": "MIT",
"author": {
diff --git a/packages/prototypekit/setup-vitest.ts b/packages/prototypey/setup-vitest.ts
similarity index 100%
rename from packages/prototypekit/setup-vitest.ts
rename to packages/prototypey/setup-vitest.ts
diff --git a/packages/prototypekit/src/index.ts b/packages/prototypey/src/index.ts
similarity index 100%
rename from packages/prototypekit/src/index.ts
rename to packages/prototypey/src/index.ts
diff --git a/packages/prototypekit/src/infer.ts b/packages/prototypey/src/infer.ts
similarity index 100%
rename from packages/prototypekit/src/infer.ts
rename to packages/prototypey/src/infer.ts
diff --git a/packages/prototypekit/src/lib.ts b/packages/prototypey/src/lib.ts
similarity index 100%
rename from packages/prototypekit/src/lib.ts
rename to packages/prototypey/src/lib.ts
diff --git a/packages/prototypekit/src/type-utils.ts b/packages/prototypey/src/type-utils.ts
similarity index 100%
rename from packages/prototypekit/src/type-utils.ts
rename to packages/prototypey/src/type-utils.ts
diff --git a/packages/prototypekit/tests/base-case.test.ts b/packages/prototypey/tests/base-case.test.ts
similarity index 100%
rename from packages/prototypekit/tests/base-case.test.ts
rename to packages/prototypey/tests/base-case.test.ts
diff --git a/packages/prototypekit/tests/bsky-actor.test.ts b/packages/prototypey/tests/bsky-actor.test.ts
similarity index 100%
rename from packages/prototypekit/tests/bsky-actor.test.ts
rename to packages/prototypey/tests/bsky-actor.test.ts
diff --git a/packages/prototypekit/tests/bsky-feed.test.ts b/packages/prototypey/tests/bsky-feed.test.ts
similarity index 100%
rename from packages/prototypekit/tests/bsky-feed.test.ts
rename to packages/prototypey/tests/bsky-feed.test.ts
diff --git a/packages/prototypekit/tests/infer.bench.ts b/packages/prototypey/tests/infer.bench.ts
similarity index 100%
rename from packages/prototypekit/tests/infer.bench.ts
rename to packages/prototypey/tests/infer.bench.ts
diff --git a/packages/prototypekit/tests/infer.test.ts b/packages/prototypey/tests/infer.test.ts
similarity index 100%
rename from packages/prototypekit/tests/infer.test.ts
rename to packages/prototypey/tests/infer.test.ts
diff --git a/packages/prototypekit/tests/primitives.test.ts b/packages/prototypey/tests/primitives.test.ts
similarity index 100%
rename from packages/prototypekit/tests/primitives.test.ts
rename to packages/prototypey/tests/primitives.test.ts
diff --git a/packages/prototypekit/tsconfig.json b/packages/prototypey/tsconfig.json
similarity index 100%
rename from packages/prototypekit/tsconfig.json
rename to packages/prototypey/tsconfig.json
diff --git a/packages/prototypekit/tsdown.config.ts b/packages/prototypey/tsdown.config.ts
similarity index 100%
rename from packages/prototypekit/tsdown.config.ts
rename to packages/prototypey/tsdown.config.ts
diff --git a/packages/prototypekit/vitest.config.ts b/packages/prototypey/vitest.config.ts
similarity index 100%
rename from packages/prototypekit/vitest.config.ts
rename to packages/prototypey/vitest.config.ts
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index af32050..20b3690 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,9 +26,9 @@ importers:
packages/cli:
dependencies:
- prototypekit:
+ prototypey:
specifier: workspace:*
- version: link:../prototypekit
+ version: link:../prototypey
sade:
specifier: ^1.8.1
version: 1.8.1
@@ -49,7 +49,7 @@ importers:
specifier: ^3.2.4
version: 3.2.4(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1)(jsdom@25.0.1)
- packages/prototypekit:
+ packages/prototypey:
devDependencies:
'@ark/attest':
specifier: ^0.49.0
From 0e12fa9c68c170449cbb5b668754852d6df9fc4e Mon Sep 17 00:00:00 2001
From: Tyler <26290074+tylersayshi@users.noreply.github.com>
Date: Sun, 19 Oct 2025 00:23:20 -0700
Subject: [PATCH 12/23] rename prototypekit back to prototypey
---
package.json | 6 ++---
packages/site/index.html | 2 +-
packages/site/package.json | 4 ++--
packages/site/src/components/Editor.tsx | 4 ++--
packages/site/src/components/Header.tsx | 2 +-
packages/site/src/components/Playground.tsx | 4 ++--
.../site/tests/components/Header.test.tsx | 2 +-
packages/site/vite.config.ts | 12 ++++------
pnpm-lock.yaml | 22 +++++++++++++++++--
9 files changed, 36 insertions(+), 22 deletions(-)
diff --git a/package.json b/package.json
index 41b5205..ba8441c 100644
--- a/package.json
+++ b/package.json
@@ -15,9 +15,9 @@
"scripts": {
"build": "pnpm -r build",
"codegen:samples": "node packages/cli/src/index.ts gen-inferred ./generated/inferred './samples/*.json'",
- "dev:site": "pnpm -F @prototypekit/site dev",
- "build:site": "pnpm -F @prototypekit/site build",
- "preview:site": "pnpm -F @prototypekit/site preview",
+ "dev:site": "pnpm -F @prototypey/site dev",
+ "build:site": "pnpm -F @prototypey/site build",
+ "preview:site": "pnpm -F @prototypey/site preview",
"format": "prettier . --list-different",
"format:fix": "prettier . --write",
"lint": "pnpm -r lint",
diff --git a/packages/site/index.html b/packages/site/index.html
index 0049fa7..e17c190 100644
--- a/packages/site/index.html
+++ b/packages/site/index.html
@@ -3,7 +3,7 @@
- prototypekit - Type-safe lexicon inference for ATProto
+ prototypey - Type-safe lexicon inference for ATProto
diff --git a/packages/site/package.json b/packages/site/package.json
index a0a0984..fc0e828 100644
--- a/packages/site/package.json
+++ b/packages/site/package.json
@@ -1,5 +1,5 @@
{
- "name": "@prototypekit/site",
+ "name": "@prototypey/site",
"version": "0.0.0",
"private": true,
"type": "module",
@@ -12,7 +12,7 @@
"dependencies": {
"@monaco-editor/react": "^4.6.0",
"monaco-editor": "^0.52.2",
- "prototypekit": "workspace:*",
+ "prototypey": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
diff --git a/packages/site/src/components/Editor.tsx b/packages/site/src/components/Editor.tsx
index a1444f5..8addf39 100644
--- a/packages/site/src/components/Editor.tsx
+++ b/packages/site/src/components/Editor.tsx
@@ -50,13 +50,13 @@ ${stripImportsExports(inferDts)}
${stripImportsExports(libDts)}
`;
- const moduleDeclaration = `declare module "prototypekit" {
+ const moduleDeclaration = `declare module "prototypey" {
${combinedTypes}
}`;
monaco.languages.typescript.typescriptDefaults.addExtraLib(
moduleDeclaration,
- "prototypekit.d.ts",
+ "prototypey.d.ts",
);
setIsReady(true);
diff --git a/packages/site/src/components/Header.tsx b/packages/site/src/components/Header.tsx
index 643584b..87ff40e 100644
--- a/packages/site/src/components/Header.tsx
+++ b/packages/site/src/components/Header.tsx
@@ -14,7 +14,7 @@ export function Header() {
marginBottom: "0.5rem",
}}
>
- at://prototypekit
+ at://prototypey
{
it("renders the title", () => {
render();
- expect(screen.getByText("prototypekit")).toBeInTheDocument();
+ expect(screen.getByText("prototypey")).toBeInTheDocument();
expect(screen.getByText("at://")).toBeInTheDocument();
});
diff --git a/packages/site/vite.config.ts b/packages/site/vite.config.ts
index d48be7c..124978e 100644
--- a/packages/site/vite.config.ts
+++ b/packages/site/vite.config.ts
@@ -3,19 +3,15 @@ import react from "@vitejs/plugin-react";
import { readFileSync } from "fs";
import { resolve } from "path";
-function servePrototypeKitTypes() {
+function servePrototypeyTypes() {
return {
- name: "serve-prototypekit-types",
+ name: "serve-prototypey-types",
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url?.startsWith("/types/") && req.url.endsWith(".d.ts")) {
const fileName = req.url.slice(7);
try {
- const filePath = resolve(
- __dirname,
- "../prototypekit/lib",
- fileName,
- );
+ const filePath = resolve(__dirname, "../prototypey/lib", fileName);
const content = readFileSync(filePath, "utf-8");
res.setHeader("Content-Type", "application/typescript");
res.end(content);
@@ -33,5 +29,5 @@ function servePrototypeKitTypes() {
}
export default defineConfig({
- plugins: [react(), servePrototypeKitTypes()],
+ plugins: [react(), servePrototypeyTypes()],
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 20b3690..8967271 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -49,6 +49,24 @@ importers:
specifier: ^3.2.4
version: 3.2.4(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1)(jsdom@25.0.1)
+ packages/prototypekit:
+ devDependencies:
+ '@ark/attest':
+ specifier: ^0.49.0
+ version: 0.49.0(typescript@5.8.3)
+ '@types/node':
+ specifier: 24.0.4
+ version: 24.0.4
+ tsdown:
+ specifier: 0.12.7
+ version: 0.12.7(typescript@5.8.3)
+ typescript:
+ specifier: 5.8.3
+ version: 5.8.3
+ vitest:
+ specifier: ^3.2.4
+ version: 3.2.4(@types/node@24.0.4)(esbuild@0.25.10)(jiti@2.6.1)(jsdom@25.0.1)
+
packages/prototypey:
devDependencies:
'@ark/attest':
@@ -75,9 +93,9 @@ importers:
monaco-editor:
specifier: ^0.52.2
version: 0.52.2
- prototypekit:
+ prototypey:
specifier: workspace:*
- version: link:../prototypekit
+ version: link:../prototypey
react:
specifier: ^18.3.1
version: 18.3.1
From 98c0fbc5aedb1ffa4101a6a334f1137a07ddff64 Mon Sep 17 00:00:00 2001
From: Tyler <26290074+tylersayshi@users.noreply.github.com>
Date: Sun, 19 Oct 2025 00:23:49 -0700
Subject: [PATCH 13/23] restore site readiness changes
---
packages/prototypey/src/infer.ts | 19 +--
packages/site/src/components/Editor.tsx | 7 +-
packages/site/src/components/OutputPanel.tsx | 49 +------
packages/site/src/components/Playground.tsx | 128 ++++++++++++++++--
.../site/tests/components/Playground.test.tsx | 34 +++++
packages/site/tests/setup.ts | 8 ++
packages/site/vitest.config.ts | 2 +-
7 files changed, 181 insertions(+), 66 deletions(-)
create mode 100644 packages/site/tests/setup.ts
diff --git a/packages/prototypey/src/infer.ts b/packages/prototypey/src/infer.ts
index 8d7037a..3741133 100644
--- a/packages/prototypey/src/infer.ts
+++ b/packages/prototypey/src/infer.ts
@@ -130,12 +130,13 @@ type ReplaceRefsInType =
* Infers the TypeScript type for a lexicon namespace, returning only the 'main' definition
* with all local refs (#user, #post, etc.) resolved to their actual types.
*/
-export type Infer }> =
- Prettify<
- "main" extends keyof T["defs"]
- ? { $type: T["id"] } & ReplaceRefsInType<
- InferType,
- { [K in keyof T["defs"]]: InferType }
- >
- : never
- >;
+export type Infer<
+ T extends { json: { id: string; defs: Record } },
+> = Prettify<
+ "main" extends keyof T["json"]["defs"]
+ ? { $type: T["json"]["id"] } & ReplaceRefsInType<
+ InferType,
+ { [K in keyof T["json"]["defs"]]: InferType }
+ >
+ : never
+>;
diff --git a/packages/site/src/components/Editor.tsx b/packages/site/src/components/Editor.tsx
index 8addf39..0736c4c 100644
--- a/packages/site/src/components/Editor.tsx
+++ b/packages/site/src/components/Editor.tsx
@@ -5,9 +5,10 @@ import { useEffect, useState } from "react";
interface EditorProps {
value: string;
onChange: (value: string) => void;
+ onReady?: () => void;
}
-export function Editor({ value, onChange }: EditorProps) {
+export function Editor({ value, onChange, onReady }: EditorProps) {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
@@ -60,9 +61,10 @@ ${combinedTypes}
);
setIsReady(true);
+ onReady?.();
});
});
- }, []);
+ }, [onReady]);
if (!isReady) {
return (
@@ -111,6 +113,7 @@ ${combinedTypes}
onChange(value || "")}
theme="vs-light"
diff --git a/packages/site/src/components/OutputPanel.tsx b/packages/site/src/components/OutputPanel.tsx
index b944d40..a771226 100644
--- a/packages/site/src/components/OutputPanel.tsx
+++ b/packages/site/src/components/OutputPanel.tsx
@@ -1,4 +1,3 @@
-import { useState } from "react";
import MonacoEditor from "@monaco-editor/react";
interface OutputPanelProps {
@@ -10,53 +9,19 @@ interface OutputPanelProps {
}
export function OutputPanel({ output }: OutputPanelProps) {
- const [activeTab, setActiveTab] = useState<"json" | "types">("json");
-
return (
-
-
+ Output
{output.error ? (
@@ -74,8 +39,8 @@ export function OutputPanel({ output }: OutputPanelProps) {
) : (
(null);
const handleCodeChange = (newCode: string) => {
setCode(newCode);
};
+ const handleEditorReady = () => {
+ setEditorReady(true);
+ };
+
+ useEffect(() => {
+ if (monaco && editorReady && !tsWorkerRef.current && !tsWorkerInstance) {
+ const initWorker = async () => {
+ try {
+ await new Promise((resolve) => setTimeout(resolve, 200));
+ const worker =
+ await monaco.languages.typescript.getTypeScriptWorker();
+ const uri = monaco.Uri.parse("file:///main.ts");
+ const client = await worker(uri);
+ tsWorkerRef.current = client;
+ tsWorkerInstance = client;
+ } catch (err) {
+ console.error("Failed to initialize TypeScript worker:", err);
+ }
+ };
+ initWorker();
+ }
+ }, [monaco, editorReady]);
+
useEffect(() => {
- const timeoutId = setTimeout(() => {
+ const timeoutId = setTimeout(async () => {
try {
- const cleanedCode = code.replace(
- /import\s+{[^}]*}\s+from\s+['"][^'"]+['"]\s*;?\s*/g,
- "",
- );
+ const cleanedCode = code
+ .replace(/import\s+{[^}]*}\s+from\s+['"][^'"]+['"]\s*;?\s*/g, "")
+ .replace(/^type\s+\w+\s*=\s*[^;]+;?\s*$/gm, "");
const lastVarMatch = cleanedCode.match(/(?:const|let|var)\s+(\w+)\s*=/);
const lastVarName = lastVarMatch ? lastVarMatch[1] : null;
@@ -28,18 +59,55 @@ export function Playground() {
const fn = new Function("lx", wrappedCode);
const result = fn(lx);
+ let typeInfo = "// Hover over .infer in the editor to see the type";
+
+ if (lastVarName && monaco && tsWorkerRef.current) {
+ try {
+ const uri = monaco.Uri.parse("file:///main.ts");
+ const existingModel = monaco.editor.getModel(uri);
+
+ if (existingModel) {
+ const inferPosition = code.indexOf(`${lastVarName}.infer`);
+ if (inferPosition !== -1) {
+ const offset =
+ inferPosition + `${lastVarName}.infer`.length - 1;
+
+ const quickInfo =
+ await tsWorkerRef.current.getQuickInfoAtPosition(
+ uri.toString(),
+ offset,
+ );
+
+ if (quickInfo?.displayParts) {
+ const typeText = quickInfo.displayParts
+ .map((part: { text: string }) => part.text)
+ .join("");
+
+ const propertyMatch = typeText.match(
+ /\(property\)\s+.*?\.infer:\s*([\s\S]+?)$/,
+ );
+ if (propertyMatch) {
+ typeInfo = formatTypeString(propertyMatch[1]);
+ }
+ }
+ }
+ }
+ } catch (err) {
+ console.error("Type extraction error:", err);
+ }
+ }
if (result && typeof result === "object" && "json" in result) {
const jsonOutput = (result as { json: unknown }).json;
setOutput({
json: JSON.stringify(jsonOutput, null, 2),
- typeInfo: "// Type inference not yet implemented in playground",
+ typeInfo,
error: "",
});
} else {
setOutput({
json: JSON.stringify(result, null, 2),
- typeInfo: "// Type inference not yet implemented in playground",
+ typeInfo,
error: "",
});
}
@@ -53,7 +121,7 @@ export function Playground() {
}, 500);
return () => clearTimeout(timeoutId);
- }, [code]);
+ }, [code, monaco]);
return (
-
+
@@ -79,7 +151,37 @@ export function Playground() {
);
}
-const DEFAULT_CODE = `import { lx } from "prototypey";
+function formatTypeString(typeStr: string): string {
+ let formatted = typeStr.trim();
+
+ formatted = formatted.replace(/\s+/g, " ");
+ formatted = formatted.replace(/;\s*/g, "\n");
+ formatted = formatted.replace(/{\s*/g, "{\n");
+ formatted = formatted.replace(/\s*}/g, "\n}");
+
+ const lines = formatted.split("\n");
+ let indentLevel = 0;
+ const indentedLines: string[] = [];
+
+ for (const line of lines) {
+ const trimmed = line.trim();
+ if (!trimmed) continue;
+
+ if (trimmed.startsWith("}")) {
+ indentLevel = Math.max(0, indentLevel - 1);
+ }
+
+ indentedLines.push(" ".repeat(indentLevel) + trimmed);
+
+ if (trimmed.endsWith("{") && !trimmed.includes("}")) {
+ indentLevel++;
+ }
+ }
+
+ return indentedLines.join("\n");
+}
+
+const DEFAULT_CODE = `import { lx, type Infer } from "prototypey";
const profileNamespace = lx.namespace("app.bsky.actor.profile", {
main: lx.record({
@@ -89,4 +191,6 @@ const profileNamespace = lx.namespace("app.bsky.actor.profile", {
description: lx.string({ maxLength: 256, maxGraphemes: 256 }),
}),
}),
-});`;
+});
+
+type ProfileInferred = Infer
;`;
diff --git a/packages/site/tests/components/Playground.test.tsx b/packages/site/tests/components/Playground.test.tsx
index caf1a9c..6c71602 100644
--- a/packages/site/tests/components/Playground.test.tsx
+++ b/packages/site/tests/components/Playground.test.tsx
@@ -10,6 +10,40 @@ vi.mock("@monaco-editor/react", () => ({
onChange={(e) => onChange(e.target.value)}
/>
),
+ useMonaco: () => null,
+ loader: {
+ init: vi.fn(() =>
+ Promise.resolve({
+ languages: {
+ typescript: {
+ typescriptDefaults: {
+ setCompilerOptions: vi.fn(),
+ setDiagnosticsOptions: vi.fn(),
+ addExtraLib: vi.fn(),
+ },
+ ScriptTarget: { ES2020: 5 },
+ ModuleResolutionKind: { NodeJs: 2 },
+ ModuleKind: { ESNext: 99 },
+ getTypeScriptWorker: vi.fn(() =>
+ Promise.resolve(() =>
+ Promise.resolve({
+ getQuickInfoAtPosition: vi.fn(() => Promise.resolve(null)),
+ }),
+ ),
+ ),
+ },
+ },
+ editor: {
+ defineTheme: vi.fn(),
+ createModel: vi.fn(() => ({ dispose: vi.fn() })),
+ getModel: vi.fn(() => null),
+ },
+ Uri: {
+ parse: vi.fn((uri: string) => ({ toString: () => uri })),
+ },
+ }),
+ ),
+ },
}));
describe("Playground", () => {
diff --git a/packages/site/tests/setup.ts b/packages/site/tests/setup.ts
new file mode 100644
index 0000000..fab9e47
--- /dev/null
+++ b/packages/site/tests/setup.ts
@@ -0,0 +1,8 @@
+import { vi } from "vitest";
+
+global.fetch = vi.fn(
+ () =>
+ Promise.resolve({
+ text: () => Promise.resolve(""),
+ }) as any,
+);
diff --git a/packages/site/vitest.config.ts b/packages/site/vitest.config.ts
index f52fa1e..3d5c116 100644
--- a/packages/site/vitest.config.ts
+++ b/packages/site/vitest.config.ts
@@ -6,6 +6,6 @@ export default defineConfig({
test: {
environment: "jsdom",
globals: true,
- setupFiles: [],
+ setupFiles: ["./tests/setup.ts"],
},
});
From 12b622614f625ef74e0db069db4c9edf794eca21 Mon Sep 17 00:00:00 2001
From: Tyler <26290074+tylersayshi@users.noreply.github.com>
Date: Sun, 19 Oct 2025 00:38:53 -0700
Subject: [PATCH 14/23] fix for playground
---
packages/site/src/components/Playground.tsx | 34 +++++++++++----------
1 file changed, 18 insertions(+), 16 deletions(-)
diff --git a/packages/site/src/components/Playground.tsx b/packages/site/src/components/Playground.tsx
index 724f959..4be9357 100644
--- a/packages/site/src/components/Playground.tsx
+++ b/packages/site/src/components/Playground.tsx
@@ -46,31 +46,28 @@ export function Playground() {
useEffect(() => {
const timeoutId = setTimeout(async () => {
try {
- const cleanedCode = code
- .replace(/import\s+{[^}]*}\s+from\s+['"][^'"]+['"]\s*;?\s*/g, "")
- .replace(/^type\s+\w+\s*=\s*[^;]+;?\s*$/gm, "");
-
- const lastVarMatch = cleanedCode.match(/(?:const|let|var)\s+(\w+)\s*=/);
- const lastVarName = lastVarMatch ? lastVarMatch[1] : null;
-
- const wrappedCode = lastVarName
- ? `${cleanedCode}\nreturn ${lastVarName};`
- : cleanedCode;
+ const nsMatch = code.match(
+ /const\s+ns\s*=\s*lx\.namespace\([^]*?\}\s*\);/,
+ );
+ if (!nsMatch) {
+ throw new Error("No namespace definition found");
+ }
+ const cleanedCode = nsMatch[0];
+ const wrappedCode = `${cleanedCode}\nreturn ns;`;
const fn = new Function("lx", wrappedCode);
const result = fn(lx);
let typeInfo = "// Hover over .infer in the editor to see the type";
- if (lastVarName && monaco && tsWorkerRef.current) {
+ if (monaco && tsWorkerRef.current) {
try {
const uri = monaco.Uri.parse("file:///main.ts");
const existingModel = monaco.editor.getModel(uri);
if (existingModel) {
- const inferPosition = code.indexOf(`${lastVarName}.infer`);
+ const inferPosition = code.indexOf(`ns.infer`);
if (inferPosition !== -1) {
- const offset =
- inferPosition + `${lastVarName}.infer`.length - 1;
+ const offset = inferPosition + `ns.infer`.length - 1;
const quickInfo =
await tsWorkerRef.current.getQuickInfoAtPosition(
@@ -183,7 +180,7 @@ function formatTypeString(typeStr: string): string {
const DEFAULT_CODE = `import { lx, type Infer } from "prototypey";
-const profileNamespace = lx.namespace("app.bsky.actor.profile", {
+const ns = lx.namespace("app.bsky.actor.profile", {
main: lx.record({
key: "self",
record: lx.object({
@@ -193,4 +190,9 @@ const profileNamespace = lx.namespace("app.bsky.actor.profile", {
}),
});
-type ProfileInferred = Infer;`;
+type ProfileInferred = Infer;
+
+const aProfile: ProfileInferred = {
+ $type: "app.bsky.actor.profile",
+ displayName: "Benny Harvey"
+}`;
From 3f8ba0c923fa873ed8c894a7fc63022d298261df Mon Sep 17 00:00:00 2001
From: Tyler <26290074+tylersayshi@users.noreply.github.com>
Date: Sun, 19 Oct 2025 13:02:46 -0700
Subject: [PATCH 15/23] formatting and tsc
---
packages/prototypey/src/lib.ts | 2 +-
packages/site/public/types/infer.d.ts | 235 +++++---
packages/site/public/types/lib.d.ts | 646 ++++++++++++---------
packages/site/public/types/type-utils.d.ts | 12 +-
pnpm-lock.yaml | 18 -
5 files changed, 546 insertions(+), 367 deletions(-)
diff --git a/packages/prototypey/src/lib.ts b/packages/prototypey/src/lib.ts
index 49414d8..91f9b20 100644
--- a/packages/prototypey/src/lib.ts
+++ b/packages/prototypey/src/lib.ts
@@ -329,7 +329,7 @@ interface SubscriptionOptions {
class Namespace {
public json: T;
- public infer: Infer = null as unknown as Infer;
+ public infer: Infer<{ json: T }> = null as unknown as Infer<{ json: T }>;
constructor(json: T) {
this.json = json;
diff --git a/packages/site/public/types/infer.d.ts b/packages/site/public/types/infer.d.ts
index 53fdb73..c259e23 100644
--- a/packages/site/public/types/infer.d.ts
+++ b/packages/site/public/types/infer.d.ts
@@ -2,90 +2,189 @@ import { Prettify } from "./type-utils.js";
//#region src/infer.d.ts
type InferType = T extends {
- type: "record";
-} ? InferRecord : T extends {
- type: "object";
-} ? InferObject : T extends {
- type: "array";
-} ? InferArray : T extends {
- type: "params";
-} ? InferParams : T extends {
- type: "union";
-} ? InferUnion : T extends {
- type: "token";
-} ? InferToken : T extends {
- type: "ref";
-} ? InferRef : T extends {
- type: "unknown";
-} ? unknown : T extends {
- type: "null";
-} ? null : T extends {
- type: "boolean";
-} ? boolean : T extends {
- type: "integer";
-} ? number : T extends {
- type: "string";
-} ? string : T extends {
- type: "bytes";
-} ? Uint8Array : T extends {
- type: "cid-link";
-} ? string : T extends {
- type: "blob";
-} ? Blob : never;
+ type: "record";
+}
+ ? InferRecord
+ : T extends {
+ type: "object";
+ }
+ ? InferObject
+ : T extends {
+ type: "array";
+ }
+ ? InferArray
+ : T extends {
+ type: "params";
+ }
+ ? InferParams
+ : T extends {
+ type: "union";
+ }
+ ? InferUnion
+ : T extends {
+ type: "token";
+ }
+ ? InferToken
+ : T extends {
+ type: "ref";
+ }
+ ? InferRef
+ : T extends {
+ type: "unknown";
+ }
+ ? unknown
+ : T extends {
+ type: "null";
+ }
+ ? null
+ : T extends {
+ type: "boolean";
+ }
+ ? boolean
+ : T extends {
+ type: "integer";
+ }
+ ? number
+ : T extends {
+ type: "string";
+ }
+ ? string
+ : T extends {
+ type: "bytes";
+ }
+ ? Uint8Array
+ : T extends {
+ type: "cid-link";
+ }
+ ? string
+ : T extends {
+ type: "blob";
+ }
+ ? Blob
+ : never;
type InferToken = T extends {
- enum: readonly (infer U)[];
-} ? U : string;
+ enum: readonly (infer U)[];
+}
+ ? U
+ : string;
type GetRequired = T extends {
- required: readonly (infer R)[];
-} ? R : never;
+ required: readonly (infer R)[];
+}
+ ? R
+ : never;
type GetNullable = T extends {
- nullable: readonly (infer N)[];
-} ? N : never;
-type InferObject & string, Required extends string = GetRequired & string, NullableAndRequired extends string = Required & Nullable & string, Normal extends string = ("properties" extends keyof T ? Exclude & string : never)> = Prettify } & { -readonly [K in Exclude]-?: InferType } & { -readonly [K in Exclude]?: InferType | null } & { -readonly [K in NullableAndRequired]: InferType
| null } : {}>;
+ nullable: readonly (infer N)[];
+}
+ ? N
+ : never;
+type InferObject<
+ T,
+ Nullable extends string = GetNullable & string,
+ Required extends string = GetRequired & string,
+ NullableAndRequired extends string = Required & Nullable & string,
+ Normal extends string = "properties" extends keyof T
+ ? Exclude & string
+ : never,
+> = Prettify<
+ T extends {
+ properties: infer P;
+ }
+ ? { -readonly [K in Normal]?: InferType } & {
+ -readonly [K in Exclude]-?: InferType<
+ P[K & keyof P]
+ >;
+ } & {
+ -readonly [K in Exclude]?: InferType<
+ P[K & keyof P]
+ > | null;
+ } & {
+ -readonly [K in NullableAndRequired]: InferType | null;
+ }
+ : {}
+>;
type InferArray = T extends {
- items: infer Items;
-} ? InferType[] : never[];
+ items: infer Items;
+}
+ ? InferType[]
+ : never[];
type InferUnion = T extends {
- refs: readonly (infer R)[];
-} ? R extends string ? {
- $type: R;
- [key: string]: unknown;
-} : never : never;
+ refs: readonly (infer R)[];
+}
+ ? R extends string
+ ? {
+ $type: R;
+ [key: string]: unknown;
+ }
+ : never
+ : never;
type InferRef = T extends {
- ref: infer R;
-} ? R extends string ? {
- $type: R;
- [key: string]: unknown;
-} : unknown : unknown;
+ ref: infer R;
+}
+ ? R extends string
+ ? {
+ $type: R;
+ [key: string]: unknown;
+ }
+ : unknown
+ : unknown;
type InferParams = InferObject;
type InferRecord = T extends {
- record: infer R;
-} ? R extends {
- type: "object";
-} ? InferObject : R extends {
- type: "union";
-} ? InferUnion : unknown : unknown;
+ record: infer R;
+}
+ ? R extends {
+ type: "object";
+ }
+ ? InferObject
+ : R extends {
+ type: "union";
+ }
+ ? InferUnion
+ : unknown
+ : unknown;
/**
* Recursively replaces stub references in a type with their actual definitions.
* Detects circular references and missing references, returning string literal error messages.
*/
type ReplaceRefsInType = T extends {
- $type: `#${infer DefName}`;
-} ? DefName extends keyof Defs ? DefName extends Visited ? `[Circular reference detected: #${DefName}]` : Prettify & {
- $type: T["$type"];
-}> : `[Reference not found: #${DefName}]` : T extends Uint8Array | Blob ? T : T extends readonly (infer Item)[] ? ReplaceRefsInType- [] : T extends object ? T extends ((...args: unknown[]) => unknown) ? T : { [K in keyof T]: ReplaceRefsInType } : T;
+ $type: `#${infer DefName}`;
+}
+ ? DefName extends keyof Defs
+ ? DefName extends Visited
+ ? `[Circular reference detected: #${DefName}]`
+ : Prettify<
+ ReplaceRefsInType & {
+ $type: T["$type"];
+ }
+ >
+ : `[Reference not found: #${DefName}]`
+ : T extends Uint8Array | Blob
+ ? T
+ : T extends readonly (infer Item)[]
+ ? ReplaceRefsInType
- []
+ : T extends object
+ ? T extends (...args: unknown[]) => unknown
+ ? T
+ : { [K in keyof T]: ReplaceRefsInType }
+ : T;
/**
* Infers the TypeScript type for a lexicon namespace, returning only the 'main' definition
* with all local refs (#user, #post, etc.) resolved to their actual types.
*/
-type Infer;
-}> = Prettify<"main" extends keyof T["defs"] ? {
- $type: T["id"];
-} & ReplaceRefsInType, { [K in keyof T["defs"]]: InferType }> : never>;
+type Infer<
+ T extends {
+ id: string;
+ defs: Record;
+ },
+> = Prettify<
+ "main" extends keyof T["defs"]
+ ? {
+ $type: T["id"];
+ } & ReplaceRefsInType<
+ InferType,
+ { [K in keyof T["defs"]]: InferType }
+ >
+ : never
+>;
//#endregion
export { GetNullable, GetRequired, Infer };
-//# sourceMappingURL=infer.d.ts.map
\ No newline at end of file
+//# sourceMappingURL=infer.d.ts.map
diff --git a/packages/site/public/types/lib.d.ts b/packages/site/public/types/lib.d.ts
index 0db0dfb..379afce 100644
--- a/packages/site/public/types/lib.d.ts
+++ b/packages/site/public/types/lib.d.ts
@@ -3,174 +3,223 @@ import { Infer } from "./infer.js";
//#region src/lib.d.ts
/** @see https://atproto.com/specs/lexicon#overview-of-types */
-type LexiconType = "null" | "boolean" | "integer" | "string" | "bytes" | "cid-link" | "blob" | "array" | "object" | "params" | "token" | "ref" | "union" | "unknown" | "record" | "query" | "procedure" | "subscription";
+type LexiconType =
+ | "null"
+ | "boolean"
+ | "integer"
+ | "string"
+ | "bytes"
+ | "cid-link"
+ | "blob"
+ | "array"
+ | "object"
+ | "params"
+ | "token"
+ | "ref"
+ | "union"
+ | "unknown"
+ | "record"
+ | "query"
+ | "procedure"
+ | "subscription";
/**
* Common options available for lexicon items.
* @see https://atproto.com/specs/lexicon#string-formats
*/
interface LexiconItemCommonOptions {
- /** Indicates this field must be provided */
- required?: boolean;
- /** Indicates this field can be explicitly set to null */
- nullable?: boolean;
+ /** Indicates this field must be provided */
+ required?: boolean;
+ /** Indicates this field can be explicitly set to null */
+ nullable?: boolean;
}
/**
* Base interface for all lexicon items.
* @see https://atproto.com/specs/lexicon#overview-of-types
*/
interface LexiconItem extends LexiconItemCommonOptions {
- type: LexiconType;
+ type: LexiconType;
}
/**
* Definition in a lexicon namespace.
* @see https://atproto.com/specs/lexicon#lexicon-document
*/
interface Def {
- type: LexiconType;
+ type: LexiconType;
}
/**
* Lexicon namespace document structure.
* @see https://atproto.com/specs/lexicon#lexicon-document
*/
interface LexiconNamespace {
- /** Namespaced identifier (NSID) for this lexicon */
- id: string;
- /** Named definitions within this namespace */
- defs: Record;
+ /** Namespaced identifier (NSID) for this lexicon */
+ id: string;
+ /** Named definitions within this namespace */
+ defs: Record