@abide/abide 0.101.0 → 0.102.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.
Files changed (157) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/bin/abide.ts +223 -164
  3. package/docs/REGISTRY.md +726 -0
  4. package/docs/RULEBOOK.md +2753 -0
  5. package/docs/abide.md +12878 -0
  6. package/package.json +5 -3
  7. package/scaffold/AGENTS.md +31 -0
  8. package/scaffold/CLAUDE.md +1 -0
  9. package/scaffold/src/layout.abide +18 -0
  10. package/src/compiler/Lowering.ts +78 -9
  11. package/src/compiler/ShadowMap.ts +2 -2
  12. package/src/compiler/TypeSession.ts +262 -67
  13. package/src/compiler/argsSchemaOf.ts +1 -1
  14. package/src/compiler/checkApp.ts +43 -15
  15. package/src/compiler/checkedShadow.ts +39 -5
  16. package/src/compiler/classifyRead.ts +6 -4
  17. package/src/compiler/classifyTemplate.ts +1 -1
  18. package/src/compiler/compileApp.ts +433 -193
  19. package/src/compiler/compiledEntry.ts +43 -39
  20. package/src/compiler/componentGraph.ts +58 -71
  21. package/src/compiler/declaredExports.ts +24 -7
  22. package/src/compiler/emitClient.ts +141 -71
  23. package/src/compiler/emitComponent.ts +37 -8
  24. package/src/compiler/emitSource.ts +106 -13
  25. package/src/compiler/handlersUnder.ts +106 -59
  26. package/src/compiler/index.ts +7 -0
  27. package/src/compiler/mountsOf.ts +138 -0
  28. package/src/compiler/nearest.ts +14 -0
  29. package/src/compiler/parseAbide.ts +157 -20
  30. package/src/compiler/projectShadow.ts +213 -7
  31. package/src/compiler/reactiveTypes.ts +20 -7
  32. package/src/compiler/readSources.ts +15 -2
  33. package/src/compiler/readsIn.ts +61 -7
  34. package/src/compiler/renderMarkup.ts +3 -3
  35. package/src/compiler/scaffold.ts +30 -11
  36. package/src/compiler/serveLanguage.ts +125 -34
  37. package/src/compiler/sourceMapOf.ts +193 -0
  38. package/src/compiler/splitImports.ts +205 -83
  39. package/src/compiler/syncTypes.ts +32 -0
  40. package/src/compiler/tokenize.ts +14 -0
  41. package/src/compiler/writeDefaultProject.ts +1 -1
  42. package/src/compiler/writeTypes.ts +82 -0
  43. package/src/server/CLIENT_BUILD.ts +11 -13
  44. package/src/server/DEFAULT_PORT.ts +1 -1
  45. package/src/server/LAUNCHER_FILES.ts +9 -0
  46. package/src/server/MODULE_QUERIES.ts +18 -0
  47. package/src/server/TABLES.ts +7 -0
  48. package/src/server/VIRTUAL_DIRECTORY.ts +4 -5
  49. package/src/server/abideModules.ts +391 -0
  50. package/src/server/answerFailure.ts +16 -6
  51. package/src/server/answerRequest.ts +235 -106
  52. package/src/server/answerRpc.ts +102 -30
  53. package/src/server/answerSocket.ts +65 -20
  54. package/src/server/answerValue.ts +22 -9
  55. package/src/server/appDirectories.ts +1 -1
  56. package/src/server/appPlugins.ts +1 -1
  57. package/src/server/appTree.ts +179 -0
  58. package/src/server/argsFor.ts +2 -2
  59. package/src/server/assistant.ts +2 -2
  60. package/src/server/bridgeRelay.ts +6 -8
  61. package/src/server/claudeCode.ts +0 -6
  62. package/src/server/clientBuildOptions.ts +8 -3
  63. package/src/server/coerceArgs.ts +1 -1
  64. package/src/server/commands.ts +37 -37
  65. package/src/server/componentModules.d.ts +3 -1
  66. package/src/server/compressAnswer.ts +4 -4
  67. package/src/server/config.ts +25 -29
  68. package/src/server/configFields.ts +31 -25
  69. package/src/server/createApp.ts +59 -73
  70. package/src/server/devWorker.ts +114 -181
  71. package/src/server/executableName.ts +4 -0
  72. package/src/server/fenceServer.ts +37 -35
  73. package/src/server/generatedSurfaces.ts +99 -66
  74. package/src/server/hooks.ts +124 -52
  75. package/src/server/iconAccent.ts +191 -0
  76. package/src/server/index.ts +11 -7
  77. package/src/server/installSourceMaps.ts +88 -0
  78. package/src/server/launcherOptions.ts +34 -31
  79. package/src/server/launcherPage.ts +107 -64
  80. package/src/server/manifestOf.ts +16 -13
  81. package/src/server/namesUnder.ts +2 -2
  82. package/src/server/nativeMenu.ts +5 -5
  83. package/src/server/negotiateEncoding.ts +1 -1
  84. package/src/server/principal.ts +10 -5
  85. package/src/server/realOrAsGiven.ts +10 -0
  86. package/src/server/refuseAddressCollision.ts +21 -14
  87. package/src/server/remote.ts +49 -52
  88. package/src/server/render.ts +65 -94
  89. package/src/server/renderComponent.ts +5 -0
  90. package/src/server/reportHealth.ts +2 -2
  91. package/src/server/requestScope.ts +12 -1
  92. package/src/server/runBuild.ts +24 -0
  93. package/src/server/runCompiled.ts +5 -5
  94. package/src/server/runDesktop.ts +52 -27
  95. package/src/server/serveBuild.ts +43 -52
  96. package/src/server/serveDev.ts +73 -37
  97. package/src/server/shell.ts +7 -22
  98. package/src/server/sourceMappings.ts +139 -0
  99. package/src/server/surfaceOf.ts +18 -3
  100. package/src/server/testBuild.ts +1 -19
  101. package/src/server/withAbideSources.ts +117 -0
  102. package/src/shared/ADDRESSES.ts +7 -18
  103. package/src/shared/APP_META.ts +7 -0
  104. package/src/shared/CopyOptions.ts +1 -1
  105. package/src/shared/DEBUG_META.ts +1 -1
  106. package/src/shared/RESERVED_DIRECTORIES.ts +15 -0
  107. package/src/shared/cookies.ts +2 -2
  108. package/src/shared/csp.ts +1 -1
  109. package/src/shared/escapeText.ts +1 -1
  110. package/src/shared/filledPath.ts +85 -0
  111. package/src/shared/framing.ts +79 -6
  112. package/src/shared/headKeyOf.ts +2 -0
  113. package/src/shared/index.ts +1 -1
  114. package/src/shared/log.ts +27 -13
  115. package/src/shared/matchRoute.ts +23 -4
  116. package/src/shared/menuItem.ts +3 -3
  117. package/src/shared/reactive/Keyed.ts +2 -2
  118. package/src/shared/reactive/NotLanded.ts +1 -1
  119. package/src/shared/reactive/Reactive.ts +43 -43
  120. package/src/shared/reactive/argsContract.ts +4 -4
  121. package/src/shared/reactive/canonicalKey.ts +4 -4
  122. package/src/shared/reactive/chunksOfStream.ts +6 -0
  123. package/src/shared/reactive/index.ts +1 -1
  124. package/src/shared/reactive/recordWrites.ts +103 -5
  125. package/src/shared/reactive/structural.ts +1 -1
  126. package/src/shared/reactive/tracking.ts +1 -1
  127. package/src/shared/reactive/watch.ts +3 -3
  128. package/src/shared/refuse.ts +1 -1
  129. package/src/shared/routePattern.ts +82 -46
  130. package/src/shared/rpc.ts +37 -8
  131. package/src/shared/rpcRequest.ts +27 -3
  132. package/src/shared/url.ts +4 -44
  133. package/src/shared/writePath.ts +1 -1
  134. package/src/ui/LAYER_NAMES.ts +1 -1
  135. package/src/ui/List.ts +3 -3
  136. package/src/ui/MOUNTING_LEVEL.ts +18 -0
  137. package/src/ui/Mismatch.ts +1 -1
  138. package/src/ui/RawRun.ts +1 -1
  139. package/src/ui/Router.ts +38 -31
  140. package/src/ui/Scope.ts +1 -1
  141. package/src/ui/TryRegion.ts +2 -2
  142. package/src/ui/applyHead.ts +33 -0
  143. package/src/ui/index.ts +1 -1
  144. package/src/ui/menuBridge.ts +1 -1
  145. package/src/ui/moveNode.ts +1 -1
  146. package/src/ui/reconcile.ts +2 -2
  147. package/src/ui/reportMenu.ts +2 -2
  148. package/src/ui/routing.ts +2 -2
  149. package/src/ui/setAttribute.ts +1 -1
  150. package/src/ui/setText.ts +1 -1
  151. package/src/ui/socketWire.ts +9 -7
  152. package/src/ui/wire.ts +1 -1
  153. package/scaffold/src/server/app.ts +0 -6
  154. package/scaffold/src/shared/.gitkeep +0 -0
  155. package/scaffold/src/ui/app.html +0 -8
  156. package/src/server/componentImports.ts +0 -56
  157. /package/scaffold/src/{ui/pages/page.abide → page.abide} +0 -0
