@kodycodes/cli 1.25.0 → 1.26.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/README.md +32 -0
- package/dist/capability-proxy.d.ts +37 -0
- package/dist/capability-proxy.js +144 -0
- package/dist/capability-proxy.js.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/dev/dev-assets.d.ts +40 -0
- package/dist/dev/dev-assets.js +202 -0
- package/dist/dev/dev-assets.js.map +1 -0
- package/dist/dev/dev-bundler.d.ts +3 -1
- package/dist/dev/dev-bundler.js +21 -7
- package/dist/dev/dev-bundler.js.map +1 -1
- package/dist/dev/dev-events.d.ts +1 -1
- package/dist/dev/dev-events.js +1 -1
- package/dist/dev/dev-events.js.map +1 -1
- package/dist/dev/dev-port.d.ts +22 -0
- package/dist/dev/dev-port.js +80 -0
- package/dist/dev/dev-port.js.map +1 -0
- package/dist/dev/prebuild.d.ts +17 -0
- package/dist/dev/prebuild.js +102 -0
- package/dist/dev/prebuild.js.map +1 -0
- package/dist/dev/process-reaper.d.ts +3 -0
- package/dist/dev/process-reaper.js +8 -1
- package/dist/dev/process-reaper.js.map +1 -1
- package/dist/dev-command.d.ts +1 -0
- package/dist/dev-command.js +65 -29
- package/dist/dev-command.js.map +1 -1
- package/dist/help.js +7 -0
- package/dist/help.js.map +1 -1
- package/dist/local-execute.js +14 -50
- package/dist/local-execute.js.map +1 -1
- package/dist/local-gateway-fetch-forward.d.ts +16 -0
- package/dist/local-gateway-fetch-forward.js +135 -0
- package/dist/local-gateway-fetch-forward.js.map +1 -0
- package/dist/local-host-runtime-surface.d.ts +55 -0
- package/dist/local-host-runtime-surface.js +75 -0
- package/dist/local-host-runtime-surface.js.map +1 -0
- package/dist/local-package-graph.js +23 -1
- package/dist/local-package-graph.js.map +1 -1
- package/dist/local-package-run.js +14 -50
- package/dist/local-package-run.js.map +1 -1
- package/dist/local-runtime-bridge.d.ts +36 -0
- package/dist/local-runtime-bridge.js +128 -0
- package/dist/local-runtime-bridge.js.map +1 -0
- package/dist/local-runtime-source.d.ts +5 -0
- package/dist/local-runtime-source.js +158 -16
- package/dist/local-runtime-source.js.map +1 -1
- package/dist/mock/mock-supported.d.ts +1 -1
- package/dist/mock/mock-supported.js +1 -0
- package/dist/mock/mock-supported.js.map +1 -1
- package/dist/package-app-dev.d.ts +16 -0
- package/dist/package-app-dev.js +166 -70
- package/dist/package-app-dev.js.map +1 -1
- package/dist/package-local-runtime-source.js +44 -47
- package/dist/package-local-runtime-source.js.map +1 -1
- package/dist/package-manifest.d.ts +11 -0
- package/dist/package-manifest.js +66 -1
- package/dist/package-manifest.js.map +1 -1
- package/dist/structured-error.d.ts +1 -1
- package/dist/structured-error.js.map +1 -1
- package/package.json +1 -1
|
@@ -42,21 +42,136 @@ export function createRuntimeFlatAliasSource() {
|
|
|
42
42
|
const runtime = JSON.stringify(localWorkerModuleNames.runtime);
|
|
43
43
|
return `export * from ${runtime};\nexport { default } from ${runtime};\n`;
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
/** Workerd-side `kody.gatewayFetchStream` + bridge install helpers (shared shape). */
|
|
46
|
+
export function localGatewayFetchStreamRuntimeSource(options) {
|
|
46
47
|
return `
|
|
47
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
48
|
-
|
|
49
48
|
let __kodyBridge = null;
|
|
50
49
|
|
|
51
50
|
export function __kodyInstallLocalBridge(bridge) {
|
|
52
|
-
|
|
51
|
+
// Object form: { call, gatewayFetch }. Legacy function form still works for /call.
|
|
52
|
+
__kodyBridge =
|
|
53
|
+
typeof bridge === 'function' ? { call: bridge, gatewayFetch: null } : bridge;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
function __kodyCall(path, args) {
|
|
56
|
-
if (__kodyBridge === null) {
|
|
57
|
-
throw new Error(
|
|
57
|
+
if (__kodyBridge === null || typeof __kodyBridge.call !== 'function') {
|
|
58
|
+
throw new Error(${JSON.stringify(options.callUnavailableMessage)});
|
|
58
59
|
}
|
|
59
|
-
return __kodyBridge(path, args);
|
|
60
|
+
return __kodyBridge.call(path, args);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const __kodyGatewayNullBodyStatuses = new Set([101, 103, 204, 205, 304]);
|
|
64
|
+
const __kodySecretResolutionHeader = 'x-kody-secret-resolution';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Streaming CapabilityProxy hop (POST /v1/capability-proxy/gateway-fetch via the
|
|
68
|
+
* loopback bridge). Returns a real Response — not the JSON gatewayFetch envelope.
|
|
69
|
+
* See kentcdodds/kody docs/contributing/contracts/cli-streaming-gateway-fetch.md.
|
|
70
|
+
*/
|
|
71
|
+
async function gatewayFetchStream(input) {
|
|
72
|
+
if (__kodyBridge === null || typeof __kodyBridge.gatewayFetch !== 'function') {
|
|
73
|
+
throw new Error(
|
|
74
|
+
'Local execute needs kody.gatewayFetchStream on the host runtime to stream a secret-aware fetch body (multipart, large, or ReadableStream). Upgrade @kodycodes/cli, or buffer the body under 4 MiB for the JSON gatewayFetch hop.',
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const record = input != null && typeof input === 'object' && !Array.isArray(input) ? input : null;
|
|
78
|
+
if (!record) {
|
|
79
|
+
throw new Error('gatewayFetchStream requires a single object argument: { request, packageId? }.');
|
|
80
|
+
}
|
|
81
|
+
const requestValue = record.request;
|
|
82
|
+
if (requestValue == null || typeof requestValue !== 'object' || Array.isArray(requestValue)) {
|
|
83
|
+
throw new Error('gatewayFetchStream requires request: { url, method?, headers?, body? }.');
|
|
84
|
+
}
|
|
85
|
+
const url = typeof requestValue.url === 'string' ? requestValue.url.trim() : '';
|
|
86
|
+
if (!url) {
|
|
87
|
+
throw new Error('gatewayFetchStream request.url must be a non-empty absolute URL.');
|
|
88
|
+
}
|
|
89
|
+
const method =
|
|
90
|
+
typeof requestValue.method === 'string' && requestValue.method.trim()
|
|
91
|
+
? requestValue.method.trim()
|
|
92
|
+
: 'GET';
|
|
93
|
+
const packageId =
|
|
94
|
+
typeof record.packageId === 'string' && record.packageId.trim()
|
|
95
|
+
? record.packageId.trim()
|
|
96
|
+
: undefined;
|
|
97
|
+
const rawHeaders =
|
|
98
|
+
requestValue.headers != null &&
|
|
99
|
+
typeof requestValue.headers === 'object' &&
|
|
100
|
+
!Array.isArray(requestValue.headers)
|
|
101
|
+
? { ...requestValue.headers }
|
|
102
|
+
: {};
|
|
103
|
+
const outboundHeaders = {};
|
|
104
|
+
let contentType = null;
|
|
105
|
+
let secretResolution = null;
|
|
106
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
107
|
+
if (typeof value !== 'string') continue;
|
|
108
|
+
const lower = key.toLowerCase();
|
|
109
|
+
if (lower === 'content-type') {
|
|
110
|
+
contentType = value;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (lower === __kodySecretResolutionHeader) {
|
|
114
|
+
secretResolution = value;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
outboundHeaders[key] = value;
|
|
118
|
+
}
|
|
119
|
+
const headers = {
|
|
120
|
+
'X-Kody-Gateway-Url': url,
|
|
121
|
+
'X-Kody-Gateway-Method': method,
|
|
122
|
+
};
|
|
123
|
+
if (packageId) headers['X-Kody-Gateway-Package-Id'] = packageId;
|
|
124
|
+
if (Object.keys(outboundHeaders).length > 0) {
|
|
125
|
+
headers['X-Kody-Gateway-Headers'] = JSON.stringify(outboundHeaders);
|
|
126
|
+
}
|
|
127
|
+
if (contentType) headers['Content-Type'] = contentType;
|
|
128
|
+
if (secretResolution) headers[__kodySecretResolutionHeader] = secretResolution;
|
|
129
|
+
const body = Object.hasOwn(requestValue, 'body') ? requestValue.body : null;
|
|
130
|
+
const init = {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
headers,
|
|
133
|
+
};
|
|
134
|
+
const upper = method.toUpperCase();
|
|
135
|
+
if (body != null && upper !== 'GET' && upper !== 'HEAD') {
|
|
136
|
+
init.body = body;
|
|
137
|
+
init.duplex = 'half';
|
|
138
|
+
}
|
|
139
|
+
if (requestValue.signal != null) init.signal = requestValue.signal;
|
|
140
|
+
if (requestValue.redirect != null) init.redirect = requestValue.redirect;
|
|
141
|
+
const response = await __kodyBridge.gatewayFetch(init);
|
|
142
|
+
const cacheControl = response.headers.get('cache-control') ?? '';
|
|
143
|
+
const contentTypeHeader = response.headers.get('content-type') ?? '';
|
|
144
|
+
if (response.status === 413 && !contentTypeHeader.includes('application/json')) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
'Request body exceeds the Worker upload limit of 104857600 bytes (100 MiB).',
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (cacheControl.includes('no-store') && contentTypeHeader.includes('application/json')) {
|
|
150
|
+
const text = await response.clone().text();
|
|
151
|
+
try {
|
|
152
|
+
const parsed = JSON.parse(text);
|
|
153
|
+
if (parsed != null && typeof parsed === 'object' && parsed.error != null) {
|
|
154
|
+
const err = parsed.error;
|
|
155
|
+
const message =
|
|
156
|
+
typeof err === 'string'
|
|
157
|
+
? err
|
|
158
|
+
: typeof err?.message === 'string'
|
|
159
|
+
? err.message
|
|
160
|
+
: 'gatewayFetchStream failed.';
|
|
161
|
+
throw new Error(message);
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (__kodyGatewayNullBodyStatuses.has(response.status)) {
|
|
168
|
+
return new Response(null, {
|
|
169
|
+
status: response.status,
|
|
170
|
+
statusText: response.statusText,
|
|
171
|
+
headers: response.headers,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return response;
|
|
60
175
|
}
|
|
61
176
|
|
|
62
177
|
function __kodyNamespace(path) {
|
|
@@ -67,6 +182,13 @@ function __kodyNamespace(path) {
|
|
|
67
182
|
return undefined;
|
|
68
183
|
}
|
|
69
184
|
if (property === 'toString' || property === 'inspect') return () => label;
|
|
185
|
+
if (
|
|
186
|
+
path.length === 1 &&
|
|
187
|
+
path[0] === 'kody' &&
|
|
188
|
+
property === 'gatewayFetchStream'
|
|
189
|
+
) {
|
|
190
|
+
return gatewayFetchStream;
|
|
191
|
+
}
|
|
70
192
|
return __kodyNamespace([...path, property]);
|
|
71
193
|
},
|
|
72
194
|
apply(_target, _thisArg, args) {
|
|
@@ -74,6 +196,15 @@ function __kodyNamespace(path) {
|
|
|
74
196
|
},
|
|
75
197
|
});
|
|
76
198
|
}
|
|
199
|
+
`.trimStart();
|
|
200
|
+
}
|
|
201
|
+
export function createLocalRuntimeModuleSource() {
|
|
202
|
+
return `
|
|
203
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
204
|
+
|
|
205
|
+
${localGatewayFetchStreamRuntimeSource({
|
|
206
|
+
callUnavailableMessage: 'kody:runtime is only available while an execute --local run is in progress.',
|
|
207
|
+
})}
|
|
77
208
|
|
|
78
209
|
export const kody = __kodyNamespace(['kody']);
|
|
79
210
|
|
|
@@ -303,15 +434,26 @@ export default {
|
|
|
303
434
|
}
|
|
304
435
|
__kodyClaimed = true;
|
|
305
436
|
const { params } = await request.json();
|
|
306
|
-
__kodyInstallLocalBridge(
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
437
|
+
__kodyInstallLocalBridge({
|
|
438
|
+
call: async (path, args) => {
|
|
439
|
+
const response = await env.KODY_BRIDGE.fetch('http://kody-bridge/call', {
|
|
440
|
+
method: 'POST',
|
|
441
|
+
headers: { 'content-type': 'application/json', [__kodyRunSecretHeader]: secret },
|
|
442
|
+
body: JSON.stringify({ path, args }),
|
|
443
|
+
});
|
|
444
|
+
const data = await response.json();
|
|
445
|
+
if (data.error) throw new Error(data.error);
|
|
446
|
+
return data.result;
|
|
447
|
+
},
|
|
448
|
+
gatewayFetch: async (init) => {
|
|
449
|
+
const headers = new Headers(init?.headers);
|
|
450
|
+
headers.set(__kodyRunSecretHeader, secret);
|
|
451
|
+
return await env.KODY_BRIDGE.fetch('http://kody-bridge/gateway-fetch', {
|
|
452
|
+
...init,
|
|
453
|
+
method: 'POST',
|
|
454
|
+
headers,
|
|
455
|
+
});
|
|
456
|
+
},
|
|
315
457
|
});
|
|
316
458
|
const logs = [];
|
|
317
459
|
const nativeConsole = globalThis.console;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-runtime-source.js","sourceRoot":"","sources":["../src/local-runtime-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,uCAAuC,CAAA;AAErF,MAAM,CAAC,MAAM,6BAA6B,GAAG,YAAY,CAAA;AACzD,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,eAAe,CAAU,CAAA;AAExE,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACrC,KAAK,EAAE,uBAAuB;IAC9B,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,cAAc;IACvB;;;;;OAKG;IACH,gBAAgB,EAAE,sBAAsB;CAC/B,CAAA;AAEV,qFAAqF;AACrF,MAAM,CAAC,MAAM,eAAe,GAAG,mBAAmB,CAAA;AAClD,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAA;AAEhD,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mCAAmC,GAAG,qBAAqB,CAAA;AAExE;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B;IAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAA;IAC9D,OAAO,iBAAiB,OAAO,8BAA8B,OAAO,KAAK,CAAA;AAC1E,CAAC;AAED,MAAM,UAAU,8BAA8B;IAC7C,OAAO
|
|
1
|
+
{"version":3,"file":"local-runtime-source.js","sourceRoot":"","sources":["../src/local-runtime-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,4 +15,4 @@ export type MockCapabilityPath = (typeof mockCapabilityPaths)[number];
|
|
|
15
15
|
export declare function isMockOpenApiOperation(id: string): id is MockOpenApiOperationId;
|
|
16
16
|
export declare function isMockCapabilityPath(pathKey: string): pathKey is MockCapabilityPath;
|
|
17
17
|
/** CapabilityProxy routes always present on every target. */
|
|
18
|
-
export declare const mockCapabilityProxyRoutes: readonly ["GET /v1/capability-proxy/session", "POST /v1/capability-proxy/call", "POST /v1/local-execute/package-graph"];
|
|
18
|
+
export declare const mockCapabilityProxyRoutes: readonly ["GET /v1/capability-proxy/session", "POST /v1/capability-proxy/call", "POST /v1/capability-proxy/gateway-fetch", "POST /v1/local-execute/package-graph"];
|
|
@@ -57,6 +57,7 @@ export function isMockCapabilityPath(pathKey) {
|
|
|
57
57
|
export const mockCapabilityProxyRoutes = [
|
|
58
58
|
'GET /v1/capability-proxy/session',
|
|
59
59
|
'POST /v1/capability-proxy/call',
|
|
60
|
+
'POST /v1/capability-proxy/gateway-fetch',
|
|
60
61
|
'POST /v1/local-execute/package-graph',
|
|
61
62
|
];
|
|
62
63
|
//# sourceMappingURL=mock-supported.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock-supported.js","sourceRoot":"","sources":["../../src/mock/mock-supported.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,8CAA8C;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACtC,QAAQ;IACR,oBAAoB;IACpB,UAAU;CACD,CAAA;AAEV,6EAA6E;AAC7E,MAAM,CAAC,MAAM,mBAAmB,GAAG;IAClC,yBAAyB;IACzB,aAAa;IACb,cAAc;IACd,eAAe;IACf,6EAA6E;IAC7E,gEAAgE;IAChE,8BAA8B;IAC9B,8BAA8B;IAC9B,iCAAiC;IACjC,+BAA+B;IAC/B,8BAA8B;IAC9B,yEAAyE;IACzE,8DAA8D;IAC9D,wBAAwB;IACxB,wBAAwB;IACxB,2BAA2B;IAC3B,yBAAyB;IACzB,wBAAwB;IACxB,wEAAwE;IACxE,2BAA2B;IAC3B,8BAA8B;IAC9B,4BAA4B;IAC5B,8BAA8B;IAC9B,yEAAyE;IACzE,4EAA4E;IAC5E,wEAAwE;IACxE,0DAA0D;IAC1D,yBAAyB;IACzB,mBAAmB;IACnB,yBAAyB;IACzB,8EAA8E;IAC9E,oBAAoB;CACX,CAAA;AAKV,MAAM,UAAU,sBAAsB,CAAC,EAAU;IAChD,OAAQ,uBAAiD,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;AACvE,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAe;IACnD,OAAQ,mBAA6C,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;AACxE,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,kCAAkC;IAClC,gCAAgC;IAChC,sCAAsC;CAC7B,CAAA"}
|
|
1
|
+
{"version":3,"file":"mock-supported.js","sourceRoot":"","sources":["../../src/mock/mock-supported.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,8CAA8C;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACtC,QAAQ;IACR,oBAAoB;IACpB,UAAU;CACD,CAAA;AAEV,6EAA6E;AAC7E,MAAM,CAAC,MAAM,mBAAmB,GAAG;IAClC,yBAAyB;IACzB,aAAa;IACb,cAAc;IACd,eAAe;IACf,6EAA6E;IAC7E,gEAAgE;IAChE,8BAA8B;IAC9B,8BAA8B;IAC9B,iCAAiC;IACjC,+BAA+B;IAC/B,8BAA8B;IAC9B,yEAAyE;IACzE,8DAA8D;IAC9D,wBAAwB;IACxB,wBAAwB;IACxB,2BAA2B;IAC3B,yBAAyB;IACzB,wBAAwB;IACxB,wEAAwE;IACxE,2BAA2B;IAC3B,8BAA8B;IAC9B,4BAA4B;IAC5B,8BAA8B;IAC9B,yEAAyE;IACzE,4EAA4E;IAC5E,wEAAwE;IACxE,0DAA0D;IAC1D,yBAAyB;IACzB,mBAAmB;IACnB,yBAAyB;IACzB,8EAA8E;IAC9E,oBAAoB;CACX,CAAA;AAKV,MAAM,UAAU,sBAAsB,CAAC,EAAU;IAChD,OAAQ,uBAAiD,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;AACvE,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAe;IACnD,OAAQ,mBAA6C,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;AACxE,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,kCAAkC;IAClC,gCAAgC;IAChC,yCAAyC;IACzC,sCAAsC;CAC7B,CAAA"}
|
|
@@ -19,7 +19,13 @@ export type PackageAppDevOptions = {
|
|
|
19
19
|
apiUrl: string;
|
|
20
20
|
target: KodyTarget;
|
|
21
21
|
host?: string;
|
|
22
|
+
/** Listen on exactly this port (0 = any free port); EADDRINUSE is an error. */
|
|
22
23
|
port?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Used when `port` is unset: try this port, then the next free ones above
|
|
26
|
+
* it. Without either, any free port.
|
|
27
|
+
*/
|
|
28
|
+
defaultPort?: number;
|
|
23
29
|
/** Public mount path the Node proxy strips (default ''). */
|
|
24
30
|
appBasePath?: string;
|
|
25
31
|
/** User-owned token org selection (`?org=`), same as execute --local. */
|
|
@@ -58,6 +64,16 @@ export type PackageAppDevOptions = {
|
|
|
58
64
|
onError?: (message: string) => void;
|
|
59
65
|
/** Rebuilds, build/runtime errors, and app logs. */
|
|
60
66
|
onEvent?: (event: DevEvent) => void;
|
|
67
|
+
/**
|
|
68
|
+
* Fired as soon as the HTTP port is bound (before the first build). Used
|
|
69
|
+
* to advertise the server in the attach registry so a second `kody dev`
|
|
70
|
+
* cannot start during the initial build window.
|
|
71
|
+
*/
|
|
72
|
+
onListening?: (info: {
|
|
73
|
+
port: number;
|
|
74
|
+
hostedUrl: string;
|
|
75
|
+
appBasePath: string;
|
|
76
|
+
}) => void | Promise<void>;
|
|
61
77
|
};
|
|
62
78
|
export type PackageAppDevServer = {
|
|
63
79
|
url: string;
|
package/dist/package-app-dev.js
CHANGED
|
@@ -13,17 +13,22 @@ import { rmSync } from 'node:fs';
|
|
|
13
13
|
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
14
14
|
import { createServer, request as httpRequest, } from 'node:http';
|
|
15
15
|
import { tmpdir } from 'node:os';
|
|
16
|
-
import { join, relative } from 'node:path';
|
|
16
|
+
import { join, relative, resolve, sep } from 'node:path';
|
|
17
17
|
import { assertTokenSafeApiUrl, callCapabilityProxy, } from './capability-proxy.js';
|
|
18
18
|
import { apiTokenEnvVar } from './defaults.js';
|
|
19
|
+
import { createDevClientBundler, parseDevAssetPath, serveDevAsset, } from './dev/dev-assets.js';
|
|
20
|
+
import { forwardLocalGatewayFetchStream } from './local-gateway-fetch-forward.js';
|
|
21
|
+
import { startLocalRuntimeBridge, } from './local-runtime-bridge.js';
|
|
19
22
|
import { createDevBundler } from './dev/dev-bundler.js';
|
|
20
23
|
import { parseWorkerdStdoutLine } from './dev/dev-events.js';
|
|
21
24
|
import { devErrorHeader, devRequestIdHeader } from './dev/dev-protocol.js';
|
|
25
|
+
import { listenDevServer } from './dev/dev-port.js';
|
|
22
26
|
import { devStatusPath } from './dev/dev-registry.js';
|
|
23
27
|
import { createDevWatcher } from './dev/dev-watcher.js';
|
|
24
28
|
import { sendDevError } from './dev/error-page.js';
|
|
25
29
|
import { createLiveReloadHub, injectLiveReloadScript, liveReloadClientPath, liveReloadClientSource, liveReloadEventsPath, liveReloadScriptTag, shouldInjectLiveReload, } from './dev/live-reload.js';
|
|
26
30
|
import { devWorkDirPrefix, startProcessReaper, sweepStaleDevDirs, } from './dev/process-reaper.js';
|
|
31
|
+
import { runPrebuild } from './dev/prebuild.js';
|
|
27
32
|
import { createStackMapper } from './dev/stack-trace.js';
|
|
28
33
|
import { cleanWorkerdStderrLine, startWorkerd, } from './dev/workerd-process.js';
|
|
29
34
|
import { BundleError, formatBundleDiagnostic } from './local-bundle.js';
|
|
@@ -36,7 +41,9 @@ import { createPackageAppEntrySource, createPackageLocalRuntimeModuleSource, run
|
|
|
36
41
|
import { resolvePackagePath } from './package-manifest.js';
|
|
37
42
|
import { redact } from './redact.js';
|
|
38
43
|
import { ensureWorkerdBinary } from './workerd-binary.js';
|
|
39
|
-
const defaultDebounceMs =
|
|
44
|
+
const defaultDebounceMs = 30;
|
|
45
|
+
/** Watcher events this soon after a prebuild finishes are its own writes, not edits. */
|
|
46
|
+
const prebuildSettleMs = 150;
|
|
40
47
|
/** Longest a request waits for an in-progress rebuild before the old build answers. */
|
|
41
48
|
const rebuildGateMs = 10_000;
|
|
42
49
|
/** Longest a retired generation may keep draining in-flight requests. */
|
|
@@ -113,18 +120,48 @@ export async function startPackageAppDev(options) {
|
|
|
113
120
|
const proxy = createServer((req, res) => {
|
|
114
121
|
void handleRequest(req, res);
|
|
115
122
|
});
|
|
116
|
-
|
|
117
|
-
proxy
|
|
118
|
-
|
|
119
|
-
|
|
123
|
+
try {
|
|
124
|
+
await listenDevServer(proxy, {
|
|
125
|
+
host,
|
|
126
|
+
port: options.port ?? options.defaultPort ?? 0,
|
|
127
|
+
explicit: options.port !== undefined,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
process.off('exit', killAllSync);
|
|
132
|
+
reaper.close();
|
|
133
|
+
await rm(workDir, { recursive: true, force: true });
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
120
136
|
const address = proxy.address();
|
|
121
137
|
const hostedUrl = `http://${host}:${address.port}`;
|
|
122
|
-
|
|
138
|
+
try {
|
|
139
|
+
await options.onListening?.({
|
|
140
|
+
port: address.port,
|
|
141
|
+
hostedUrl,
|
|
142
|
+
appBasePath,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
// Port is bound but startPackageAppDev has not returned yet — close the
|
|
147
|
+
// listen handle now or the process stays alive after the throw.
|
|
148
|
+
process.off('exit', killAllSync);
|
|
149
|
+
reaper.close();
|
|
150
|
+
proxy.closeAllConnections();
|
|
151
|
+
await new Promise((resolve) => {
|
|
152
|
+
proxy.close(() => resolve());
|
|
153
|
+
});
|
|
154
|
+
await rm(workDir, { recursive: true, force: true });
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
const contextFor = (client) => buildLocalPackageContext({
|
|
123
158
|
kodyId: options.manifest.kodyId,
|
|
124
159
|
packageId: `local:${options.manifest.kodyId}`,
|
|
125
160
|
hostedUrl,
|
|
126
161
|
appBasePath,
|
|
162
|
+
clientModuleUrl: client ? clientModuleUrlFor(client) : null,
|
|
127
163
|
});
|
|
164
|
+
const clientModuleUrlFor = (client) => `${hostedUrl}${appBasePath}/_assets/${client.name}`;
|
|
128
165
|
const mockFetchGateway = await resolveMockFetchGateway({
|
|
129
166
|
target: options.target,
|
|
130
167
|
packageRoot: root,
|
|
@@ -132,9 +169,9 @@ export async function startPackageAppDev(options) {
|
|
|
132
169
|
allowPrivateNetwork: options.allowPrivateNetwork,
|
|
133
170
|
onStatus: options.onStatus,
|
|
134
171
|
});
|
|
135
|
-
const bridge = await
|
|
172
|
+
const bridge = await startLocalRuntimeBridge({
|
|
136
173
|
runSecret,
|
|
137
|
-
|
|
174
|
+
forwardCall: async (call, signal) => {
|
|
138
175
|
const mocked = tryHandleMockGatewayFetchCall({
|
|
139
176
|
gateway: mockFetchGateway,
|
|
140
177
|
path: call.path,
|
|
@@ -149,12 +186,36 @@ export async function startPackageAppDev(options) {
|
|
|
149
186
|
signal,
|
|
150
187
|
});
|
|
151
188
|
},
|
|
189
|
+
forwardGatewayFetchStream: async ({ request, response, signal }) => {
|
|
190
|
+
await forwardLocalGatewayFetchStream({
|
|
191
|
+
request,
|
|
192
|
+
response,
|
|
193
|
+
signal,
|
|
194
|
+
client,
|
|
195
|
+
...(mockFetchGateway ? { mockFetchGateway } : {}),
|
|
196
|
+
});
|
|
197
|
+
},
|
|
152
198
|
});
|
|
153
|
-
const
|
|
199
|
+
const app = options.manifest.app;
|
|
200
|
+
const entryPath = resolvePackagePath(options.manifest, app.entry);
|
|
154
201
|
const bundler = createDevBundler({ entryPath, root });
|
|
202
|
+
const clientBundler = app.client
|
|
203
|
+
? createDevClientBundler({
|
|
204
|
+
entryPath: resolvePackagePath(options.manifest, app.client),
|
|
205
|
+
root,
|
|
206
|
+
externals: app.clientExternals,
|
|
207
|
+
})
|
|
208
|
+
: null;
|
|
209
|
+
const assetsDir = app.assets ? resolve(root, app.assets) : null;
|
|
210
|
+
const prebuild = options.manifest.dev?.prebuild;
|
|
211
|
+
let prebuildRunning = false;
|
|
212
|
+
let prebuildSettledAt = 0;
|
|
213
|
+
let prebuildAbort;
|
|
155
214
|
const workerdEnv = { ...process.env, [runSecretEnvVar]: runSecret };
|
|
156
215
|
delete workerdEnv[apiTokenEnvVar];
|
|
157
216
|
function requestBuild(paths) {
|
|
217
|
+
if (closed)
|
|
218
|
+
return Promise.resolve();
|
|
158
219
|
for (const path of paths)
|
|
159
220
|
changedFiles.add(path);
|
|
160
221
|
if (buildLoop) {
|
|
@@ -176,12 +237,17 @@ export async function startPackageAppDev(options) {
|
|
|
176
237
|
async function buildOnce(files) {
|
|
177
238
|
const started = performance.now();
|
|
178
239
|
emit({ type: 'rebuild_start', files: files.map((file) => relative(root, file) || file) });
|
|
179
|
-
|
|
240
|
+
const assetsChanged = assetsDir !== null &&
|
|
241
|
+
files.some((file) => file === assetsDir || file.startsWith(`${assetsDir}${sep}`));
|
|
180
242
|
try {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
243
|
+
if (prebuild) {
|
|
244
|
+
await runPrebuildStep(prebuild);
|
|
245
|
+
}
|
|
246
|
+
const [bundle, client] = await Promise.all([
|
|
247
|
+
bundler.build(),
|
|
248
|
+
clientBundler ? clientBundler.build() : Promise.resolve(null),
|
|
249
|
+
]);
|
|
250
|
+
const midBuildEdits = (await watcher?.track([...bundle.inputs, ...(client?.inputs ?? []), options.manifest.packageJsonPath], { ignoreChanges: files })) ?? [];
|
|
185
251
|
// Queue mid-build edits for the next loop iteration directly — do not
|
|
186
252
|
// call onChange (debounced); chokidar may already have the same paths
|
|
187
253
|
// pending and would double-schedule a rebuild after debounce.
|
|
@@ -190,7 +256,10 @@ export async function startPackageAppDev(options) {
|
|
|
190
256
|
changedFiles.add(path);
|
|
191
257
|
rebuildAgain = true;
|
|
192
258
|
}
|
|
193
|
-
if (active &&
|
|
259
|
+
if (active &&
|
|
260
|
+
!buildFailure &&
|
|
261
|
+
bundle.code === active.code &&
|
|
262
|
+
client?.name === active.client?.name) {
|
|
194
263
|
// Still stamp lastRebuild so agents polling lastRebuild.at see
|
|
195
264
|
// the no-op rebuild finish (status already flipped building→ready).
|
|
196
265
|
lastRebuild = { ms: elapsed(started), at: new Date().toISOString() };
|
|
@@ -200,9 +269,12 @@ export async function startPackageAppDev(options) {
|
|
|
200
269
|
buildId: active.id,
|
|
201
270
|
restarted: false,
|
|
202
271
|
});
|
|
272
|
+
if (assetsChanged || prebuild) {
|
|
273
|
+
liveReloadHub.broadcast({ event: 'reload', data: { buildId: active.id } });
|
|
274
|
+
}
|
|
203
275
|
return;
|
|
204
276
|
}
|
|
205
|
-
const generation = await startGeneration(bundle);
|
|
277
|
+
const generation = await startGeneration(bundle, client);
|
|
206
278
|
if (closed) {
|
|
207
279
|
await stopGeneration(generation);
|
|
208
280
|
return;
|
|
@@ -233,13 +305,54 @@ export async function startPackageAppDev(options) {
|
|
|
233
305
|
liveReloadHub.broadcast({ event: 'build_error', data: buildErrorView(buildFailure) });
|
|
234
306
|
}
|
|
235
307
|
}
|
|
236
|
-
|
|
308
|
+
let clearWatcherPending = null;
|
|
309
|
+
async function runPrebuildStep(command) {
|
|
310
|
+
prebuildRunning = true;
|
|
311
|
+
prebuildAbort = new AbortController();
|
|
312
|
+
let failed = false;
|
|
313
|
+
try {
|
|
314
|
+
await runPrebuild({
|
|
315
|
+
command,
|
|
316
|
+
cwd: root,
|
|
317
|
+
signal: prebuildAbort.signal,
|
|
318
|
+
onSpawn: (pid) => reaper.trackGroup(pid),
|
|
319
|
+
onExit: (pid) => reaper.untrackGroup(pid),
|
|
320
|
+
onLine: (line, stream) => emit({
|
|
321
|
+
type: 'log',
|
|
322
|
+
source: 'prebuild',
|
|
323
|
+
level: stream === 'stderr' ? 'warn' : 'log',
|
|
324
|
+
message: redact(line),
|
|
325
|
+
request: null,
|
|
326
|
+
}),
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
failed = true;
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
finally {
|
|
334
|
+
prebuildAbort = undefined;
|
|
335
|
+
prebuildRunning = false;
|
|
336
|
+
prebuildSettledAt = performance.now();
|
|
337
|
+
// Absorb writes even when prebuild fails — otherwise the 1s poll
|
|
338
|
+
// treats partial/failed output as new edits and loops rebuilds.
|
|
339
|
+
await watcher?.rebaseline();
|
|
340
|
+
if (failed) {
|
|
341
|
+
// Drop chokidar echoes of the failed prebuild / trigger burst so
|
|
342
|
+
// they cannot restart the loop after buildLoop clears.
|
|
343
|
+
changedFiles.clear();
|
|
344
|
+
rebuildAgain = false;
|
|
345
|
+
clearWatcherPending?.();
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async function startGeneration(bundle, client) {
|
|
237
350
|
const id = ++generationCounter;
|
|
238
351
|
const dir = join(workDir, `g${id}`);
|
|
239
352
|
await writeWorkerFiles({
|
|
240
353
|
workDir: dir,
|
|
241
354
|
code: bundle.code,
|
|
242
|
-
packageContext,
|
|
355
|
+
packageContext: contextFor(client),
|
|
243
356
|
packageRoot: root,
|
|
244
357
|
mockStorage: options.target === 'mock',
|
|
245
358
|
bridgePort: bridge.port,
|
|
@@ -292,6 +405,7 @@ export async function startPackageAppDev(options) {
|
|
|
292
405
|
dir,
|
|
293
406
|
workerd,
|
|
294
407
|
code: bundle.code,
|
|
408
|
+
client,
|
|
295
409
|
mapStack: createStackMapper({
|
|
296
410
|
sourceMap: bundle.sourceMap,
|
|
297
411
|
userModule: localWorkerModuleNames.user,
|
|
@@ -413,6 +527,20 @@ export async function startPackageAppDev(options) {
|
|
|
413
527
|
if (handleDevRoute(req, res))
|
|
414
528
|
return;
|
|
415
529
|
await waitForBuild();
|
|
530
|
+
const assetPath = parseDevAssetPath(stripMount(new URL(req.url ?? '/', hostedUrl).pathname, appBasePath));
|
|
531
|
+
if (assetPath !== null) {
|
|
532
|
+
const client = active?.client ?? null;
|
|
533
|
+
await serveDevAsset({
|
|
534
|
+
req,
|
|
535
|
+
res,
|
|
536
|
+
relativePath: assetPath,
|
|
537
|
+
assetsDir,
|
|
538
|
+
client,
|
|
539
|
+
clientModuleUrl: client ? clientModuleUrlFor(client) : null,
|
|
540
|
+
appBasePath,
|
|
541
|
+
});
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
416
544
|
if (buildFailure) {
|
|
417
545
|
sendDevError(req, res, 500, buildErrorView(buildFailure), {
|
|
418
546
|
htmlTail: errorPageTail,
|
|
@@ -551,11 +679,25 @@ export async function startPackageAppDev(options) {
|
|
|
551
679
|
? null
|
|
552
680
|
: (() => {
|
|
553
681
|
const pending = new Set();
|
|
682
|
+
clearWatcherPending = () => {
|
|
683
|
+
pending.clear();
|
|
684
|
+
clearTimeout(debounceTimer);
|
|
685
|
+
debouncePending = false;
|
|
686
|
+
const resolve = resolveDebounceFlush;
|
|
687
|
+
resolveDebounceFlush = null;
|
|
688
|
+
debounceFlush = null;
|
|
689
|
+
resolve?.();
|
|
690
|
+
};
|
|
554
691
|
return createDevWatcher({
|
|
555
692
|
root,
|
|
693
|
+
ignore: options.manifest.dev?.ignore,
|
|
556
694
|
onChange: (path) => {
|
|
557
695
|
if (closed)
|
|
558
696
|
return;
|
|
697
|
+
if (prebuildRunning ||
|
|
698
|
+
performance.now() - prebuildSettledAt < prebuildSettleMs) {
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
559
701
|
// Fold mid-build watcher events into the active loop
|
|
560
702
|
// (including another save of a file already building).
|
|
561
703
|
// Debouncing them instead schedules a second rebuild
|
|
@@ -600,9 +742,11 @@ export async function startPackageAppDev(options) {
|
|
|
600
742
|
resolveDebounceFlush = null;
|
|
601
743
|
debounceFlush = null;
|
|
602
744
|
resolve?.();
|
|
745
|
+
prebuildAbort?.abort();
|
|
603
746
|
await watcher?.close();
|
|
604
747
|
await buildLoop?.catch(() => { });
|
|
605
748
|
await bundler.dispose();
|
|
749
|
+
await clientBundler?.dispose();
|
|
606
750
|
await Promise.all([...generations].map((generation) => stopGeneration(generation)));
|
|
607
751
|
reaper.close();
|
|
608
752
|
await bridge.close();
|
|
@@ -649,7 +793,9 @@ export async function startPackageAppDev(options) {
|
|
|
649
793
|
port: address.port,
|
|
650
794
|
hostedUrl,
|
|
651
795
|
appBasePath,
|
|
652
|
-
packageContext
|
|
796
|
+
get packageContext() {
|
|
797
|
+
return contextFor(active?.client ?? null);
|
|
798
|
+
},
|
|
653
799
|
fetchPath,
|
|
654
800
|
status,
|
|
655
801
|
close,
|
|
@@ -748,54 +894,4 @@ function joinUrlPath(base, path) {
|
|
|
748
894
|
return rest;
|
|
749
895
|
return `${base}${rest === '/' ? '' : rest}` || base;
|
|
750
896
|
}
|
|
751
|
-
async function startBridge(input) {
|
|
752
|
-
const inFlight = new AbortController();
|
|
753
|
-
const server = createServer(async (request, response) => {
|
|
754
|
-
const reply = (status, body) => {
|
|
755
|
-
response.writeHead(status, { 'content-type': 'application/json' });
|
|
756
|
-
response.end(JSON.stringify(body));
|
|
757
|
-
};
|
|
758
|
-
if (request.method !== 'POST' ||
|
|
759
|
-
request.url !== '/call' ||
|
|
760
|
-
request.headers[runSecretHeader] !== input.runSecret) {
|
|
761
|
-
reply(403, { error: 'Forbidden' });
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
try {
|
|
765
|
-
const parsed = JSON.parse(await readBody(request));
|
|
766
|
-
if (!Array.isArray(parsed.path) ||
|
|
767
|
-
parsed.path.length === 0 ||
|
|
768
|
-
!parsed.path.every((segment) => typeof segment === 'string')) {
|
|
769
|
-
throw new Error('Invalid kody:runtime call path.');
|
|
770
|
-
}
|
|
771
|
-
const result = await input.forward({
|
|
772
|
-
path: parsed.path,
|
|
773
|
-
args: Array.isArray(parsed.args) ? parsed.args : [],
|
|
774
|
-
}, inFlight.signal);
|
|
775
|
-
reply(200, { result });
|
|
776
|
-
}
|
|
777
|
-
catch (error) {
|
|
778
|
-
reply(200, { error: redact(error instanceof Error ? error.message : String(error)) });
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
await new Promise((resolve, reject) => {
|
|
782
|
-
server.once('error', reject);
|
|
783
|
-
server.listen(0, '127.0.0.1', () => resolve());
|
|
784
|
-
});
|
|
785
|
-
return {
|
|
786
|
-
port: server.address().port,
|
|
787
|
-
close: () => new Promise((resolve) => {
|
|
788
|
-
inFlight.abort();
|
|
789
|
-
server.closeAllConnections();
|
|
790
|
-
server.close(() => resolve());
|
|
791
|
-
}),
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
|
-
async function readBody(stream) {
|
|
795
|
-
const chunks = [];
|
|
796
|
-
for await (const chunk of stream) {
|
|
797
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
798
|
-
}
|
|
799
|
-
return Buffer.concat(chunks).toString('utf8');
|
|
800
|
-
}
|
|
801
897
|
//# sourceMappingURL=package-app-dev.js.map
|