@abide/abide 0.41.1 → 0.43.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/AGENTS.md +234 -294
- package/CHANGELOG.md +82 -0
- package/README.md +51 -95
- package/package.json +3 -2
- package/src/abideLsp.ts +46 -8
- package/src/abideResolverPlugin.ts +107 -19
- package/src/buildCli.ts +1 -1
- package/src/discoveryEntry.ts +3 -3
- package/src/lib/bundle/BundleMenuItem.ts +1 -1
- package/src/lib/cli/createClient.ts +12 -12
- package/src/lib/cli/dispatchCommand.ts +1 -1
- package/src/lib/cli/printSessionStatus.ts +1 -1
- package/src/lib/cli/resolveCliTarget.ts +1 -1
- package/src/lib/cli/runCli.ts +1 -1
- package/src/lib/cli/types/CliManifest.ts +1 -1
- package/src/lib/cli/types/CliManifestEntry.ts +2 -2
- package/src/lib/mcp/annotationsForMethod.ts +5 -5
- package/src/lib/mcp/createMcpServer.ts +3 -3
- package/src/lib/mcp/mcpSurface.ts +14 -14
- package/src/lib/mcp/types/McpServerOptions.ts +1 -1
- package/src/lib/server/DELETE.ts +4 -4
- package/src/lib/server/GET.ts +4 -4
- package/src/lib/server/HEAD.ts +4 -4
- package/src/lib/server/PATCH.ts +4 -4
- package/src/lib/server/POST.ts +4 -4
- package/src/lib/server/PUT.ts +4 -4
- package/src/lib/server/agent.ts +4 -4
- package/src/lib/server/env.ts +1 -1
- package/src/lib/server/error.ts +49 -7
- package/src/lib/server/json.ts +1 -1
- package/src/lib/server/rpc/buildErrorConstructors.ts +19 -0
- package/src/lib/server/rpc/{defineVerb.ts → defineRpc.ts} +52 -34
- package/src/lib/server/rpc/{dispatchVerbInProcess.ts → dispatchRpcInProcess.ts} +3 -3
- package/src/lib/server/rpc/fieldErrorsFromIssues.ts +23 -0
- package/src/lib/server/rpc/{findVerbByCommandName.ts → findRpcByCommandName.ts} +5 -5
- package/src/lib/server/rpc/parseArgs.ts +6 -6
- package/src/lib/server/rpc/readBodyWithinLimit.ts +2 -2
- package/src/lib/server/rpc/registerRpc.ts +6 -0
- package/src/lib/server/rpc/{verbRegistry.ts → rpcRegistry.ts} +4 -4
- package/src/lib/server/rpc/{runWithVerbTimeout.ts → runWithRpcTimeout.ts} +4 -4
- package/src/lib/server/rpc/types/RemoteHandler.ts +9 -3
- package/src/lib/server/rpc/types/{VerbHelper.ts → RpcHelper.ts} +57 -23
- package/src/lib/server/rpc/types/{VerbRegistryEntry.ts → RpcRegistryEntry.ts} +3 -3
- package/src/lib/server/rpc/types/TypedResponse.ts +2 -2
- package/src/lib/server/rpc/unprocessed.ts +6 -7
- package/src/lib/server/rpc/validationError.ts +17 -0
- package/src/lib/server/runtime/buildInspectorSurface.ts +7 -7
- package/src/lib/server/runtime/buildOpenApiSpec.ts +6 -6
- package/src/lib/server/runtime/createRouteDispatcher.ts +7 -7
- package/src/lib/server/runtime/createServer.ts +4 -4
- package/src/lib/server/runtime/crossOriginForbidden.ts +1 -1
- package/src/lib/server/runtime/crossOriginGate.ts +4 -4
- package/src/lib/server/runtime/devClientFingerprint.ts +2 -1
- package/src/lib/server/runtime/logExposedSurfaces.ts +4 -4
- package/src/lib/server/runtime/registryManifests.ts +2 -2
- package/src/lib/server/runtime/types/InspectorCacheEntry.ts +1 -1
- package/src/lib/server/runtime/types/InspectorRpc.ts +27 -0
- package/src/lib/server/runtime/types/InspectorSurface.ts +4 -4
- package/src/lib/server/runtime/types/RequestStore.ts +1 -1
- package/src/lib/server/runtime/warnUnguardedMcp.ts +2 -2
- package/src/lib/server/sockets/createSocketDispatcher.ts +15 -14
- package/src/lib/server/sockets/types/SocketOperation.ts +2 -2
- package/src/lib/shared/HttpError.ts +15 -1
- package/src/lib/shared/UNREACHABLE_STATUSES.ts +13 -0
- package/src/lib/shared/buildRpcRequest.ts +5 -5
- package/src/lib/shared/cache.ts +50 -168
- package/src/lib/shared/carriesBodyArgs.ts +4 -4
- package/src/lib/shared/createCacheStore.ts +12 -0
- package/src/lib/shared/createRemoteFunction.ts +11 -6
- package/src/lib/shared/decodeResponse.ts +2 -2
- package/src/lib/shared/detectRpcMethod.ts +17 -0
- package/src/lib/shared/emitLogRecord.ts +3 -3
- package/src/lib/shared/escapeRegex.ts +9 -0
- package/src/lib/shared/extraForwardHeaders.ts +1 -1
- package/src/lib/shared/fileName.ts +9 -0
- package/src/lib/shared/fileStem.ts +3 -1
- package/src/lib/shared/findExportCallSite.ts +30 -273
- package/src/lib/shared/forwardHeaders.ts +2 -2
- package/src/lib/shared/httpErrorFor.ts +26 -0
- package/src/lib/shared/isAsciiWhitespace.ts +5 -0
- package/src/lib/shared/isIdentPart.ts +9 -0
- package/src/lib/shared/isIdentStart.ts +8 -0
- package/src/lib/shared/isReadOnlyMethod.ts +4 -4
- package/src/lib/shared/keyForRemoteCall.ts +4 -4
- package/src/lib/shared/log.ts +1 -1
- package/src/lib/shared/outboxProbeSlot.ts +20 -0
- package/src/lib/shared/pending.ts +4 -1
- package/src/lib/shared/prepareRpcModule.ts +96 -14
- package/src/lib/shared/probeRegistries.ts +17 -4
- package/src/lib/shared/programNameForPackage.ts +3 -1
- package/src/lib/shared/queryStringFromArgs.ts +1 -1
- package/src/lib/shared/remoteMetaStore.ts +2 -2
- package/src/lib/shared/resolveClientFlags.ts +1 -1
- package/src/lib/shared/skipNonCode.ts +230 -0
- package/src/lib/shared/streamResponse.ts +9 -6
- package/src/lib/shared/stripImport.ts +6 -4
- package/src/lib/shared/types/CacheEntry.ts +2 -4
- package/src/lib/shared/types/CacheOnContext.ts +1 -11
- package/src/lib/shared/types/CacheStore.ts +13 -7
- package/src/lib/shared/types/ClientFlags.ts +1 -1
- package/src/lib/shared/types/ErrorConstructors.ts +17 -0
- package/src/lib/shared/types/ErrorDescriptor.ts +10 -0
- package/src/lib/shared/types/ErrorSpec.ts +11 -0
- package/src/lib/shared/types/HttpMethod.ts +1 -0
- package/src/lib/shared/types/Outbox.ts +9 -0
- package/src/lib/shared/types/OutboxEntry.ts +27 -0
- package/src/lib/shared/types/RawRemoteFunction.ts +2 -2
- package/src/lib/shared/types/RemoteCallable.ts +2 -2
- package/src/lib/shared/types/RemoteFunction.ts +21 -6
- package/src/lib/shared/types/RequestScopeInfo.ts +1 -1
- package/src/lib/shared/types/RpcErrorGuard.ts +40 -0
- package/src/lib/shared/types/RpcInvoker.ts +1 -1
- package/src/lib/shared/types/StandardSchemaV1.ts +1 -1
- package/src/lib/shared/types/ValidationErrorData.ts +20 -0
- package/src/lib/shared/url.ts +3 -3
- package/src/lib/shared/writeRpcDts.ts +7 -7
- package/src/lib/shared/writeTestRpcDts.ts +3 -3
- package/src/lib/test/createTestApp.ts +10 -10
- package/src/lib/ui/compile/ABIDE_SEMANTIC_TOKENS_LEGEND.ts +45 -0
- package/src/lib/ui/compile/abideUiPlugin.ts +2 -1
- package/src/lib/ui/compile/awaitPlan.ts +48 -0
- package/src/lib/ui/compile/compileShadow.ts +117 -31
- package/src/lib/ui/compile/createShadowLanguageService.ts +48 -0
- package/src/lib/ui/compile/desugarSignals.ts +42 -6
- package/src/lib/ui/compile/encodeSemanticTokens.ts +37 -0
- package/src/lib/ui/compile/generateBuild.ts +45 -62
- package/src/lib/ui/compile/generateSSR.ts +90 -60
- package/src/lib/ui/compile/ifPlan.ts +30 -0
- package/src/lib/ui/compile/makeVarNamer.ts +10 -0
- package/src/lib/ui/compile/offsetToPosition.ts +9 -0
- package/src/lib/ui/compile/parseTemplate.ts +565 -115
- package/src/lib/ui/compile/stripEffects.ts +17 -9
- package/src/lib/ui/compile/structuralBlockTokens.ts +101 -0
- package/src/lib/ui/compile/switchPlan.ts +22 -0
- package/src/lib/ui/compile/tryPlan.ts +29 -0
- package/src/lib/ui/compile/types/SemanticToken.ts +11 -0
- package/src/lib/ui/compile/types/TemplateAttr.ts +8 -2
- package/src/lib/ui/compile/types/TemplateNode.ts +15 -2
- package/src/lib/ui/createScope.ts +20 -2
- package/src/lib/ui/dom/appendText.ts +1 -5
- package/src/lib/ui/dom/applyResolved.ts +1 -5
- package/src/lib/ui/dom/disposeRange.ts +6 -10
- package/src/lib/ui/dom/hydrate.ts +2 -5
- package/src/lib/ui/dom/isComment.ts +6 -0
- package/src/lib/ui/dom/mount.ts +1 -2
- package/src/lib/ui/dom/withScope.ts +6 -9
- package/src/lib/ui/effect.ts +4 -2
- package/src/lib/ui/navigate.ts +40 -13
- package/src/lib/ui/outbox.ts +30 -110
- package/src/lib/ui/remoteProxy.ts +160 -10
- package/src/lib/ui/router.ts +3 -3
- package/src/lib/ui/rpcOutbox/createOutboxQueue.ts +281 -0
- package/src/lib/ui/rpcOutbox/outboxRegistry.ts +69 -0
- package/src/lib/ui/runtime/createDoc.ts +3 -3
- package/src/lib/ui/runtime/pathSegments.ts +10 -0
- package/src/lib/ui/socketChannel.ts +7 -1
- package/src/lib/ui/types/Scope.ts +20 -5
- package/template/.zed/settings.json +4 -0
- package/template/CLAUDE.md +1 -1
- package/template/src/server/rpc/getHello.ts +3 -3
- package/template/src/ui/pages/page.abide +4 -4
- package/template/test/app.test.ts +2 -2
- package/src/lib/server/rpc/registerVerb.ts +0 -6
- package/src/lib/server/runtime/types/InspectorVerb.ts +0 -27
- package/src/lib/shared/detectVerbMethod.ts +0 -17
- package/src/lib/shared/types/HttpVerb.ts +0 -1
- package/src/lib/ui/types/Outbox.ts +0 -14
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { fileName } from './fileName.ts'
|
|
2
|
+
|
|
1
3
|
/*
|
|
2
4
|
The bare filename of a path, with directory and trailing extension stripped —
|
|
3
5
|
e.g. `users/list.ts` → `list`, `/_virtual/mcp-resources.ts` → `mcp-resources`.
|
|
@@ -5,5 +7,5 @@ Used to derive a virtual-module name from its path and to check an $rpc /
|
|
|
5
7
|
$sockets module's single export name against its file stem.
|
|
6
8
|
*/
|
|
7
9
|
export function fileStem(path: string): string {
|
|
8
|
-
return (path
|
|
10
|
+
return fileName(path).replace(/\.[^.]+$/, '')
|
|
9
11
|
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { isAsciiWhitespace } from './isAsciiWhitespace.ts'
|
|
2
|
+
import { isIdentPart } from './isIdentPart.ts'
|
|
3
|
+
import { isIdentStart } from './isIdentStart.ts'
|
|
4
|
+
import { skipNonCode } from './skipNonCode.ts'
|
|
5
|
+
|
|
1
6
|
/*
|
|
2
7
|
Scans a module source character-by-character — skipping strings,
|
|
3
|
-
templates, comments,
|
|
4
|
-
`export const <name> = <IDENT>(...)` binding the caller
|
|
5
|
-
On a match returns the identifier text, the export name, and
|
|
6
|
-
ranges of the call's open and close parens; the $rpc and $sockets
|
|
8
|
+
templates, comments, regex literals (via skipNonCode), and TypeScript
|
|
9
|
+
generics — for an `export const <name> = <IDENT>(...)` binding the caller
|
|
10
|
+
cares about. On a match returns the identifier text, the export name, and
|
|
11
|
+
the byte ranges of the call's open and close parens; the $rpc and $sockets
|
|
7
12
|
rewriters splice their runtime bindings into those ranges.
|
|
8
13
|
|
|
9
14
|
The scanner enforces a single matching export per module: a second match
|
|
@@ -32,20 +37,12 @@ export function findExportCallSite(
|
|
|
32
37
|
const len = source.length
|
|
33
38
|
let i = 0
|
|
34
39
|
while (i < len) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
i = afterSlash
|
|
39
|
-
continue
|
|
40
|
-
}
|
|
41
|
-
if (c === '"' || c === "'") {
|
|
42
|
-
i = skipString(source, i + 1, c)
|
|
43
|
-
continue
|
|
44
|
-
}
|
|
45
|
-
if (c === '`') {
|
|
46
|
-
i = skipTemplate(source, i + 1)
|
|
40
|
+
const skipped = skipNonCode(source, i)
|
|
41
|
+
if (skipped !== undefined) {
|
|
42
|
+
i = skipped
|
|
47
43
|
continue
|
|
48
44
|
}
|
|
45
|
+
const c = source[i]
|
|
49
46
|
if (isIdentStart(c) && !isIdentPart(source[i - 1])) {
|
|
50
47
|
let j = i + 1
|
|
51
48
|
while (j < len && isIdentPart(source[j])) {
|
|
@@ -84,106 +81,9 @@ export function findExportCallSite(
|
|
|
84
81
|
return found
|
|
85
82
|
}
|
|
86
83
|
|
|
87
|
-
function skipString(source: string, start: number, quote: string): number {
|
|
88
|
-
let i = start
|
|
89
|
-
while (i < source.length) {
|
|
90
|
-
const c = source[i]
|
|
91
|
-
if (c === '\\') {
|
|
92
|
-
i += 2
|
|
93
|
-
continue
|
|
94
|
-
}
|
|
95
|
-
if (c === quote) {
|
|
96
|
-
return i + 1
|
|
97
|
-
}
|
|
98
|
-
if (c === '\n') {
|
|
99
|
-
return i
|
|
100
|
-
}
|
|
101
|
-
i++
|
|
102
|
-
}
|
|
103
|
-
return source.length
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function skipTemplate(source: string, start: number): number {
|
|
107
|
-
let i = start
|
|
108
|
-
while (i < source.length) {
|
|
109
|
-
const c = source[i]
|
|
110
|
-
if (c === '\\') {
|
|
111
|
-
i += 2
|
|
112
|
-
continue
|
|
113
|
-
}
|
|
114
|
-
if (c === '`') {
|
|
115
|
-
return i + 1
|
|
116
|
-
}
|
|
117
|
-
if (c === '$' && source[i + 1] === '{') {
|
|
118
|
-
i = skipTemplateExpression(source, i + 2)
|
|
119
|
-
continue
|
|
120
|
-
}
|
|
121
|
-
i++
|
|
122
|
-
}
|
|
123
|
-
return source.length
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function skipTemplateExpression(source: string, start: number): number {
|
|
127
|
-
let depth = 1
|
|
128
|
-
let i = start
|
|
129
|
-
while (i < source.length && depth > 0) {
|
|
130
|
-
const c = source[i]
|
|
131
|
-
if (c === '{') {
|
|
132
|
-
depth++
|
|
133
|
-
i++
|
|
134
|
-
continue
|
|
135
|
-
}
|
|
136
|
-
if (c === '}') {
|
|
137
|
-
depth--
|
|
138
|
-
i++
|
|
139
|
-
continue
|
|
140
|
-
}
|
|
141
|
-
if (c === '"' || c === "'") {
|
|
142
|
-
i = skipString(source, i + 1, c)
|
|
143
|
-
continue
|
|
144
|
-
}
|
|
145
|
-
if (c === '`') {
|
|
146
|
-
i = skipTemplate(source, i + 1)
|
|
147
|
-
continue
|
|
148
|
-
}
|
|
149
|
-
const afterSlash = skipSlashConstruct(source, i)
|
|
150
|
-
if (afterSlash !== undefined) {
|
|
151
|
-
i = afterSlash
|
|
152
|
-
continue
|
|
153
|
-
}
|
|
154
|
-
i++
|
|
155
|
-
}
|
|
156
|
-
return i
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/*
|
|
160
|
-
When `i` sits on a `/` introducing a line comment, block comment, or regex
|
|
161
|
-
literal, returns the index immediately after the construct; undefined when
|
|
162
|
-
the `/` is division (or `i` isn't a `/`). One statement of the skip rules
|
|
163
|
-
shared by every scanner loop so they can't drift.
|
|
164
|
-
*/
|
|
165
|
-
function skipSlashConstruct(source: string, i: number): number | undefined {
|
|
166
|
-
if (source[i] !== '/') {
|
|
167
|
-
return undefined
|
|
168
|
-
}
|
|
169
|
-
const next = source[i + 1]
|
|
170
|
-
if (next === '/') {
|
|
171
|
-
const newline = source.indexOf('\n', i + 2)
|
|
172
|
-
return newline === -1 ? source.length : newline + 1
|
|
173
|
-
}
|
|
174
|
-
if (next === '*') {
|
|
175
|
-
const end = source.indexOf('*/', i + 2)
|
|
176
|
-
return end === -1 ? source.length : end + 2
|
|
177
|
-
}
|
|
178
|
-
if (isRegexContext(source, i)) {
|
|
179
|
-
return skipRegex(source, i + 1)
|
|
180
|
-
}
|
|
181
|
-
return undefined
|
|
182
|
-
}
|
|
183
|
-
|
|
184
84
|
function matchCallTail(source: string, after: number): number | undefined {
|
|
185
85
|
let j = after
|
|
186
|
-
while (j < source.length &&
|
|
86
|
+
while (j < source.length && isAsciiWhitespace(source[j])) {
|
|
187
87
|
j++
|
|
188
88
|
}
|
|
189
89
|
if (source[j] === '<') {
|
|
@@ -192,7 +92,7 @@ function matchCallTail(source: string, after: number): number | undefined {
|
|
|
192
92
|
return undefined
|
|
193
93
|
}
|
|
194
94
|
j = closed
|
|
195
|
-
while (j < source.length &&
|
|
95
|
+
while (j < source.length && isAsciiWhitespace(source[j])) {
|
|
196
96
|
j++
|
|
197
97
|
}
|
|
198
98
|
}
|
|
@@ -216,15 +116,12 @@ function skipGenerics(source: string, start: number): number | undefined {
|
|
|
216
116
|
let bracketDepth = 0
|
|
217
117
|
let i = start
|
|
218
118
|
while (i < source.length) {
|
|
219
|
-
const
|
|
220
|
-
if (
|
|
221
|
-
i =
|
|
222
|
-
continue
|
|
223
|
-
}
|
|
224
|
-
if (c === '`') {
|
|
225
|
-
i = skipTemplate(source, i + 1)
|
|
119
|
+
const skipped = skipNonCode(source, i)
|
|
120
|
+
if (skipped !== undefined) {
|
|
121
|
+
i = skipped
|
|
226
122
|
continue
|
|
227
123
|
}
|
|
124
|
+
const c = source[i]
|
|
228
125
|
if (c === '<') {
|
|
229
126
|
angleDepth++
|
|
230
127
|
} else if (c === '>') {
|
|
@@ -254,28 +151,20 @@ function skipGenerics(source: string, start: number): number | undefined {
|
|
|
254
151
|
}
|
|
255
152
|
|
|
256
153
|
/*
|
|
257
|
-
Walks the call body, skipping strings/templates/comments and
|
|
258
|
-
nested `()` so brackets inside object literals or nested calls
|
|
259
|
-
throw the depth count.
|
|
154
|
+
Walks the call body, skipping strings/templates/comments/regex and
|
|
155
|
+
respecting nested `()` so brackets inside object literals or nested calls
|
|
156
|
+
don't throw the depth count.
|
|
260
157
|
*/
|
|
261
158
|
function findCallEnd(source: string, parenStart: number): number | undefined {
|
|
262
159
|
let depth = 1
|
|
263
160
|
let i = parenStart + 1
|
|
264
161
|
while (i < source.length) {
|
|
265
|
-
const
|
|
266
|
-
if (
|
|
267
|
-
i =
|
|
268
|
-
continue
|
|
269
|
-
}
|
|
270
|
-
if (c === '`') {
|
|
271
|
-
i = skipTemplate(source, i + 1)
|
|
272
|
-
continue
|
|
273
|
-
}
|
|
274
|
-
const afterSlash = skipSlashConstruct(source, i)
|
|
275
|
-
if (afterSlash !== undefined) {
|
|
276
|
-
i = afterSlash
|
|
162
|
+
const skipped = skipNonCode(source, i)
|
|
163
|
+
if (skipped !== undefined) {
|
|
164
|
+
i = skipped
|
|
277
165
|
continue
|
|
278
166
|
}
|
|
167
|
+
const c = source[i]
|
|
279
168
|
if (c === '(') {
|
|
280
169
|
depth++
|
|
281
170
|
} else if (c === ')') {
|
|
@@ -297,14 +186,14 @@ isn't the module's declared export.
|
|
|
297
186
|
*/
|
|
298
187
|
function detectExportName(source: string, callStart: number): string | undefined {
|
|
299
188
|
let i = callStart - 1
|
|
300
|
-
while (i >= 0 &&
|
|
189
|
+
while (i >= 0 && isAsciiWhitespace(source[i])) {
|
|
301
190
|
i--
|
|
302
191
|
}
|
|
303
192
|
if (source[i] !== '=') {
|
|
304
193
|
return undefined
|
|
305
194
|
}
|
|
306
195
|
i--
|
|
307
|
-
while (i >= 0 &&
|
|
196
|
+
while (i >= 0 && isAsciiWhitespace(source[i])) {
|
|
308
197
|
i--
|
|
309
198
|
}
|
|
310
199
|
const nameEnd = i + 1
|
|
@@ -316,14 +205,14 @@ function detectExportName(source: string, callStart: number): string | undefined
|
|
|
316
205
|
return undefined
|
|
317
206
|
}
|
|
318
207
|
const name = source.slice(nameStart, nameEnd)
|
|
319
|
-
while (i >= 0 &&
|
|
208
|
+
while (i >= 0 && isAsciiWhitespace(source[i])) {
|
|
320
209
|
i--
|
|
321
210
|
}
|
|
322
211
|
if (!matchesBackwards(source, i, 'const')) {
|
|
323
212
|
return undefined
|
|
324
213
|
}
|
|
325
214
|
i -= 'const'.length
|
|
326
|
-
while (i >= 0 &&
|
|
215
|
+
while (i >= 0 && isAsciiWhitespace(source[i])) {
|
|
327
216
|
i--
|
|
328
217
|
}
|
|
329
218
|
if (!matchesBackwards(source, i, 'export')) {
|
|
@@ -342,135 +231,3 @@ function matchesBackwards(source: string, end: number, keyword: string): boolean
|
|
|
342
231
|
}
|
|
343
232
|
return start === 0 || !isIdentPart(source[start - 1])
|
|
344
233
|
}
|
|
345
|
-
|
|
346
|
-
function isIdentStart(c: string | undefined): boolean {
|
|
347
|
-
if (c === undefined) {
|
|
348
|
-
return false
|
|
349
|
-
}
|
|
350
|
-
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c === '_' || c === '$'
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
function isIdentPart(c: string | undefined): boolean {
|
|
354
|
-
if (c === undefined) {
|
|
355
|
-
return false
|
|
356
|
-
}
|
|
357
|
-
return isIdentStart(c) || (c >= '0' && c <= '9')
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function isWhitespace(c: string | undefined): boolean {
|
|
361
|
-
return c === ' ' || c === '\t' || c === '\n' || c === '\r'
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/*
|
|
365
|
-
A `/` starts a regex literal when the prior expression context expects an
|
|
366
|
-
expression rather than a value — after an open delimiter, operator, or
|
|
367
|
-
expression-prefix keyword (return, typeof, instanceof, in, of, delete,
|
|
368
|
-
void, await, yield, new, throw, case, do). Otherwise `/` is division.
|
|
369
|
-
Without this disambiguation a regex like `/^\//` reads as `/` (division),
|
|
370
|
-
then `^`, `\`, `/`, `/` — and the final `//` pair fakes a line comment
|
|
371
|
-
that swallows the rest of the line, eating any `)` that closes the
|
|
372
|
-
enclosing call.
|
|
373
|
-
*/
|
|
374
|
-
const REGEX_PREFIX_KEYWORDS = new Set([
|
|
375
|
-
'return',
|
|
376
|
-
'typeof',
|
|
377
|
-
'instanceof',
|
|
378
|
-
'in',
|
|
379
|
-
'of',
|
|
380
|
-
'delete',
|
|
381
|
-
'void',
|
|
382
|
-
'await',
|
|
383
|
-
'yield',
|
|
384
|
-
'new',
|
|
385
|
-
'throw',
|
|
386
|
-
'case',
|
|
387
|
-
'do',
|
|
388
|
-
])
|
|
389
|
-
|
|
390
|
-
const REGEX_PUNCTUATION = new Set([
|
|
391
|
-
'(',
|
|
392
|
-
'[',
|
|
393
|
-
'{',
|
|
394
|
-
',',
|
|
395
|
-
';',
|
|
396
|
-
':',
|
|
397
|
-
'?',
|
|
398
|
-
'!',
|
|
399
|
-
'&',
|
|
400
|
-
'|',
|
|
401
|
-
'^',
|
|
402
|
-
'~',
|
|
403
|
-
'+',
|
|
404
|
-
'-',
|
|
405
|
-
'*',
|
|
406
|
-
'%',
|
|
407
|
-
'<',
|
|
408
|
-
'>',
|
|
409
|
-
'=',
|
|
410
|
-
'/',
|
|
411
|
-
])
|
|
412
|
-
|
|
413
|
-
function isRegexContext(source: string, slashIndex: number): boolean {
|
|
414
|
-
let i = slashIndex - 1
|
|
415
|
-
while (i >= 0 && isWhitespace(source[i])) {
|
|
416
|
-
i--
|
|
417
|
-
}
|
|
418
|
-
if (i < 0) {
|
|
419
|
-
return true
|
|
420
|
-
}
|
|
421
|
-
const prev = source[i] as string
|
|
422
|
-
if (REGEX_PUNCTUATION.has(prev)) {
|
|
423
|
-
return true
|
|
424
|
-
}
|
|
425
|
-
if (isIdentPart(prev)) {
|
|
426
|
-
let start = i
|
|
427
|
-
while (start > 0 && isIdentPart(source[start - 1])) {
|
|
428
|
-
start--
|
|
429
|
-
}
|
|
430
|
-
return REGEX_PREFIX_KEYWORDS.has(source.slice(start, i + 1))
|
|
431
|
-
}
|
|
432
|
-
return false
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/*
|
|
436
|
-
Walks past a regex literal body, respecting character classes (`[...]`
|
|
437
|
-
where `/` is literal) and backslash escapes, then consumes trailing
|
|
438
|
-
flag identifiers. Returns the index immediately after the regex. An
|
|
439
|
-
unterminated regex (newline before closing `/`) returns the newline
|
|
440
|
-
position so the outer scanner can resume normally on the next line.
|
|
441
|
-
*/
|
|
442
|
-
function skipRegex(source: string, start: number): number {
|
|
443
|
-
let i = start
|
|
444
|
-
let inClass = false
|
|
445
|
-
while (i < source.length) {
|
|
446
|
-
const c = source[i]
|
|
447
|
-
if (c === '\\') {
|
|
448
|
-
i += 2
|
|
449
|
-
continue
|
|
450
|
-
}
|
|
451
|
-
if (c === '\n') {
|
|
452
|
-
return i
|
|
453
|
-
}
|
|
454
|
-
if (inClass) {
|
|
455
|
-
if (c === ']') {
|
|
456
|
-
inClass = false
|
|
457
|
-
}
|
|
458
|
-
i++
|
|
459
|
-
continue
|
|
460
|
-
}
|
|
461
|
-
if (c === '[') {
|
|
462
|
-
inClass = true
|
|
463
|
-
i++
|
|
464
|
-
continue
|
|
465
|
-
}
|
|
466
|
-
if (c === '/') {
|
|
467
|
-
let j = i + 1
|
|
468
|
-
while (j < source.length && isIdentPart(source[j])) {
|
|
469
|
-
j++
|
|
470
|
-
}
|
|
471
|
-
return j
|
|
472
|
-
}
|
|
473
|
-
i++
|
|
474
|
-
}
|
|
475
|
-
return source.length
|
|
476
|
-
}
|
|
@@ -4,8 +4,8 @@ import { extraForwardHeaders } from './extraForwardHeaders.ts'
|
|
|
4
4
|
Headers abide forwards from an inbound HTTP/MCP request onto every
|
|
5
5
|
synthesized in-process rpc Request — cookies + bearer auth + the four
|
|
6
6
|
forwarding hints proxies set when terminating TLS in front of the app.
|
|
7
|
-
|
|
8
|
-
dispatcher uses it when piping a tool invocation through
|
|
7
|
+
defineRpc uses this when an SSR pass calls a rpc in-process; the MCP
|
|
8
|
+
dispatcher uses it when piping a tool invocation through rpc.fetch.
|
|
9
9
|
|
|
10
10
|
WARNING — this is an allowlist: every inbound header NOT named here (and
|
|
11
11
|
not added via app.forwardHeaders) is DROPPED on the in-process path. A
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { contentBodyKind } from './contentBodyKind.ts'
|
|
2
|
+
import { contentTypeOf } from './contentTypeOf.ts'
|
|
3
|
+
import { HttpError } from './HttpError.ts'
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
Builds the HttpError for a non-2xx response, parsing a typed-error body
|
|
7
|
+
(`{ $abideError, data }`, emitted by `error(errors.x(...))` and validation 422)
|
|
8
|
+
onto `.kind` / `.data`. Reads a clone so the original `response.body` stays
|
|
9
|
+
unread for callers that inspect it. A non-JSON or malformed body leaves
|
|
10
|
+
`.kind` / `.data` undefined (a plain `error(status, text)`). Shared by the
|
|
11
|
+
plain decode path (decodeResponse) and the streaming path (streamResponse) so
|
|
12
|
+
both surface the same typed error on a non-2xx.
|
|
13
|
+
*/
|
|
14
|
+
export async function httpErrorFor(response: Response): Promise<HttpError> {
|
|
15
|
+
if (contentBodyKind(contentTypeOf(response.headers)) === 'json') {
|
|
16
|
+
try {
|
|
17
|
+
const body = await response.clone().json()
|
|
18
|
+
if (body !== null && typeof body === 'object' && '$abideError' in body) {
|
|
19
|
+
return new HttpError(response, body.$abideError, body.data)
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
/* malformed JSON error body — fall through to a plain HttpError */
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return new HttpError(response)
|
|
26
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/* True for the source-scanner whitespace set (space, tab, CR, LF). Distinct from
|
|
2
|
+
isWhitespaceText, which classifies template text nodes. */
|
|
3
|
+
export function isAsciiWhitespace(c: string | undefined): boolean {
|
|
4
|
+
return c === ' ' || c === '\t' || c === '\n' || c === '\r'
|
|
5
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { isIdentStart } from './isIdentStart.ts'
|
|
2
|
+
|
|
3
|
+
/* True when `c` can continue a JavaScript identifier — an identifier-start char or a digit. */
|
|
4
|
+
export function isIdentPart(c: string | undefined): boolean {
|
|
5
|
+
if (c === undefined) {
|
|
6
|
+
return false
|
|
7
|
+
}
|
|
8
|
+
return isIdentStart(c) || (c >= '0' && c <= '9')
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/* True when `c` can begin a JavaScript identifier (ASCII letters, `_`, `$`). Shared by the
|
|
2
|
+
source scanners (findExportCallSite, skipNonCode) to find identifiers and regex flags. */
|
|
3
|
+
export function isIdentStart(c: string | undefined): boolean {
|
|
4
|
+
if (c === undefined) {
|
|
5
|
+
return false
|
|
6
|
+
}
|
|
7
|
+
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c === '_' || c === '$'
|
|
8
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HttpMethod } from './types/HttpMethod.ts'
|
|
2
2
|
|
|
3
3
|
/*
|
|
4
4
|
Read-only (safe) HTTP methods — they don't mutate server state. Abide
|
|
5
|
-
uses this to decide which
|
|
5
|
+
uses this to decide which rpcs auto-expose to MCP: reads flip MCP on
|
|
6
6
|
when a schema is present, mutations require an explicit `clients.mcp`
|
|
7
7
|
opt-in so a model can't delete/overwrite data just because the handler
|
|
8
8
|
carries a schema. Also feeds the MCP tool `readOnlyHint` annotation.
|
|
9
9
|
*/
|
|
10
|
-
const READ_ONLY_METHODS = new Set<
|
|
10
|
+
const READ_ONLY_METHODS = new Set<HttpMethod>(['GET', 'HEAD'])
|
|
11
11
|
|
|
12
|
-
export function isReadOnlyMethod(method:
|
|
12
|
+
export function isReadOnlyMethod(method: HttpMethod): boolean {
|
|
13
13
|
return READ_ONLY_METHODS.has(method)
|
|
14
14
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { canonicalJson } from './canonicalJson.ts'
|
|
2
2
|
import { carriesBodyArgs } from './carriesBodyArgs.ts'
|
|
3
3
|
import { queryStringFromArgs } from './queryStringFromArgs.ts'
|
|
4
|
-
import type {
|
|
4
|
+
import type { HttpMethod } from './types/HttpMethod.ts'
|
|
5
5
|
|
|
6
6
|
/*
|
|
7
|
-
Derives a cache key from a
|
|
7
|
+
Derives a cache key from a rpc-defined remote function and its args. The
|
|
8
8
|
prefix is `${method} ${url}` where `url` is the route template. GET/DELETE/HEAD
|
|
9
9
|
serialise args onto the URL as `?key=value` (sorted, via queryStringFromArgs —
|
|
10
10
|
the same encoder buildRpcRequest builds its query with, so the key and the
|
|
11
11
|
synthesized Request can't disagree); POST/PUT/PATCH join args after a space as
|
|
12
|
-
canonical JSON. The
|
|
12
|
+
canonical JSON. The rpc split mirrors buildRpcRequest exactly.
|
|
13
13
|
*/
|
|
14
|
-
export function keyForRemoteCall(method:
|
|
14
|
+
export function keyForRemoteCall(method: HttpMethod, url: string, args: unknown): string {
|
|
15
15
|
const prefix = `${method} ${url}`
|
|
16
16
|
if (!carriesBodyArgs(method)) {
|
|
17
17
|
if (args && typeof args === 'object' && !Array.isArray(args)) {
|
package/src/lib/shared/log.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { Log } from './types/Log.ts'
|
|
|
5
5
|
|
|
6
6
|
/*
|
|
7
7
|
The unified logger: every record carries the request-scope context (short
|
|
8
|
-
trace id, +elapsed,
|
|
8
|
+
trace id, +elapsed, method+path) when one is active, plus a channel — the
|
|
9
9
|
line's speaker. `log(...)`/`warn`/`error`/`trace` speak on the app's own
|
|
10
10
|
always-on channel (the app name, resolved per emission so boot order doesn't
|
|
11
11
|
matter); `log.channel(name)` returns the same shape on a DEBUG-gated
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Internal slot the browser outbox registry registers its prober into, so the
|
|
3
|
+
shared pending() probe can count parked durable-rpc writes without shared/
|
|
4
|
+
importing browser/. Mirrors tailProbeSlot. The prober reads the doc-backed
|
|
5
|
+
queue entries (reactive inside $derived / $effect) and reports whether any
|
|
6
|
+
undelivered entry matches the selector: a durable rpc selector (carries a
|
|
7
|
+
`url`) narrows to its own queue, the bare form spans every registered queue,
|
|
8
|
+
optional args narrow to one parked call by structural compare. Outbox state is
|
|
9
|
+
pending-only — a parked write has no value, so it never contributes to
|
|
10
|
+
refreshing(); probeRegistries reads this slot only on its `field === 'pending'`
|
|
11
|
+
branch, which is why the slot mirrors tailProbeSlot but reports a bare boolean
|
|
12
|
+
rather than a { pending, refreshing } pair. When no prober is registered (server
|
|
13
|
+
render, or no durable rpc was ever used) pending() sees no parked writes,
|
|
14
|
+
exactly as on the server where there are no client queues.
|
|
15
|
+
*/
|
|
16
|
+
export const outboxProbeSlot: {
|
|
17
|
+
probe: ((selector: unknown, args: unknown) => boolean) | undefined
|
|
18
|
+
} = {
|
|
19
|
+
probe: undefined,
|
|
20
|
+
}
|
|
@@ -15,9 +15,12 @@ tail registry (streams). Pending means "no value yet":
|
|
|
15
15
|
pending(subscribable) → that stream awaiting its first frame
|
|
16
16
|
(tail.status === 'pending'; true when nothing
|
|
17
17
|
is reading yet — there is no value either way)
|
|
18
|
+
pending(durableRpc) → that durable rpc's parked writes (a parked write
|
|
19
|
+
has no value yet); the bare form counts them too
|
|
18
20
|
Probes report, never act: reading one opens no fetch and no stream. SSR
|
|
19
21
|
loading state is driven by {#await}, not this. Scan semantics (tap order,
|
|
20
|
-
selector grammar, registry spans) live in
|
|
22
|
+
selector grammar, registry spans, the parked-write term) live in
|
|
23
|
+
probeRegistries.
|
|
21
24
|
*/
|
|
22
25
|
// @documentation probes
|
|
23
26
|
export function pending<Args, Return>(
|