Skip to content

Commit bd55f81

Browse files
authored
improve(anyware): extensions (#1264)
This PR brings extensions to anyware. The HTTP and Memory transports are moved to being anyware extensions now. The next step is to lift them one level up into Graffle extensions. This will require the ability to statically apply anyware extensions (should be fairly simple).
1 parent ce671dd commit bd55f81

File tree

17 files changed

+462
-335
lines changed

17 files changed

+462
-335
lines changed

examples/__outputs__/20_output/output_preset__standard-graphql.output.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ ContextualError: There was an error in the core implementation of hook "exchange
1313
context: { hookName: 'exchange', source: 'implementation' },
1414
[cause]: TypeError: Failed to parse URL from ...
1515
at new Request (node:internal/deps/undici/undici:XX:XX)
16-
at Object.run (/some/path/to/RequestPipeline.ts:XX:XX:27)
16+
at Object.run (/some/path/to/httpTransport.ts:XX:XX:27)
1717
... 6 lines matching cause stack trace ...
1818
at async Module.run (/some/path/to/run.ts:XX:XX:10)
1919
at async Object.send (/some/path/to/gql.ts:XX:XX:26) {
2020
[cause]: TypeError: Invalid URL
2121
at new URL (node:internal/url:XX:XX)
2222
at new Request (node:internal/deps/undici/undici:XX:XX)
23-
at Object.run (/some/path/to/RequestPipeline.ts:XX:XX:27)
23+
at Object.run (/some/path/to/httpTransport.ts:XX:XX:27)
2424
at Object.run (/some/path/to/builder.ts:XX:XX:53)
2525
at runStep (/some/path/to/runStep.ts:XX:XX:37)
2626
at runPipeline (/some/path/to/runPipeline.ts:XX:XX:8)

src/lib/anyware/Extension/Builder.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { ConfigManager } from '../../config-manager/__.js'
2+
import type { Overload } from '../Overload/__.js'
3+
import type { Pipeline } from '../Pipeline/__.js'
4+
5+
export interface Builder<
6+
$PipelineContext extends Pipeline.Context = Pipeline.Context,
7+
$Context extends Context = Context,
8+
> {
9+
context: $Context
10+
/**
11+
* TODO
12+
*/
13+
overload: <$OverloadBuilder extends Overload.Builder<$PipelineContext>>(
14+
overloadBuilder: Overload.BuilderCallback<$PipelineContext, $OverloadBuilder>,
15+
) => Builder<
16+
$PipelineContext,
17+
ConfigManager.AppendAtKey<
18+
$Context,
19+
'overloads',
20+
$OverloadBuilder['context']
21+
>
22+
>
23+
}
24+
25+
export interface Context {
26+
overloads: Overload.BuilderContext[]
27+
}
28+
29+
export interface ContextEmpty extends Context {
30+
overloads: []
31+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { __ } from '../../prelude.js'
2+
import { Overload } from '../Overload/__.js'
3+
import type { Pipeline } from '../Pipeline/__.js'
4+
import type { Builder, Context, ContextEmpty } from './Builder.js'
5+
6+
export * from './Builder.js'
7+
8+
export const create: Create = () => {
9+
const context: Context = {
10+
overloads: [],
11+
}
12+
13+
const builder: Builder = {
14+
context,
15+
overload: (builderCallback) => {
16+
const overload = builderCallback({ create: Overload.create })
17+
context.overloads.push(overload.context)
18+
return builder as any
19+
},
20+
}
21+
22+
return builder as any
23+
}
24+
25+
type Create = <$PipelineContext extends Pipeline.Context>() => Builder<$PipelineContext, ContextEmpty>

src/lib/anyware/Extension/__.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * as Extension from './Extension.js'

src/lib/anyware/Overload/Builder.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import type { ConfigManager } from '../../config-manager/__.js'
2+
import type { Tuple } from '../../prelude.js'
3+
import type { Pipeline } from '../Pipeline/__.js'
4+
import type { Step } from '../Step.js'
5+
6+
export interface Builder<
7+
$RootContext extends Pipeline.Context = Pipeline.Context,
8+
$Context extends BuilderContext = BuilderContextEmpty,
9+
> {
10+
context: $Context
11+
/**
12+
* TODO
13+
*/
14+
step: BuilderStep<$RootContext, $Context>
15+
/**
16+
* TODO
17+
*/
18+
extendInput: <$InputExtension extends object>() => Builder<
19+
$RootContext,
20+
ConfigManager.UpdateOneKey<$Context, 'input', $InputExtension>
21+
>
22+
/**
23+
* TODO
24+
*/
25+
stepWithExtendedInput: <$InputExtension extends object>() => BuilderStep<
26+
$RootContext,
27+
$Context,
28+
$InputExtension
29+
>
30+
}
31+
32+
interface BuilderStep<
33+
$RootContext extends Pipeline.Context,
34+
$Context extends BuilderContext,
35+
$InputExtension extends object = {},
36+
> {
37+
<
38+
$Name extends $RootContext['steps'][number]['name'],
39+
$Slots extends undefined | Step.Slots = undefined,
40+
$Input =
41+
& InferStepInput<
42+
$Context,
43+
Extract<$RootContext['steps'][number], { name: $Name }>,
44+
Tuple.PreviousItem<$RootContext['steps'], { name: $Name }>
45+
>
46+
& $InputExtension,
47+
$Output = unknown,
48+
>(
49+
name: $Name,
50+
spec: {
51+
slots?: $Slots
52+
run: (input: $Input, slots: $Slots) => $Output
53+
},
54+
): Builder<
55+
$RootContext,
56+
ConfigManager.UpdateOneKey<
57+
$Context,
58+
'steps',
59+
& $Context['steps']
60+
& {
61+
[_ in $Name]: {
62+
name: $Name
63+
input: $Input
64+
output: Awaited<$Output>
65+
slots: ConfigManager.OrDefault2<$Slots, {}>
66+
}
67+
}
68+
>
69+
>
70+
}
71+
72+
// dprint-ignore
73+
type InferStepInput<
74+
$OverloadContext extends BuilderContext,
75+
$CurrentStep extends Step,
76+
$PreviousStep extends Step | undefined,
77+
> =
78+
$PreviousStep extends Step
79+
? $PreviousStep['name'] extends keyof $OverloadContext['steps']
80+
? $OverloadContext['steps'][$PreviousStep['name']]['output']
81+
:
82+
& $CurrentStep['input']
83+
& $OverloadContext['input']
84+
& { [_ in $OverloadContext['discriminant'][0]]: $OverloadContext['discriminant'][1] }
85+
:
86+
& $CurrentStep['input']
87+
& $OverloadContext['input']
88+
& { [_ in $OverloadContext['discriminant'][0]]: $OverloadContext['discriminant'][1] }
89+
90+
export interface BuilderContext {
91+
discriminant: DiscriminantSpec
92+
input: object
93+
steps: Record<string, Step>
94+
}
95+
96+
export type DiscriminantSpec = readonly [string, DiscriminantPropertyValue]
97+
98+
export type DiscriminantPropertyValue = string | number | symbol
99+
100+
interface BuilderContextEmpty extends BuilderContext {
101+
input: {}
102+
steps: {}
103+
}

src/lib/anyware/Overload/Overload.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { Pipeline } from '../Pipeline/__.js'
2+
import type { Step } from '../Step.js'
3+
import type { Overload } from './__.js'
4+
import type { Builder, BuilderContext, DiscriminantSpec } from './Builder.js'
5+
6+
export * from './Builder.js'
7+
8+
export const create: Create = (parameters) => {
9+
const context_: Omit<BuilderContext, 'input'> = {
10+
discriminant: parameters.discriminant,
11+
steps: {},
12+
}
13+
const context = context_ as BuilderContext
14+
15+
const builder: Builder = {
16+
context,
17+
extendInput: () => builder as any,
18+
stepWithExtendedInput: () => builder.step as any,
19+
step: (name, spec) => {
20+
context.steps[name] = {
21+
name,
22+
...spec,
23+
} as unknown as Step
24+
return builder as any
25+
},
26+
}
27+
28+
return builder as any
29+
}
30+
31+
export type Create<$RootContext extends Pipeline.Context = Pipeline.Context> = <
32+
const $DiscriminantSpec extends DiscriminantSpec,
33+
>(
34+
parameters: { discriminant: $DiscriminantSpec },
35+
) => Builder<
36+
$RootContext,
37+
{
38+
discriminant: $DiscriminantSpec
39+
input: {}
40+
steps: {}
41+
}
42+
>
43+
44+
export type BuilderCallback<
45+
$Context extends Pipeline.Context,
46+
$OverloadBuilder extends Builder<$Context>,
47+
> = (
48+
overloadBuilder: { create: Overload.Create<$Context> },
49+
) => $OverloadBuilder

src/lib/anyware/Overload/__.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * as Overload from './Overload.js'

src/lib/anyware/Pipeline/builder.test-d.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,13 @@ describe(`overload`, () => {
126126
[{ discriminant: d; input: {}; steps: {} }]
127127
>()
128128
})
129-
test(`overload constructor with input and discriminant`, () => {
130-
expectTypeOf(b0.overload(o => o.createWithInput<{ x: 1 }>()({ discriminant: d })).context.overloads)
131-
.toMatchTypeOf<[{ discriminant: d; input: { x: 1 }; steps: {} }]>()
132-
})
129+
})
130+
131+
// overload extends input
132+
133+
test(`overload constructor with input and discriminant`, () => {
134+
expectTypeOf(b0.overload(o => o.create({ discriminant: d }).extendInput<{ x: 1 }>()).context.overloads)
135+
.toMatchTypeOf<[{ discriminant: d; input: { x: 1 }; steps: {} }]>()
133136
})
134137

135138
// step
@@ -171,7 +174,7 @@ describe(`overload`, () => {
171174
test(`can extend input type`, () => {
172175
expectTypeOf(
173176
b0.step(`a`).overload(o =>
174-
o.create({ discriminant: d }).stepWithInputExtension<{ ex: 1 }>()(`a`, {
177+
o.create({ discriminant: d }).stepWithExtendedInput<{ ex: 1 }>()(`a`, {
175178
run: (input) => {
176179
expectTypeOf(input).toEqualTypeOf<initialInput & dObject & { ex: 1 }>()
177180
},
@@ -242,11 +245,11 @@ describe(`overload`, () => {
242245

243246
// Overloads Merging Into Pipeline
244247

245-
test(`overload inputs become a pipeline union input`, () => {
248+
test(`overload input extensions become a pipeline union input`, () => {
246249
const p = b0
247250
.step(`a`)
248-
.overload(o => o.createWithInput<{ ol1: 1 }>()({ discriminant: d }))
249-
.overload(o => o.createWithInput<{ ol2: 2 }>()({ discriminant: d2 }))
251+
.overload(o => o.create({ discriminant: d }).extendInput<{ ol1: 1 }>())
252+
.overload(o => o.create({ discriminant: d2 }).extendInput<{ ol2: 2 }>())
250253
.done()
251254
expectTypeOf(p.input).toMatchTypeOf<
252255
| (initialInput & dObject & { ol1: 1 })

0 commit comments

Comments
 (0)