@@ -10,19 +10,19 @@ import { namedLogger } from '#shared/log.ts'
10
10
  import { compose } from '#shared/Middleware.ts'
11
11
  import { MOUNT, unmounted } from '#shared/mount.ts'
12
12
  import { RPC_SERVER } from '#shared/RPC_SERVER.ts'
13
- import {
14
- type AppSources,
15
- answerRequest,
16
- type LoadedSources,
17
- } from './answerRequest.ts'
13
+ import { servedTable } from './abideModules.ts'
14
+ import { type AppSources, answerRequest } from './answerRequest.ts'
18
15
  import { Mux, type MuxConnection } from './answerSocket.ts'
16
+ import { appDirectories } from './appDirectories.ts'
17
+ import { holdsAbide } from './appTree.ts'
19
18
  import { buildDirectory } from './buildDirectory.ts'
20
19
  import { APP_PACKAGE, type AppPackage, config, packageUnder } from './config.ts'
21
20
  import { McpSurface } from './generatedSurfaces.ts'
22
- import { HOOKS, reportUnexpected } from './hooks.ts'
21
+ import { HOOKS, inOrder, reportUnexpected, rootRungs } from './hooks.ts'
23
22
  import { installServer } from './installServer.ts'
24
23
  import { admitsMux, upgraded } from './muxUpgrade.ts'
