@code0-tech/triangulum 0.37.2 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import { default as ts } from 'typescript';
2
- import { FunctionDefinition, SubFlowValue } from '@code0-tech/sagittarius-graphql-types';
2
+ import { FunctionDefinition } from '@code0-tech/sagittarius-graphql-types';
3
+ import { SuggestionCandidate } from './suggestion.util';
3
4
  /**
4
5
  * Extracts and creates a list of SubFlowValue objects from function declarations
5
6
  * that are compatible with the given parameter type.
@@ -13,7 +14,8 @@ import { FunctionDefinition, SubFlowValue } from '@code0-tech/sagittarius-graphq
13
14
  * @param {FunctionDefinition[]} functions - Array of function definitions for reference lookup
14
15
  * @param {ts.Type} paramType - The target parameter type that functions must be compatible with
15
16
  *
16
- * @returns {SubFlowValue[]} An array of SubFlowValue objects for compatible functions,
17
- * or an empty array if the paramType is not a sub-flow type
17
+ * @returns {SuggestionCandidate[]} An array of sub-flow candidates for compatible
18
+ * functions, each carrying how certainly it satisfies the slot, or an
19
+ * empty array if the paramType is not a sub-flow type
18
20
  */
19
- export declare const getSubFlows: (checker: ts.TypeChecker, functionDeclarations: ts.FunctionDeclaration[], functions: FunctionDefinition[], paramType: ts.Type) => SubFlowValue[];
21
+ export declare const getSubFlows: (checker: ts.TypeChecker, functionDeclarations: ts.FunctionDeclaration[], functions: FunctionDefinition[], paramType: ts.Type) => SuggestionCandidate[];
@@ -0,0 +1,161 @@
1
+ import { default as ts } from 'typescript';
2
+ import { LiteralValue, NodeFunction, ReferenceValue, SubFlowValue } from '@code0-tech/sagittarius-graphql-types';
3
+ /**
4
+ * A value a slot can be filled with: a literal option of the slot's own type, a
5
+ * reference to something already in scope, a function node whose result is fed
6
+ * into the slot, or a function bound as a sub-flow.
7
+ */
8
+ export type SuggestionValue = NodeFunction | ReferenceValue | LiteralValue | SubFlowValue;
9
+ /**
10
+ * How certain it is that a suggestion actually produces the slot's type.
11
+ *
12
+ * - `exact`: the candidate's own type is assignable to the slot's type. Picking
13
+ * it always yields a value the slot accepts.
14
+ * - `possible`: the match was only reached by weakening one of the two sides —
15
+ * the candidate *can* produce a fitting value, but is not guaranteed to. See
16
+ * {@link SuggestionMatchReason} for what was weakened.
17
+ *
18
+ * Both kinds are offered: a `possible` candidate is frequently the one the user
19
+ * wants (a generic `std::object::get`, a reference into an optional field). The
20
+ * distinction exists so a consumer can tell "this slot has dedicated producers"
21
+ * from "everything happens to fit here", and because a `possible` pick is the
22
+ * case that may need a cast to type-check.
23
+ */
24
+ export type SuggestionMatch = "exact" | "possible";
25
+ /**
26
+ * Why a suggestion matched only possibly.
27
+ *
28
+ * - `unconstrained`: one of the two sides constrains nothing (`any`, `unknown`,
29
+ * `{}`) — the candidate fits every slot, or the slot is fit by every candidate.
30
+ * Either way the match says nothing about this pairing.
31
+ * - `generic`: the candidate's type is a type parameter and the match was made
32
+ * against its constraint. A concrete instantiation may be narrower.
33
+ * - `union-member`: the candidate is a union and only some of its members fit.
34
+ * Which member materializes is a runtime decision.
35
+ * - `nullable`: the candidate may be `null`/`undefined` where the slot is not.
36
+ *
37
+ * Listed weakest first: a candidate weakened on several axes reports the first
38
+ * of these that applies (see {@link weakestCertainty}).
39
+ */
40
+ export type SuggestionMatchReason = "unconstrained" | "generic" | "union-member" | "nullable";
41
+ /**
42
+ * The certainty of a single suggestion. `reason` is present exactly when
43
+ * `match` is `possible`.
44
+ */
45
+ export interface SuggestionCertainty {
46
+ match: SuggestionMatch;
47
+ reason?: SuggestionMatchReason;
48
+ }
49
+ /**
50
+ * A suggestion together with its certainty — the form suggestions travel in
51
+ * while a schema is being built.
52
+ *
53
+ * On the finished schema the two halves are split into the index-aligned
54
+ * `suggestions` / `suggestionCertainty` pair (see {@link Input}) so the
55
+ * suggestion values stay plain GraphQL values a consumer can use as-is. Every
56
+ * step in between passes candidates around instead of the two arrays, so the
57
+ * alignment cannot drift.
58
+ */
59
+ export interface SuggestionCandidate {
60
+ value: SuggestionValue;
61
+ certainty: SuggestionCertainty;
62
+ }
63
+ /** The certainty of a candidate that is guaranteed to fit its slot. */
64
+ export declare const EXACT_MATCH: SuggestionCertainty;
65
+ /** The certainty of a candidate that fits only under the given condition. */
66
+ export declare const possibleMatch: (reason: SuggestionMatchReason) => SuggestionCertainty;
67
+ /** Pairs each value with {@link EXACT_MATCH}, for sources that cannot be weakened. */
68
+ export declare const exactCandidates: (values: SuggestionValue[]) => SuggestionCandidate[];
69
+ /**
70
+ * Picks the stronger of two certainties for the same value — `exact` wins, and
71
+ * among two `possible` ones the first is kept. Used when the same suggestion is
72
+ * collected twice against differently scoped types (see the merge in
73
+ * getSignatureSchema): the slot accepts it for the better of the two reasons.
74
+ */
75
+ export declare const strongerCertainty: (a: SuggestionCertainty, b: SuggestionCertainty) => SuggestionCertainty;
76
+ /**
77
+ * Picks the weakest of the given certainties: a candidate weakened on more than
78
+ * one axis (e.g. a generic return that also turns out to be `any`) is only as
79
+ * strong as its weakest link. `exact` is the result only when nothing was
80
+ * weakened at all.
81
+ */
82
+ export declare const weakestCertainty: (...certainties: SuggestionCertainty[]) => SuggestionCertainty;
83
+ /**
84
+ * True for a type that constrains nothing, so an assignability test against it
85
+ * is vacuous: `any` and `unknown`, the empty object type — `{}`, which every
86
+ * non-nullish value satisfies and which is what a generic `OBJECT<T>` or an
87
+ * uninstantiated payload bound resolves to — and an object whose only structure
88
+ * is an index signature that says nothing about its values either. That last
89
+ * case is what an `OBJECT<T>` instantiated without an argument resolves to:
90
+ * `{[key: string]: any}`, a slot every object fits. A signature with a real
91
+ * value type (`Record<string, NUMBER>`) does constrain, and keeps its slot
92
+ * concrete.
93
+ *
94
+ * It matters on both sides of a match. A candidate of this type fits every slot,
95
+ * and a *slot* of this type is fitted by every candidate — the second case is
96
+ * the one that would otherwise report a hundred exact "producers" for a slot
97
+ * that simply accepts anything.
98
+ *
99
+ * A callable, an array and anything with properties is excluded: those
100
+ * constrain their values. An index signature is inspected only `depth` levels
101
+ * deep (one, from the outside) because an index type may refer back to its own
102
+ * container (`type Json = {[key: string]: Json}`).
103
+ */
104
+ export declare const isUnconstrainedType: (type: ts.Type | undefined, depth?: number) => boolean;
105
+ /**
106
+ * Decides whether a candidate type fits an expected (slot) type, and how
107
+ * certainly — the shared rule behind every assignability-based suggestion
108
+ * source.
109
+ *
110
+ * Returns `null` when the candidate does not fit at all. Otherwise the match is
111
+ * `exact`, or `possible` with the reason the fit is not guaranteed:
112
+ *
113
+ * - either side constrains nothing (`any`, `unknown`, `{}`) → `unconstrained`.
114
+ * For the candidate that means it fits everywhere; for the slot that it is
115
+ * fitted by everything. Neither tells the consumer that this candidate is a
116
+ * producer *of this type*, which is the whole point of the distinction. This
117
+ * only *weakens* the verdict — whether the candidate fits at all is still
118
+ * decided by assignability, so an open candidate is not waved through into a
119
+ * slot that would reject it;
120
+ * - the candidate is a union and only some members fit → `union-member`. A
121
+ * union is only assignable as a whole when *every* member is, so the partial
122
+ * case is exactly the one strict assignability would reject while the slot can
123
+ * still be served at runtime;
124
+ * - the candidate may be nullish where the slot is not → `nullable`. The nullish
125
+ * part is deliberately ignored for matching (a `string | null` producer is a
126
+ * useful suggestion for a `string` slot), which is what makes the distinction
127
+ * worth reporting.
128
+ *
129
+ * A purely nullish candidate strips down to `never`, which is assignable to
130
+ * anything, and is therefore rejected explicitly.
131
+ */
132
+ export declare const typeMatchCertainty: (type: ts.Type, checker: ts.TypeChecker, expectedType: ts.Type) => SuggestionCertainty | null;
133
+ /**
134
+ * Reads a schema's suggestions back as candidates. A schema that carries
135
+ * suggestions without certainties (produced before the pair existed, or hand
136
+ * written) is read as fully exact rather than rejected.
137
+ */
138
+ export declare const suggestionCandidates: (schema: {
139
+ suggestions?: SuggestionValue[];
140
+ suggestionCertainty?: SuggestionCertainty[];
141
+ }) => SuggestionCandidate[];
142
+ /**
143
+ * Splits candidates into the schema's index-aligned pair of keys, both keys
144
+ * always present — the convention of a freshly collected schema, which carries
145
+ * the keys even when nothing matched (an empty array marks "this is an input
146
+ * slot and these are all its candidates", which normalizeNodeSchema then
147
+ * collapses). Use {@link suggestionFields} everywhere else.
148
+ */
149
+ export declare const suggestionPair: (candidates: SuggestionCandidate[]) => {
150
+ suggestions: SuggestionValue[];
151
+ suggestionCertainty: SuggestionCertainty[];
152
+ };
153
+ /**
154
+ * Splits candidates back into the schema's index-aligned pair of keys. Both keys
155
+ * are omitted for an empty set, matching the convention that a schema without
156
+ * suggestions carries neither key.
157
+ */
158
+ export declare const suggestionFields: (candidates: SuggestionCandidate[] | undefined) => {
159
+ suggestions?: SuggestionValue[];
160
+ suggestionCertainty?: SuggestionCertainty[];
161
+ };