Skip to content

Commit 71c8473

Browse files
authored
don't restart recipe if recipe didn't change (commontoolsinc#478)
1 parent 06be51d commit 71c8473

File tree

2 files changed

+44
-33
lines changed

2 files changed

+44
-33
lines changed

typescript/packages/common-runner/src/runner.ts

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,25 @@ import { type AddCancel, type Cancel, useCancelGroup } from "./cancel.js";
3535
import "./builtins/index.ts";
3636
import { addRecipe, getRecipe, getRecipeId } from "./recipe-map.js";
3737
import { isCell } from "./cell.js";
38+
import { isQueryResultForDereferencing } from "./query-result-proxy.js";
39+
import { getDocLinkOrThrow } from "./query-result-proxy.js";
3840

3941
export const cancels = new WeakMap<DocImpl<any>, Cancel>();
4042

4143
/**
4244
* Run a recipe.
4345
*
44-
* When called with a result cell, it'll read from the cell and update the
45-
* instance if appropriate. This can be used to rehydrate an instance.
46+
* resultCell is required and should have an id. processCell is created if not
47+
* already set.
4648
*
47-
* When called without a result cell, or the result cell is empty, a new
48-
* instance is created.
49+
* If no recipe is provided, the previous one is used, and the recipe is started
50+
* if it isn't already started.
51+
*
52+
* If no argument is provided, the previous one is used, and the recipe is
53+
* started if it isn't already running.
54+
*
55+
* If a new recipe or any argument value is provided, a currently running recipe
56+
* is stopped, the recipe and argument replaced and the recipe restarted.
4957
*
5058
* @param recipeFactory - Function that takes the argument and returns a recipe.
5159
* @param argument - The argument to pass to the recipe. Can be static data
@@ -69,22 +77,6 @@ export function run<T, R = any>(
6977
argument: T,
7078
resultCell: DocImpl<R>,
7179
): DocImpl<R> {
72-
if (cancels.has(resultCell)) {
73-
// If it's already running and no new recipe or argument are given,
74-
// we are just returning the result cell
75-
if (recipeOrModule === undefined && argument === undefined) {
76-
return resultCell;
77-
}
78-
79-
// Otherwise stop execution of the old recipe. TODO: Await, but this will
80-
// make all this async.
81-
stop(resultCell);
82-
}
83-
84-
// Keep track of subscriptions to cancel them later
85-
const [cancel, addCancel] = useCancelGroup();
86-
cancels.set(resultCell, cancel);
87-
8880
let processCell: DocImpl<{
8981
[TYPE]: string;
9082
argument?: T;
@@ -97,7 +89,7 @@ export function run<T, R = any>(
9789
// TODO: Allow keeping of previous argument but still supply defaults
9890
argument = argument ?? (processCell.get()?.argument as T);
9991
} else {
100-
processCell = getDoc();
92+
processCell = getDoc(undefined, { cell: resultCell, path: [] }, resultCell.space) as any;
10193
resultCell.sourceCell = processCell;
10294
}
10395

@@ -136,19 +128,45 @@ export function run<T, R = any>(
136128
recipe = recipeOrModule as Recipe;
137129
}
138130

131+
recipeId ??= addRecipe(recipe);
132+
133+
if (cancels.has(resultCell)) {
134+
// If it's already running and no new recipe or argument are given,
135+
// we are just returning the result cell
136+
if (argument === undefined && recipeId === processCell.get()?.[TYPE]) return resultCell;
137+
138+
// TODO: If recipe is the same, but argument is different, just update the argument without stopping
139+
140+
// Otherwise stop execution of the old recipe. TODO: Await, but this will
141+
// make all this async.
142+
stop(resultCell);
143+
}
144+
145+
// Keep track of subscriptions to cancel them later
146+
const [cancel, addCancel] = useCancelGroup();
147+
cancels.set(resultCell, cancel);
148+
139149
// Walk the recipe's schema and extract all default values
140150
const defaults = extractDefaultValues(recipe.argumentSchema);
141151

142152
// If the bindings are a cell or cell reference, convert them to an object
143153
// where each property is a cell reference.
144154
// TODO: If new keys are added after first load, this won't work.
145-
if (isDoc(argument) || isDocLink(argument) || isCell(argument)) {
155+
156+
if (
157+
isDoc(argument) ||
158+
isDocLink(argument) ||
159+
isCell(argument) ||
160+
isQueryResultForDereferencing(argument)
161+
) {
146162
// If it's a cell, turn it into a cell reference
147163
const ref = isDocLink(argument)
148164
? argument
149165
: isCell(argument)
150166
? argument.getAsDocLink()
151-
: ({ cell: argument, path: [] } satisfies DocLink);
167+
: isQueryResultForDereferencing(argument)
168+
? getDocLinkOrThrow(argument)
169+
: ({ cell: argument, path: [] } satisfies DocLink);
152170

153171
// Get value, but just to get the keys. Throw if it isn't an object.
154172
const value = ref.cell.getAsQueryResult(ref.path);
@@ -166,15 +184,6 @@ export function run<T, R = any>(
166184
}
167185
}
168186

169-
// TODO: Add a causal relationship. For example a recipe that only transforms
170-
// data from a fixed source could have an idea that depends on that source
171-
// alone, as any instance of the recipe will do the same thing. The trouble is
172-
// though that we support the recipe to change over time, and such a change
173-
// might change this condition and we'd need distinct ideas for different
174-
// instances of this recipe again.
175-
if (!processCell.entityId && resultCell.entityId)
176-
processCell.generateEntityId({ cell: resultCell, path: [] }, resultCell.space);
177-
178187
const internal = processCell.get()?.internal ?? (recipe.initial as { internal: any })?.internal;
179188

180189
// Ensure static data is converted to cell references, e.g. for arrays
@@ -184,7 +193,7 @@ export function run<T, R = any>(
184193
if (defaults) argument = mergeObjects(argument, deepCopy(defaults));
185194

186195
processCell.send({
187-
[TYPE]: recipeId ?? addRecipe(recipe),
196+
[TYPE]: recipeId,
188197
argument,
189198
...(internal ? { internal: deepCopy(internal) } : {}),
190199
resultRef: { cell: resultCell, path: [] },

typescript/packages/common-runner/src/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ export function mergeObjects(...objects: any[]): any {
5656
Array.isArray(obj) ||
5757
isAlias(obj) ||
5858
isDocLink(obj) ||
59+
isDoc(obj) ||
60+
isCell(obj) ||
5961
isStatic(obj)
6062
) {
6163
return obj;

0 commit comments

Comments
 (0)