carrick 0.3.72 → 0.3.73
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.
- package/package.json +6 -6
- package/sidecar/dist/src/capture/anchors.d.ts +15 -0
- package/sidecar/dist/src/capture/anchors.js +70 -5
- package/sidecar/dist/src/capture/api.d.ts +39 -4
- package/sidecar/dist/src/capture/check-classify.d.ts +2 -0
- package/sidecar/dist/src/capture/check-classify.js +28 -0
- package/sidecar/dist/src/capture/check-deep.js +1 -1
- package/sidecar/dist/src/capture/check-probe.d.ts +1 -1
- package/sidecar/dist/src/capture/check-probe.js +16 -0
- package/sidecar/dist/src/capture/deep-walk.d.ts +33 -6
- package/sidecar/dist/src/capture/deep-walk.js +81 -28
- package/sidecar/dist/src/capture/index.js +16 -6
- package/sidecar/dist/src/capture/installed-package.d.ts +58 -0
- package/sidecar/dist/src/capture/installed-package.js +311 -0
- package/sidecar/dist/src/capture/lockfile.d.ts +8 -0
- package/sidecar/dist/src/capture/lockfile.js +1 -1
- package/sidecar/dist/src/capture/node-builder.d.ts +27 -0
- package/sidecar/dist/src/capture/node-builder.js +107 -1
- package/sidecar/dist/src/capture/paths-rewrite.d.ts +23 -6
- package/sidecar/dist/src/capture/paths-rewrite.js +43 -11
- package/sidecar/dist/src/capture/self-check.js +12 -3
- package/sidecar/dist/src/capture/unresolved.d.ts +28 -0
- package/sidecar/dist/src/capture/unresolved.js +107 -0
- package/sidecar/dist/src/type-inferrer.d.ts +108 -11
- package/sidecar/dist/src/type-inferrer.js +513 -90
- package/sidecar/dist/src/type-structural-expander.d.ts +23 -2
- package/sidecar/dist/src/type-structural-expander.js +55 -17
- package/sidecar/dist/src/type-text-canonicalizer.d.ts +14 -0
- package/sidecar/dist/src/type-text-canonicalizer.js +23 -2
- package/sidecar/dist/src/validators.d.ts +10 -0
- package/sidecar/dist/src/validators.js +1 -0
|
@@ -62,6 +62,17 @@ export interface MemberOverrides {
|
|
|
62
62
|
readonly applied: Set<string>;
|
|
63
63
|
readonly at: Node;
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* The representation a type is printed in.
|
|
67
|
+
*
|
|
68
|
+
* `'declared'` prints the type the source declares. `'json'` prints what a
|
|
69
|
+
* JSON serialiser puts on the wire for a value of that type (carrick#1163):
|
|
70
|
+
* `JSON.stringify` calls a value's `toJSON()` and serialises its RESULT, so a
|
|
71
|
+
* `Date` travels as the string its `toJSON` returns, and so does any other
|
|
72
|
+
* type that declares one. The mapping is read from the compiler's own
|
|
73
|
+
* signature, never from a list of type names.
|
|
74
|
+
*/
|
|
75
|
+
export type WireFormat = 'declared' | 'json';
|
|
65
76
|
/**
|
|
66
77
|
* Recursively render a `Type` as fully-inlined structural text.
|
|
67
78
|
*
|
|
@@ -70,9 +81,19 @@ export interface MemberOverrides {
|
|
|
70
81
|
* functions stay by name. The `seen` set (object type ids on the current
|
|
71
82
|
* branch) breaks reference cycles; `depth` is a hard backstop. `overrides`
|
|
72
83
|
* substitutes a type at named member positions (`MemberOverrides`); without
|
|
73
|
-
* it the print is unchanged.
|
|
84
|
+
* it the print is unchanged. `wire` picks the representation (`WireFormat`).
|
|
85
|
+
*/
|
|
86
|
+
export declare function expandTypeStructural(type: Type, seen?: Set<number>, depth?: number, overrides?: MemberOverrides, wire?: WireFormat): string;
|
|
87
|
+
/**
|
|
88
|
+
* The type `JSON.stringify` serialises in place of a value of `type`: the
|
|
89
|
+
* return type of the value's own `toJSON()`, or `undefined` when it declares
|
|
90
|
+
* none (carrick#1163).
|
|
91
|
+
*
|
|
92
|
+
* Only a callable `toJSON` member counts, and only a return type that says
|
|
93
|
+
* something: an `any` return describes nothing, and a `toJSON` that returns
|
|
94
|
+
* its own type again maps nothing either.
|
|
74
95
|
*/
|
|
75
|
-
export declare function
|
|
96
|
+
export declare function jsonWireType(type: Type): Type | undefined;
|
|
76
97
|
/**
|
|
77
98
|
* Non-expanded text for a type. Passes `undefined` as the enclosing node so
|
|
78
99
|
* the compiler can't throw on an invalid node context (tuples and some
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* structural form rather than a dangling name.
|
|
30
30
|
*/
|
|
31
31
|
import { ts } from 'ts-morph';
|
|
32
|
-
import { canonicalizeUnionsInText } from './type-text-canonicalizer.js';
|
|
32
|
+
import { canonicalizeUnionsInText, foldBooleanLiterals, } from './type-text-canonicalizer.js';
|
|
33
33
|
/**
|
|
34
34
|
* Bound on the structural-expansion recursion. Deep enough for every realistic
|
|
35
35
|
* request/response shape; a backstop against pathological/recursive types the
|
|
@@ -65,12 +65,43 @@ function childCursor(cursor, segment) {
|
|
|
65
65
|
* functions stay by name. The `seen` set (object type ids on the current
|
|
66
66
|
* branch) breaks reference cycles; `depth` is a hard backstop. `overrides`
|
|
67
67
|
* substitutes a type at named member positions (`MemberOverrides`); without
|
|
68
|
-
* it the print is unchanged.
|
|
68
|
+
* it the print is unchanged. `wire` picks the representation (`WireFormat`).
|
|
69
69
|
*/
|
|
70
|
-
export function expandTypeStructural(type, seen = new Set(), depth = 0, overrides) {
|
|
71
|
-
return expandAt(type, seen, depth, overrides ? { overrides, position: '' } : undefined);
|
|
70
|
+
export function expandTypeStructural(type, seen = new Set(), depth = 0, overrides, wire = 'declared') {
|
|
71
|
+
return expandAt(type, seen, depth, overrides ? { overrides, position: '' } : undefined, wire);
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
/**
|
|
74
|
+
* The type `JSON.stringify` serialises in place of a value of `type`: the
|
|
75
|
+
* return type of the value's own `toJSON()`, or `undefined` when it declares
|
|
76
|
+
* none (carrick#1163).
|
|
77
|
+
*
|
|
78
|
+
* Only a callable `toJSON` member counts, and only a return type that says
|
|
79
|
+
* something: an `any` return describes nothing, and a `toJSON` that returns
|
|
80
|
+
* its own type again maps nothing either.
|
|
81
|
+
*/
|
|
82
|
+
export function jsonWireType(type) {
|
|
83
|
+
if (!type.isObject() || type.isArray() || isTuple(type))
|
|
84
|
+
return undefined;
|
|
85
|
+
const member = type.getProperty('toJSON');
|
|
86
|
+
if (!member)
|
|
87
|
+
return undefined;
|
|
88
|
+
const declaration = member.getValueDeclaration() ?? member.getDeclarations()[0];
|
|
89
|
+
const memberType = declaration
|
|
90
|
+
? member.getTypeAtLocation(declaration)
|
|
91
|
+
: member.getDeclaredType();
|
|
92
|
+
const signature = memberType.getCallSignatures()[0];
|
|
93
|
+
if (!signature)
|
|
94
|
+
return undefined;
|
|
95
|
+
const serialised = signature.getReturnType();
|
|
96
|
+
if (serialised.isAny() || serialised.isUnknown())
|
|
97
|
+
return undefined;
|
|
98
|
+
const id = type.compilerType.id;
|
|
99
|
+
const serialisedId = serialised.compilerType.id;
|
|
100
|
+
if (id != null && id === serialisedId)
|
|
101
|
+
return undefined;
|
|
102
|
+
return serialised;
|
|
103
|
+
}
|
|
104
|
+
function expandAt(type, seen, depth, at, wire) {
|
|
74
105
|
if (depth > MAX_EXPANSION_DEPTH)
|
|
75
106
|
return backstopText(type);
|
|
76
107
|
let cursor = at;
|
|
@@ -78,7 +109,7 @@ function expandAt(type, seen, depth, at) {
|
|
|
78
109
|
const replacement = cursor.overrides.types.get(cursor.position);
|
|
79
110
|
if (replacement) {
|
|
80
111
|
cursor.overrides.applied.add(cursor.position);
|
|
81
|
-
return expandAt(replacement, seen, depth, undefined);
|
|
112
|
+
return expandAt(replacement, seen, depth, undefined, wire);
|
|
82
113
|
}
|
|
83
114
|
// Nothing to substitute below here: print exactly as without overrides.
|
|
84
115
|
if (!hasOverrideBelow(cursor))
|
|
@@ -102,10 +133,10 @@ function expandAt(type, seen, depth, at) {
|
|
|
102
133
|
}
|
|
103
134
|
// Unions / intersections: expand each member, in canonical order.
|
|
104
135
|
if (type.isUnion()) {
|
|
105
|
-
return canonicalMembers(type.getUnionTypes(), seen, depth, cursor).join(' | ');
|
|
136
|
+
return canonicalMembers(type.getUnionTypes(), seen, depth, cursor, wire).join(' | ');
|
|
106
137
|
}
|
|
107
138
|
if (type.isIntersection()) {
|
|
108
|
-
return canonicalMembers(type.getIntersectionTypes(), seen, depth, cursor).join(' & ');
|
|
139
|
+
return canonicalMembers(type.getIntersectionTypes(), seen, depth, cursor, wire).join(' & ');
|
|
109
140
|
}
|
|
110
141
|
// Tuples are array-like but must keep their `[a, b]` shape, not be walked
|
|
111
142
|
// as objects (which explodes into `Array.prototype`). Handle before arrays.
|
|
@@ -116,7 +147,7 @@ function expandAt(type, seen, depth, at) {
|
|
|
116
147
|
const element = type.getArrayElementType();
|
|
117
148
|
if (!element)
|
|
118
149
|
return namedText(type);
|
|
119
|
-
const inner = expandAt(element, seen, depth + 1, childCursor(cursor, '<0>'));
|
|
150
|
+
const inner = expandAt(element, seen, depth + 1, childCursor(cursor, '<0>'), wire);
|
|
120
151
|
// Parenthesise a union/intersection element so `(A | B)[]` doesn't misparse
|
|
121
152
|
// as `A | B[]`. Decide from the TYPE, not the string: a single object
|
|
122
153
|
// literal like `{ a: A | B }` is NOT a union and must not be parenthesised,
|
|
@@ -124,6 +155,13 @@ function expandAt(type, seen, depth, at) {
|
|
|
124
155
|
const needsParens = element.isUnion() || element.isIntersection();
|
|
125
156
|
return needsParens ? `(${inner})[]` : `${inner}[]`;
|
|
126
157
|
}
|
|
158
|
+
// On the JSON wire a value with `toJSON()` is its serialised form, library
|
|
159
|
+
// type or not, so this runs before the by-name bail-out below.
|
|
160
|
+
if (wire === 'json') {
|
|
161
|
+
const serialised = jsonWireType(type);
|
|
162
|
+
if (serialised)
|
|
163
|
+
return expandAt(serialised, seen, depth + 1, undefined, wire);
|
|
164
|
+
}
|
|
127
165
|
// Library / built-in types (Date, Promise, RegExp, …): keep by name, unless
|
|
128
166
|
// a member below has to be substituted — a schema library declares its
|
|
129
167
|
// inferred object types itself, and they are only walkable, not by-name.
|
|
@@ -144,7 +182,7 @@ function expandAt(type, seen, depth, at) {
|
|
|
144
182
|
const props = type.getProperties();
|
|
145
183
|
if (props.length === 0)
|
|
146
184
|
return namedText(type);
|
|
147
|
-
const parts = props.map((prop) => expandProperty(prop, nextSeen, depth, cursor));
|
|
185
|
+
const parts = props.map((prop) => expandProperty(prop, nextSeen, depth, cursor, wire));
|
|
148
186
|
return `{ ${parts.join('; ')}; }`;
|
|
149
187
|
}
|
|
150
188
|
return namedText(type);
|
|
@@ -203,8 +241,8 @@ function backstopText(type) {
|
|
|
203
241
|
* Ties can only happen between two members that render identically, in which
|
|
204
242
|
* case the joined output is the same whichever way round they go.
|
|
205
243
|
*/
|
|
206
|
-
function canonicalMembers(members, seen, depth, cursor) {
|
|
207
|
-
return orderMembers(members, (member) => expandAt(member, seen, depth + 1, cursor));
|
|
244
|
+
function canonicalMembers(members, seen, depth, cursor, wire) {
|
|
245
|
+
return orderMembers(members, (member) => expandAt(member, seen, depth + 1, cursor, wire));
|
|
208
246
|
}
|
|
209
247
|
/**
|
|
210
248
|
* The canonical order itself, over whatever text `render` gives each member.
|
|
@@ -213,9 +251,9 @@ function canonicalMembers(members, seen, depth, cursor) {
|
|
|
213
251
|
* cannot be ordered two ways depending on how deep it sits.
|
|
214
252
|
*/
|
|
215
253
|
function orderMembers(members, render) {
|
|
216
|
-
const rendered = members.map((
|
|
254
|
+
const rendered = foldBooleanLiterals(members.map(render)).map((text, index) => ({
|
|
217
255
|
index,
|
|
218
|
-
text
|
|
256
|
+
text,
|
|
219
257
|
}));
|
|
220
258
|
rendered.sort((a, b) => {
|
|
221
259
|
if (a.text !== b.text)
|
|
@@ -225,7 +263,7 @@ function orderMembers(members, render) {
|
|
|
225
263
|
return rendered.map((entry) => entry.text);
|
|
226
264
|
}
|
|
227
265
|
/** Render a single property as `name[?]: <expanded>`. */
|
|
228
|
-
function expandProperty(prop, seen, depth, at) {
|
|
266
|
+
function expandProperty(prop, seen, depth, at, wire) {
|
|
229
267
|
const optional = (prop.getFlags() & ts.SymbolFlags.Optional) !== 0;
|
|
230
268
|
const propDecl = prop.getDeclarations()[0];
|
|
231
269
|
// Render the key from the declaration's name node so quoted/computed keys
|
|
@@ -257,11 +295,11 @@ function expandProperty(prop, seen, depth, at) {
|
|
|
257
295
|
propType = nonUndefined[0];
|
|
258
296
|
}
|
|
259
297
|
else if (nonUndefined.length > 1) {
|
|
260
|
-
const inner = canonicalMembers(nonUndefined, seen, depth, cursor).join(' | ');
|
|
298
|
+
const inner = canonicalMembers(nonUndefined, seen, depth, cursor, wire).join(' | ');
|
|
261
299
|
return `${name}?: ${inner}`;
|
|
262
300
|
}
|
|
263
301
|
}
|
|
264
|
-
const inner = expandAt(propType, seen, depth + 1, cursor);
|
|
302
|
+
const inner = expandAt(propType, seen, depth + 1, cursor, wire);
|
|
265
303
|
return `${name}${optional ? '?' : ''}: ${inner}`;
|
|
266
304
|
}
|
|
267
305
|
/**
|
|
@@ -38,3 +38,17 @@
|
|
|
38
38
|
* a definition resolve must not fail because one member confused a scanner.
|
|
39
39
|
*/
|
|
40
40
|
export declare function canonicalizeUnionsInText(text: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* A union's rendered members with the literal pair `false`, `true` folded into
|
|
43
|
+
* `boolean` (carrick#1165).
|
|
44
|
+
*
|
|
45
|
+
* The compiler stores `boolean` as exactly that pair, and its own printer folds
|
|
46
|
+
* it back. Anything that renders a union member by member — the structural
|
|
47
|
+
* walk, and this file's reordering of a compiler print — sees the two literals
|
|
48
|
+
* separately, and sorting them apart produced `false | null | true` for
|
|
49
|
+
* `boolean | null`. Folding before the sort gives the one print a reader and a
|
|
50
|
+
* diff expect. A lone literal is a literal type and is kept. Shared by
|
|
51
|
+
* `orderMembers` in `type-structural-expander.ts`, so both halves of the normal
|
|
52
|
+
* form fold the same way.
|
|
53
|
+
*/
|
|
54
|
+
export declare function foldBooleanLiterals(members: string[]): string[];
|
|
@@ -156,8 +156,8 @@ function canonicalizeMember(member) {
|
|
|
156
156
|
* the compiler rendered it.
|
|
157
157
|
*/
|
|
158
158
|
function orderTextMembers(members) {
|
|
159
|
-
return members
|
|
160
|
-
.map((text, index) => ({ text
|
|
159
|
+
return foldBooleanLiterals(members.map((text) => text.trim()))
|
|
160
|
+
.map((text, index) => ({ text, index }))
|
|
161
161
|
.sort((a, b) => {
|
|
162
162
|
if (a.text !== b.text)
|
|
163
163
|
return a.text < b.text ? -1 : 1;
|
|
@@ -165,6 +165,27 @@ function orderTextMembers(members) {
|
|
|
165
165
|
})
|
|
166
166
|
.map((entry) => entry.text);
|
|
167
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* A union's rendered members with the literal pair `false`, `true` folded into
|
|
170
|
+
* `boolean` (carrick#1165).
|
|
171
|
+
*
|
|
172
|
+
* The compiler stores `boolean` as exactly that pair, and its own printer folds
|
|
173
|
+
* it back. Anything that renders a union member by member — the structural
|
|
174
|
+
* walk, and this file's reordering of a compiler print — sees the two literals
|
|
175
|
+
* separately, and sorting them apart produced `false | null | true` for
|
|
176
|
+
* `boolean | null`. Folding before the sort gives the one print a reader and a
|
|
177
|
+
* diff expect. A lone literal is a literal type and is kept. Shared by
|
|
178
|
+
* `orderMembers` in `type-structural-expander.ts`, so both halves of the normal
|
|
179
|
+
* form fold the same way.
|
|
180
|
+
*/
|
|
181
|
+
export function foldBooleanLiterals(members) {
|
|
182
|
+
const falseAt = members.indexOf('false');
|
|
183
|
+
const trueAt = members.indexOf('true');
|
|
184
|
+
if (falseAt === -1 || trueAt === -1)
|
|
185
|
+
return members;
|
|
186
|
+
const rest = members.filter((member) => member !== 'false' && member !== 'true');
|
|
187
|
+
return rest.includes('boolean') ? rest : [...rest, 'boolean'];
|
|
188
|
+
}
|
|
168
189
|
/**
|
|
169
190
|
* The spans between depth-zero occurrences of `separator`. Depth counts the
|
|
170
191
|
* four bracket pairs; quoted runs are skipped whole, so a separator inside a
|
|
@@ -415,16 +415,19 @@ export declare const CaptureV2RequestSchema: z.ZodObject<{
|
|
|
415
415
|
alias: z.ZodString;
|
|
416
416
|
type_text: z.ZodString;
|
|
417
417
|
anchor_origin: z.ZodEnum<["llm-symbol", "deterministic-infer", "anchor-backfill"]>;
|
|
418
|
+
source_file: z.ZodOptional<z.ZodString>;
|
|
418
419
|
}, "strip", z.ZodTypeAny, {
|
|
419
420
|
kind: "literal";
|
|
420
421
|
alias: string;
|
|
421
422
|
anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill";
|
|
422
423
|
type_text: string;
|
|
424
|
+
source_file?: string | undefined;
|
|
423
425
|
}, {
|
|
424
426
|
kind: "literal";
|
|
425
427
|
alias: string;
|
|
426
428
|
anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill";
|
|
427
429
|
type_text: string;
|
|
430
|
+
source_file?: string | undefined;
|
|
428
431
|
}>]>, "many">;
|
|
429
432
|
out_dir: z.ZodString;
|
|
430
433
|
tsconfig_path: z.ZodOptional<z.ZodString>;
|
|
@@ -461,6 +464,7 @@ export declare const CaptureV2RequestSchema: z.ZodObject<{
|
|
|
461
464
|
alias: string;
|
|
462
465
|
anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill";
|
|
463
466
|
type_text: string;
|
|
467
|
+
source_file?: string | undefined;
|
|
464
468
|
})[];
|
|
465
469
|
out_dir: string;
|
|
466
470
|
tsconfig_path?: string | undefined;
|
|
@@ -497,6 +501,7 @@ export declare const CaptureV2RequestSchema: z.ZodObject<{
|
|
|
497
501
|
alias: string;
|
|
498
502
|
anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill";
|
|
499
503
|
type_text: string;
|
|
504
|
+
source_file?: string | undefined;
|
|
500
505
|
})[];
|
|
501
506
|
out_dir: string;
|
|
502
507
|
tsconfig_path?: string | undefined;
|
|
@@ -1476,16 +1481,19 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
|
|
|
1476
1481
|
alias: z.ZodString;
|
|
1477
1482
|
type_text: z.ZodString;
|
|
1478
1483
|
anchor_origin: z.ZodEnum<["llm-symbol", "deterministic-infer", "anchor-backfill"]>;
|
|
1484
|
+
source_file: z.ZodOptional<z.ZodString>;
|
|
1479
1485
|
}, "strip", z.ZodTypeAny, {
|
|
1480
1486
|
kind: "literal";
|
|
1481
1487
|
alias: string;
|
|
1482
1488
|
anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill";
|
|
1483
1489
|
type_text: string;
|
|
1490
|
+
source_file?: string | undefined;
|
|
1484
1491
|
}, {
|
|
1485
1492
|
kind: "literal";
|
|
1486
1493
|
alias: string;
|
|
1487
1494
|
anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill";
|
|
1488
1495
|
type_text: string;
|
|
1496
|
+
source_file?: string | undefined;
|
|
1489
1497
|
}>]>, "many">;
|
|
1490
1498
|
out_dir: z.ZodString;
|
|
1491
1499
|
tsconfig_path: z.ZodOptional<z.ZodString>;
|
|
@@ -1522,6 +1530,7 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
|
|
|
1522
1530
|
alias: string;
|
|
1523
1531
|
anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill";
|
|
1524
1532
|
type_text: string;
|
|
1533
|
+
source_file?: string | undefined;
|
|
1525
1534
|
})[];
|
|
1526
1535
|
out_dir: string;
|
|
1527
1536
|
tsconfig_path?: string | undefined;
|
|
@@ -1558,6 +1567,7 @@ export declare const SidecarRequestSchema: z.ZodDiscriminatedUnion<"action", [z.
|
|
|
1558
1567
|
alias: string;
|
|
1559
1568
|
anchor_origin: "llm-symbol" | "deterministic-infer" | "anchor-backfill";
|
|
1560
1569
|
type_text: string;
|
|
1570
|
+
source_file?: string | undefined;
|
|
1561
1571
|
})[];
|
|
1562
1572
|
out_dir: string;
|
|
1563
1573
|
tsconfig_path?: string | undefined;
|
|
@@ -219,6 +219,7 @@ const CaptureAnchorRequestSchema = z.discriminatedUnion('kind', [
|
|
|
219
219
|
alias: z.string().min(1),
|
|
220
220
|
type_text: z.string().min(1),
|
|
221
221
|
anchor_origin: AnchorOriginSchema,
|
|
222
|
+
source_file: z.string().min(1).optional(),
|
|
222
223
|
}),
|
|
223
224
|
]);
|
|
224
225
|
export const CaptureV2RequestSchema = BaseRequestSchema.extend({
|