Skip to content

Commit 1aec1bb

Browse files
authored
generate schemas for 'derive' (#1826)
* generate schemas for 'derive' * couple more fixtures for derive schemas
1 parent f7d1d5d commit 1aec1bb

File tree

10 files changed

+193
-7
lines changed

10 files changed

+193
-7
lines changed

packages/api/index.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -442,10 +442,24 @@ export type HandlerFunction = {
442442
): ModuleFactory<StripCell<T>, E>;
443443
};
444444

445-
export type DeriveFunction = <In, Out>(
446-
input: Opaque<In>,
447-
f: (input: In) => Out | Promise<Out>,
448-
) => OpaqueRef<Out>;
445+
export type DeriveFunction = {
446+
<
447+
InputSchema extends JSONSchema = JSONSchema,
448+
ResultSchema extends JSONSchema = JSONSchema,
449+
>(
450+
argumentSchema: InputSchema,
451+
resultSchema: ResultSchema,
452+
input: Opaque<SchemaWithoutCell<InputSchema>>,
453+
f: (
454+
input: Schema<InputSchema>,
455+
) => Schema<ResultSchema> | Promise<Schema<ResultSchema>>,
456+
): OpaqueRef<SchemaWithoutCell<ResultSchema>>;
457+
458+
<In, Out>(
459+
input: Opaque<In>,
460+
f: (input: In) => Out | Promise<Out>,
461+
): OpaqueRef<Out>;
462+
};
449463

450464
export type ComputeFunction = <T>(fn: () => T) => OpaqueRef<T>;
451465

packages/runner/src/builder/module.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,42 @@ export function handler<E, T>(
177177
return factory;
178178
}
179179

180-
export const derive = <In, Out>(
180+
export function derive<
181+
InputSchema extends JSONSchema = JSONSchema,
182+
ResultSchema extends JSONSchema = JSONSchema,
183+
>(
184+
argumentSchema: InputSchema,
185+
resultSchema: ResultSchema,
186+
input: Opaque<SchemaWithoutCell<InputSchema>>,
187+
f: (
188+
input: Schema<InputSchema>,
189+
) => Schema<ResultSchema> | Promise<Schema<ResultSchema>>,
190+
): OpaqueRef<SchemaWithoutCell<ResultSchema>>;
191+
export function derive<In, Out>(
181192
input: Opaque<In>,
182193
f: (input: In) => Out | Promise<Out>,
183-
): OpaqueRef<Out> => lift(f)(input) as OpaqueRef<Out>;
194+
): OpaqueRef<Out>;
195+
export function derive<In, Out>(...args: any[]): OpaqueRef<any> {
196+
if (args.length === 4) {
197+
const [argumentSchema, resultSchema, input, f] = args as [
198+
JSONSchema,
199+
JSONSchema,
200+
Opaque<SchemaWithoutCell<any>>,
201+
(input: Schema<any>) => Schema<any> | Promise<Schema<any>>,
202+
];
203+
return lift(
204+
argumentSchema,
205+
resultSchema,
206+
f as (input: Schema<any>) => Schema<any> | Promise<Schema<any>>,
207+
)(input);
208+
}
209+
210+
const [input, f] = args as [
211+
Opaque<In>,
212+
(input: In) => Out | Promise<Out>,
213+
];
214+
return lift(f)(input);
215+
}
184216

185217
// unsafe closures: like derive, but doesn't need any arguments
186218
export const compute: <T>(fn: () => T) => OpaqueRef<T> = (fn: () => any) =>

packages/static/assets/types/commontools.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,10 @@ export type HandlerFunction = {
259259
}): ModuleFactory<StripCell<T>, E>;
260260
<E, T>(handler: (event: E, props: HandlerState<T>) => any): ModuleFactory<StripCell<T>, E>;
261261
};
262-
export type DeriveFunction = <In, Out>(input: Opaque<In>, f: (input: In) => Out | Promise<Out>) => OpaqueRef<Out>;
262+
export type DeriveFunction = {
263+
<InputSchema extends JSONSchema = JSONSchema, ResultSchema extends JSONSchema = JSONSchema>(argumentSchema: InputSchema, resultSchema: ResultSchema, input: Opaque<SchemaWithoutCell<InputSchema>>, f: (input: Schema<InputSchema>) => Schema<ResultSchema> | Promise<Schema<ResultSchema>>): OpaqueRef<SchemaWithoutCell<ResultSchema>>;
264+
<In, Out>(input: Opaque<In>, f: (input: In) => Out | Promise<Out>): OpaqueRef<Out>;
265+
};
263266
export type ComputeFunction = <T>(fn: () => T) => OpaqueRef<T>;
264267
export type RenderFunction = <T>(fn: () => T) => OpaqueRef<T>;
265268
export type StrFunction = (strings: TemplateStringsArray, ...values: any[]) => OpaqueRef<string>;

