Skip to content

Commit 42c4dfc

Browse files
committed
Revert "Feature/map closures (#1864)"
This reverts commit f9ded59.
1 parent e7c6c3a commit 42c4dfc

File tree

93 files changed

+357
-5454
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+357
-5454
lines changed

packages/api/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,6 @@ export interface OpaqueRefMethods<T> {
6565
array: T,
6666
) => Opaque<S>,
6767
): Opaque<S[]>;
68-
mapWithPattern<S>(
69-
op: Recipe,
70-
params: Record<string, any>,
71-
): Opaque<S[]>;
7268
}
7369

7470
// Factory types

packages/runner/src/builder/opaque-ref.ts

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { hasValueAtPath, setValueAtPath } from "../path-utils.ts";
1818
import { getTopFrame, recipe } from "./recipe.ts";
1919
import { createNodeFactory } from "./module.ts";
2020

21-
let mapFactory: NodeFactory<any, any> | undefined;
21+
let mapFactory: NodeFactory<any, any>;
2222

2323
// A opaque ref factory that creates future cells with optional default values.
2424
//
@@ -140,22 +140,6 @@ export function opaqueRef<T>(
140140
),
141141
});
142142
},
143-
mapWithPattern: <S>(
144-
op: Recipe,
145-
params: Record<string, any>,
146-
) => {
147-
// Create the factory if it doesn't exist. Doing it here to avoid
148-
// circular dependency.
149-
mapFactory ||= createNodeFactory({
150-
type: "ref",
151-
implementation: "map",
152-
});
153-
return mapFactory({
154-
list: proxy,
155-
op: op,
156-
params: params,
157-
});
158-
},
159143
toJSON: () => null, // TODO(seefeld): Merge with Cell and cover doc-less case
160144
/**
161145
* We assume the cell is an array and will provide an infinite iterator.

packages/runner/src/builder/types.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,6 @@ declare module "@commontools/api" {
128128
array: T,
129129
) => Opaque<S>,
130130
): Opaque<S[]>;
131-
mapWithPattern<S>(
132-
op: Recipe,
133-
params: Record<string, any>,
134-
): Opaque<S[]>;
135131
toJSON(): unknown;
136132
[Symbol.iterator](): Iterator<T>;
137133
[Symbol.toPrimitive](hint: string): T;

packages/runner/src/builtins/map.ts

Lines changed: 8 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,16 @@ import type { IRuntime } from "../runtime.ts";
66
import type { IExtendedStorageTransaction } from "../storage/interface.ts";
77

88
/**
9-
* Implementation of built-in map module. Unlike regular modules, this will be
9+
* Implemention of built-in map module. Unlike regular modules, this will be
1010
* called once at setup and thus sets up its own actions for the scheduler.
1111
*
12-
* This supports both legacy map calls and closure-transformed map calls:
13-
* - Legacy mode (params === undefined): Passes { element, index, array } to recipe
14-
* - Closure mode (params !== undefined): Passes { element, index, array, params } to recipe
15-
*
1612
* The goal is to keep the output array current without recomputing too much.
1713
*
1814
* Approach:
1915
* 1. Create a doc to store the result.
2016
* 2. Create a handler to update the result doc when the input doc changes.
2117
* 3. Create a handler to update the result doc when the op doc changes.
22-
* 4. Create a handler to update the result doc when the params doc changes (closure mode).
23-
* 5. For each value in the input doc, create a handler to update the result
18+
* 4. For each value in the input doc, create a handler to update the result
2419
* doc when the value changes.
2520
*
2621
* TODO: Optimization depends on javascript objects and not lookslike objects.
@@ -29,14 +24,12 @@ import type { IExtendedStorageTransaction } from "../storage/interface.ts";
2924
*
3025
* @param list - A doc containing an array of values to map over.
3126
* @param op - A recipe to apply to each value.
32-
* @param params - Optional object containing captured variables from outer scope (closure mode).
3327
* @returns A doc containing the mapped values.
3428
*/
3529
export function map(
3630
inputsCell: Cell<{
3731
list: any[];
3832
op: Recipe;
39-
params?: Record<string, any>;
4033
}>,
4134
sendResult: (tx: IExtendedStorageTransaction, result: any) => void,
4235
addCancel: AddCancel,
@@ -69,13 +62,12 @@ export function map(
6962
sendResult(tx, result);
7063
}
7164
const resultWithLog = result.withTx(tx);
72-
const { list, op, params } = inputsCell.asSchema(
65+
const { list, op } = inputsCell.asSchema(
7366
{
7467
type: "object",
7568
properties: {
7669
list: { type: "array", items: { asCell: true } },
7770
op: { asCell: true },
78-
params: { type: "object" },
7971
},
8072
required: ["list", "op"],
8173
additionalProperties: false,
@@ -119,26 +111,14 @@ export function map(
119111
undefined,
120112
tx,
121113
);
122-
// Determine which mode we're in based on presence of params
123-
const recipeInputs = params !== undefined
124-
? {
125-
// Closure mode: include params
126-
element: inputsCell.key("list").key(initializedUpTo),
127-
index: initializedUpTo,
128-
array: inputsCell.key("list"),
129-
params: inputsCell.key("params"),
130-
}
131-
: {
132-
// Legacy mode: no params
133-
element: inputsCell.key("list").key(initializedUpTo),
134-
index: initializedUpTo,
135-
array: inputsCell.key("list"),
136-
};
137-
138114
runtime.runner.run(
139115
tx,
140116
opRecipe,
141-
recipeInputs,
117+
{
118+
element: inputsCell.key("list").key(initializedUpTo),
119+
index: initializedUpTo,
120+
array: inputsCell.key("list"),
121+
},
142122
resultCell,
143123
);
144124
resultCell.getSourceCell()!.setSourceCell(parentCell);

packages/schema-generator/src/formatters/common-tools-formatter.ts

Lines changed: 0 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,6 @@ export class CommonToolsFormatter implements TypeFormatter {
3434
return true;
3535
}
3636

37-
// Check if this is an Opaque<T> union (T | OpaqueRef<T>)
38-
if (this.isOpaqueUnion(type, context.typeChecker)) {
39-
return true;
40-
}
41-
4237
// Check if this is a wrapper type (Cell/Stream/OpaqueRef) via type structure
4338
const wrapperInfo = this.getWrapperTypeInfo(type);
4439
return wrapperInfo !== undefined;
@@ -47,26 +42,6 @@ export class CommonToolsFormatter implements TypeFormatter {
4742
formatType(type: ts.Type, context: GenerationContext): SchemaDefinition {
4843
const n = context.typeNode;
4944

50-
// Check if this is an Opaque<T> union and handle it first
51-
// This prevents the UnionFormatter from creating an anyOf
52-
const opaqueUnionInfo = this.getOpaqueUnionInfo(type, context.typeChecker);
53-
if (opaqueUnionInfo) {
54-
// Format the base type T and add asOpaque: true
55-
const innerSchema = this.schemaGenerator.formatChildType(
56-
opaqueUnionInfo.baseType,
57-
context,
58-
undefined, // Don't pass typeNode since we're working with the unwrapped type
59-
);
60-
61-
// Handle boolean schemas
62-
if (typeof innerSchema === "boolean") {
63-
return innerSchema === false
64-
? { asOpaque: true, not: true } as SchemaDefinition // false = "no value is valid"
65-
: { asOpaque: true } as SchemaDefinition; // true = "any value is valid"
66-
}
67-
return { ...innerSchema, asOpaque: true } as SchemaDefinition;
68-
}
69-
7045
// Check via typeNode for all wrapper types (handles both direct usage and aliases)
7146
const resolvedWrapper = n
7247
? resolveWrapperNode(n, context.typeChecker)
@@ -216,133 +191,6 @@ export class CommonToolsFormatter implements TypeFormatter {
216191
return { ...innerSchema, [propertyName]: true };
217192
}
218193

219-
/**
220-
* Check if a type is an Opaque<T> union (T | OpaqueRef<T>)
221-
*/
222-
private isOpaqueUnion(type: ts.Type, checker: ts.TypeChecker): boolean {
223-
return this.getOpaqueUnionInfo(type, checker) !== undefined;
224-
}
225-
226-
/**
227-
* Extract information from an Opaque<T> union type.
228-
* Opaque<T> is defined as: T | OpaqueRef<T>
229-
* This function detects this pattern and returns the base type T.
230-
*/
231-
private getOpaqueUnionInfo(
232-
type: ts.Type,
233-
checker: ts.TypeChecker,
234-
): { baseType: ts.Type } | undefined {
235-
// Must be a union type
236-
if (!(type.flags & ts.TypeFlags.Union)) {
237-
return undefined;
238-
}
239-
240-
const unionType = type as ts.UnionType;
241-
const members = unionType.types;
242-
243-
// Must have exactly 2 members
244-
if (members.length !== 2) {
245-
return undefined;
246-
}
247-
248-
// One member should be OpaqueRef<T>, the other should be T
249-
let opaqueRefMember: ts.Type | undefined;
250-
let baseMember: ts.Type | undefined;
251-
252-
for (const member of members) {
253-
// Check if this member is an OpaqueRef type (it will be an intersection)
254-
const isOpaqueRef = this.isOpaqueRefType(member);
255-
if (isOpaqueRef) {
256-
opaqueRefMember = member;
257-
} else {
258-
baseMember = member;
259-
}
260-
}
261-
262-
// Both members must be present for this to be an Opaque<T> union
263-
if (!opaqueRefMember || !baseMember) {
264-
return undefined;
265-
}
266-
267-
// Verify that the OpaqueRef's type argument matches the base type
268-
// Extract T from OpaqueRef<T>
269-
const opaqueRefInnerType = this.extractOpaqueRefTypeArgument(
270-
opaqueRefMember,
271-
checker,
272-
);
273-
if (!opaqueRefInnerType) {
274-
return undefined;
275-
}
276-
277-
// The inner type of OpaqueRef should match the base member
278-
// Use type equality check
279-
const innerTypeString = checker.typeToString(opaqueRefInnerType);
280-
const baseTypeString = checker.typeToString(baseMember);
281-
282-
if (innerTypeString !== baseTypeString) {
283-
// Not a matching Opaque<T> pattern
284-
return undefined;
285-
}
286-
287-
return { baseType: baseMember };
288-
}
289-
290-
/**
291-
* Check if a type is an OpaqueRef type (intersection with OpaqueRefMethods)
292-
*/
293-
private isOpaqueRefType(type: ts.Type): boolean {
294-
// OpaqueRef types are intersection types
295-
if (!(type.flags & ts.TypeFlags.Intersection)) {
296-
return false;
297-
}
298-
299-
const intersectionType = type as ts.IntersectionType;
300-
for (const constituent of intersectionType.types) {
301-
if (constituent.flags & ts.TypeFlags.Object) {
302-
const objectType = constituent as ts.ObjectType;
303-
if (objectType.objectFlags & ts.ObjectFlags.Reference) {
304-
const typeRef = objectType as ts.TypeReference;
305-
const name = typeRef.target?.symbol?.name;
306-
if (name === "OpaqueRefMethods") {
307-
return true;
308-
}
309-
}
310-
}
311-
}
312-
return false;
313-
}
314-
315-
/**
316-
* Extract the type argument T from OpaqueRef<T>
317-
*/
318-
private extractOpaqueRefTypeArgument(
319-
type: ts.Type,
320-
checker: ts.TypeChecker,
321-
): ts.Type | undefined {
322-
if (!(type.flags & ts.TypeFlags.Intersection)) {
323-
return undefined;
324-
}
325-
326-
const intersectionType = type as ts.IntersectionType;
327-
for (const constituent of intersectionType.types) {
328-
if (constituent.flags & ts.TypeFlags.Object) {
329-
const objectType = constituent as ts.ObjectType;
330-
if (objectType.objectFlags & ts.ObjectFlags.Reference) {
331-
const typeRef = objectType as ts.TypeReference;
332-
const name = typeRef.target?.symbol?.name;
333-
if (name === "OpaqueRefMethods") {
334-
// Found OpaqueRefMethods<T>, extract T
335-
const typeArgs = checker.getTypeArguments(typeRef);
336-
if (typeArgs && typeArgs.length > 0) {
337-
return typeArgs[0];
338-
}
339-
}
340-
}
341-
}
342-
}
343-
return undefined;
344-
}
345-
346194
/**
347195
* Get wrapper type information (Cell/Stream/OpaqueRef)
348196
* Handles both direct references and intersection types (e.g., OpaqueRef<"literal">)

packages/schema-generator/src/interface.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,4 @@ export interface SchemaGenerator {
6767
checker: ts.TypeChecker,
6868
typeNode?: ts.TypeNode,
6969
): SchemaDefinition;
70-
71-
/**
72-
* Generate schema from a synthetic TypeNode that doesn't resolve to a proper Type.
73-
* Used by transformers that create synthetic type structures programmatically.
74-
*
75-
* @param typeNode - Synthetic TypeNode to analyze
76-
* @param checker - TypeScript type checker
77-
* @param typeRegistry - Optional WeakMap of Node → Type for registered synthetic nodes
78-
*/
79-
generateSchemaFromSyntheticTypeNode(
80-
typeNode: ts.TypeNode,
81-
checker: ts.TypeChecker,
82-
typeRegistry?: WeakMap<ts.Node, ts.Type>,
83-
): SchemaDefinition;
8470
}

packages/schema-generator/src/plugin.ts

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,18 @@ import ts from "typescript";
22
import { SchemaGenerator } from "./schema-generator.ts";
33

44
/**
5-
* Plugin function that creates a schema transformer with access to both
6-
* Type-based and synthetic TypeNode-based schema generation
5+
* Plugin function that matches the existing typeToJsonSchema signature
6+
* This allows our new system to be a drop-in replacement
77
*/
8-
export function createSchemaTransformerV2() {
8+
export function createSchemaTransformerV2(): (
9+
type: ts.Type,
10+
checker: ts.TypeChecker,
11+
typeArg?: ts.TypeNode,
12+
) => any {
913
const generator = new SchemaGenerator();
1014

11-
return {
12-
generateSchema(
13-
type: ts.Type,
14-
checker: ts.TypeChecker,
15-
typeArg?: ts.TypeNode,
16-
) {
17-
return generator.generateSchema(type, checker, typeArg);
18-
},
19-
20-
generateSchemaFromSyntheticTypeNode(
21-
typeNode: ts.TypeNode,
22-
checker: ts.TypeChecker,
23-
typeRegistry?: WeakMap<ts.Node, ts.Type>,
24-
) {
25-
return generator.generateSchemaFromSyntheticTypeNode(
26-
typeNode,
27-
checker,
28-
typeRegistry,
29-
);
30-
},
15+
return (type: ts.Type, checker: ts.TypeChecker, typeArg?: ts.TypeNode) => {
16+
return generator.generateSchema(type, checker, typeArg);
3117
};
3218
}
3319

0 commit comments

Comments
 (0)