@abide/abide 0.123.2 → 0.123.3

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 (94) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/bin/abide.ts +36 -65
  3. package/package.json +1 -1
  4. package/src/compiler/Diagnostics.ts +198 -0
  5. package/src/compiler/KeptChecker.ts +97 -0
  6. package/src/compiler/Lowering.ts +1 -1
  7. package/src/compiler/PositionedRefusal.ts +1 -1
  8. package/src/compiler/TypeSession.ts +62 -85
  9. package/src/compiler/appLayout.ts +28 -20
  10. package/src/compiler/argsDefaultsIn.ts +1 -1
  11. package/src/compiler/checkApp.ts +64 -68
  12. package/src/compiler/checkedShadow.ts +1 -1
  13. package/src/compiler/compileApp.ts +101 -70
  14. package/src/compiler/declaredExports.ts +2 -4
  15. package/src/compiler/emitClient.ts +1 -1
  16. package/src/compiler/formatAbide.ts +2 -2
  17. package/src/compiler/handlersUnder.ts +17 -29
  18. package/src/compiler/lineOf.ts +0 -19
  19. package/src/compiler/matchersOf.ts +3 -3
  20. package/src/compiler/mountsOf.ts +3 -3
  21. package/src/compiler/readSources.ts +16 -18
  22. package/src/compiler/readsIn.ts +1 -1
  23. package/src/compiler/serveLanguage.ts +23 -57
  24. package/src/compiler/sharedModuleWrites.ts +1 -1
  25. package/src/compiler/templateWarnings.ts +1 -2
  26. package/src/compiler/trackingWarnings.ts +1 -1
  27. package/src/server/CLIENT_BUILD.ts +4 -32
  28. package/src/server/RESUME_MILLISECONDS.ts +2 -0
  29. package/src/server/answerRequest.ts +11 -300
  30. package/src/server/answerRpc.ts +12 -54
  31. package/src/server/answerSocket.ts +8 -35
  32. package/src/server/appBuild.ts +377 -0
  33. package/src/server/appPlugins.ts +15 -17
  34. package/src/server/appTree.ts +6 -5
  35. package/src/server/argsContract.ts +209 -0
  36. package/src/server/bridgeRelay.ts +153 -65
  37. package/src/server/claudeCode.ts +30 -127
  38. package/src/server/config.ts +6 -7
  39. package/src/server/createApp.ts +30 -64
  40. package/src/server/devWorker.ts +11 -15
  41. package/src/server/handlerFileMount.ts +40 -0
  42. package/src/server/index.ts +2 -2
  43. package/src/server/packageDirectory.ts +14 -0
  44. package/src/server/preload.ts +3 -2
  45. package/src/server/principal.ts +4 -3
  46. package/src/server/remote.ts +1 -1
  47. package/src/server/runCompiled.ts +1 -1
  48. package/src/server/runDesktop.ts +1 -1
  49. package/src/server/seedBuffer.ts +1 -1
  50. package/src/server/serveDev.ts +24 -58
  51. package/src/server/servedApp.ts +390 -0
  52. package/src/server/signInChanged.ts +20 -0
  53. package/src/server/surfaceOf.ts +2 -1
  54. package/src/server/versionOf.ts +6 -15
  55. package/src/shared/applyPatch.ts +1 -1
  56. package/src/shared/muxFrames.ts +1 -1
  57. package/src/shared/reactive/Keyed.ts +1 -1
  58. package/src/shared/reactive/Reactive.ts +21 -8
  59. package/src/shared/reactive/argsContract.ts +131 -4
  60. package/src/shared/reactive/index.ts +1 -0
  61. package/src/shared/reactive/patchLog.ts +79 -0
  62. package/src/shared/reactive/patchMarks.ts +34 -0
  63. package/src/shared/reactive/recordWrites.ts +11 -14
  64. package/src/shared/reactive/share.ts +4 -3
  65. package/src/shared/rpc.ts +12 -19
  66. package/src/ui/ArmRegion.ts +10 -4
  67. package/src/ui/Level.ts +11 -20
  68. package/src/ui/LevelChain.ts +272 -0
  69. package/src/ui/Router.ts +54 -245
  70. package/src/ui/Scope.ts +17 -3
  71. package/src/ui/TryRegion.ts +22 -23
  72. package/src/ui/bindSpread.ts +2 -4
  73. package/src/ui/listenForRoutes.ts +3 -10
  74. package/src/ui/outboxQueue.ts +21 -3
  75. package/src/ui/reconcile.ts +4 -6
  76. package/src/ui/routing.ts +2 -1
  77. package/src/ui/rows.ts +2 -2
  78. package/src/ui/setAttribute.ts +12 -3
  79. package/src/ui/socketWire.ts +26 -11
  80. package/src/compiler/Finding.ts +0 -2
  81. package/src/compiler/Refusals.ts +0 -65
  82. package/src/compiler/formatDiagnosis.ts +0 -20
  83. package/src/server/appDirectories.ts +0 -9
  84. package/src/server/argsFor.ts +0 -39
  85. package/src/server/buildDirectory.ts +0 -10
  86. package/src/server/coerceArgs.ts +0 -109
  87. package/src/server/derivedArgs.ts +0 -26
  88. package/src/server/runBuild.ts +0 -24
  89. package/src/server/serveBuild.ts +0 -144
  90. package/src/server/testBuild.ts +0 -220
  91. package/src/shared/reactive/FOLLOWED.ts +0 -8
  92. package/src/shared/reactive/PATCHES.ts +0 -11
  93. package/src/shared/reactive/PATCH_SINKS.ts +0 -26
  94. package/src/shared/reactive/canonicalKey.ts +0 -95