25
24
  import { plainAnswer } from './plainAnswer.ts'
25
+ import { realOrAsGiven } from './realOrAsGiven.ts'
26
26
  import {
27
27
  type AnsweringApp,
28
28
  applyWritten,
@@ -35,7 +35,7 @@ import {
35
35
  import { testBuild } from './testBuild.ts'
36
36
  import { traceResponse } from './trace.ts'
37
37
 
38
- // 28.9 — one line per request.
38
+ // 28.8 — one line per request.
39
39
  const REQUEST_LOG = namedLogger('abide:request')
40
40
 
41
41
  /** The app object: what a host, a test or a script holds the app as. */
@@ -54,29 +54,16 @@ export interface App {
54
54
  [Symbol.asyncDispose](): Promise<void>
55
55
  }
56
56
 
57
- // 37.2 — the two shapes `default` may take.
58
- type OwnRoute =
59
- | ((
60
- request: Request,
61
- server: Bun.Server<unknown> | undefined,
62
- ) => Response | undefined | Promise<Response | undefined>)
63
- | {
64
- fetch: (
65
- request: Request,
66
- server: Bun.Server<unknown> | undefined,
67
- ) => unknown
68
- }
69
-
70
57
  /**
71
58
  * The app this process was started in, booted off what `abide build` left beside it.
72
59
  *
73
- * 42.1 — found where the CLI finds it (38.26), and booted off the build (38.28).
60
+ * 42.1 — found where the CLI finds it (38.25), and booted off the build (38.27).
74
61
  */
75
62
  export async function createApp(): Promise<App> {
76
63
  const root = appRoot(undefined)
77
64
  if (root === undefined)
78
65
  throw new Error(
79
- `abide: createApp found no app. Looked for ui/ in ${process.cwd()} and in ${resolve('src')}.`,
66
+ `abide: createApp found no app. Looked for a .abide under ${resolve('src')}, or under ${process.cwd()} where it has no src.`,
80
67
  )
81
68
  // 42.16 — under `bun test` the app is built for its sources as they stand rather than read off
82
69
  // whatever `abide build` last left, which a test run would otherwise need first and could find
@@ -85,21 +72,34 @@ export async function createApp(): Promise<App> {
85
72
  process.env.NODE_ENV === 'test'
86
73
  ? await testBuild(root)
87
74
  : buildDirectory(root)
88
- const { loadBuild } = await import('./serveBuild.ts')
75
+ const { loadBuild, serveBuild } = await import('./serveBuild.ts')
76
+ if (!(await serveBuild(root, out)))
77
+ throw new Error(
78
+ `abide: createApp found nothing under ${root} to build.`,
79
+ )
89
80
  return bootApp(originUnder(root), () => loadBuild({ root, out }))
90
81
  }
91
82
 
92
83
  /**
93
- * The directory `ui/` sits under, or `undefined` where none is found.
84
+ * The app's root — `--root`, else `src` where the working directory holds one, else the working
85
+ * directory — or `undefined` where it is no app's.
94
86
  *
95
- * 38.26 and 38.27 — `undefined` is a refusal and not a guess.
87
+ * 38.25 and 38.26 — `undefined` is a refusal and not a guess. An app's root is in a package — it or
88
+ * the directory above it holds the `package.json` (`appDirectories`) — and holds a `.abide`
89
+ * file; the package is asked first, so a command run from `/` answers at once rather than
90
+ * walking the disk.
91
+ *
92
+ * ITS REAL PATH, as the bundler and the resolver name every file under it: an app under `/tmp` is
93
+ * `/private/tmp` to both, and one spelling here is one fewer at every reader downstream.
96
94
  */
97
95
  export function appRoot(given: string | undefined): string | undefined {
98
- if (given !== undefined) return resolve(given)
99
- const here = process.cwd()
100
- if (existsSync(`${here}/ui`)) return here
96
+ if (given !== undefined) return realOrAsGiven(resolve(given))
101
97
  const under = resolve('src')
102
- return existsSync(`${under}/ui`) ? under : undefined
98
+ const root = realOrAsGiven(existsSync(under) ? under : process.cwd())
99
+ let packaged = false
100
+ for (const directory of appDirectories(root))
101
+ packaged ||= existsSync(`${directory}/package.json`)
102
+ return packaged && holdsAbide(root) ? root : undefined
103
103
  }
104
104
 
105
105
  /**
@@ -110,26 +110,26 @@ export function appRoot(given: string | undefined): string | undefined {
110
110
  * under.
111
111
  */
112
112
  export type AppOrigin = {
113
- /** The import of `src/server/app.ts`, where the app has one. 37.1. */
114
- hooks: (() => Promise<{ default?: OwnRoute }>) | undefined
113
+ /**
114
+ * Imports every module holding a `<script server>`, where the app has one. 37.1.
115
+ *
116
+ * Imported for what each CALLS: every hook it runs is filed by the time the import settles.
117
+ */
118
+ hooks: (() => Promise<unknown>) | undefined
115
119
  /** 27.3's name and version. */
116
120
  package: AppPackage
117
121
  }
118
122
 
119
- /** The path of the app's hooks module under its source root, where it has one. 37.1. */
120
- export function hooksModuleUnder(root: string): string | undefined {
121
- const hooksModule = `${root}/server/app.ts`
122
- return existsSync(hooksModule) ? hooksModule : undefined
123
- }
124
-
125
- /** The `AppOrigin` of an app on disk, found under its source root. */
123
+ /**
124
+ * The `AppOrigin` of an app on disk: its package under `root`, and its server blocks, out of the
125
+ * compilation this process serves (`serveCompilation`) — the build's, or `abide dev`'s.
126
+ */
126
127
  export function originUnder(root: string): AppOrigin {
127
- const hooksModule = hooksModuleUnder(root)
128
128
  return {
129
- hooks:
130
- hooksModule === undefined
131
- ? undefined
132
- : () => import(Bun.pathToFileURL(hooksModule).href),
129
+ hooks: async () => {
130
+ const table = await servedTable(root)
131
+ await Promise.all(table.servers.map((load) => load()))
132
+ },
133
133
  package: packageUnder(root),
134
134
  }
135
135
  }
@@ -143,15 +143,12 @@ export type BootedApp = App & { openMux(): Mux }
143
143
  /** An `App` booted over the sources `load` supplies, inside the app's `onStart` hooks. */
144
144
  export async function bootApp(
145
145
  origin: AppOrigin,
146
- load: () => Promise<LoadedSources>,
146
+ load: () => Promise<AppSources>,
147
147
  ): Promise<BootedApp> {
148
148
  installServer()
149
- // 37.1 — OPTIONAL, and imported for what it CALLS: every hook it runs is filed by the time the
150
- // import settles.
151
- const own =
152
- origin.hooks === undefined
153
- ? undefined
154
- : ((await origin.hooks()).default ?? undefined)
149
+ // 37.1 — EVERY SERVER BLOCK, imported for what it CALLS: every hook it runs is filed by the
150
+ // time the import settles, which is before `onStart` is composed around the boot.
151
+ if (origin.hooks !== undefined) await origin.hooks()
155
152
 
156
153
  // 37.4 — THE BOOT, INSIDE `onStart`. 37.6 — a throw out of a hook or out of the boot rejects
157
154
  // `createApp`, and nothing is served.
@@ -159,8 +156,8 @@ export async function bootApp(
159
156
  // 42.2 — THE CONFIGURATION IS READ AS PART OF THE BOOT, so a configuration that does not resolve
160
157
  // is a boot that fails rather than a first request that does.
161
158
  APP_PACKAGE.current = origin.package
162
- let sources: LoadedSources | undefined
163
- await compose(HOOKS.starts, async () => {
159
+ let sources: AppSources | undefined
160
+ await compose(inOrder(HOOKS.starts), async () => {
164
161
  config.invalidate()
165
162
  await config
166
163
  sources = await load()
@@ -169,17 +166,16 @@ export async function bootApp(
169
166
  throw new Error(
170
167
  'abide: an onStart hook returned without calling start(), so the app never booted. RULEBOOK 37.4.',
171
168
  )
172
- const booted: AppSources = {
173
- ...sources,
174
- own: own === undefined ? undefined : ownRoute(own),
175
- }
169
+ const booted = sources
176
170
 
177
- // 16.14, 37.3 — THE APP LANE'S ONION, composed once over the whole request path. 21.20 — the
178
- // centre takes its own time back out of what `fetch` counts for the whole onion.
179
- const answer = compose(HOOKS.rungs, async ({ request }) => {
171
+ // 16.14, 37.3 — THE APP LANE'S ONION, composed once over the whole request path: the root
172
+ // route's rungs, the ones below it running after routing (37.2, `answerRequest`). 21.20 — the
173
+ // center takes its own time back out of what `fetch` counts for the whole onion.
174
+ const rungs = rootRungs()
175
+ const answer = compose(rungs, async ({ request }) => {
180
176
  const began = performance.now()
181
177
  const answered = await answerRequest(booted, request)
182
- if (HOOKS.rungs.length > 0) rungsTook(began - performance.now())
178
+ if (rungs.length > 0) rungsTook(began - performance.now())
183
179
  return answered
184
180
  })
185
181
 
@@ -213,14 +209,14 @@ export async function bootApp(
213
209
  let answered: Response
214
210
  try {
215
211
  answered = await answer({ request })
216
- if (HOOKS.rungs.length > 0)
212
+ if (rungs.length > 0)
217
213
  rungsTook(performance.now() - started)
218
214
  } catch (thrown) {
219
215
  reportUnexpected(thrown)
220
216
  answered = plainAnswer('internal server error', 500)
221
217
  }
222
218
  const stamped = stamp(answered)
223
- // 28.8 — a closed namespace does not pay for the line it would have written.
219
+ // 28.7 — a closed namespace does not pay for the line it would have written.
224
220
  if (REQUEST_LOG.enabled())
225
221
  REQUEST_LOG.debug(
226
222
  request.method,
@@ -289,7 +285,7 @@ export async function bootApp(
289
285
  // 16.42 — a stopped app answers no `raw` call made outside every request.
290
286
  if (RPC_SERVER.raw === answerRaw) RPC_SERVER.raw = null
291
287
  bound = undefined
292
- await compose(HOOKS.stops, async () => {})(undefined)
288
+ await compose(inOrder(HOOKS.stops), async () => {})(undefined)
293
289
  }
294
290
 
295
291
  return {
@@ -416,13 +412,3 @@ function belowMount(request: Request): Request | Response {
416
412
  url.pathname = below
417
413
  return new Request(url, request)
418
414
  }
419
-
420
- function ownRoute(
421
- own: OwnRoute,
422
- ): (request: Request) => Promise<Response | undefined> {
423
- const answer = typeof own === 'function' ? own : own.fetch.bind(own)
424
- return async (request) => {
425
- const answered = await answer(request, LISTENING.server)
426
- return answered instanceof Response ? answered : undefined
427
- }
428
- }