@abide/abide 0.101.0 → 0.103.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.
- package/CHANGELOG.md +26 -0
- package/bin/abide.ts +226 -164
- package/docs/REGISTRY.md +730 -0
- package/docs/RULEBOOK.md +2768 -0
- package/docs/abide.md +13127 -0
- package/package.json +6 -3
- package/scaffold/AGENTS.md +38 -0
- package/scaffold/CLAUDE.md +1 -0
- package/scaffold/src/layout.abide +18 -0
- package/src/compiler/Lowering.ts +78 -9
- package/src/compiler/ShadowMap.ts +2 -2
- package/src/compiler/TypeSession.ts +341 -71
- package/src/compiler/appMap.ts +96 -0
- package/src/compiler/argsSchemaOf.ts +1 -1
- package/src/compiler/checkApp.ts +53 -8
- package/src/compiler/checkedShadow.ts +73 -6
- package/src/compiler/classifyRead.ts +6 -4
- package/src/compiler/classifyTemplate.ts +1 -1
- package/src/compiler/compileApp.ts +598 -200
- package/src/compiler/compiledEntry.ts +48 -40
- package/src/compiler/componentGraph.ts +57 -71
- package/src/compiler/declaredExports.ts +26 -9
- package/src/compiler/emitClient.ts +142 -72
- package/src/compiler/emitComponent.ts +41 -12
- package/src/compiler/emitSource.ts +102 -13
- package/src/compiler/handlersUnder.ts +164 -76
- package/src/compiler/hooksCalled.ts +35 -0
- package/src/compiler/index.ts +7 -0
- package/src/compiler/mountsOf.ts +150 -0
- package/src/compiler/nearest.ts +14 -0
- package/src/compiler/parseAbide.ts +157 -20
- package/src/compiler/projectShadow.ts +250 -19
- package/src/compiler/reactiveTypes.ts +20 -7
- package/src/compiler/readSources.ts +15 -2
- package/src/compiler/readsIn.ts +62 -8
- package/src/compiler/renderMarkup.ts +3 -3
- package/src/compiler/scaffold.ts +37 -15
- package/src/compiler/serveLanguage.ts +101 -34
- package/src/compiler/sharedModuleWrites.ts +2 -2
- package/src/compiler/sourceMapOf.ts +193 -0
- package/src/compiler/splitImports.ts +205 -83
- package/src/compiler/tokenize.ts +14 -0
- package/src/compiler/writeDefaultProject.ts +29 -2
- package/src/server/CLIENT_BUILD.ts +11 -13
- package/src/server/DEFAULT_PORT.ts +1 -1
- package/src/server/MODULE_QUERIES.ts +27 -0
- package/src/server/TABLES.ts +7 -0
- package/src/server/VIRTUAL_DIRECTORY.ts +4 -5
- package/src/server/abideModules.ts +457 -0
- package/src/server/answerFailure.ts +16 -6
- package/src/server/answerRequest.ts +241 -107
- package/src/server/answerRpc.ts +107 -33
- package/src/server/answerSocket.ts +65 -20
- package/src/server/answerValue.ts +22 -9
- package/src/server/appDirectories.ts +1 -1
- package/src/server/appPlugins.ts +1 -1
- package/src/server/appTree.ts +238 -0
- package/src/server/argsFor.ts +2 -2
- package/src/server/assistant.ts +2 -2
- package/src/server/bridgeRelay.ts +6 -8
- package/src/server/claudeCode.ts +0 -6
- package/src/server/clientBuildOptions.ts +8 -3
- package/src/server/coerceArgs.ts +1 -1
- package/src/server/commands.ts +38 -37
- package/src/server/componentModules.d.ts +2 -1
- package/src/server/compressAnswer.ts +4 -4
- package/src/server/config.ts +25 -29
- package/src/server/configFields.ts +31 -25
- package/src/server/createApp.ts +77 -79
- package/src/server/devWorker.ts +118 -181
- package/src/server/executableName.ts +4 -0
- package/src/server/fenceServer.ts +57 -32
- package/src/server/generatedSurfaces.ts +99 -66
- package/src/server/hooks.ts +137 -52
- package/src/server/iconAccent.ts +191 -0
- package/src/server/index.ts +12 -7
- package/src/server/installSourceMaps.ts +93 -0
- package/src/server/launcherOptions.ts +33 -31
- package/src/server/launcherPage.ts +107 -64
- package/src/server/manifestOf.ts +16 -13
- package/src/server/namesUnder.ts +2 -2
- package/src/server/nativeMenu.ts +6 -6
- package/src/server/negotiateEncoding.ts +1 -1
- package/src/server/preload.ts +13 -0
- package/src/server/principal.ts +10 -5
- package/src/server/realOrAsGiven.ts +10 -0
- package/src/server/refuseAddressCollision.ts +21 -14
- package/src/server/remote.ts +49 -52
- package/src/server/render.ts +65 -94
- package/src/server/renderComponent.ts +5 -0
- package/src/server/reportHealth.ts +2 -2
- package/src/server/requestScope.ts +12 -1
- package/src/server/runBuild.ts +24 -0
- package/src/server/runCompiled.ts +5 -5
- package/src/server/runDesktop.ts +48 -28
- package/src/server/serveBuild.ts +43 -52
- package/src/server/serveDev.ts +79 -37
- package/src/server/shell.ts +7 -22
- package/src/server/sourceMappings.ts +139 -0
- package/src/server/surfaceOf.ts +20 -3
- package/src/server/testBuild.ts +1 -19
- package/src/server/withAbideSources.ts +117 -0
- package/src/shared/ADDRESSES.ts +10 -18
- package/src/shared/APP_META.ts +7 -0
- package/src/shared/CopyOptions.ts +1 -1
- package/src/shared/DEBUG_META.ts +1 -1
- package/src/shared/LAUNCHER_FILES.ts +9 -0
- package/src/shared/RESERVED_DIRECTORIES.ts +24 -0
- package/src/shared/cookies.ts +2 -2
- package/src/shared/csp.ts +1 -1
- package/src/shared/escapeText.ts +1 -1
- package/src/shared/filledPath.ts +85 -0
- package/src/shared/framing.ts +79 -6
- package/src/shared/headKeyOf.ts +2 -0
- package/src/shared/index.ts +2 -1
- package/src/shared/log.ts +27 -13
- package/src/shared/matchRoute.ts +23 -4
- package/src/{ui → shared}/menuBridge.ts +2 -2
- package/src/shared/menuItem.ts +4 -4
- package/src/shared/onMenu.ts +58 -0
- package/src/shared/reactive/Keyed.ts +2 -2
- package/src/shared/reactive/NotLanded.ts +1 -1
- package/src/shared/reactive/Reactive.ts +43 -43
- package/src/shared/reactive/argsContract.ts +4 -4
- package/src/shared/reactive/canonicalKey.ts +4 -4
- package/src/shared/reactive/chunksOfStream.ts +6 -0
- package/src/shared/reactive/index.ts +1 -1
- package/src/shared/reactive/recordWrites.ts +103 -5
- package/src/shared/reactive/structural.ts +1 -1
- package/src/shared/reactive/tracking.ts +1 -1
- package/src/shared/reactive/watch.ts +3 -3
- package/src/shared/refuse.ts +1 -1
- package/src/{ui → shared}/reportMenu.ts +8 -10
- package/src/shared/routePattern.ts +122 -48
- package/src/shared/rpc.ts +37 -8
- package/src/shared/rpcRequest.ts +27 -3
- package/src/shared/url.ts +4 -44
- package/src/shared/writePath.ts +1 -1
- package/src/ui/LAYER_NAMES.ts +1 -1
- package/src/ui/List.ts +3 -3
- package/src/ui/MOUNTING_LEVEL.ts +18 -0
- package/src/ui/Mismatch.ts +1 -1
- package/src/ui/RawRun.ts +1 -1
- package/src/ui/Router.ts +37 -36
- package/src/ui/Scope.ts +1 -1
- package/src/ui/TryRegion.ts +2 -2
- package/src/ui/applyHead.ts +33 -0
- package/src/ui/index.ts +1 -1
- package/src/ui/moveNode.ts +1 -1
- package/src/ui/reconcile.ts +2 -2
- package/src/ui/routing.ts +2 -2
- package/src/ui/setAttribute.ts +1 -1
- package/src/ui/setText.ts +1 -1
- package/src/ui/socketWire.ts +9 -7
- package/src/ui/wire.ts +1 -1
- package/scaffold/src/server/app.ts +0 -6
- package/scaffold/src/shared/.gitkeep +0 -0
- package/scaffold/src/ui/app.html +0 -8
- package/src/server/componentImports.ts +0 -56
- /package/scaffold/src/{ui/pages/page.abide → index.abide} +0 -0
- /package/src/{compiler → shared}/SPECIFIERS.ts +0 -0
|
@@ -9,7 +9,7 @@ import { isRecord } from '#shared/isRecord.ts'
|
|
|
9
9
|
import { namedLogger } from '#shared/log.ts'
|
|
10
10
|
import abidePackage from '../../package.json' with { type: 'json' }
|
|
11
11
|
import { appIdentity, config } from './config.ts'
|
|
12
|
-
import { HOOKS } from './hooks.ts'
|
|
12
|
+
import { HOOKS, inOrder } from './hooks.ts'
|
|
13
13
|
|
|
14
14
|
const HEALTH_LOG = namedLogger('abide:health')
|
|
15
15
|
|
|
@@ -23,7 +23,7 @@ export async function reportHealth(): Promise<Health> {
|
|
|
23
23
|
abide: abidePackage.version,
|
|
24
24
|
startedAt: STARTED_AT,
|
|
25
25
|
}
|
|
26
|
-
for (const report of HOOKS.health) {
|
|
26
|
+
for (const report of inOrder(HOOKS.health)) {
|
|
27
27
|
let answered: unknown
|
|
28
28
|
try {
|
|
29
29
|
answered = await report()
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { AsyncLocalStorage } from 'node:async_hooks' // bun implements AsyncLocalStorage and has no context-carrying api of its own
|
|
13
13
|
import { Cookies } from '#shared/cookies.ts'
|
|
14
|
+
import type { Params } from '#shared/matchRoute.ts'
|
|
14
15
|
import type { Principal } from '#shared/principal.ts'
|
|
15
16
|
import { SCOPING, type Scope } from '#shared/reactive/scoping.ts'
|
|
16
17
|
import type { RpcRequest } from '#shared/rpcRequest.ts'
|
|
@@ -51,6 +52,14 @@ export type RequestScope = Scope & {
|
|
|
51
52
|
trace: { id: string; span: string; flags: string } | undefined
|
|
52
53
|
/** `undefined` where the scope was opened without one — 42.8's scope, which 22.1 throws in. */
|
|
53
54
|
request: Request | undefined
|
|
55
|
+
/**
|
|
56
|
+
* The route the request matched, once routing has run: the file route whose hooks answer it.
|
|
57
|
+
*
|
|
58
|
+
* 37.5 — which `onError` hooks an error in this request reaches.
|
|
59
|
+
*/
|
|
60
|
+
route: string | undefined
|
|
61
|
+
/** What that route matched in the address, for the answer its rungs reach (16.14). */
|
|
62
|
+
params: Params | undefined
|
|
54
63
|
/**
|
|
55
64
|
* The app that opened this scope: the `App.fetch` `RPC_SERVER.raw` answers through, and its MCP
|
|
56
65
|
* surface.
|
|
@@ -158,6 +167,8 @@ export function scopeFor(
|
|
|
158
167
|
principal: undefined,
|
|
159
168
|
applied: false,
|
|
160
169
|
request: given,
|
|
170
|
+
route: undefined,
|
|
171
|
+
params: undefined,
|
|
161
172
|
received,
|
|
162
173
|
app,
|
|
163
174
|
response: { status: 200, headers: new Headers() },
|
|
@@ -376,7 +387,7 @@ export function handlerTook(rpc: object, duration: number): void {
|
|
|
376
387
|
/**
|
|
377
388
|
* Adds `duration` to the time spent in rungs.
|
|
378
389
|
*
|
|
379
|
-
* 21.20 — or takes it back out where it was the
|
|
390
|
+
* 21.20 — or takes it back out where it was the center's.
|
|
380
391
|
*/
|
|
381
392
|
export function rungsTook(duration: number): void {
|
|
382
393
|
const timing = STORAGE.getStore()?.timing
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// The CLI, spawned rather than imported: the compiler is not reachable from the server seam.
|
|
2
|
+
const ABIDE_BIN = Bun.fileURLToPath(
|
|
3
|
+
new URL('../../bin/abide.ts', import.meta.url),
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* `abide build` of the app under `root` into `out`, run as the CLI.
|
|
8
|
+
*
|
|
9
|
+
* SPAWNED RATHER THAN IMPORTED: the compiler is not reachable from the server seam.
|
|
10
|
+
*/
|
|
11
|
+
export async function runBuild(root: string, out: string): Promise<void> {
|
|
12
|
+
const child = Bun.spawn(
|
|
13
|
+
[process.execPath, ABIDE_BIN, 'build', '--root', root, '--out', out],
|
|
14
|
+
{ stdout: 'ignore', stderr: 'pipe' },
|
|
15
|
+
)
|
|
16
|
+
const [exitCode, said] = await Promise.all([
|
|
17
|
+
child.exited,
|
|
18
|
+
new Response(child.stderr).text(),
|
|
19
|
+
])
|
|
20
|
+
if (exitCode !== 0)
|
|
21
|
+
throw new Error(
|
|
22
|
+
`abide: could not build the app under ${root}.\n${said}`,
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// 38.
|
|
1
|
+
// 38.50 — A COMPILED APP'S COMMAND LINE: `abide`'s list (`commands.ts`), over the app the executable
|
|
2
2
|
// holds rather than one on disk, and spoken under the name it was started by — a `--platforms` build
|
|
3
3
|
// is `ledger-linux-x64`, and that is what a reader types.
|
|
4
4
|
|
|
@@ -9,11 +9,11 @@ import { type BuildParts, builtSources, serveApp } from './serveBuild.ts'
|
|
|
9
9
|
export async function runCompiled(
|
|
10
10
|
origin: AppOrigin,
|
|
11
11
|
parts: BuildParts,
|
|
12
|
-
// 38.
|
|
13
|
-
// and the app's
|
|
12
|
+
// 38.56 — every specifier a script `run` may import out of the executable: abide's own entries
|
|
13
|
+
// and the app's `.abide` modules, each to the module it holds.
|
|
14
14
|
specifiers: Record<string, () => Promise<Record<string, unknown>>>,
|
|
15
15
|
): Promise<void> {
|
|
16
|
-
// REGISTERED AT START AND LOADED ON FIRST IMPORT:
|
|
16
|
+
// REGISTERED AT START AND LOADED ON FIRST IMPORT: an app module a script never names never
|
|
17
17
|
// runs its top level, as it would not under `abide run`.
|
|
18
18
|
Bun.plugin({
|
|
19
19
|
name: 'abide:compiled-specifiers',
|
|
@@ -30,7 +30,7 @@ export async function runCompiled(
|
|
|
30
30
|
handlers: async () => parts.handlers,
|
|
31
31
|
appPackage: () => origin.package,
|
|
32
32
|
boot: () => bootApp(origin, () => builtSources(parts)),
|
|
33
|
-
// 38.
|
|
33
|
+
// 38.49.
|
|
34
34
|
serve: ({ values: { port } }) =>
|
|
35
35
|
serveApp(
|
|
36
36
|
origin,
|
package/src/server/runDesktop.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// 38.
|
|
1
|
+
// 38.70 — WHAT `abide bundle` WRITES, STARTED WITH NO COMMAND: a window on the app. Given a command
|
|
2
2
|
// it is the executable `abide compile` writes (`runCompiled`), so a terminal still reaches every one.
|
|
3
3
|
//
|
|
4
4
|
// ONE PROCESS. The app, the launcher's own page and the window share it: AppKit's events are drained
|
|
@@ -25,18 +25,19 @@ import {
|
|
|
25
25
|
import { APP_PACKAGE, configuredMount, dataDirectory } from './config.ts'
|
|
26
26
|
import { configFields } from './configFields.ts'
|
|
27
27
|
import { type App, type AppOrigin, bootApp } from './createApp.ts'
|
|
28
|
-
import {
|
|
28
|
+
import { executableName } from './executableName.ts'
|
|
29
|
+
import { HOOKS, inOrder, reportUnexpected } from './hooks.ts'
|
|
29
30
|
import { installServer } from './installServer.ts'
|
|
30
|
-
import
|
|
31
|
+
import { launcherOf } from './launcherOptions.ts'
|
|
31
32
|
import { type Launch, launcherPage } from './launcherPage.ts'
|
|
32
33
|
import { installMenu, menuBar, type NativeItem } from './nativeMenu.ts'
|
|
33
34
|
import { randomToken } from './randomToken.ts'
|
|
34
|
-
import { type Component,
|
|
35
|
+
import { type Component, renderDocument } from './render.ts'
|
|
35
36
|
import { LISTENING } from './requestScope.ts'
|
|
36
37
|
import { runCompiled } from './runCompiled.ts'
|
|
37
38
|
import { type BuildParts, builtSources } from './serveBuild.ts'
|
|
38
39
|
|
|
39
|
-
/** What the launcher did last, and what its forms held, kept across launches. 38.
|
|
40
|
+
/** What the launcher did last, and what its forms held, kept across launches. 38.73. */
|
|
40
41
|
type Remembered = {
|
|
41
42
|
last: { start: Record<string, string> } | { connect: string } | null
|
|
42
43
|
values: Record<string, string>
|
|
@@ -50,15 +51,17 @@ type Failure = {
|
|
|
50
51
|
issues: Record<string, string[]>
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
/** What `abide bundle` read
|
|
54
|
+
/** What `abide bundle` read of the launcher's files, carried in the executable. */
|
|
54
55
|
export type Desktop = {
|
|
55
|
-
|
|
56
|
-
/** 38.83 — `src/bundle/launch.abide`, where the app has one. */
|
|
56
|
+
/** 38.82 — `src/launcher.abide`'s page, where the app has one that paints. */
|
|
57
57
|
launch:
|
|
58
58
|
| (() => Promise<{ default: (props: Launch) => Component }>)
|
|
59
59
|
| undefined
|
|
60
|
-
/** The document the launch page renders into, its stylesheet already in its head. */
|
|
61
|
-
|
|
60
|
+
/** The document the launch page renders into, its stylesheet already in its head (38.84). */
|
|
61
|
+
document: string
|
|
62
|
+
/** 38.101 — the app's icon as a data URL, and the accent taken from it. */
|
|
63
|
+
icon: string | undefined
|
|
64
|
+
accent: string | undefined
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
const LOOPBACK = '127.0.0.1'
|
|
@@ -79,14 +82,24 @@ export async function runDesktop(
|
|
|
79
82
|
async function launch(
|
|
80
83
|
origin: AppOrigin,
|
|
81
84
|
load: () => ReturnType<typeof builtSources>,
|
|
82
|
-
{
|
|
85
|
+
{ launch: launchModule, document, icon, accent }: Desktop,
|
|
83
86
|
): Promise<void> {
|
|
84
87
|
installServer()
|
|
85
88
|
APP_PACKAGE.current = origin.package
|
|
86
|
-
// 37.1 — imported for what it files, so the start form reads `onConfig
|
|
89
|
+
// 37.1 — imported for what it files, so the start form reads `onConfig` and the launcher's
|
|
90
|
+
// `onLauncher` is filed.
|
|
87
91
|
await origin.hooks?.()
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
// 38.85 — a launcher page that fails to load leaves the built-in page, with its defaults.
|
|
93
|
+
const launchPage = await launchModule?.().catch((thrown: unknown) => {
|
|
94
|
+
reportUnexpected(thrown)
|
|
95
|
+
return undefined
|
|
96
|
+
})
|
|
97
|
+
// 38.80 — what its `onLauncher` answers.
|
|
98
|
+
const options = launcherOf(inOrder(HOOKS.launcher).at(-1)?.())
|
|
99
|
+
// 38.81 — an `onLauncher` answer the checker would have refused, written where nothing checked it.
|
|
100
|
+
if (typeof options === 'string')
|
|
101
|
+
throw new Error(`abide: ${options}. RULEBOOK 38.81.`)
|
|
102
|
+
const appName = executableName(origin.package.name)
|
|
90
103
|
const directory = dataDirectory(origin.package.name ?? 'app')
|
|
91
104
|
const stateFile = `${directory}/desktop.json`
|
|
92
105
|
const remembered: Remembered = {
|
|
@@ -149,21 +162,24 @@ async function launch(
|
|
|
149
162
|
)
|
|
150
163
|
return connected === undefined ? onwards() : page('connect')
|
|
151
164
|
}
|
|
165
|
+
// The Connect… menu item asks for the page with its connect form open.
|
|
152
166
|
return page(
|
|
153
167
|
url.searchParams.get('focus') === 'connect'
|
|
154
168
|
? 'connect'
|
|
155
|
-
:
|
|
169
|
+
: undefined,
|
|
156
170
|
)
|
|
157
171
|
},
|
|
158
172
|
})
|
|
159
173
|
const base = `${launcher.url.origin}/${token}/`
|
|
160
|
-
// 38.
|
|
174
|
+
// 38.71 — the answer to a start or a connect that took: the app the window now shows.
|
|
161
175
|
const onwards = (): Response =>
|
|
162
176
|
new Response(null, {
|
|
163
177
|
status: 303,
|
|
164
178
|
headers: { location: current?.url ?? base },
|
|
165
179
|
})
|
|
166
|
-
const page = async (
|
|
180
|
+
const page = async (
|
|
181
|
+
focus: 'start' | 'connect' | undefined,
|
|
182
|
+
): Promise<Response> => {
|
|
167
183
|
const shown = failure
|
|
168
184
|
failure = undefined
|
|
169
185
|
const props: Launch = {
|
|
@@ -173,17 +189,21 @@ async function launch(
|
|
|
173
189
|
fields: configFields(derived()),
|
|
174
190
|
url: remembered.url,
|
|
175
191
|
failure: shown,
|
|
176
|
-
focus:
|
|
192
|
+
focus:
|
|
193
|
+
shown?.form ??
|
|
194
|
+
(focus === 'connect' && options.connect ? focus : undefined),
|
|
195
|
+
icon,
|
|
196
|
+
accent,
|
|
177
197
|
}
|
|
178
198
|
const headers = { 'content-type': 'text/html; charset=utf-8' }
|
|
179
199
|
if (launchPage !== undefined)
|
|
180
200
|
try {
|
|
181
201
|
return new Response(
|
|
182
|
-
await
|
|
202
|
+
await renderDocument(launchPage.default(props), document),
|
|
183
203
|
{ headers },
|
|
184
204
|
)
|
|
185
205
|
} catch (thrown) {
|
|
186
|
-
// 38.
|
|
206
|
+
// 38.85 — the built-in page stands in for one that failed.
|
|
187
207
|
reportUnexpected(thrown)
|
|
188
208
|
}
|
|
189
209
|
return new Response(launcherPage(props), { headers })
|
|
@@ -196,7 +216,7 @@ async function launch(
|
|
|
196
216
|
await leaving.app.stop().catch(reportUnexpected)
|
|
197
217
|
}
|
|
198
218
|
|
|
199
|
-
// 38.
|
|
219
|
+
// 38.72 — the form's values are the environment the app is booted under, so 27.8 coerces them
|
|
200
220
|
// and 27.7's schema refuses them as it would any other.
|
|
201
221
|
async function start(
|
|
202
222
|
values: Record<string, string>,
|
|
@@ -215,7 +235,7 @@ async function launch(
|
|
|
215
235
|
}
|
|
216
236
|
try {
|
|
217
237
|
await app
|
|
218
|
-
// 38.
|
|
238
|
+
// 38.75.
|
|
219
239
|
.listen({ hostname: LOOPBACK, port: remembered.port })
|
|
220
240
|
// The port the last launch served on, so the app's origin — and its storage with
|
|
221
241
|
// it — is the one it had; any free one where that is taken.
|
|
@@ -296,7 +316,7 @@ async function launch(
|
|
|
296
316
|
types: 'Q@:@',
|
|
297
317
|
args: [FFIType.ptr],
|
|
298
318
|
returns: FFIType.u64,
|
|
299
|
-
// NSTerminateCancel: 38.
|
|
319
|
+
// NSTerminateCancel: 38.76 — the app is stopped first, and the process ends after.
|
|
300
320
|
run: () => {
|
|
301
321
|
quit()
|
|
302
322
|
return 0
|
|
@@ -382,20 +402,20 @@ async function launch(
|
|
|
382
402
|
),
|
|
383
403
|
null,
|
|
384
404
|
)
|
|
385
|
-
// 38.
|
|
405
|
+
// 38.88 — THE MENU BAR IS THE PAGE'S: the defaults until the page the window loads tells it
|
|
386
406
|
// what its route exports, and again after every navigation it makes.
|
|
387
407
|
const showMenu = (told: NativeItem[]): void =>
|
|
388
408
|
installMenu(menuBar(appName, options.connect, told), (item) => {
|
|
389
409
|
if (item.role === 'connect')
|
|
390
410
|
return navigate(`${base}?focus=connect`)
|
|
391
|
-
// 38.
|
|
411
|
+
// 38.74.
|
|
392
412
|
if (item.role === 'disconnect') {
|
|
393
413
|
remembered.last = null
|
|
394
414
|
void remember()
|
|
395
415
|
return void disconnect().then(() => navigate(base))
|
|
396
416
|
}
|
|
397
417
|
if (item.role === 'reload') return void send(view, 'reload')
|
|
398
|
-
// 38.
|
|
418
|
+
// 38.90, 38.91 — the page that numbered the item does what it means.
|
|
399
419
|
if (item.id !== undefined)
|
|
400
420
|
send(
|
|
401
421
|
view,
|
|
@@ -433,7 +453,7 @@ async function launch(
|
|
|
433
453
|
nsString('abideMenu'),
|
|
434
454
|
)
|
|
435
455
|
const navigate = (address: string): void => {
|
|
436
|
-
// 38.
|
|
456
|
+
// 38.92.
|
|
437
457
|
showMenu([])
|
|
438
458
|
send(
|
|
439
459
|
view,
|
|
@@ -485,7 +505,7 @@ async function launch(
|
|
|
485
505
|
send(window, 'setTitle:', nsString(title))
|
|
486
506
|
}, DRAIN_MILLISECONDS)
|
|
487
507
|
|
|
488
|
-
// 38.
|
|
508
|
+
// 38.73 — WHAT IT DID LAST, done again.
|
|
489
509
|
const last = remembered.last
|
|
490
510
|
if (options.start && last !== null && 'start' in last)
|
|
491
511
|
await start(last.start)
|
package/src/server/serveBuild.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// 38.6 and 38.
|
|
1
|
+
// 38.6 and 38.10 — `abide start` and a compiled app: the built app, over http. The request path is
|
|
2
2
|
// `answerRequest`'s, shared with `abide dev`; what this module is, is the SUPPLIER — where each part
|
|
3
3
|
// comes from when the parts are what `abide build` produced.
|
|
4
4
|
//
|
|
@@ -8,19 +8,22 @@
|
|
|
8
8
|
|
|
9
9
|
import { CLIENT_ADDRESS } from '#shared/ADDRESSES.ts'
|
|
10
10
|
import {
|
|
11
|
+
type Compilation,
|
|
12
|
+
serveCompilation,
|
|
13
|
+
servedTable,
|
|
14
|
+
} from './abideModules.ts'
|
|
15
|
+
import {
|
|
16
|
+
type AppSources,
|
|
11
17
|
appSources,
|
|
12
|
-
type LoadedSources,
|
|
13
|
-
type ServerTable,
|
|
14
18
|
type SourceParts,
|
|
15
19
|
} from './answerRequest.ts'
|
|
16
|
-
import type { Mount } from './answerRpc.ts'
|
|
17
20
|
import {
|
|
18
21
|
CLIENT_DIRECTORY,
|
|
22
|
+
COMPILATION,
|
|
19
23
|
ENCODING_SUFFIX,
|
|
20
24
|
MANIFEST,
|
|
21
25
|
PUBLIC_DIRECTORY,
|
|
22
26
|
} from './CLIENT_BUILD.ts'
|
|
23
|
-
import { componentImports } from './componentImports.ts'
|
|
24
27
|
import { type App, type AppOrigin, bootApp } from './createApp.ts'
|
|
25
28
|
import { stopOnSignalOrFault } from './faultPolicy.ts'
|
|
26
29
|
import type { Manifest } from './manifestOf.ts'
|
|
@@ -30,13 +33,13 @@ import {
|
|
|
30
33
|
type Encoding,
|
|
31
34
|
negotiateEncoding,
|
|
32
35
|
} from './negotiateEncoding.ts'
|
|
33
|
-
import {
|
|
36
|
+
import { runBuild } from './runBuild.ts'
|
|
34
37
|
|
|
35
38
|
/**
|
|
36
39
|
* What `abide build` produced, as the parts an `App` is booted over.
|
|
37
40
|
*
|
|
38
41
|
* Read off disk by `abide start` (`loadBuild`), and out of the executable itself by a compiled app
|
|
39
|
-
* (38.
|
|
42
|
+
* (38.10). ONE SHAPE, so the two are answered by one path and differ only in where a byte is read
|
|
40
43
|
* from — a file is a PATH in both, and `Bun.file` reads the executable's own the same way it reads
|
|
41
44
|
* the disk's.
|
|
42
45
|
*/
|
|
@@ -53,7 +56,7 @@ export type BuildParts = Omit<SourceParts, 'client' | 'explained'> & {
|
|
|
53
56
|
*/
|
|
54
57
|
export async function serveApp(
|
|
55
58
|
origin: AppOrigin,
|
|
56
|
-
load: () => Promise<
|
|
59
|
+
load: () => Promise<AppSources>,
|
|
57
60
|
port: number | undefined,
|
|
58
61
|
): Promise<App> {
|
|
59
62
|
const app = await bootApp(origin, load)
|
|
@@ -64,63 +67,51 @@ export async function serveApp(
|
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
/**
|
|
67
|
-
*
|
|
70
|
+
* Serves the compilation `abide build` kept in `out` to this process's imports; `false` where there
|
|
71
|
+
* is none, because the app under `root` holds nothing to build.
|
|
68
72
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
73
|
+
* 38.6 — what `abide start` boots: no compiler is loaded, and every `.abide` the app imports is
|
|
74
|
+
* answered out of what the build compiled. 38.100 — AN APP NOBODY BUILT IS BUILT, by `build`, rather
|
|
75
|
+
* than refused, so every caller boots the same way whether or not a build came first. The CLI passes
|
|
76
|
+
* its own build, in-process; anything else spawns it.
|
|
71
77
|
*/
|
|
72
|
-
export function
|
|
78
|
+
export async function serveBuild(
|
|
73
79
|
root: string,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const rpc = (await import(
|
|
83
|
-
Bun.pathToFileURL(`${directory}/rpc.ts`).href
|
|
84
|
-
)) as {
|
|
85
|
-
handlers: Mount[]
|
|
80
|
+
out: string,
|
|
81
|
+
build: (root: string, out: string) => Promise<unknown> = runBuild,
|
|
82
|
+
): Promise<boolean> {
|
|
83
|
+
const path = `${out}/${COMPILATION}`
|
|
84
|
+
if (!(await Bun.file(path).exists())) {
|
|
85
|
+
await build(root, out)
|
|
86
|
+
// A FRESH HANDLE: one taken before the build wrote the file reads it as empty.
|
|
87
|
+
if (!(await Bun.file(path).exists())) return false
|
|
86
88
|
}
|
|
87
|
-
|
|
89
|
+
serveCompilation(root, (await Bun.file(path).json()) as Compilation)
|
|
90
|
+
return true
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* What `abide build` left on disk, as the sources an `App` is booted over — its compilation served
|
|
95
|
+
* already (`serveBuild`).
|
|
96
|
+
*/
|
|
91
97
|
export async function loadBuild(options: {
|
|
92
98
|
/**
|
|
93
|
-
* The app's source root — the directory
|
|
94
|
-
* shell is resolved against.
|
|
99
|
+
* The app's source root — the directory its pages sit under.
|
|
95
100
|
*/
|
|
96
101
|
root: string
|
|
97
102
|
/** Where `abide build` put its output. */
|
|
98
103
|
out: string
|
|
99
|
-
}): Promise<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// independent of each other.
|
|
106
|
-
//
|
|
107
|
-
// THE SHELL IS READ ONCE, at boot. `render`'s own note prices the per-render read at 98% of the
|
|
108
|
-
// op and leaves the caching to whatever server got written; this is that server. The manifest
|
|
109
|
-
// names the stylesheet only where the bundler collected one, so an app that imports none links
|
|
110
|
-
// none.
|
|
111
|
-
const [table, handlers, shell, manifest] = await Promise.all([
|
|
112
|
-
import(
|
|
113
|
-
Bun.pathToFileURL(`${options.out}/server.ts`).href
|
|
114
|
-
) as Promise<ServerTable>,
|
|
115
|
-
handlersIn(options.out),
|
|
116
|
-
shellUnder(options.root),
|
|
104
|
+
}): Promise<AppSources> {
|
|
105
|
+
// THE BOOT READS OVERLAP: the tables and the manifest are independent of each other. The
|
|
106
|
+
// manifest names the stylesheet only where the bundler collected one, so an app that imports
|
|
107
|
+
// none links none.
|
|
108
|
+
const [table, manifest] = await Promise.all([
|
|
109
|
+
servedTable(options.root),
|
|
117
110
|
Bun.file(`${options.out}/${MANIFEST}`).json() as Promise<Manifest>,
|
|
118
111
|
])
|
|
119
112
|
return builtSources({
|
|
120
113
|
table,
|
|
121
|
-
handlers,
|
|
122
|
-
handlerModule: sourceModuleLoader(options.root, options.out),
|
|
123
|
-
shell,
|
|
114
|
+
handlers: table.handlers,
|
|
124
115
|
manifest,
|
|
125
116
|
client: pathsUnder(`${options.out}/${CLIENT_DIRECTORY}`),
|
|
126
117
|
published: pathsUnder(`${options.out}/${PUBLIC_DIRECTORY}`),
|
|
@@ -136,9 +127,9 @@ export function pathsUnder(directory: string): Map<string, string> {
|
|
|
136
127
|
}
|
|
137
128
|
|
|
138
129
|
/** The sources an `App` is booted over, out of the parts `abide build` produced. */
|
|
139
|
-
export function builtSources(parts: BuildParts): Promise<
|
|
130
|
+
export function builtSources(parts: BuildParts): Promise<AppSources> {
|
|
140
131
|
// THE BUNDLE, ENUMERATED RATHER THAN RESOLVED. A request names a file in this set or it names
|
|
141
|
-
// nothing — there is no path to
|
|
132
|
+
// nothing — there is no path to normalize, so `..` is not a case that has to be got right,
|
|
142
133
|
// and what is servable is exactly what the build produced.
|
|
143
134
|
//
|
|
144
135
|
// THE PRECOMPRESSED COPIES ARE NOT ADDRESSES OF THEIR OWN. They are the same file in another
|
|
@@ -173,7 +164,7 @@ export function builtSources(parts: BuildParts): Promise<LoadedSources> {
|
|
|
173
164
|
'content-type': type,
|
|
174
165
|
vary: 'accept-encoding',
|
|
175
166
|
}
|
|
176
|
-
// 38.
|
|
167
|
+
// 38.29 — the precompressed copy where the request takes its encoding.
|
|
177
168
|
const encoding = negotiateEncoding(
|
|
178
169
|
request.headers.get('accept-encoding'),
|
|
179
170
|
encodings,
|