@@ -22,15 +22,19 @@ import {
22
22
  type PlacedSource,
23
23
  shapeOf,
24
24
  } from './appLayout.ts'
25
+ import { type Diagnosis, Diagnostics } from './Diagnostics.ts'
25
26
  import { emitSource } from './emitSource.ts'
26
27
  import { handlersUnder } from './handlersUnder.ts'
27
28
  import { isAbideSpecifier } from './hookCalls.ts'
28
29
  import { nearest } from './nearest.ts'
29
30
  import { projectShadow } from './projectShadow.ts'
30
- import { type ReadSource, readSources } from './readSources.ts'
31
- import { Refusals } from './Refusals.ts'
31
+ import {
32
+ type ParsedSource,
33
+ type ReadSource,
34
+ readSources,
35
+ } from './readSources.ts'
32
36
  import { respecified } from './splitImports.ts'
33
- import { type Diagnosis, TypeSession } from './TypeSession.ts'
37
+ import { TypeSession } from './TypeSession.ts'
34
38
  import { writeDefaultProject } from './writeDefaultProject.ts'
35
39
 
36
40
  // AN APP'S `.abide` SOURCES, COMPILED — every file keyed by its path below the root, and loaded
@@ -80,7 +84,7 @@ export type AppChecker = {
80
84
  * The same nearest tsconfig and package.json, neither touched since. Asked rather than heard,
81
85
  * because either can sit above the watched root — the dogfood's do — and either changes what
82
86
  * every file resolves to. Where it is, the session is told every move `saw` held
83
- * (`TypeSession.edited`); where it is not, the caller opens another.
87
+ * (`TypeSession.edited`); where it is not, `KeptChecker` opens another.
84
88
  */
85
89
  current: () => boolean
86
90
  close: () => Promise<void>
@@ -102,8 +106,7 @@ export async function openChecker(appRoot: string): Promise<AppChecker> {
102
106
  found === undefined
103
107
  ? await writeDefaultProject(root)
104
108
  : { tsconfig: found, remove: async (): Promise<void> => {} }
105
- const session = await TypeSession.open(project.tsconfig)
106
- session.root = root
109
+ const session = await TypeSession.open(project.tsconfig, root)
107
110
  const moved = new Set<string>()
108
111
  let movedSomewhere = false
109
112
  let layout: Promise<AppLayout> | undefined
@@ -193,7 +196,7 @@ export async function compileApp(
193
196
 
194
197
  /**
195
198
  * The app at `appRoot` compiled — THE ONE PIPELINE `abide build` and `abide check` both run, the
196
- * layout, the reads, the lints and the emit, differing only in what a refusal does (`Refusals`) and
199
+ * layout, the reads, the lints and the emit, differing only in what a refusal does (`Diagnostics`) and
197
200
  * in what is checked beside: a build stops at the first refusal, and a check (`checking`) reports
198
201
  * every one at its file with what the checker says of each file (38.3), and goes on checking
199
202
  * whatever a refusal does not stop (38.33).
@@ -204,11 +207,11 @@ export async function compiled(
204
207
  checking: boolean,
205
208
  ): Promise<{ app: CompiledApp; findings: Diagnosis[] }> {
206
209
  const root = realpathSync(appRoot)
207
- const refusals = new Refusals(root, checking)
210
+ const diagnostics = new Diagnostics(root, checking)
208
211
  // ONE LAYOUT for the sources, the handler files and the public files, and what the tree refuses
209
212
  // is met before the checker is asked anything.
210
213
  const layout = await (kept?.layout() ?? appLayout(root))
211
- refusals.adopt([...layout.unparsed, ...layout.refused])
214
+ diagnostics.adopt([...layout.unparsed, ...layout.refused])
212
215
  const app: CompiledApp = {
213
216
  files: {},
214
217
  tables: {},
@@ -216,7 +219,7 @@ export async function compiled(
216
219
  routes: [],
217
220
  mounts: [],
218
221
  sources: layout.own,
219
- warnings: [],
222
+ warnings: diagnostics.warnings,
220
223
  published: layout.published,
221
224
  }
222
225
  // 16.2 — an app of handler files alone is still an app; one of neither has nothing to build, and
@@ -231,62 +234,94 @@ export async function compiled(
231
234
  // One session for the whole compile: opening it spawns the checker, and the cost is paid once
232
235
  // rather than per file.
233
236
  return withChecker(root, kept, async (session) => {
234
- // 38.33 — A FILE WHOSE PROJECTION IS REFUSED (34.3, 34.17) is refused at its file, found here
235
- // because every later step projects it and would refuse it for the whole app.
236
- const projected: PlacedSource[] = []
237
- for (const source of layout.sources)
238
- try {
239
- projectShadow(source.file)
240
- projected.push(source)
241
- } catch (thrown) {
242
- refusals.at(source.path, thrown)
243
- }
244
- const { read, warnings } = await readSources(
237
+ const emitted = await compiledSources(
238
+ layout,
245
239
  session,
246
- projected,
247
- refusals,
240
+ layout.sources,
241
+ [],
242
+ diagnostics,
243
+ checking,
244
+ true,
248
245
  )
249
- let found: Diagnosis[] = []
250
- let emittable = read
251
- if (checking) {
252
- // THE APP'S OWN FILES ARE REPORTED ON: a dependency's is compiled with them (34.23), and
253
- // what is wrong inside a package is the package's to report.
254
- const reported: typeof read = []
255
- for (const source of read)
256
- if (!source.dependency) reported.push(source)
257
- const diagnosed = await session.diagnose(reported, layout)
258
- found = diagnosed.diagnoses
259
- // A FILE WHOSE LOWERING REFUSED is `found` already, at its line, and is not emitted.
260
- emittable = []
261
- for (const source of read)
262
- if (!diagnosed.refused.has(source.path)) emittable.push(source)
263
- }
264
- const { ambient, unkeyed } = await session.lints(root, projected)
265
- // 11.37 — a build error, every read named.
266
- refusals.adopt(ambient)
267
- // What the emit throws with no one file to name is refused at the root; a refusal of a file
268
- // is refused at it.
269
- let emitWarnings: Diagnosis[] = []
246
+ if (emitted !== undefined) Object.assign(app, emitted)
247
+ return { app, findings: diagnostics.findings }
248
+ })
249
+ }
250
+
251
+ /**
252
+ * What the compile makes of `own`, everything it finds in it kept in `diagnostics` — THE ONE STEP
253
+ * `abide check` runs over the app and an editor over the one file it holds (38.11), so the squiggle
254
+ * and the line CI fails on are one finding: the projection, the reads, the checker's diagnoses
255
+ * (`checking`), the lints and the emit.
256
+ *
257
+ * `beside` is lowered quietly with `own`, for the types it lends (33.4) and reported on by nobody;
258
+ * `scripts` is whether the app's `.ts` files are checked and read for handlers too, which an editor
259
+ * asking about one page does not want. `undefined` where the emit threw, which `diagnostics` holds
260
+ * at the root.
261
+ */
262
+ export async function compiledSources(
263
+ layout: AppLayout,
264
+ session: TypeSession,
265
+ own: readonly PlacedSource[],
266
+ beside: readonly ParsedSource[],
267
+ diagnostics: Diagnostics,
268
+ checking: boolean,
269
+ scripts: boolean,
270
+ ): Promise<Awaited<ReturnType<typeof emitApp>> | undefined> {
271
+ // 38.33 — A FILE WHOSE PROJECTION IS REFUSED (34.3, 34.17) is refused at its file, found here
272
+ // because every later step projects it and would refuse it for the whole app.
273
+ const projected: PlacedSource[] = []
274
+ for (const source of own)
270
275
  try {
271
- const whole = await emitApp(layout, session, emittable, refusals)
272
- Object.assign(app, whole)
273
- emitWarnings = whole.warnings
276
+ projectShadow(source.file)
277
+ projected.push(source)
274
278
  } catch (thrown) {
275
- refusals.at(root, thrown)
279
+ diagnostics.at(source.path, thrown)
276
280
  }
277
- // 11.30 — warned on, and the build goes on.
278
- app.warnings = [...warnings, ...unkeyed, ...emitWarnings]
279
- return {
280
- app,
281
- findings: [
282
- ...refusals.found,
283
- ...unkeyed,
284
- ...warnings,
285
- ...emitWarnings,
286
- ...found,
287
- ],
288
- }
289
- })
281
+ const read = await readSources(
282
+ session,
283
+ [...projected, ...beside],
284
+ diagnostics,
285
+ )
286
+ const placed: ReadSource<PlacedSource>[] = []
287
+ for (const source of read)
288
+ if (!source.quiet) placed.push(source as ReadSource<PlacedSource>)
289
+ let emittable = placed
290
+ if (checking) {
291
+ // THE APP'S OWN FILES ARE REPORTED ON: a dependency's is compiled with them (34.23), and
292
+ // what is wrong inside a package is the package's to report.
293
+ const reported: typeof read = []
294
+ for (const source of read)
295
+ if (source.quiet || !(source as Partial<PlacedSource>).dependency)
296
+ reported.push(source)
297
+ const diagnosed = await session.diagnose(reported, layout, scripts)
298
+ diagnostics.check(diagnosed.diagnoses)
299
+ // A FILE WHOSE LOWERING REFUSED is reported already, at its line, and is not emitted.
300
+ emittable = []
301
+ for (const source of placed)
302
+ if (!diagnosed.refused.has(source.path)) emittable.push(source)
303
+ }
304
+ const { ambient, unkeyed } = await session.lints(
305
+ layout.root,
306
+ projected,
307
+ scripts,
308
+ )
309
+ // 11.37 — a build error, every read named. 11.30 — warned on, and the build goes on.
310
+ diagnostics.adopt(ambient)
311
+ diagnostics.warn(unkeyed)
312
+ // What the emit throws with no one file to name is refused at the root; a refusal of a file
313
+ // is refused at it.
314
+ try {
315
+ return await emitApp(
316
+ scripts ? layout : { ...layout, handlers: [] },
317
+ session,
318
+ emittable,
319
+ diagnostics,
320
+ )
321
+ } catch (thrown) {
322
+ diagnostics.at(layout.root, thrown)
323
+ return undefined
324
+ }
290
325
  }
291
326
 
292
327
  /** The emit half of `compiled`, over sources already laid out and read. */
@@ -297,12 +332,9 @@ async function emitApp(
297
332
  >,
298
333
  session: TypeSession,
299
334
  sources: readonly ReadSource<PlacedSource>[],
300
- refusals: Refusals,
335
+ refusals: Diagnostics,
301
336
  ): Promise<
302
- Pick<
303
- CompiledApp,
304
- 'files' | 'tables' | 'handlers' | 'routes' | 'mounts' | 'warnings'
305
- >
337
+ Pick<CompiledApp, 'files' | 'tables' | 'handlers' | 'routes' | 'mounts'>
306
338
  > {
307
339
  const { root, routes, errors } = layout
308
340
  const files: Record<string, CompiledFile> = {}
@@ -311,7 +343,7 @@ async function emitApp(
311
343
  // and the browser holds its wrapper in its place — a handler file's as that file, a private
312
344
  // handler's in the server block's place in its own file's module. Written whether or not there
313
345
  // is a page: a handler is answered over http to a caller that has none.
314
- const { mounts, warnings } = await handlersUnder(
346
+ const mounts = await handlersUnder(
315
347
  session,
316
348
  sources,
317
349
  layout.handlers,
@@ -387,7 +419,6 @@ async function emitApp(
387
419
  handlers,
388
420
  routes,
389
421
  mounts,
390
- warnings,
391
422
  }
392
423
  }
393
424
 
@@ -442,7 +473,7 @@ function tableImport(source: string): string {
442
473
  function refuseSharedRoutes(
443
474
  routes: AppRoute[],
444
475
  mounts: Mount[],
445
- refusals: Refusals,
476
+ refusals: Diagnostics,
446
477
  ): void {
447
478
  const owners = new Map<string, string>()
448
479
  const claim = (method: string, route: string, source: string): void => {
@@ -476,7 +507,7 @@ function refuseAddressCollision(
476
507
  routes: AppRoute[],
477
508
  mounts: Mount[],
478
509
  published: Map<string, string>,
479
- refusals: Refusals,
510
+ refusals: Diagnostics,
480
511
  ): void {
481
512
  const patterns = new Set<string>()
482
513
  for (const route of routes) patterns.add(route.pattern)
@@ -43,7 +43,7 @@ import {
43
43
  resultSchemaOf,
44
44
  type Walk,
45
45
  } from './argsSchemaOf.ts'
46
- import { diagnosisAt } from './lineOf.ts'
46
+ import { type Diagnosis, diagnosisAt, type Diagnostics } from './Diagnostics.ts'
47
47
  import { isReactiveCall, METHODS, optionNamed } from './reactiveOptions.ts'
48
48
  import {
49
49
  abideIn,
@@ -51,8 +51,6 @@ import {
51
51
  isReactiveMember,
52
52
  valueTypeOf,
53
53
  } from './reactiveTypes.ts'
54
- import type { Refusals } from './Refusals.ts'
55
- import type { Diagnosis } from './TypeSession.ts'
56
54
 
57
55
  /**
58
56
  * Every value each module exports, with the abide method that declares it.
@@ -73,7 +71,7 @@ export async function declaredExports(
73
71
  projectName: string,
74
72
  paths: readonly string[],
75
73
  named: readonly string[],
76
- refusals: Refusals,
74
+ refusals: Diagnostics,
77
75
  ): Promise<(DeclaredExport[] | undefined)[]> {
78
76
  const checker = project.checker
79
77
  // ABIDE'S FILE AS THE CHECKER NAMES IT, not as this module does: it canonicalizes a path to
@@ -143,7 +143,7 @@ class RegionEmission {
143
143
  */
144
144
  export type SelfFill = 'none' | 'routed' | 'invoked' | 'both'
145
145
 
146
- // The mount's third argument, which only the router passes (`Router.mountFrom`).
146
+ // The mount's third argument, which only the router passes (`LevelChain.mountFrom`).
147
147
  const ROUTED = '__routed'
148
148
 
149
149
  /**
@@ -2,7 +2,8 @@ import { dirname } from 'node:path' // bun exposes no path helpers of its own
2
2
 
3
3
  import { SyntaxKind } from 'typescript/unstable/ast'
4
4
 
5
- import { diagnosisAt, offsetOf } from './lineOf.ts'
5
+ import { type Diagnosis, diagnosisAt } from './Diagnostics.ts'
6
+ import { offsetOf } from './lineOf.ts'
6
7
  import { nearest } from './nearest.ts'
7
8
  import {
8
9
  type AbideFile,
@@ -13,7 +14,6 @@ import {
13
14
  } from './parseAbide.ts'
14
15
  import { sameTemplate } from './sameTemplate.ts'
15
16
  import { isSpace, slotEnd, tokenize } from './tokenize.ts'
16
- import type { Diagnosis } from './TypeSession.ts'
17
17
  import { VOID_ELEMENTS } from './VOID_ELEMENTS.ts'
18
18
 
19
19
  // 38.125, 38.126 — A `.abide` IS FORMATTED AS PIECES, and `bun fmt` formats every piece that is code: a
@@ -11,6 +11,7 @@
11
11
 
12
12
  import type { Mount } from '#server/answerRpc.ts'
13
13
  import { frameworkRouteRefusal } from '#server/appTree.ts'
14
+ import { handlerFileMount } from '#server/handlerFileMount.ts'
14
15
  import { PRIVATE_EXPORT } from '#server/PRIVATE_EXPORT.ts'
15
16
  import { surfaceName } from '#server/surfaceOf.ts'
16
17
  import { PRIVATE_HANDLERS_ADDRESS } from '#shared/ADDRESSES.ts'
@@ -18,8 +19,8 @@ import { parseRoutePattern } from '#shared/routePattern.ts'
18
19
 
19
20
  import type { PlacedHandler, PlacedSource } from './appLayout.ts'
20
21
  import { declaredExports } from './declaredExports.ts'
21
- import type { Refusals } from './Refusals.ts'
22
- import type { Diagnosis, TypeSession } from './TypeSession.ts'
22
+ import type { Diagnostics } from './Diagnostics.ts'
23
+ import type { TypeSession } from './TypeSession.ts'
23
24
 
24
25
  /** A handler file, by its path and its path below the app's root. */
25
26
  export type HandlerFile = {
@@ -36,7 +37,7 @@ type Source = Pick<
36
37
  >
37
38
 
38
39
  /**
39
- * The app's handler mounts, with the warnings the build raised reading them.
40
+ * The app's handler mounts, the warnings the build raised reading them kept in `diagnostics`.
40
41
  *
41
42
  * Off one read of the checker. 18.15 — with what the build could not read of a copy's options.
42
43
  * 19.1's one owner per route and method is `compileApp`'s, which holds the pages as well.
@@ -48,8 +49,8 @@ export async function handlersUnder(
48
49
  PlacedHandler,
49
50
  'path' | 'relative' | 'route' | 'scope' | 'matchers'
50
51
  >[],
51
- refusals: Refusals,
52
- ): Promise<{ mounts: Mount[]; warnings: Diagnosis[] }> {
52
+ diagnostics: Diagnostics,
53
+ ): Promise<Mount[]> {
53
54
  const serving: Source[] = []
54
55
  for (const source of sources)
55
56
  if (source.file.servers.length > 0) serving.push(source)
@@ -75,10 +76,9 @@ export async function handlersUnder(
75
76
  session.tsconfig,
76
77
  paths,
77
78
  named,
78
- refusals,
79
+ diagnostics,
79
80
  )
80
81
  const mounts: Mount[] = []
81
- const warnings: Diagnosis[] = []
82
82
  const names = new Map<string, string>()
83
83
  for (let at = 0; at < files.length; at += 1) {
84
84
  const {
@@ -114,27 +114,15 @@ export async function handlersUnder(
114
114
  `abide: ${named} and ${relative} are both named ${surface} on the generated surfaces. RULEBOOK 20.13.`,
115
115
  )
116
116
  names.set(surface, relative)
117
- mounts.push({
118
- address,
119
- method: handler.method,
120
- source: relative,
121
- name: address.slice(address.lastIndexOf('/') + 1),
122
- export: 'default',
123
- scope,
124
- matchers,
125
- private: false,
126
- ui: handler.ui,
127
- cli: handler.cli,
128
- mcp: handler.mcp,
129
- description: handler.description,
130
- copy: handler.copy,
131
- defaults: handler.defaults,
132
- args: handler.args,
133
- result: handler.result,
134
- })
135
- warnings.push(...handler.unread)
117
+ mounts.push(
118
+ handlerFileMount(
119
+ { address, source: relative, scope, matchers },
120
+ { ...handler, method: handler.method },
121
+ ),
122
+ )
123
+ diagnostics.warn(handler.unread)
136
124
  } catch (thrown) {
137
- refusals.at(relative, thrown)
125
+ diagnostics.at(relative, thrown)
138
126
  }
139
127
  }
140
128
  for (let at = 0; at < serving.length; at += 1) {
@@ -164,10 +152,10 @@ export async function handlersUnder(
164
152
  args: each.args,
165
153
  result: each.result,
166
154
  })
167
- warnings.push(...each.unread)
155
+ diagnostics.warn(each.unread)
168
156
  }
169
157
  }
170
- return { mounts, warnings }
158
+ return mounts
171
159
  }
172
160
 
173
161
  /**
@@ -1,5 +1,3 @@
1
- import type { Diagnosis } from './TypeSession.ts'
2
-
3
1
  /**
4
2
  * An offset into a file's text as the line and column an editor shows, both from 1.
5
3
  *
@@ -36,20 +34,3 @@ export function offsetOf(
36
34
  }
37
35
  return at + position.character
38
36
  }
39
-
40
- /** abide's own finding at `offset` into `text`, or at line 0 where the text was not handed in. */
41
- export function diagnosisAt(
42
- path: string,
43
- text: string | undefined,
44
- offset: number,
45
- category: Diagnosis['category'],
46
- message: string,
47
- ): Diagnosis {
48
- return {
49
- path,
50
- ...(text === undefined ? { line: 0, column: 0 } : lineOf(text, offset)),
51
- code: 0,
52
- category,
53
- message,
54
- }
55
- }
@@ -10,8 +10,8 @@
10
10
 
11
11
  import { parseRoutePattern } from '#shared/routePattern.ts'
12
12
 
13
+ import type { Diagnostics } from './Diagnostics.ts'
13
14
  import { matcherOf } from './paramFile.ts'
14
- import type { Refusals } from './Refusals.ts'
15
15
 
16
16
  /** A directory a file mounts (23.19), where it mounts, and the param files under it, by path. */
17
17
  export type MountedTree = { directory: string; at: string; params: string[] }
@@ -28,7 +28,7 @@ type Routed = {
28
28
  * Each routed file's matchers, by its path: every name its route's segments give, to the anchored
29
29
  * source of the param file it resolves to — empty where its route names none.
30
30
  *
31
- * `params` is every param file of the app's own, by path. Refusals land at the file the fault is in:
31
+ * `params` is every param file of the app's own, by path. Diagnostics land at the file the fault is in:
32
32
  * a param file that declares no matcher at itself, a name that resolves to none at the segment's file.
33
33
  */
34
34
  export async function matchersOf(
@@ -36,7 +36,7 @@ export async function matchersOf(
36
36
  routed: readonly Routed[],
37
37
  params: readonly string[],
38
38
  trees: readonly MountedTree[],
39
- refusals: Refusals,
39
+ refusals: Diagnostics,
40
40
  ): Promise<Map<string, Record<string, string>>> {
41
41
  // Every param file's matcher, read once whichever routes name it; `undefined` where 23.24 or
42
42
  // 23.25 refused the file, so a segment naming it refuses nothing more.
@@ -14,9 +14,9 @@ import { dirname, relative, resolve } from 'node:path' // bun exposes no path he
14
14
  import { appFiles } from '#server/appTree.ts'
15
15
  import { placeOf } from '#server/placement.ts'
16
16
 
17
+ import { Diagnostics } from './Diagnostics.ts'
17
18
  import { nearest } from './nearest.ts'
18
19
  import { type AbideFile, scriptBodies } from './parseAbide.ts'
19
- import { Refusals } from './Refusals.ts'
20
20
  import { attributedImports } from './splitImports.ts'
21
21
 
22
22
  /** A `.abide` on disk, and the position it takes in the app. */
@@ -49,7 +49,7 @@ export function mountsOf(
49
49
  path: string,
50
50
  position: string,
51
51
  file: AbideFile,
52
- refusals: Refusals,
52
+ refusals: Diagnostics,
53
53
  ): DirectoryMount[] {
54
54
  const mounts: DirectoryMount[] = []
55
55
  const at = position.includes('/')
@@ -78,7 +78,7 @@ export function mountsOf(
78
78
  return []
79
79
  }
80
80
  // The package's tree is the package's to refuse; a mount places what it finds.
81
- const found = appFiles(directory, new Refusals(directory, true))
81
+ const found = appFiles(directory, new Diagnostics(directory, true))
82
82
  const placed = (below: string): Placed => ({
83
83
  path: `${directory}/${below}`,
84
84
  position: `${at}${below}`,
@@ -1,11 +1,11 @@
1
1
  import { type CheckerAnswer, UNCHECKED } from './CheckerAnswer.ts'
2
+ import type { Diagnostics } from './Diagnostics.ts'
2
3
  import { type AbideFile, parseAbide } from './parseAbide.ts'
3
- import type { Refusals } from './Refusals.ts'
4
- import type { Diagnosis, TypeSession } from './TypeSession.ts'
4
+ import type { TypeSession } from './TypeSession.ts'
5
5
 
6
6
  // EVERY COMPILE OF A `.abide` STARTS THE SAME WAY: the text parsed, then every file read by the
7
7
  // checker in one snapshot. `abide build`, `abide check` and an editor's one file differ only in
8
- // what a refusal does (`Refusals`) — the build stops at it, and a check reports it and goes on with
8
+ // what a refusal does (`Diagnostics`) — the build stops at it, and a check reports it and goes on with
9
9
  // the rest.
10
10
 
11
11
  /** One `.abide`, parsed. `quiet` is read for its props alone and reported on by nobody (33.4). */
@@ -28,7 +28,7 @@ export function parseSources(
28
28
  text: string
29
29
  quiet?: boolean
30
30
  }[],
31
- refusals: Refusals,
31
+ refusals: Diagnostics,
32
32
  ): ParsedSource[] {
33
33
  const parsed: ParsedSource[] = []
34
34
  for (const source of sources) {
@@ -51,37 +51,35 @@ export function parseSources(
51
51
  }
52
52
 
53
53
  /**
54
- * Each parsed source with the checker's answer, and the compiler's warnings — in one snapshot.
54
+ * Each parsed source with the checker's answer — in one snapshot — the compiler's warnings kept in
55
+ * `diagnostics`.
55
56
  *
56
57
  * ONE SNAPSHOT FOR THE LOT, which is the whole of the compiler's build time (`readsFor`). Where
57
- * `refusals` collects, a batch that refuses is asked again a file at a time, so which file it was is
58
- * known and the others still answer; where it throws, so does the batch.
58
+ * `diagnostics` collects, a batch that refuses is asked again a file at a time, so which file it was
59
+ * is known and the others still answer; where it throws, so does the batch.
59
60
  */
60
61
  export async function readSources<Source extends ParsedSource>(
61
62
  session: TypeSession,
62
63
  parsed: readonly Source[],
63
- refusals: Refusals,
64
- ): Promise<{
65
- read: ReadSource<Source>[]
66
- warnings: Diagnosis[]
67
- }> {
64
+ diagnostics: Diagnostics,
65
+ ): Promise<ReadSource<Source>[]> {
68
66
  try {
69
67
  const { answers, warnings } = await session.readsFor(parsed)
70
- return { read: answered(parsed, answers), warnings }
68
+ diagnostics.warn(warnings)
69
+ return answered(parsed, answers)
71
70
  } catch (thrown) {
72
- if (!refusals.collecting) throw thrown
71
+ if (!diagnostics.collecting) throw thrown
73
72
  }
74
73
  const read: ReadSource<Source>[] = []
75
- const warnings: Diagnosis[] = []
76
74
  for (const source of parsed)
77
75
  try {
78
76
  const alone = await session.readsFor([source])
79
77
  read.push(...answered([source], alone.answers))
80
- warnings.push(...alone.warnings)
78
+ diagnostics.warn(alone.warnings)
81
79
  } catch (thrown) {
82
- if (!source.quiet) refusals.at(source.path, thrown)
80
+ if (!source.quiet) diagnostics.at(source.path, thrown)
83
81
  }
84
- return { read, warnings }
82
+ return read
85
83
  }
86
84
 
87
85
  function answered<Source extends ParsedSource>(
@@ -45,7 +45,7 @@ import {
45
45
  type Read,
46
46
  TRANSPARENT,
47
47
  } from './classifyRead.ts'
48
- import type { Finding } from './Finding.ts'
48
+ import type { Finding } from './Diagnostics.ts'
49
49
  import type { AbideFile } from './parseAbide.ts'
50
50
  import {
51
51
  holdsReactive,