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
18 changes: 9 additions & 9 deletions packages/builder/src/built-in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export interface BuiltInLLMState<T> {
pending: boolean;
result?: T;
partial?: string;
error: any;
error: unknown;
}

export const llm = createNodeFactory({
Expand All @@ -60,7 +60,7 @@ export const fetchData = createNodeFactory({
options?: RequestInit;
result?: T;
}>,
) => Opaque<{ pending: boolean; result: T; error: any }>;
) => Opaque<{ pending: boolean; result: T; error: unknown }>;

export const streamData = createNodeFactory({
type: "ref",
Expand All @@ -71,9 +71,9 @@ export const streamData = createNodeFactory({
options?: RequestInit;
result?: T;
}>,
) => Opaque<{ pending: boolean; result: T; error: any }>;
) => Opaque<{ pending: boolean; result: T; error: unknown }>;

export function ifElse<T = any, U = any, V = any>(
export function ifElse<T = unknown, U = unknown, V = unknown>(
condition: Opaque<T>,
ifTrue: Opaque<U>,
ifFalse: Opaque<V>,
Expand All @@ -82,30 +82,30 @@ export function ifElse<T = any, U = any, V = any>(
type: "ref",
implementation: "ifElse",
});
return ifElseFactory([condition, ifTrue, ifFalse]);
return ifElseFactory([condition, ifTrue, ifFalse]) as OpaqueRef<U | V>;
}

let ifElseFactory: NodeFactory<[any, any, any], any> | undefined;
let ifElseFactory: NodeFactory<[unknown, unknown, unknown], any> | undefined;

export const navigateTo = createNodeFactory({
type: "ref",
implementation: "navigateTo",
}) as (cell: OpaqueRef<any>) => OpaqueRef<string>;
}) as (cell: OpaqueRef<unknown>) => OpaqueRef<string>;

