Skip to content

Commit

Permalink
improve(anyware): add concept of overloads (#1261)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt authored Nov 13, 2024
1 parent aad4cac commit 4aea3bf
Show file tree
Hide file tree
Showing 33 changed files with 1,189 additions and 574 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,27 @@ ContextualError: There was an error in the core implementation of hook "exchange
at runPipeline (/some/path/to/runPipeline.ts:XX:XX:18)
at async runPipeline (/some/path/to/runPipeline.ts:XX:XX:14)
at async runPipeline (/some/path/to/runPipeline.ts:XX:XX:14)
... 3 lines matching cause stack trace ...
... 2 lines matching cause stack trace ...
at async Object.send (/some/path/to/gql.ts:XX:XX:26)
at async <anonymous> (/some/path/to/output_preset__standard-graphql.ts:XX:XX:16) {
context: { hookName: 'exchange', source: 'implementation' },
[cause]: TypeError: Failed to parse URL from ...
at new Request (node:internal/deps/undici/undici:XX:XX)
at Object.run (/some/path/to/RequestPipeline.ts:XX:XX:29)
at Object.run (/some/path/to/RequestPipeline.ts:XX:XX:27)
... 6 lines matching cause stack trace ...
at async Object.send (/some/path/to/gql.ts:XX:XX:26)
at async <anonymous> (/some/path/to/output_preset__standard-graphql.ts:XX:XX:16) {
at async Module.run (/some/path/to/run.ts:XX:XX:10)
at async Object.send (/some/path/to/gql.ts:XX:XX:26) {
[cause]: TypeError: Invalid URL
at new URL (node:internal/url:XX:XX)
at new Request (node:internal/deps/undici/undici:XX:XX)
at Object.run (/some/path/to/RequestPipeline.ts:XX:XX:29)
at Object.run (/some/path/to/RequestPipeline.ts:XX:XX:27)
at Object.run (/some/path/to/builder.ts:XX:XX:53)
at runStep (/some/path/to/runStep.ts:XX:XX:37)
at runPipeline (/some/path/to/runPipeline.ts:XX:XX:8)
at runPipeline (/some/path/to/runPipeline.ts:XX:XX:20)
at async runPipeline (/some/path/to/runPipeline.ts:XX:XX:14)
at async <anonymous> (/some/path/to/runner.ts:XX:XX:20)
at async Module.run (/some/path/to/run.ts:XX:XX:10)
at async Object.send (/some/path/to/gql.ts:XX:XX:26) {
at async Module.run (/some/path/to/run.ts:XX:XX:10) {
code: 'ERR_INVALID_URL',
input: '...'
}
Expand Down
6 changes: 3 additions & 3 deletions src/client/builderExtensions/anyware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createExtension } from '../../extension/extension.js'
import type { Anyware as AnywareLib } from '../../lib/anyware/__.js'
import { Builder } from '../../lib/builder/__.js'
import type { requestPipeline } from '../../requestPipeline/__.js'
import type { RequestPipeline } from '../../requestPipeline/__.js'
import { type Context } from '../context.js'

export interface BuilderExtensionAnyware extends Builder.Extension {
Expand All @@ -16,14 +16,14 @@ export interface Anyware<$Arguments extends Builder.Extension.Parameters<Builder
*/
anyware: (
interceptor: AnywareLib.Interceptor.InferConstructor<
requestPipeline.Spec
RequestPipeline['spec']
>,
) => Builder.Definition.MaterializeWithNewContext<$Arguments['chain'], $Arguments['context']>
}

export const builderExtensionAnyware = Builder.Extension.create<BuilderExtensionAnyware>((builder, context) => {
const properties = {
anyware: (interceptor: AnywareLib.Interceptor.InferConstructor<requestPipeline.Spec>) => {
anyware: (interceptor: AnywareLib.Interceptor.InferConstructor<RequestPipeline['spec']>) => {
return builder({
...context,
extensions: [
Expand Down
5 changes: 2 additions & 3 deletions src/client/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
joinTemplateStringArrayAndArgs,
type TemplateStringsArguments,
} from '../../lib/template-string.js'
import { requestPipeline } from '../../requestPipeline/__.js' // todo
import { type RequestPipeline, requestPipeline } from '../../requestPipeline/__.js' // todo
import { type Context } from '../context.js'
import { handleOutput } from '../handleOutput.js'
import { type DocumentController, resolveSendArguments, type sendArgumentsImplementation } from './send.js'
Expand Down Expand Up @@ -69,9 +69,8 @@ export const builderExtensionGql = Builder.Extension.create<BuilderExtensionGql>
state: context,
url,
schema,
// request,
request: analyzedRequest,
} as requestPipeline.Steps.HookDefEncode['input']
} as RequestPipeline['spec']['input']

const result = await Anyware.Pipeline.run(requestPipeline, {
initialInput,
Expand Down
4 changes: 2 additions & 2 deletions src/client/handleOutput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
type GetOrNever,
type Values,
} from '../lib/prelude.js'
import type { requestPipeline } from '../requestPipeline/RequestPipeline.js'
import type { RequestPipeline, requestPipeline } from '../requestPipeline/RequestPipeline.js'
import type { GlobalRegistry } from '../types/GlobalRegistry/GlobalRegistry.js'
import type { TransportHttp } from '../types/Transport.js'
import type { Context } from './context.js'
Expand Down Expand Up @@ -50,7 +50,7 @@ export type GraffleExecutionResultEnvelope<$Config extends Config = Config> =

export const handleOutput = (
state: Context,
result: requestPipeline.Result,
result: RequestPipeline['output'],
) => {
if (isContextConfigTraditionalGraphQLOutput(state.config)) {
if (result instanceof Error) throw result
Expand Down
4 changes: 2 additions & 2 deletions src/documentBuilder/requestMethods/requestMethods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Builder } from '../../lib/builder/__.js'
import type { Grafaid } from '../../lib/grafaid/__.js'
import { getOperationDefinition } from '../../lib/grafaid/document.js'
import { isSymbol } from '../../lib/prelude.js'
import { requestPipeline } from '../../requestPipeline/__.js'
import { type RequestPipeline, requestPipeline } from '../../requestPipeline/__.js'
import type { GlobalRegistry } from '../../types/GlobalRegistry/GlobalRegistry.js'
import { Select } from '../Select/__.js'
import { SelectionSetGraphqlMapper } from '../SelectGraphQLMapper/__.js'
Expand Down Expand Up @@ -135,7 +135,7 @@ const executeDocument = async (
url,
schema,
request,
} as requestPipeline.Steps.HookDefEncode['input']
} as RequestPipeline['input']

const result = await Anyware.Pipeline.run(requestPipeline, {
initialInput,
Expand Down
6 changes: 3 additions & 3 deletions src/extension/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { Builder } from '../lib/builder/__.js'
import type { AssertExtends } from '../lib/prelude.js'
import type { TypeFunction } from '../lib/type-function/__.js'
import type { Fn } from '../lib/type-function/TypeFunction.js'
import type { requestPipeline } from '../requestPipeline/__.js'
import type { RequestPipeline } from '../requestPipeline/__.js'
import type { GlobalRegistry } from '../types/GlobalRegistry/GlobalRegistry.js'

export interface TypeHooks {
Expand Down Expand Up @@ -57,7 +57,7 @@ export interface Extension<
/**
* Anyware executed on every request.
*/
onRequest?: Anyware.Interceptor.InferConstructor<requestPipeline.Spec>
onRequest?: Anyware.Interceptor.InferConstructor<RequestPipeline['spec']>
/**
* Manipulate the builder.
* You can extend the builder with new properties at both runtime AND buildtime (types, TypeScript).
Expand Down Expand Up @@ -169,7 +169,7 @@ export const createExtension = <
custom?: $Custom
create: (params: { config: $Config }) => {
builder?: $BuilderExtension
onRequest?: Anyware.Interceptor.InferConstructor<requestPipeline.Spec>
onRequest?: Anyware.Interceptor.InferConstructor<RequestPipeline['spec']>
typeHooks?: () => $TypeHooks
}
},
Expand Down
7 changes: 3 additions & 4 deletions src/lib/anyware/ExecutableStep.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { Step } from './Step.js'

export interface ExecutableStep extends Step {
run: (params: any) => any
export interface ExecutableStep extends Omit<Step, 'slots' | 'input' | 'output'> {
slots?: Step.Slots
run: Step.Runner<any, any, any>
}

export interface ExecutableStepRuntime extends Omit<ExecutableStep, 'input' | 'output'> {}
11 changes: 7 additions & 4 deletions src/lib/anyware/Interceptor/Interceptor.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe(`interceptor constructor`, () => {
.step({ name: `b`, run: () => results.b })
.step({ name: `c`, run: () => results.c })
.done()
type i1 = Interceptor.InferConstructor<typeof p1>
type i1 = Interceptor.InferConstructor<typeof p1['spec']>
expectTypeOf<Parameters<i1>>().toMatchTypeOf<[steps: { a: any; b: any; c: any }]>()
expectTypeOf<Parameters<i1>>().toMatchTypeOf<[steps: {
a: (params: { input?: initialInput }) => Promise<{ b: (params: { input?: results['a'] }) => any }>
Expand Down Expand Up @@ -91,7 +91,10 @@ describe(`interceptor constructor`, () => {
// --- Helpers ---

// dprint-ignore
// @ts-expect-error
type GetTriggerFromPipeline<$Pipeline extends Pipeline.PipelineExecutable, $TriggerName extends string> = Parameters<Interceptor.InferConstructor<$Pipeline>>[0][$TriggerName]
type GetTriggerFromPipeline<$Pipeline extends Pipeline.ExecutablePipeline, $TriggerName extends string> =
// @ts-expect-error
Parameters<Interceptor.InferConstructor<$Pipeline['spec']>>[0][$TriggerName]

// dprint-ignore
type GetReturnTypeFromPipeline<$Pipeline extends Pipeline.PipelineExecutable> = ReturnType<Interceptor.InferConstructor<$Pipeline>>
type GetReturnTypeFromPipeline<$Pipeline extends Pipeline.ExecutablePipeline> =
ReturnType<Interceptor.InferConstructor<$Pipeline['spec']>>
8 changes: 3 additions & 5 deletions src/lib/anyware/Interceptor/Interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ export namespace Interceptor {
$PipelineSpec extends PipelineSpec = PipelineSpec,
> // $Options extends InterceptorOptions = InterceptorOptions,
{
(
steps: Simplify<InferConstructorKeywordArguments<$PipelineSpec>>,
): Promise<
(steps: Simplify<InferConstructorKeywordArguments<$PipelineSpec>>): Promise<
| $PipelineSpec['output']
| StepTriggerEnvelope
>
Expand All @@ -42,15 +40,15 @@ export namespace Interceptor {

export type InterceptorGeneric = NonRetryingInterceptor | RetryingInterceptor

export type NonRetryingInterceptor = {
export interface NonRetryingInterceptor {
retrying: false
name: string
entrypoint: string
body: Deferred<unknown>
currentChunk: Deferred<StepTriggerEnvelope | ResultSuccess>
}

export type RetryingInterceptor = {
export interface RetryingInterceptor {
retrying: true
name: string
entrypoint: string
Expand Down
14 changes: 0 additions & 14 deletions src/lib/anyware/Pipeline/Executable.ts

This file was deleted.

17 changes: 17 additions & 0 deletions src/lib/anyware/Pipeline/ExecutablePipeline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { ExecutableStep } from '../ExecutableStep.js'
import type { Step } from '../Step.js'
import type { Config } from './Config.js'
import type { Result } from './Result.js'
import type { PipelineSpec } from './Spec.js'

export interface ExecutablePipeline {
spec: PipelineSpec
config: Config
input: object
output: Result
steps: ExecutableStep[]
stepsIndex: StepsIndex
}

export type StepsIndex<$Name extends Step.Name = Step.Name, $ExecutableStep extends ExecutableStep = ExecutableStep> =
Map<$Name, $ExecutableStep>
10 changes: 5 additions & 5 deletions src/lib/anyware/Pipeline/Spec.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ assertEqual<

assertEqual<
PipelineSpecFromSteps<[{ name: 'a' }]>,
{ steps: [{ name: 'a'; slots: undefined; input: object; output: unknown }]; input: object; output: unknown }
{ steps: [{ name: 'a'; slots: {}; input: object; output: unknown }]; input: object; output: unknown }
>()

assertEqual<
PipelineSpecFromSteps<[{ name: 'a'; output: 1 }]>,
{
steps: [{ name: 'a'; slots: undefined; input: object; output: MaybePromise<1> }]
steps: [{ name: 'a'; slots: {}; input: object; output: MaybePromise<1> }]
input: object
output: 1
// ^^^^^^ pipeline output inferred from last step output
Expand All @@ -26,9 +26,9 @@ assertEqual<
PipelineSpecFromSteps<[{ name: 'a' }, { name: 'b'; input: { x: 1 } }]>,
{
steps: [
{ name: 'a'; slots: undefined; input: object; output: MaybePromise<{ x: 1 }> },
{ name: 'a'; slots: {}; input: object; output: MaybePromise<{ x: 1 }> },
// step output inferred from next step input ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{ name: 'b'; slots: undefined; input: { x: 1 }; output: unknown },
{ name: 'b'; slots: {}; input: { x: 1 }; output: unknown },
]
input: object
output: unknown
Expand All @@ -39,7 +39,7 @@ assertEqual<
PipelineSpecFromSteps<[{ name: 'a'; output: Promise<1> }]>,
// ^^^^^^^^^^^^^^^^^^ Step promised output flattened within MaybePromise
{
steps: [{ name: 'a'; slots: undefined; input: object; output: MaybePromise<1> }]
steps: [{ name: 'a'; slots: {}; input: object; output: MaybePromise<1> }]
// ^^^^^^^^^^^^^^^
input: object
output: 1
Expand Down
3 changes: 2 additions & 1 deletion src/lib/anyware/Pipeline/Spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { IsUnknown } from 'type-fest'
import type { ConfigManager } from '../../config-manager/__.js'
import type { MaybePromise, Tuple } from '../../prelude.js'
import type { Step } from '../Step.js'

Expand All @@ -25,7 +26,7 @@ type InferStepSpecs_<$StepSpecPrevious extends Step| undefined, $StepSpecInputs
$StepSpecInputs extends [infer $StepSpecInput extends Step.SpecInput, ...infer $StepSpecInputsRest extends Step.SpecInput[]]
? InferStepSpecs__<{
name: $StepSpecInput['name']
slots: IsUnknown<$StepSpecInput['slots']> extends true ? undefined : $StepSpecInput['slots']
slots: ConfigManager.OrDefault2<$StepSpecInput['slots'],{}>
input: IsUnknown<$StepSpecInput['input']> extends true
? $StepSpecPrevious extends Step
? $StepSpecPrevious['output']
Expand Down
2 changes: 1 addition & 1 deletion src/lib/anyware/Pipeline/_.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export * from '../run/run.js'
export * from './builder.js'
export * from './createWithSpec.js'
export * from './Executable.js'
export * from './ExecutablePipeline.js'
export * from './Result.js'
export * from './Spec.js'
Loading

0 comments on commit 4aea3bf

Please sign in to comment.