Skip to content

Commit 6122021

Browse files
committed
Updates to get, at helpers, tests
1 parent 7cda692 commit 6122021

File tree

5 files changed

+103
-132
lines changed

5 files changed

+103
-132
lines changed

src/Future.ts

Lines changed: 54 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,24 @@ export class Future<T> {
247247
}
248248
}
249249

250+
/**
251+
* `concat` combines multiple `string` or `Future<string>` items together into a new `Future<string>`.
252+
*
253+
* @example
254+
*
255+
* let newFuture = concat("string", node.future.someString, "!")
256+
*/
250257
export const concat = (...items: (string | Future<string>)[]) => {
251258
return new Future<string>(new StringConcat(items));
252259
};
253260

261+
/**
262+
* `interpolate` creates a `Future<string>` using interpolate and supports `Future<string>` values.
263+
*
264+
* @example
265+
*
266+
* let newFuture = interpolate`hello ${"world"}, you look ${node.future.niceString} today.`
267+
*/
254268
export const interpolate = (
255269
strings: TemplateStringsArray,
256270
...exprs: ({ toString(): string } | Future<string>)[]
@@ -263,6 +277,14 @@ export const interpolate = (
263277
);
264278
};
265279

280+
/**
281+
* `jq` supports running [jq](https://jqlang.github.io/jq) operations on a `Future` and returns a new `Future<T>`.
282+
*
283+
* @example
284+
*
285+
* let newFuture = jq<string>(node.future.json_object, ".country")
286+
*
287+
*/
266288
export const jq = <T>(
267289
future: JQDirectiveTarget,
268290
query: string,
@@ -272,83 +294,36 @@ export const jq = <T>(
272294
return new Future<T>(directive);
273295
};
274296

275-
// export class FutureBoolean extends Future<boolean> {}
276-
//
277-
// export class FutureString extends Future<string> {
278-
// static concat(...items: (string | FutureString)[]) {
279-
// return new FutureString(new StringConcat(items));
280-
// }
281-
//
282-
// static interpolate(
283-
// strings: TemplateStringsArray,
284-
// ...exprs: ({ toString(): string } | FutureString)[]
285-
// ): FutureString {
286-
// return FutureString.concat(
287-
// ...strings
288-
// .filter((s) => s !== "") // FIXME: Work around until SubstrateLabs/substrate#514 is live
289-
// .flatMap((s: string, i: number) => {
290-
// const expr = exprs[i];
291-
// return expr
292-
// ? [s, expr instanceof Future ? expr : expr.toString()]
293-
// : [s];
294-
// }),
295-
// );
296-
// }
297-
//
298-
// concat(...items: (string | FutureString)[]) {
299-
// return FutureString.concat(...[this, ...items]);
300-
// }
301-
//
302-
// protected override async _result(): Promise<string> {
303-
// return super._result();
304-
// }
305-
// }
306-
//
307-
// export class FutureNumber extends Future<number> {}
308-
//
309-
// export abstract class FutureArray extends Future<any[] | FutureArray> {
310-
// abstract at(index: number): Future<any>;
311-
//
312-
// protected override async _result(): Promise<any[] | FutureArray> {
313-
// return super._result();
314-
// }
315-
// }
316-
//
317-
// export abstract class FutureObject extends Future<Object> {
318-
// get(path: string): Future<any> {
319-
// const props = parsePath(path);
320-
// return props.reduce((future, prop) => {
321-
// if (future instanceof FutureAnyObject) {
322-
// return typeof prop === "string"
323-
// ? future.get(prop as string)
324-
// : future.at(prop as number);
325-
// } else {
326-
// // @ts-ignore
327-
// return typeof prop === "string" ? future[prop] : future.at(prop);
328-
// }
329-
// }, this) as Future<any>;
330-
// }
331-
//
332-
// protected override async _result(): Promise<Object> {
333-
// return super._result();
334-
// }
335-
// }
336-
337-
/** Represents a future of some unknown type, and so it is equipped with special accessors for getting values at arbitrary paths or indexes */
338-
export class FutureAny<T> extends Future<T> {
339-
get<T>(path: string | Future<string>) {
340-
const d =
341-
typeof path === "string"
342-
? this._directive.next(...parsePath(path))
343-
: this._directive.next(path);
344-
return new FutureAny<T>(d);
345-
}
346-
347-
at<T>(index: number | Future<number>) {
348-
return new FutureAny<T>(this._directive.next(index));
349-
}
297+
/**
298+
* `get` returns a `Future` by selecting a value by path from a `Future<Object>`.
299+
*
300+
* @example
301+
*
302+
* let newFuture = get<string>(node.future, "choices[0].text")
303+
*
304+
*/
305+
export const get = <T = unknown>(
306+
future: Future<Object>,
307+
path: string | Future<string>,
308+
) => {
309+
const d =
310+
typeof path === "string"
311+
? // @ts-ignore (protected _directive)
312+
future._directive.next(...parsePath(path))
313+
: // @ts-ignore (protected _directive)
314+
future._directive.next(path);
315+
return new Future<T>(d);
316+
};
350317

351-
protected override async _result(): Promise<T> {
352-
return super._result();
353-
}
354-
}
318+
/**
319+
* `at` returns a `Future` item at some index of a `Future` containing an array.
320+
*
321+
* @example
322+
*
323+
* let newFuture = at<string>(node.future.strings, 0);
324+
*
325+
*/
326+
export const at = <T>(future: Future<T[]>, index: number | Future<number>) => {
327+
// @ts-ignore (protected _directive)
328+
return new Future<T>(future._directive.next(index));
329+
};

src/sb.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { concat, interpolate, jq } from "substrate/Future";
1+
import { concat, interpolate, jq, get, at } from "substrate/Future";
22
import { StreamingResponse } from "substrate/SubstrateStreamingResponse";
33

44
export const sb = {
55
concat,
66
jq,
77
interpolate,
8+
get,
9+
at,
810
streaming: {
911
fromSSEResponse: StreamingResponse.fromReponse,
1012
},

tests/Future.test.ts

Lines changed: 32 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import "substrate/nodejs/polyfill";
44
import { expect, describe, test } from "vitest";
55
import {
66
Future,
7-
FutureString,
8-
FutureNumber,
97
Trace,
108
StringConcat,
9+
concat,
10+
interpolate,
1111
} from "substrate/Future";
1212
import { Node } from "substrate/Node";
1313
import { SubstrateResponse } from "substrate/SubstrateResponse";
@@ -43,9 +43,9 @@ describe("Future", () => {
4343
});
4444

4545
test(".referencedFutures", () => {
46-
const a = new FutureString(new Trace([], node()));
47-
const b = new FutureString(new Trace([], node()));
48-
const c = new FutureString(new StringConcat([a, b]));
46+
const a = new Future<string>(new Trace([], node()));
47+
const b = new Future<string>(new Trace([], node()));
48+
const c = new Future<string>(new StringConcat([a, b]));
4949
const f = new FooFuture(new StringConcat([c, "d"]));
5050

5151
// @ts-expect-error (accessing protected property)
@@ -54,8 +54,8 @@ describe("Future", () => {
5454

5555
describe("Trace (Directive)", () => {
5656
test(".next", () => {
57-
const s = new FutureString(new Trace([], node("123")));
58-
const n = new FutureNumber(new Trace([], node("456")));
57+
const s = new Future<string>(new Trace([], node("123")));
58+
const n = new Future<number>(new Trace([], node("456")));
5959
const d = new Trace(["a", 1, s, n], node("NodeId"));
6060
const d2 = d.next("b", 2);
6161

@@ -75,16 +75,16 @@ describe("Future", () => {
7575
expect(t1.result()).resolves.toEqual("result1");
7676

7777
// when the trace contains futures, they get resolved
78-
const fs = new FutureString(new Trace([], staticNode("b")));
79-
const fn = new FutureNumber(new Trace([], staticNode(1)));
78+
const fs = new Future<string>(new Trace([], staticNode("b")));
79+
const fn = new Future<number>(new Trace([], staticNode(1)));
8080
const n2 = staticNode({ a: [{ b: [undefined, "result2"] }] });
8181
const t2 = new Trace(["a", 0, fs, fn], n2);
8282
expect(t2.result()).resolves.toEqual("result2");
8383
});
8484

8585
test(".toJSON", () => {
86-
const s = new FutureString(new Trace([], node()), "123");
87-
const n = new FutureNumber(new Trace([], node()), "456");
86+
const s = new Future<string>(new Trace([], node()), "123");
87+
const n = new Future<number>(new Trace([], node()), "456");
8888
const d = new Trace(["a", 1, s, n], node("NodeId"));
8989

9090
expect(d.toJSON()).toEqual({
@@ -94,14 +94,15 @@ describe("Future", () => {
9494
Trace.Operation.key("attr", "a"),
9595
Trace.Operation.key("item", 1),
9696
Trace.Operation.future("attr", "123"),
97-
Trace.Operation.future("item", "456"),
97+
Trace.Operation.future("attr", "456"), // LIAM_TODO: there isn't a way to distinguish at runtime using static types
98+
// Trace.Operation.future("item", "456"),
9899
],
99100
});
100101
});
101102

102103
test(".referencedFutures", () => {
103-
const s = new FutureString(new Trace([], node()));
104-
const n = new FutureNumber(new Trace([], node()));
104+
const s = new Future<string>(new Trace([], node()));
105+
const n = new Future<number>(new Trace([], node()));
105106
const d = new Trace(["a", 1, s, n], node("NodeId"));
106107

107108
expect(d.referencedFutures()).toEqual([s, n]);
@@ -110,8 +111,8 @@ describe("Future", () => {
110111

111112
describe("StringConcat (Directive)", () => {
112113
test(".next", () => {
113-
const s = new FutureString(new Trace([], node()));
114-
const s2 = new FutureString(new Trace([], node()));
114+
const s = new Future<string>(new Trace([], node()));
115+
const s2 = new Future<string>(new Trace([], node()));
115116
const d = new StringConcat(["a", s]);
116117
const d2 = d.next("b", s2);
117118

@@ -128,13 +129,13 @@ describe("Future", () => {
128129
expect(s1.result()).resolves.toEqual("ab");
129130

130131
// when the items includes primitive values and futures
131-
const fs = new FutureString(new Trace([], staticNode("b")));
132+
const fs = new Future<string>(new Trace([], staticNode("b")));
132133
const s2 = new StringConcat(["a", fs]);
133134
expect(s2.result()).resolves.toEqual("ab");
134135
});
135136

136137
test(".toJSON", () => {
137-
const s = new FutureString(new Trace([], node()), "123");
138+
const s = new Future<string>(new Trace([], node()), "123");
138139
const d = new StringConcat(["a", s]);
139140

140141
expect(d.toJSON()).toEqual({
@@ -147,44 +148,37 @@ describe("Future", () => {
147148
});
148149

149150
test(".referencedFutures", () => {
150-
const a = new FutureString(new Trace([], node()));
151-
const b = new FutureString(new Trace([], node()));
152-
const c = new FutureString(new StringConcat([a, b]));
151+
const a = new Future<string>(new Trace([], node()));
152+
const b = new Future<string>(new Trace([], node()));
153+
const c = new Future<string>(new StringConcat([a, b]));
153154
const d = new StringConcat([c, "d"]);
154155

155156
expect(d.referencedFutures()).toEqual([c, a, b]);
156157
});
157158
});
158159

159-
describe("FutureString", () => {
160-
test(".concat (static)", () => {
161-
const s = FutureString.concat("a");
162-
expect(s).toBeInstanceOf(FutureString);
163-
// @ts-expect-error (protected access)
164-
expect(s._directive).toEqual(new StringConcat(["a"]));
165-
});
166-
167-
test(".concat", () => {
168-
const s1 = FutureString.concat("a");
169-
const s2 = s1.concat("b", "c");
170-
expect(s2).toBeInstanceOf(FutureString);
160+
describe("Strings", () => {
161+
test("concat", () => {
162+
const s1 = concat("a");
163+
const s2 = concat(s1, "b", "c");
164+
expect(s2).toBeInstanceOf(Future);
171165
// @ts-expect-error (protected access)
172166
expect(s2._directive).toEqual(new StringConcat([s1, "b", "c"]));
173167
});
174168

175-
test(".interpolate", async () => {
169+
test("interpolate", async () => {
176170
const world = "world";
177171
const nice = "nice";
178-
const i1 = FutureString.interpolate`hello ${world}, you look ${nice} today.`;
172+
const i1 = interpolate`hello ${world}, you look ${nice} today.`;
179173

180174
// @ts-expect-error
181175
expect(i1._result()).resolves.toEqual(
182176
"hello world, you look nice today.",
183177
);
184178

185-
const f1 = FutureString.concat("texas", " ", "sun");
186-
const f2 = FutureString.concat("texas", " ", "moon");
187-
const i2 = FutureString.interpolate`~~ ${f1} x ${f2} ~~`;
179+
const f1 = concat("texas", " ", "sun");
180+
const f2 = concat("texas", " ", "moon");
181+
const i2 = interpolate`~~ ${f1} x ${f2} ~~`;
188182

189183
// @ts-expect-error
190184
expect(i2._result()).resolves.toEqual("~~ texas sun x texas moon ~~");

tests/Node.test.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect, describe, test } from "vitest";
22
import { Node } from "substrate/Node";
3-
import { FutureString, Trace, StringConcat } from "substrate/Future";
3+
import { Future, Trace, StringConcat, get } from "substrate/Future";
44

55
class FooNode extends Node {}
66

@@ -13,10 +13,10 @@ describe("Node", () => {
1313
});
1414

1515
test(".toJSON", () => {
16-
const a = new FutureString(new Trace([], new FooNode({})));
17-
const b = new FutureString(new Trace([], new FooNode({})));
18-
const c = new FutureString(new StringConcat([a, b]));
19-
const d = new FutureString(new StringConcat([c, "d"]));
16+
const a = new Future<string>(new Trace([], new FooNode({})));
17+
const b = new Future<string>(new Trace([], new FooNode({})));
18+
const c = new Future<string>(new StringConcat([a, b]));
19+
const d = new Future<string>(new StringConcat([c, "d"]));
2020
const n = new FooNode({ prompt: d });
2121

2222
expect(n.toJSON()).toEqual({
@@ -31,13 +31,13 @@ describe("Node", () => {
3131
});
3232

3333
test(".references", () => {
34-
const a = new FooNode({ x: "x" }, { id: "a" });
35-
const f1 = a.future.get("x");
36-
const f2 = a.future.get("y");
37-
const b = new FooNode({ x: f1, z: f2 }, { id: "b" });
38-
const f3 = b.future.get("x");
39-
const c = new FooNode({ x: f3 }, { id: "c" });
40-
const d = new FooNode({}, { id: "d", depends: [c] });
34+
const a = new FooNode({ x: "x" });
35+
const f1 = get(a.future, "x");
36+
const f2 = get(a.future, "y");
37+
const b = new FooNode({ x: f1, z: f2 });
38+
const f3 = get(b.future, "x");
39+
const c = new FooNode({ x: f3 });
40+
const d = new FooNode({}, { depends: [c] });
4141

4242
// @ts-ignore (protected)
4343
const { nodes, futures } = d.references();

tests/Substrate.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ describe("Substrate", () => {
1010
test("when there are nodes and futures", () => {
1111
const a = new FooNode({ a: 123 }, { id: "a" });
1212
const b = new FooNode(
13-
{ b: a.future.get("x"), c: sb.concat("x", "y") },
13+
{ b: sb.get(a.future, "x"), c: sb.concat("x", "y") },
1414
{ id: "b" },
1515
);
1616

@@ -79,7 +79,7 @@ describe("Substrate", () => {
7979

8080
test("when there are nodes and futures, but we only supply the 'final' node", () => {
8181
const a = new FooNode({ a: 123 });
82-
const b = new FooNode({ b: a.future.get("x"), c: sb.concat("x", "y") });
82+
const b = new FooNode({ b: sb.get(a.future, "x"), c: sb.concat("x", "y") });
8383

8484
// Here we're only supplying `b` and relying on the graph-serialiation to find `a`
8585
const result = Substrate.serialize(b);

0 commit comments

Comments
 (0)