// Example:
// str`Hello, ${name}!`
//
// TODO(seefeld): This should be a built-in module
export function str(
strings: TemplateStringsArray,
...values: any[]
...values: unknown[]
): OpaqueRef<string> {
const interpolatedString = ({
strings,
values,
}: {
strings: TemplateStringsArray;
values: any[];
values: unknown[];
}) =>
strings.reduce(
(result, str, i) => result + str + (i < values.length ? values[i] : ""),
Expand Down
8 changes: 6 additions & 2 deletions packages/builder/src/opaque-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { getTopFrame, recipe } from "./recipe.ts";
import { createNodeFactory } from "./module.ts";
import { SchemaWithoutCell } from "./schema-to-ts.ts";
import { ContextualFlowControl } from "../../runner/src/index.ts";
import { isRecord } from "@commontools/utils/types";

let mapFactory: NodeFactory<any, any>;

Expand Down Expand Up @@ -110,8 +111,11 @@ export function opaqueRef<T>(
unsafe_getExternal: () => {
if (!unsafe_binding) return proxy;
const value = unsafe_materialize(unsafe_binding, path);
if (typeof value === "object" && value !== null && value[toOpaqueRef]) {
return value[toOpaqueRef]();
if (
isRecord(value) && value[toOpaqueRef] &&
typeof value[toOpaqueRef] === "function"
) {
return (value[toOpaqueRef] as () => OpaqueRef<any>)();
} else return proxy;
},
map: <S>(
Expand Down
15 changes: 7 additions & 8 deletions packages/builder/src/recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
traverseValue,
} from "./utils.ts";
import { SchemaWithoutCell } from "./schema-to-ts.ts";
import { isRecord } from "@commontools/utils/types";

/** Declare a recipe
*
Expand Down Expand Up @@ -187,13 +188,11 @@ function factoryFromRecipe<T, R>(
// Fill in reasonable names for all cells, where possible:

// First from results
if (typeof outputs === "object" && outputs !== null) {
Object.entries(outputs).forEach(([key, value]) => {
if (
isOpaqueRef(value) && !value.export().path.length &&
!value.export().name
) {
value.setName(key);
if (isRecord(outputs)) {
Object.entries(outputs).forEach(([key, value]: [string, unknown]) => {
if (isOpaqueRef(value)) {
const ref = value; // Typescript needs this to avoid type errors
if (!ref.export().path.length && !ref.export().name) ref.setName(key);
}
});
}
Expand All @@ -202,7 +201,7 @@ function factoryFromRecipe<T, R>(
cells.forEach((cell) => {
if (cell.export().path.length) return;
cell.export().nodes.forEach((node: NodeRef) => {
if (typeof node.inputs === "object" && node.inputs !== null) {
if (isRecord(node.inputs)) {
Object.entries(node.inputs).forEach(([key, input]) => {
if (
isOpaqueRef(input) && input.cell === cell && !cell.export().name
Expand Down
62 changes: 32 additions & 30 deletions packages/builder/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export type OpaqueRefMethods<T> = {
set(value: Opaque<T> | T): void;
key<K extends keyof T>(key: K): OpaqueRef<T[K]>;
setDefault(value: Opaque<T> | T): void;
setPreExisting(ref: any): void;
setPreExisting(ref: unknown): void;
setName(name: string): void;
setSchema(schema: JSONSchema): void;
connect(node: NodeRef): void;
Expand All @@ -42,7 +42,7 @@ export type OpaqueRefMethods<T> = {
value?: Opaque<T>;
defaultValue?: Opaque<T>;
nodes: Set<NodeRef>;
external?: any;
external?: unknown;
name?: string;
schema?: JSONSchema;
rootSchema?: JSONSchema;
Expand All @@ -60,16 +60,17 @@ export type OpaqueRefMethods<T> = {
array: T,
) => Opaque<S>,
): Opaque<S[]>;
toJSON(): any;
toJSON(): unknown;
[Symbol.iterator](): Iterator<T>;
[Symbol.toPrimitive](hint: string): T;
[isOpaqueRefMarker]: true;
};

export const isOpaqueRefMarker = Symbol("isOpaqueRef");

export function isOpaqueRef(value: any): value is OpaqueRef<any> {
return value && typeof value[isOpaqueRefMarker] === "boolean";
export function isOpaqueRef<T = any>(value: unknown): value is OpaqueRef<T> {
return !!value &&
typeof (value as OpaqueRef<T>)[isOpaqueRefMarker] === "boolean";
}

export type NodeRef = {
Expand All @@ -80,7 +81,7 @@ export type NodeRef = {
};

export type toJSON = {
toJSON(): any;
toJSON(): unknown;
};

export type NodeFactory<T, R> =
Expand Down Expand Up @@ -118,16 +119,16 @@ export interface JSONObject extends Record<string, JSONValue> {}
// Annotations when writing data that help determine the entity id. They are
// removed before sending to storage.
export interface IDFields {
[ID]?: any;
[ID_FIELD]?: any;
[ID]?: unknown;
[ID_FIELD]?: unknown;
}

// TODO(@ubik2) When specifying a JSONSchema, you can often use a boolean
// This is particularly useful for specifying the schema of a property.
// That will require reworking some things, so for now, I'm not doing it
export type JSONSchema = {
readonly [ID]?: any;
readonly [ID_FIELD]?: any;
readonly [ID]?: unknown;
readonly [ID_FIELD]?: unknown;
readonly type?:
| "object"
| "array"
Expand Down Expand Up @@ -158,14 +159,14 @@ export type JSONSchemaMutable = Mutable<JSONSchema>;

export type Alias = {
$alias: {
cell?: any;
cell?: unknown;
path: PropertyKey[];
schema?: JSONSchema;
rootSchema?: JSONSchema;
};
};

export function isAlias(value: any): value is Alias {
export function isAlias(value: unknown): value is Alias {
return isObject(value) && "$alias" in value && isObject(value.$alias) &&
"path" in value.$alias &&
Array.isArray(value.$alias.path);
Expand All @@ -175,8 +176,8 @@ export type StreamAlias = {
$stream: true;
};

export function isStreamAlias(value: any): value is StreamAlias {
return !!value && typeof value.$stream === "boolean" && value.$stream;
export function isStreamAlias(value: unknown): value is StreamAlias {
return isObject(value) && "$stream" in value && value.$stream === true;
}

export type Module = {
Expand All @@ -191,10 +192,10 @@ export type Handler<T = any, R = any> = Module & {
with: (inputs: Opaque<T>) => OpaqueRef<R>;
};

export function isModule(value: any): value is Module {
export function isModule(value: unknown): value is Module {
return (
(typeof value === "function" || typeof value === "object") &&
typeof value.type === "string"
value !== null && typeof (value as unknown as Module).type === "string"
);
}

Expand All @@ -221,24 +222,24 @@ export type Recipe = {
[unsafe_materializeFactory]?: (log: any) => (path: PropertyKey[]) => any;
};

export function isRecipe(value: any): value is Recipe {
export function isRecipe(value: unknown): value is Recipe {
return (
(typeof value === "function" || typeof value === "object") &&
value !== null &&
!!value.argumentSchema &&
!!value.resultSchema &&
!!value.nodes &&
Array.isArray(value.nodes)
!!(value as any).argumentSchema &&
!!(value as any).resultSchema &&
!!(value as any).nodes &&
Array.isArray((value as any).nodes)
);
}

type CanBeOpaqueRef = { [toOpaqueRef]: () => OpaqueRef<any> };

export function canBeOpaqueRef(value: any): value is CanBeOpaqueRef {
export function canBeOpaqueRef(value: unknown): value is CanBeOpaqueRef {
return (
(typeof value === "object" || typeof value === "function") &&
value !== null &&
typeof value[toOpaqueRef] === "function"
typeof (value as any)[toOpaqueRef] === "function"
);
}

Expand All @@ -252,12 +253,13 @@ export type ShadowRef = {
shadowOf: OpaqueRef<any> | ShadowRef;
};

export function isShadowRef(value: any): value is ShadowRef {
export function isShadowRef(value: unknown): value is ShadowRef {
return (
!!value &&
typeof value === "object" &&
"shadowOf" in value &&
(isOpaqueRef(value.shadowOf) || isShadowRef(value.shadowOf))
(isOpaqueRef((value as ShadowRef).shadowOf) ||
isShadowRef((value as ShadowRef).shadowOf))
);
}

Expand All @@ -269,7 +271,7 @@ export type UnsafeBinding = {

export type Frame = {
parent?: Frame;
cause?: any;
cause?: unknown;
generatedIdCounter: number;
opaqueRefs: Set<OpaqueRef<any>>;
unsafe_binding?: UnsafeBinding;
Expand All @@ -281,12 +283,12 @@ export type Static = {
[isStaticMarker]: true;
};

export function isStatic(value: any): value is Static {
export function isStatic(value: unknown): value is Static {
return typeof value === "object" && value !== null &&
value[isStaticMarker] === true;
(value as any)[isStaticMarker] === true;
}

export function markAsStatic(value: any): any {
value[isStaticMarker] = true;
export function markAsStatic(value: unknown): unknown {
(value as any)[isStaticMarker] = true;
return value;
}
22 changes: 11 additions & 11 deletions packages/builder/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
isCellLink,
isDoc,
} from "@commontools/runner";
import { isObject, isRecord } from "@commontools/utils/types";

/**
* Traverse a value, _not_ entering cells
Expand All @@ -52,8 +53,7 @@ export function traverseValue(
(!isOpaqueRef(value) &&
!canBeOpaqueRef(value) &&
!isShadowRef(value) &&
typeof value === "object" &&
value !== null) ||
isRecord(value)) ||
isRecipe(value)
) {
return staticWrap(
Expand Down Expand Up @@ -105,7 +105,7 @@ export function getValueAtPath(obj: any, path: PropertyKey[]): any {
export function hasValueAtPath(obj: any, path: PropertyKey[]): boolean {
let current = obj;
for (const key of path) {
if (!current || typeof current !== "object" || !(key in current)) {
if (!isRecord(current) || !(key in current)) {
return false;
}
current = current[key];
Expand All @@ -115,7 +115,7 @@ export function hasValueAtPath(obj: any, path: PropertyKey[]): boolean {

export const deepEqual = (a: any, b: any): boolean => {
if (a === b) return true;
if (a && b && typeof a === "object" && typeof b === "object") {
if (isRecord(a) && isRecord(b)) {
if (a.constructor !== b.constructor) return false;
const keysA = Object.keys(a);
const keysB = Object.keys(b);
Expand All @@ -138,8 +138,8 @@ export function toJSONWithAliases(
): JSONValue | undefined {
if (isStatic(value) && !processStatic) {
return markAsStatic(
toJSONWithAliases(value, paths, ignoreSelfAliases, path, true),
);
toJSONWithAliases(value, paths, ignoreSelfAliases, path, true) as any,
) as JSONValue;
} // Convert regular cells to opaque refs
else if (canBeOpaqueRef(value)) value = makeOpaqueRef(value);
// Convert parent opaque refs to shadow refs
Expand Down Expand Up @@ -196,7 +196,7 @@ export function toJSONWithAliases(
} else if (!("cell" in alias) || typeof alias.cell === "number") {
return {
$alias: {
cell: (alias.cell ?? 0) + 1,
cell: ((alias.cell as number) ?? 0) + 1,
path: alias.path as (string | number)[],
},
} satisfies Alias;
Expand All @@ -211,7 +211,7 @@ export function toJSONWithAliases(
);
}

if (typeof value === "object" || isRecipe(value)) {
if (isRecord(value) || isRecipe(value)) {
const result: any = {};
let hasValue = false;
for (const key in value as any) {
Expand Down Expand Up @@ -278,11 +278,11 @@ export function createJsonSchema(
schema.items = {};
} else {
const first = value[0];
if (first && typeof first === "object" && !Array.isArray(first)) {
if (isObject(first)) {
const properties: { [key: string]: any } = {};
for (let i = 0; i < value.length; i++) {
const item = value?.[i];
if (typeof item === "object" && item !== null) {
if (isRecord(item)) {
Object.keys(item).forEach((key) => {
if (!(key in properties)) {
properties[key] = analyzeType(
Expand Down Expand Up @@ -426,7 +426,7 @@ function attachCfcToOutputs<T, R>(
const cfcSchema: JSONSchema = { ...outputSchema, ifc };
(outputs as OpaqueRef<T>).setSchema(cfcSchema);
return;
} else if (typeof outputs === "object" && outputs !== null) {
} else if (isRecord(outputs)) {
// Descend into objects and arrays
for (const [key, value] of Object.entries(outputs)) {
attachCfcToOutputs(value, cfc, lubClassification);
Expand Down
Loading