carrick 0.3.53

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.
Files changed (114) hide show
  1. package/LICENSE.md +99 -0
  2. package/README.md +79 -0
  3. package/bin/carrick.mjs +168 -0
  4. package/dist/channel.d.ts +16 -0
  5. package/dist/channel.js +37 -0
  6. package/dist/channel.js.map +1 -0
  7. package/dist/cli.d.ts +29 -0
  8. package/dist/cli.js +60 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/contract.d.ts +154 -0
  11. package/dist/contract.js +141 -0
  12. package/dist/contract.js.map +1 -0
  13. package/dist/diagnostics.d.ts +54 -0
  14. package/dist/diagnostics.js +199 -0
  15. package/dist/diagnostics.js.map +1 -0
  16. package/dist/hook/post-edit.d.ts +13 -0
  17. package/dist/hook/post-edit.js +76 -0
  18. package/dist/hook/post-edit.js.map +1 -0
  19. package/dist/hook/session-start.d.ts +2 -0
  20. package/dist/hook/session-start.js +44 -0
  21. package/dist/hook/session-start.js.map +1 -0
  22. package/dist/init/identity.d.ts +20 -0
  23. package/dist/init/identity.js +60 -0
  24. package/dist/init/identity.js.map +1 -0
  25. package/dist/init/repos.d.ts +19 -0
  26. package/dist/init/repos.js +46 -0
  27. package/dist/init/repos.js.map +1 -0
  28. package/dist/init/run.d.ts +11 -0
  29. package/dist/init/run.js +228 -0
  30. package/dist/init/run.js.map +1 -0
  31. package/dist/init/settings.d.ts +50 -0
  32. package/dist/init/settings.js +138 -0
  33. package/dist/init/settings.js.map +1 -0
  34. package/dist/log.d.ts +2 -0
  35. package/dist/log.js +35 -0
  36. package/dist/log.js.map +1 -0
  37. package/dist/native.d.ts +48 -0
  38. package/dist/native.js +127 -0
  39. package/dist/native.js.map +1 -0
  40. package/dist/render.d.ts +65 -0
  41. package/dist/render.js +334 -0
  42. package/dist/render.js.map +1 -0
  43. package/dist/root.d.ts +30 -0
  44. package/dist/root.js +82 -0
  45. package/dist/root.js.map +1 -0
  46. package/dist/server.d.ts +2 -0
  47. package/dist/server.js +255 -0
  48. package/dist/server.js.map +1 -0
  49. package/dist/templates.d.ts +18 -0
  50. package/dist/templates.js +66 -0
  51. package/dist/templates.js.map +1 -0
  52. package/package.json +72 -0
  53. package/plugin/.claude-plugin/plugin.json +6 -0
  54. package/plugin/.lsp.json +14 -0
  55. package/plugin/hooks/hooks.json +27 -0
  56. package/sidecar/dist/src/bundler.d.ts +141 -0
  57. package/sidecar/dist/src/bundler.js +680 -0
  58. package/sidecar/dist/src/capture/anchors.d.ts +61 -0
  59. package/sidecar/dist/src/capture/anchors.js +1132 -0
  60. package/sidecar/dist/src/capture/api.d.ts +378 -0
  61. package/sidecar/dist/src/capture/api.js +10 -0
  62. package/sidecar/dist/src/capture/augmentations.d.ts +20 -0
  63. package/sidecar/dist/src/capture/augmentations.js +60 -0
  64. package/sidecar/dist/src/capture/check-classify.d.ts +58 -0
  65. package/sidecar/dist/src/capture/check-classify.js +189 -0
  66. package/sidecar/dist/src/capture/check-deep.d.ts +32 -0
  67. package/sidecar/dist/src/capture/check-deep.js +91 -0
  68. package/sidecar/dist/src/capture/check-poison.d.ts +40 -0
  69. package/sidecar/dist/src/capture/check-poison.js +155 -0
  70. package/sidecar/dist/src/capture/check-probe.d.ts +70 -0
  71. package/sidecar/dist/src/capture/check-probe.js +147 -0
  72. package/sidecar/dist/src/capture/check-scrub.d.ts +31 -0
  73. package/sidecar/dist/src/capture/check-scrub.js +71 -0
  74. package/sidecar/dist/src/capture/check-workspace.d.ts +52 -0
  75. package/sidecar/dist/src/capture/check-workspace.js +218 -0
  76. package/sidecar/dist/src/capture/check.d.ts +39 -0
  77. package/sidecar/dist/src/capture/check.js +450 -0
  78. package/sidecar/dist/src/capture/deep-walk.d.ts +62 -0
  79. package/sidecar/dist/src/capture/deep-walk.js +243 -0
  80. package/sidecar/dist/src/capture/index.d.ts +36 -0
  81. package/sidecar/dist/src/capture/index.js +477 -0
  82. package/sidecar/dist/src/capture/lockfile.d.ts +48 -0
  83. package/sidecar/dist/src/capture/lockfile.js +490 -0
  84. package/sidecar/dist/src/capture/machinery.d.ts +59 -0
  85. package/sidecar/dist/src/capture/machinery.js +160 -0
  86. package/sidecar/dist/src/capture/node-builder.d.ts +37 -0
  87. package/sidecar/dist/src/capture/node-builder.js +123 -0
  88. package/sidecar/dist/src/capture/paths-rewrite.d.ts +34 -0
  89. package/sidecar/dist/src/capture/paths-rewrite.js +100 -0
  90. package/sidecar/dist/src/capture/self-check.d.ts +38 -0
  91. package/sidecar/dist/src/capture/self-check.js +317 -0
  92. package/sidecar/dist/src/capture/specifiers.d.ts +39 -0
  93. package/sidecar/dist/src/capture/specifiers.js +56 -0
  94. package/sidecar/dist/src/definition-resolver.d.ts +57 -0
  95. package/sidecar/dist/src/definition-resolver.js +153 -0
  96. package/sidecar/dist/src/index.d.ts +14 -0
  97. package/sidecar/dist/src/index.js +564 -0
  98. package/sidecar/dist/src/monorepo-builder.d.ts +129 -0
  99. package/sidecar/dist/src/monorepo-builder.js +584 -0
  100. package/sidecar/dist/src/project-loader.d.ts +130 -0
  101. package/sidecar/dist/src/project-loader.js +399 -0
  102. package/sidecar/dist/src/type-inferrer.d.ts +940 -0
  103. package/sidecar/dist/src/type-inferrer.js +3540 -0
  104. package/sidecar/dist/src/type-structural-expander.d.ts +61 -0
  105. package/sidecar/dist/src/type-structural-expander.js +283 -0
  106. package/sidecar/dist/src/type-text-canonicalizer.d.ts +40 -0
  107. package/sidecar/dist/src/type-text-canonicalizer.js +297 -0
  108. package/sidecar/dist/src/types.d.ts +669 -0
  109. package/sidecar/dist/src/types.js +5 -0
  110. package/sidecar/dist/src/validators.d.ts +2214 -0
  111. package/sidecar/dist/src/validators.js +336 -0
  112. package/sidecar/package.json +6 -0
  113. package/templates/carrick.json +7 -0
  114. package/templates/carrick.yml +45 -0