packages/ts-transformers/src/opaque-ref/rules/schema-injection.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,39 @@ export function createSchemaInjectionRule(): OpaqueRefRule {
134134
}
135135
}
136136

137+
if (callKind?.kind === "derive") {
138+
const factory = transformation.factory;
139+
140+
if (node.typeArguments && node.typeArguments.length >= 2) {
141+
const argumentType = node.typeArguments[0];
142+
const resultType = node.typeArguments[1];
143+
if (!argumentType || !resultType) {
144+
return ts.visitEachChild(node, visit, transformation);
145+
}
146+
147+
const toSchemaArgument = factory.createCallExpression(
148+
factory.createIdentifier("toSchema"),
149+
[argumentType],
150+
[],
151+
);
152+
const toSchemaResult = factory.createCallExpression(
153+
factory.createIdentifier("toSchema"),
154+
[resultType],
155+
[],
156+
);
157+
158+
ensureToSchemaImport();
159+
160+
const updated = factory.createCallExpression(
161+
node.expression,
162+
undefined,
163+
[toSchemaArgument, toSchemaResult, ...node.arguments],
164+
);
165+
166+
return ts.visitEachChild(updated, visit, transformation);
167+
}
168+
}
169+
137170
if (callKind?.kind === "builder" && callKind.builderName === "lift") {
138171
const factory = transformation.factory;
139172

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/// <cts-enable />
2+
import { derive as deriveAlias, JSONSchema } from "commontools";
3+
type AliasInput = {
4+
text: string;
5+
};
6+
type AliasResult = {
7+
length: number;
8+
};
9+
declare const state: AliasInput;
10+
export const textLength = deriveAlias({
11+
type: "object",
12+
properties: {
13+
text: {
14+
type: "string"
15+
}
16+
},
17+
required: ["text"]
18+
} as const satisfies JSONSchema, {
19+
type: "object",
20+
properties: {
21+
length: {
22+
type: "number"
23+
}
24+
},
25+
required: ["length"]
26+
} as const satisfies JSONSchema, state, (value) => ({
27+
length: value.text.length,
28+
}));
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/// <cts-enable />
2+
import { derive as deriveAlias } from "commontools";
3+
4+
type AliasInput = {
5+
text: string;
6+
};
7+
8+
type AliasResult = {
9+
length: number;
10+
};
11+
12+
declare const state: AliasInput;
13+
14+
export const textLength = deriveAlias<AliasInput, AliasResult>(
15+
state,
16+
(value) => ({
17+
length: value.text.length,
18+
}),
19+
);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// <cts-enable />
2+
import { derive } from "commontools";
3+
declare const total: number;
4+
export const doubled = derive(total, (value: number) => value * 2);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/// <cts-enable />
2+
import { derive } from "commontools";
3+
4+
declare const total: number;
5+
6+
export const doubled = derive(total, (value: number) => value * 2);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/// <cts-enable />
2+
import { derive, JSONSchema } from "commontools";
3+
type DeriveInput = {
4+
count: number;
5+
};
6+
type DeriveResult = {
7+
doubled: number;
8+
};
9+
declare const source: DeriveInput;
10+
export const doubledValue = derive({
11+
type: "object",
12+
properties: {
13+
count: {
14+
type: "number"
15+
}
16+
},
17+
required: ["count"]
18+
} as const satisfies JSONSchema, {
19+
type: "object",
20+
properties: {
21+
doubled: {
22+
type: "number"
23+
}
24+
},
25+
required: ["doubled"]
26+
} as const satisfies JSONSchema, source, (input) => ({
27+
doubled: input.count * 2,
28+
}));
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/// <cts-enable />
2+
import { derive } from "commontools";
3+
4+
type DeriveInput = {
5+
count: number;
6+
};
7+
8+
type DeriveResult = {
9+
doubled: number;
10+
};
11+
12+
declare const source: DeriveInput;
13+
14+
export const doubledValue = derive<DeriveInput, DeriveResult>(
15+
source,
16+
(input) => ({
17+
doubled: input.count * 2,
18+
}),
19+
);

0 commit comments

Comments
 (0)