@@ -0,0 +1,243 @@
1
+ /**
2
+ * The structural any/unknown walk, shared by the two type surfaces that need it
3
+ * (carrick#448, carrick#707).
4
+ *
5
+ * Capture-time it runs over the emitted stub tree and decides whether an alias
6
+ * carries a member-level decay the check phase must pre-gate. Check-time it
7
+ * runs over the assembled probe workspace, where the pinned externals ARE
8
+ * installed, and decides whether a verdict is a fact about two known types or
9
+ * an artefact of a type nobody could see (`CheckVerdict.resolved`). The
10
+ * whole-type probe gates cannot answer that: `any` three members down passes
11
+ * every one of them.
12
+ *
13
+ * One walk, one budget, one path notation, so a finding means the same thing
14
+ * wherever it is reported.
15
+ */
16
+ import ts from 'typescript';
17
+ /** Cap on findings reported per alias. The FIRST one is what the check phase
18
+ * pre-gates on, so verdicts never depend on this number; the rest are there to
19
+ * tell a reader which fields are `any` (carrick#376), and a type with more
20
+ * than this many is already better described by "this type is not typed". */
21
+ const MAX_DEEP_FINDINGS = 32;
22
+ /**
23
+ * Depth-bounded structural walk for a disqualifying top type at ANY depth:
24
+ * a member, array element, index signature, type argument, or callable RETURN
25
+ * that resolved to `any` (bidirectionally assignable — an arbitrary
26
+ * counterparty shape reads compatible) or `unknown` (a failed-inference bake;
27
+ * carries no shape information). The check phase's probe gates are WHOLE-type
28
+ * only, so this walk owns the depths they cannot see. It is a genuine SUPERSET
29
+ * of v1's text-scan disqualifier (`contains_disqualifying_top_type`) at ALL
30
+ * depths/widths — so removing the `type_state == Unknown` pre-verdict (carrick
31
+ * #448) never turns a shape v1 would have abstained on into a false-compatible.
32
+ * The superset holds unconditionally because budget EXHAUSTION FAILS CLOSED
33
+ * (returns the `budget_exhausted` sentinel, not "clean"): a subtree too deep or
34
+ * wide to finish within budget is unverifiable, never silently compatible. A
35
+ * bigger bound would only relocate the fail-open cliff; failing closed removes
36
+ * it. v1's text scan is itself unbounded, so any disqualifier it would flag
37
+ * that lies past this walk's finite budget is still caught — as
38
+ * `budget_exhausted` rather than its exact kind.
39
+ *
40
+ * Cycle-safe. Two deliberate exceptions to "flag any/unknown anywhere":
41
+ * - callable PARAMETER types are NOT descended (only return types): a
42
+ * parameter `any` is contravariant and genuinely permissive (`(x: any) =>
43
+ * void` safely accepts a stricter counterparty), so it is not a masked
44
+ * mismatch and demoting it would over-demote a sound shape;
45
+ * - TypeScript's unresolved-reference `error` placeholder (`intrinsicName ===
46
+ * 'error'`) is excluded (see `flagOf`): it heals when the check installs the
47
+ * pinned external, so it is a healable decay, not an author-baked `any`.
48
+ * A type the walk cannot cheaply finish is NOT flagged — over-demoting a
49
+ * legitimately fully-resolved type is the failure mode this guard must not have.
50
+ */
51
+ export function findDisqualifyingTopTypes(root, program, checker, location) {
52
+ // Cover v1's inline-expander reach with margin so this structural walk is a
53
+ // genuine superset of v1's text-scan disqualifier AT DEPTH: anything v1 could
54
+ // expand-and-flag as `any`/`unknown`, this walk reaches too. v1's expander
55
+ // (`MAX_EXPANSION_DEPTH` in ../type-structural-expander.ts) reaches 12; 16
56
+ // clears it with margin. That constant is NOT imported: the capture bundle
57
+ // seam (`capture-v2-seam.test.ts`) forbids capture/ from importing across the
58
+ // boundary, so the value is pinned here and kept ">= MAX_EXPANSION_DEPTH" by
59
+ // that contract. The node budget scales with the deeper bound so a real
60
+ // deep-but-narrow type cannot exhaust it before the walk reaches a buried
61
+ // `any` — running out early would fail OPEN (read compatible).
62
+ const MAX_DEPTH = 16;
63
+ const MAX_VISITED = 4096;
64
+ const seen = new Set();
65
+ let visited = 0;
66
+ const flagOf = (t) => {
67
+ if (t.flags & ts.TypeFlags.Any) {
68
+ // TypeScript's unresolved-reference placeholder (e.g. `import('ext').Foo`
69
+ // on a bare checkout) carries `TypeFlags.Any` but `intrinsicName ===
70
+ // 'error'` — NOT an author-baked `any`. It resolves to the real type once
71
+ // the check phase installs the pinned external, so it must not count as a
72
+ // disqualifier: treating it as `any` would demote a healable external
73
+ // reference. Genuine author `any` carries `intrinsicName === 'any'`.
74
+ // (`intrinsicName` is internal but stable since TS 1.x — same standing as
75
+ // its use in anchors.ts.)
76
+ //
77
+ // The `error` placeholder also stands in for NON-healable causes (TS2304
78
+ // undefined name, TS2315 wrong-arity generic, a dangling internal
79
+ // specifier). Excluding those here is not a hole: each emits a diagnostic
80
+ // in the alias's own closure, so the closure-failure classification
81
+ // (`internalFailure` -> decayed_internal) or the check-phase POISON rule
82
+ // — NOT this deep walk — is their backstop, and both fail closed.
83
+ const name = t.intrinsicName;
84
+ return name === 'error' ? undefined : 'any';
85
+ }
86
+ return t.flags & ts.TypeFlags.Unknown ? 'unknown' : undefined;
87
+ };
88
+ // Findings accumulate rather than short-circuiting: the FIRST is what the
89
+ // check phase pre-gates on (so the verdict is identical to the
90
+ // stop-at-first walk this replaces), and the rest answer "which fields are
91
+ // `any`, and why" for a reader of the published type (carrick#376). The walk
92
+ // stops early only on budget exhaustion, which is a fail-closed sentinel
93
+ // about the whole type and makes any further finding meaningless.
94
+ const found = [];
95
+ let exhausted = false;
96
+ const walk = (t, path, depth) => {
97
+ // Genuine cycle handling — NOT fail-open. `t` is already on the walk stack
98
+ // (or was fully explored earlier), so the owning frame completes it;
99
+ // returning "clean" here is sound because a type reaches `seen` ONLY after a
100
+ // visit that fully completed clean. A visit that hit the budget below
101
+ // returns the `budget_exhausted` sentinel, which bubbles up (every frame
102
+ // propagates a truthy child return) and terminates the whole walk before
103
+ // any shallower re-entry — so `seen` never memoizes a truncated visit as
104
+ // clean. And a type fully explored clean at depth d1 is clean at any d2 < d1
105
+ // (the shallower reach is a superset of the deeper), so reusing it is safe.
106
+ if (exhausted || seen.has(t))
107
+ return;
108
+ // Budget exhaustion FAILS CLOSED. Returning "clean" here would let an
109
+ // `any` buried past the depth/node budget read compatible — the fail-open
110
+ // cliff a bigger number only relocates. Instead abstain: the alias demotes
111
+ // to unverifiable, over-abstaining on a legitimately clean type
112
+ // deeper/wider than budget rather than ever false-compatible. The sentinel
113
+ // is placed FIRST so the check phase pre-gates on it whatever else the
114
+ // walk had already collected.
115
+ if (depth > MAX_DEPTH || visited > MAX_VISITED) {
116
+ exhausted = true;
117
+ const sentinel = {
118
+ kind: 'budget_exhausted',
119
+ path: path === '' ? '<root>' : path,
120
+ };
121
+ // Head position decides the check phase's pre-gate, so it must be
122
+ // whatever the stop-at-first walk would have returned. That walk could
123
+ // only exhaust its budget having found NOTHING — it returned the moment
124
+ // it found something. So the sentinel leads only when nothing was found;
125
+ // otherwise the first real finding keeps the head and the pre-gate is
126
+ // byte-identical to before. (Putting the sentinel first regardless would
127
+ // turn a `gate_caught_baked_any` into an `unverifiable`: same downstream
128
+ // reading, different verdict, and this change moves no verdicts.)
129
+ found.push(sentinel);
130
+ return;
131
+ }
132
+ seen.add(t);
133
+ visited++;
134
+ // Root-level top types are the caller's whole-type check (and the check
135
+ // phase's probe gates catch them); this walk owns depth > 0.
136
+ if (depth > 0) {
137
+ const kind = flagOf(t);
138
+ if (kind) {
139
+ if (found.length < MAX_DEEP_FINDINGS)
140
+ found.push({ kind, path });
141
+ return;
142
+ }
143
+ }
144
+ if (t.flags & (ts.TypeFlags.Union | ts.TypeFlags.Intersection)) {
145
+ for (const part of t.types) {
146
+ walk(part, path, depth + 1);
147
+ if (exhausted)
148
+ return;
149
+ }
150
+ return;
151
+ }
152
+ if (!(t.flags & ts.TypeFlags.Object))
153
+ return;
154
+ // Callable members: descend the RETURN type of every call/construct
155
+ // signature — a return is a COVARIANT wire position, so `() => any`
156
+ // covariantly widens `() => string` and an `any` there masks a real
157
+ // mismatch exactly as a plain-member `any` does (v1's text scan flags it).
158
+ // PARAMETER types are deliberately NOT descended: a parameter `any` is
159
+ // contravariant and genuinely permissive (`(x: any) => void` safely
160
+ // accepts a stricter counterparty), so demoting it would over-demote a
161
+ // sound shape. Fall through afterwards so a hybrid callable
162
+ // (`{ (): T; data: any }`) still has its own members walked below.
163
+ for (const sig of [...t.getCallSignatures(), ...t.getConstructSignatures()]) {
164
+ walk(sig.getReturnType(), `${path}()`, depth + 1);
165
+ if (exhausted)
166
+ return;
167
+ }
168
+ // Type arguments: arrays, tuples, Promise<T>, Map<K, V>, ...
169
+ if (t.objectFlags & ts.ObjectFlags.Reference) {
170
+ const args = checker.getTypeArguments(t);
171
+ for (let i = 0; i < args.length; i++) {
172
+ walk(args[i], `${path}<${i}>`, depth + 1);
173
+ if (exhausted)
174
+ return;
175
+ }
176
+ }
177
+ // Index signatures: { [k: string]: T }, Record<string, T>.
178
+ for (const info of checker.getIndexInfosOfType(t)) {
179
+ walk(info.type, `${path}[index]`, depth + 1);
180
+ if (exhausted)
181
+ return;
182
+ }
183
+ for (const prop of t.getProperties()) {
184
+ // Skip the built-in method suite (`Array#map`, `Promise#then`, ...): a
185
+ // lib-declared member is machinery, never a wire payload, and descending
186
+ // its return type recurses (map -> U[] -> map -> ...) until it exhausts
187
+ // the budget — which pre-fail-closed silently read "clean" and now would
188
+ // over-abstain on every ordinary `T[]`. The element/value type is still
189
+ // covered via the type-argument branch above; a USER function-typed
190
+ // member (`getData: () => any`) is not lib-declared, so it is still walked.
191
+ const decl = prop.valueDeclaration ?? prop.declarations?.[0];
192
+ if (decl && program.isSourceFileDefaultLibrary(decl.getSourceFile())) {
193
+ continue;
194
+ }
195
+ const propType = checker.getTypeOfSymbolAtLocation(prop, location);
196
+ walk(propType, path === '' ? prop.getName() : `${path}.${prop.getName()}`, depth + 1);
197
+ if (exhausted)
198
+ return;
199
+ }
200
+ };
201
+ walk(root, '', 0);
202
+ // The HEAD is what the check phase pre-gates on, so it must be exactly what
203
+ // the stop-at-first walk this replaces would have returned: the first
204
+ // finding in walk order. Findings are appended in that order, so `found[0]`
205
+ // already is it — including the budget sentinel, which can only lead when
206
+ // nothing was found before the budget ran out (the old walk returned the
207
+ // moment it found anything, so it could only exhaust having found nothing).
208
+ //
209
+ // The TAIL is provenance for a reader, and is sorted by member path. Walk
210
+ // order is the checker's declaration order and stable per input, but
211
+ // `scan-twice.sh` byte-identity rests on this list, so the order is stated
212
+ // rather than inherited.
213
+ const [head, ...rest] = found;
214
+ if (head === undefined)
215
+ return [];
216
+ rest.sort((a, b) => a.path.localeCompare(b.path));
217
+ return [head, ...rest];
218
+ }
219
+ /**
220
+ * Turn a deep finding into the published provenance entry (carrick#376).
221
+ *
222
+ * The self-check reads EMITTED declaration text, so a top type it finds is
223
+ * text: whatever produced it — an author annotation, or an emitter that
224
+ * printed a value it could not resolve — no install re-resolves it. That is
225
+ * `declared`, and saying so is more useful than the bare `any` a reader gets
226
+ * today. The one other cause it can distinguish is its own budget.
227
+ */
228
+ export function provenanceOf(finding) {
229
+ if (finding.kind === 'budget_exhausted') {
230
+ return {
231
+ path: finding.path,
232
+ kind: 'budget_exhausted',
233
+ reason: 'budget_exhausted',
234
+ detail: 'the type is too deep or wide to verify within the capture budget here, so it is reported unverified rather than assumed clean',
235
+ };
236
+ }
237
+ return {
238
+ path: finding.path,
239
+ kind: finding.kind,
240
+ reason: 'declared',
241
+ detail: `the captured declaration states '${finding.kind}' at this position, so no counterparty shape can disagree with it`,
242
+ };
243
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Type-compat v2 capture core: "tsc as the serializer"
3
+ * (docs/reference/type-compat-synthetic-monorepo.md, Capture phase).
4
+ *
5
+ * Produces a types-only stub package for one service:
6
+ *
7
+ * @carrick/<service>/
8
+ * |- package.json name, types entry, pinned deps (exact versions)
9
+ * |- tsconfig.snapshot.json
10
+ * |- carrick-manifest.json per-alias records + fidelity metric
11
+ * `- types/
12
+ * |- surface.d.ts entry: export type <alias> = ...
13
+ * `- nested .d.ts tree compiler-emitted declaration closure
14
+ *
15
+ * Two-phase flow:
16
+ * Phase A (analysis): a placeholder surface entry + the anchors' source
17
+ * files form a program; addressable anchors run their guards, anonymous
18
+ * anchors are located and printed via the SymbolTracker-backed node
19
+ * builder (anchored at their placeholder -- the destination file).
20
+ * Phase B (emit): the final entry runs `tsc --noCheck --declaration
21
+ * --emitDeclarationOnly` with the repo's own parsed options, plus every
22
+ * detected augmentation file as an extra root; the tree is relocated
23
+ * into the stub, specifiers are rewritten, deps pinned, and the
24
+ * per-alias self-check classifies the result.
25
+ *
26
+ * Seam note: this directory is the whole v2 capture bundle. It imports only
27
+ * node builtins and `typescript`; the rest of the sidecar reaches it only
28
+ * through ./api.js types and this file's `captureStub`.
29
+ */
30
+ import type { CaptureStubOptions, CaptureStubResult } from './api.js';
31
+ export type { CaptureStubOptions, CaptureStubResult } from './api.js';
32
+ export { runCheck } from './check.js';
33
+ export type { CheckProgress } from './check.js';
34
+ /** Same normalization intent as bundle_file_stems on the Rust side. */
35
+ export declare function sanitizeServiceName(name: string): string;
36
+ export declare function captureStub(opts: CaptureStubOptions): CaptureStubResult;