@kodycodes/cli 1.26.1 → 1.26.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-4FW6GCLG.js +26 -0
- package/dist/auth-4FW6GCLG.js.map +6 -0
- package/dist/bin.js +27 -3
- package/dist/bin.js.map +6 -1
- package/dist/chunk-46IJPEID.js +30 -0
- package/dist/chunk-46IJPEID.js.map +6 -0
- package/dist/chunk-4D6ORNXY.js +24404 -0
- package/dist/chunk-4D6ORNXY.js.map +6 -0
- package/dist/chunk-64IM6G4Z.js +341 -0
- package/dist/chunk-64IM6G4Z.js.map +6 -0
- package/dist/{package-local-runtime-source.js → chunk-64XN367G.js} +373 -52
- package/dist/chunk-64XN367G.js.map +6 -0
- package/dist/chunk-6XQP5SWX.js +26 -0
- package/dist/chunk-6XQP5SWX.js.map +6 -0
- package/dist/chunk-75RZUXQL.js +62 -0
- package/dist/chunk-75RZUXQL.js.map +6 -0
- package/dist/chunk-BISUL7CE.js +106 -0
- package/dist/chunk-BISUL7CE.js.map +6 -0
- package/dist/chunk-BPGY4IE7.js +57 -0
- package/dist/chunk-BPGY4IE7.js.map +6 -0
- package/dist/chunk-CASGRYOF.js +21 -0
- package/dist/chunk-CASGRYOF.js.map +6 -0
- package/dist/chunk-CWWHR4FH.js +19 -0
- package/dist/chunk-CWWHR4FH.js.map +6 -0
- package/dist/chunk-D55XPY3T.js +1382 -0
- package/dist/chunk-D55XPY3T.js.map +6 -0
- package/dist/chunk-EOCV74B2.js +948 -0
- package/dist/chunk-EOCV74B2.js.map +6 -0
- package/dist/chunk-FBVNFWR6.js +437 -0
- package/dist/chunk-FBVNFWR6.js.map +6 -0
- package/dist/chunk-G4YN6XP5.js +14 -0
- package/dist/chunk-G4YN6XP5.js.map +6 -0
- package/dist/chunk-G6LJWROP.js +18 -0
- package/dist/chunk-G6LJWROP.js.map +6 -0
- package/dist/chunk-IHP6PV6X.js +49 -0
- package/dist/chunk-IHP6PV6X.js.map +6 -0
- package/dist/chunk-JWEFKKAD.js +24 -0
- package/dist/chunk-JWEFKKAD.js.map +6 -0
- package/dist/chunk-LWNB26KO.js +147 -0
- package/dist/chunk-LWNB26KO.js.map +6 -0
- package/dist/chunk-OVGXPRSY.js +20 -0
- package/dist/chunk-OVGXPRSY.js.map +6 -0
- package/dist/chunk-PDWCKWQF.js +453 -0
- package/dist/chunk-PDWCKWQF.js.map +6 -0
- package/dist/chunk-QHBCP4OB.js +44 -0
- package/dist/chunk-QHBCP4OB.js.map +6 -0
- package/dist/chunk-R4IUV5P5.js +664 -0
- package/dist/chunk-R4IUV5P5.js.map +6 -0
- package/dist/chunk-RM34N3KI.js +525 -0
- package/dist/chunk-RM34N3KI.js.map +6 -0
- package/dist/chunk-RTWUNV76.js +309 -0
- package/dist/chunk-RTWUNV76.js.map +6 -0
- package/dist/chunk-SMURF26I.js +23 -0
- package/dist/chunk-SMURF26I.js.map +6 -0
- package/dist/chunk-SXHULGL4.js +426 -0
- package/dist/chunk-SXHULGL4.js.map +6 -0
- package/dist/chunk-TIL35OWA.js +1959 -0
- package/dist/chunk-TIL35OWA.js.map +6 -0
- package/dist/chunk-VQS5YSCI.js +54 -0
- package/dist/chunk-VQS5YSCI.js.map +6 -0
- package/dist/chunk-XDCOWU5R.js +696 -0
- package/dist/chunk-XDCOWU5R.js.map +6 -0
- package/dist/chunk-XOPPGRUP.js +605 -0
- package/dist/chunk-XOPPGRUP.js.map +6 -0
- package/dist/cli.js +74 -1500
- package/dist/cli.js.map +6 -1
- package/dist/dev/dev-watcher.d.ts +5 -1
- package/dist/dev-command-3LX234N6.js +4900 -0
- package/dist/dev-command-3LX234N6.js.map +6 -0
- package/dist/git-credential-FUE3JE3T.js +163 -0
- package/dist/git-credential-FUE3JE3T.js.map +6 -0
- package/dist/install-ZML2TCDG.js +10694 -0
- package/dist/install-ZML2TCDG.js.map +6 -0
- package/dist/local-execute-SZV6XN4T.js +368 -0
- package/dist/local-execute-SZV6XN4T.js.map +6 -0
- package/dist/local-package-run-2TMCAZZ2.js +23 -0
- package/dist/local-package-run-2TMCAZZ2.js.map +6 -0
- package/dist/mcp-G2AWTIXR.js +21 -0
- package/dist/mcp-G2AWTIXR.js.map +6 -0
- package/dist/mock-dep-resolve-TVABDNR7.js +52 -0
- package/dist/mock-dep-resolve-TVABDNR7.js.map +6 -0
- package/dist/mock-fetch-gateway-B5TFGGQS.js +34 -0
- package/dist/mock-fetch-gateway-B5TFGGQS.js.map +6 -0
- package/dist/mock-local-deps-5SZT3PWT.js +197 -0
- package/dist/mock-local-deps-5SZT3PWT.js.map +6 -0
- package/dist/mock-server-SV2IKC4I.js +21 -0
- package/dist/mock-server-SV2IKC4I.js.map +6 -0
- package/dist/open-api-client-DVVMLY7P.js +42 -0
- package/dist/open-api-client-DVVMLY7P.js.map +6 -0
- package/dist/org-X7B5BI4R.js +132 -0
- package/dist/org-X7B5BI4R.js.map +6 -0
- package/dist/package-manifest-QH2RU64C.js +14 -0
- package/dist/package-manifest-QH2RU64C.js.map +6 -0
- package/dist/remote-execute-QMQDVHOY.js +110 -0
- package/dist/remote-execute-QMQDVHOY.js.map +6 -0
- package/dist/setup-git-5KCUBYPB.js +88 -0
- package/dist/setup-git-5KCUBYPB.js.map +6 -0
- package/dist/skill-ENPATRIW.js +43 -0
- package/dist/skill-ENPATRIW.js.map +6 -0
- package/dist/store-WTMRH527.js +48 -0
- package/dist/store-WTMRH527.js.map +6 -0
- package/dist/structured-error-C65GQ53Z.js +13 -0
- package/dist/structured-error-C65GQ53Z.js.map +6 -0
- package/dist/target-3QHY7JT4.js +45 -0
- package/dist/target-3QHY7JT4.js.map +6 -0
- package/dist/webapi-NTVP4BO3.js +2482 -0
- package/dist/webapi-NTVP4BO3.js.map +6 -0
- package/package.json +8 -8
- package/dist/add-mcp-install.js +0 -59
- package/dist/add-mcp-install.js.map +0 -1
- package/dist/api-token-store.js +0 -120
- package/dist/api-token-store.js.map +0 -1
- package/dist/api-token.js +0 -238
- package/dist/api-token.js.map +0 -1
- package/dist/auth-bootstrap.js +0 -373
- package/dist/auth-bootstrap.js.map +0 -1
- package/dist/auth.js +0 -386
- package/dist/auth.js.map +0 -1
- package/dist/capability-proxy.js +0 -527
- package/dist/capability-proxy.js.map +0 -1
- package/dist/credential-call-map.js +0 -155
- package/dist/credential-call-map.js.map +0 -1
- package/dist/credential-client-runtime-source.js +0 -9
- package/dist/credential-client-runtime-source.js.map +0 -1
- package/dist/credentialed-fetch-scan.js +0 -98
- package/dist/credentialed-fetch-scan.js.map +0 -1
- package/dist/defaults.js +0 -34
- package/dist/defaults.js.map +0 -1
- package/dist/detect-running-hosts.js +0 -13
- package/dist/detect-running-hosts.js.map +0 -1
- package/dist/dev/dev-assets.js +0 -202
- package/dist/dev/dev-assets.js.map +0 -1
- package/dist/dev/dev-bundler.js +0 -65
- package/dist/dev/dev-bundler.js.map +0 -1
- package/dist/dev/dev-events.js +0 -94
- package/dist/dev/dev-events.js.map +0 -1
- package/dist/dev/dev-port.js +0 -80
- package/dist/dev/dev-port.js.map +0 -1
- package/dist/dev/dev-protocol.js +0 -14
- package/dist/dev/dev-protocol.js.map +0 -1
- package/dist/dev/dev-registry.js +0 -95
- package/dist/dev/dev-registry.js.map +0 -1
- package/dist/dev/dev-watcher.js +0 -156
- package/dist/dev/dev-watcher.js.map +0 -1
- package/dist/dev/error-page.js +0 -64
- package/dist/dev/error-page.js.map +0 -1
- package/dist/dev/live-reload.js +0 -148
- package/dist/dev/live-reload.js.map +0 -1
- package/dist/dev/prebuild.js +0 -102
- package/dist/dev/prebuild.js.map +0 -1
- package/dist/dev/process-reaper.js +0 -151
- package/dist/dev/process-reaper.js.map +0 -1
- package/dist/dev/stack-trace.js +0 -56
- package/dist/dev/stack-trace.js.map +0 -1
- package/dist/dev/workerd-process.js +0 -118
- package/dist/dev/workerd-process.js.map +0 -1
- package/dist/dev-command.js +0 -672
- package/dist/dev-command.js.map +0 -1
- package/dist/discover-login-identity.js +0 -77
- package/dist/discover-login-identity.js.map +0 -1
- package/dist/discover-org.js +0 -62
- package/dist/discover-org.js.map +0 -1
- package/dist/git-credential.js +0 -147
- package/dist/git-credential.js.map +0 -1
- package/dist/help.js +0 -333
- package/dist/help.js.map +0 -1
- package/dist/host-catalog.js +0 -427
- package/dist/host-catalog.js.map +0 -1
- package/dist/host-config.js +0 -80
- package/dist/host-config.js.map +0 -1
- package/dist/http-error-body.js +0 -35
- package/dist/http-error-body.js.map +0 -1
- package/dist/install-hosts.js +0 -328
- package/dist/install-hosts.js.map +0 -1
- package/dist/install.js +0 -120
- package/dist/install.js.map +0 -1
- package/dist/invoke-passthrough.js +0 -60
- package/dist/invoke-passthrough.js.map +0 -1
- package/dist/local-bundle.js +0 -200
- package/dist/local-bundle.js.map +0 -1
- package/dist/local-execute-auth.js +0 -53
- package/dist/local-execute-auth.js.map +0 -1
- package/dist/local-execute.js +0 -338
- package/dist/local-execute.js.map +0 -1
- package/dist/local-gateway-fetch-forward.js +0 -135
- package/dist/local-gateway-fetch-forward.js.map +0 -1
- package/dist/local-host-runtime-surface.js +0 -75
- package/dist/local-host-runtime-surface.js.map +0 -1
- package/dist/local-package-graph.js +0 -405
- package/dist/local-package-graph.js.map +0 -1
- package/dist/local-package-run.js +0 -307
- package/dist/local-package-run.js.map +0 -1
- package/dist/local-runtime-bridge.js +0 -132
- package/dist/local-runtime-bridge.js.map +0 -1
- package/dist/local-runtime-source.js +0 -541
- package/dist/local-runtime-source.js.map +0 -1
- package/dist/login-callback-assets.js +0 -10
- package/dist/login-callback-assets.js.map +0 -1
- package/dist/login-callback-page.js +0 -362
- package/dist/login-callback-page.js.map +0 -1
- package/dist/mcp.js +0 -134
- package/dist/mcp.js.map +0 -1
- package/dist/mock/ensure-msw.js +0 -215
- package/dist/mock/ensure-msw.js.map +0 -1
- package/dist/mock/install-mock-fetch-shim.js +0 -36
- package/dist/mock/install-mock-fetch-shim.js.map +0 -1
- package/dist/mock/load-package-mocks.js +0 -136
- package/dist/mock/load-package-mocks.js.map +0 -1
- package/dist/mock/mock-fetch-gateway.js +0 -343
- package/dist/mock/mock-fetch-gateway.js.map +0 -1
- package/dist/mock/mock-gateway-fetch-shim.js +0 -104
- package/dist/mock/mock-gateway-fetch-shim.js.map +0 -1
- package/dist/mock/mock-server.js +0 -499
- package/dist/mock/mock-server.js.map +0 -1
- package/dist/mock/mock-storage.js +0 -186
- package/dist/mock/mock-storage.js.map +0 -1
- package/dist/mock/mock-supported.js +0 -63
- package/dist/mock/mock-supported.js.map +0 -1
- package/dist/mock-dep-resolve.js +0 -47
- package/dist/mock-dep-resolve.js.map +0 -1
- package/dist/mock-local-deps.js +0 -207
- package/dist/mock-local-deps.js.map +0 -1
- package/dist/network-error.js +0 -11
- package/dist/network-error.js.map +0 -1
- package/dist/node-engine.js +0 -21
- package/dist/node-engine.js.map +0 -1
- package/dist/oauth-provider.js +0 -107
- package/dist/oauth-provider.js.map +0 -1
- package/dist/onboarding-prompt.js +0 -15
- package/dist/onboarding-prompt.js.map +0 -1
- package/dist/open-api-client.js +0 -432
- package/dist/open-api-client.js.map +0 -1
- package/dist/open-url.js +0 -21
- package/dist/open-url.js.map +0 -1
- package/dist/org-resolve.js +0 -16
- package/dist/org-resolve.js.map +0 -1
- package/dist/org-slug.js +0 -11
- package/dist/org-slug.js.map +0 -1
- package/dist/org.js +0 -100
- package/dist/org.js.map +0 -1
- package/dist/package-app-dev.js +0 -905
- package/dist/package-app-dev.js.map +0 -1
- package/dist/package-collaborators.js +0 -179
- package/dist/package-collaborators.js.map +0 -1
- package/dist/package-info.js +0 -19
- package/dist/package-info.js.map +0 -1
- package/dist/package-local-runtime-source.js.map +0 -1
- package/dist/package-manifest.js +0 -364
- package/dist/package-manifest.js.map +0 -1
- package/dist/process-info.js +0 -14
- package/dist/process-info.js.map +0 -1
- package/dist/redact.js +0 -16
- package/dist/redact.js.map +0 -1
- package/dist/remote-execute.js +0 -108
- package/dist/remote-execute.js.map +0 -1
- package/dist/setup-git.js +0 -676
- package/dist/setup-git.js.map +0 -1
- package/dist/skill.js +0 -35
- package/dist/skill.js.map +0 -1
- package/dist/store.js +0 -597
- package/dist/store.js.map +0 -1
- package/dist/structured-error.js +0 -57
- package/dist/structured-error.js.map +0 -1
- package/dist/target.js +0 -52
- package/dist/target.js.map +0 -1
- package/dist/tool-result.js +0 -19
- package/dist/tool-result.js.map +0 -1
- package/dist/which.js +0 -22
- package/dist/which.js.map +0 -1
- package/dist/workerd-binary.js +0 -98
- package/dist/workerd-binary.js.map +0 -1
|
@@ -0,0 +1,4900 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
appLogMarker,
|
|
4
|
+
buildLocalPackageContext,
|
|
5
|
+
createPackageAppEntrySource,
|
|
6
|
+
createPackageLocalRuntimeModuleSource,
|
|
7
|
+
devErrorHeader,
|
|
8
|
+
devRequestIdHeader,
|
|
9
|
+
runLocalPackageModule
|
|
10
|
+
} from "./chunk-64XN367G.js";
|
|
11
|
+
import {
|
|
12
|
+
forwardLocalGatewayFetchStream,
|
|
13
|
+
installMockFetchShimModules,
|
|
14
|
+
startLocalRuntimeBridge
|
|
15
|
+
} from "./chunk-SXHULGL4.js";
|
|
16
|
+
import {
|
|
17
|
+
BundleError,
|
|
18
|
+
bundleLocalUserModule,
|
|
19
|
+
createBundleError,
|
|
20
|
+
createRuntimeFlatAliasSource,
|
|
21
|
+
createWorkerdConfig,
|
|
22
|
+
formatBundleDiagnostic,
|
|
23
|
+
init,
|
|
24
|
+
localBundleOptions,
|
|
25
|
+
localExecuteGatewayFetchShimModuleName,
|
|
26
|
+
localWorkerModuleNames,
|
|
27
|
+
parse,
|
|
28
|
+
resolveLocalPackageGraph,
|
|
29
|
+
runSecretEnvVar,
|
|
30
|
+
runSecretHeader,
|
|
31
|
+
writePackageModuleFiles
|
|
32
|
+
} from "./chunk-D55XPY3T.js";
|
|
33
|
+
import {
|
|
34
|
+
ensureMockTarget
|
|
35
|
+
} from "./chunk-XDCOWU5R.js";
|
|
36
|
+
import {
|
|
37
|
+
packageMocksRelativePath,
|
|
38
|
+
resolveMockFetchGateway,
|
|
39
|
+
tryHandleMockGatewayFetchCall
|
|
40
|
+
} from "./chunk-XOPPGRUP.js";
|
|
41
|
+
import {
|
|
42
|
+
loadPackageManifest,
|
|
43
|
+
resolveExportModulePath,
|
|
44
|
+
resolvePackagePath
|
|
45
|
+
} from "./chunk-64IM6G4Z.js";
|
|
46
|
+
import {
|
|
47
|
+
StructuredCliError
|
|
48
|
+
} from "./chunk-BPGY4IE7.js";
|
|
49
|
+
import {
|
|
50
|
+
formatToolResult
|
|
51
|
+
} from "./chunk-46IJPEID.js";
|
|
52
|
+
import {
|
|
53
|
+
assertTokenSafeApiUrl,
|
|
54
|
+
callCapabilityProxy
|
|
55
|
+
} from "./chunk-R4IUV5P5.js";
|
|
56
|
+
import {
|
|
57
|
+
ensureWorkerdBinary,
|
|
58
|
+
userCacheDir
|
|
59
|
+
} from "./chunk-BISUL7CE.js";
|
|
60
|
+
import "./chunk-G4YN6XP5.js";
|
|
61
|
+
import {
|
|
62
|
+
redact
|
|
63
|
+
} from "./chunk-CASGRYOF.js";
|
|
64
|
+
import "./chunk-G6LJWROP.js";
|
|
65
|
+
import "./chunk-RTWUNV76.js";
|
|
66
|
+
import "./chunk-RM34N3KI.js";
|
|
67
|
+
import {
|
|
68
|
+
apiTokenEnvVar
|
|
69
|
+
} from "./chunk-IHP6PV6X.js";
|
|
70
|
+
import "./chunk-QHBCP4OB.js";
|
|
71
|
+
|
|
72
|
+
// src/dev-command.ts
|
|
73
|
+
import { access, mkdir as mkdir3, readFile as readFile4, rm as rm4, writeFile as writeFile4 } from "node:fs/promises";
|
|
74
|
+
import { dirname as dirname3, join as join8, resolve as resolve7 } from "node:path";
|
|
75
|
+
|
|
76
|
+
// src/credentialed-fetch-scan.ts
|
|
77
|
+
var esModuleLexerReady = init();
|
|
78
|
+
async function detectCredentialedFetchInSource(code, where = "source") {
|
|
79
|
+
await esModuleLexerReady;
|
|
80
|
+
const signals = [];
|
|
81
|
+
const seen = /* @__PURE__ */ new Set();
|
|
82
|
+
const push = (kind) => {
|
|
83
|
+
if (seen.has(kind)) return;
|
|
84
|
+
seen.add(kind);
|
|
85
|
+
signals.push({ kind, where });
|
|
86
|
+
};
|
|
87
|
+
const codeWithoutComments = stripCommentsForScan(code);
|
|
88
|
+
if (/\bcreateAuthenticatedFetch\b/.test(codeWithoutComments)) {
|
|
89
|
+
push("createAuthenticatedFetch");
|
|
90
|
+
}
|
|
91
|
+
if (/\bauthenticatedFetch\b/.test(codeWithoutComments)) {
|
|
92
|
+
push("authenticatedFetch");
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const [imports] = parse(code);
|
|
96
|
+
for (const im of imports) {
|
|
97
|
+
if (im.type === "dynamic") {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (im.typeOnly === true) continue;
|
|
101
|
+
if (im.specifier !== "kody:runtime") continue;
|
|
102
|
+
const record = im;
|
|
103
|
+
const statementStart = record.importStart ?? record.ss;
|
|
104
|
+
const statementEnd = record.importEnd ?? record.se;
|
|
105
|
+
if (typeof statementStart !== "number" || typeof statementEnd !== "number") {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const statement = code.slice(statementStart, statementEnd);
|
|
109
|
+
if (/\bcredentials?\b/.test(statement)) {
|
|
110
|
+
push("credential_runtime");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
return signals;
|
|
116
|
+
}
|
|
117
|
+
function stripCommentsForScan(code) {
|
|
118
|
+
return code.replace(/\/\*[\s\S]*?\*\//g, (block) => block.replace(/[^\n]/g, " ")).replace(/(^|[^:])\/\/.*$/gm, "$1");
|
|
119
|
+
}
|
|
120
|
+
function formatCredentialedFetchKinds(signals) {
|
|
121
|
+
const kinds = [...new Set(signals.map((s) => s.kind))];
|
|
122
|
+
return kinds.map((kind) => {
|
|
123
|
+
switch (kind) {
|
|
124
|
+
case "createAuthenticatedFetch":
|
|
125
|
+
return "createAuthenticatedFetch";
|
|
126
|
+
case "authenticatedFetch":
|
|
127
|
+
return "authenticatedFetch";
|
|
128
|
+
case "credential_runtime":
|
|
129
|
+
return "credential()/credentials()";
|
|
130
|
+
default: {
|
|
131
|
+
const _exhaustive = kind;
|
|
132
|
+
return _exhaustive;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}).join(", ");
|
|
136
|
+
}
|
|
137
|
+
function mocksSuggestionDetail(input) {
|
|
138
|
+
if (input.signals.length === 0) return null;
|
|
139
|
+
const kinds = formatCredentialedFetchKinds(input.signals);
|
|
140
|
+
if (input.hasMocksFile) {
|
|
141
|
+
return `present (credentialed fetch detected: ${kinds})`;
|
|
142
|
+
}
|
|
143
|
+
return `suggested \u2014 package uses credentialed fetch (${kinds}); add .kody/mocks.ts so --target mock can MSW-match outbound requests (unmocked credentialed hops fail closed)`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/package-app-dev.ts
|
|
147
|
+
import { randomBytes } from "node:crypto";
|
|
148
|
+
import { rmSync } from "node:fs";
|
|
149
|
+
import { mkdir as mkdir2, mkdtemp, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
|
|
150
|
+
import {
|
|
151
|
+
createServer,
|
|
152
|
+
request as httpRequest
|
|
153
|
+
} from "node:http";
|
|
154
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
155
|
+
import { join as join7, relative as relative4, resolve as resolve6, sep as sep3 } from "node:path";
|
|
156
|
+
|
|
157
|
+
// src/dev/dev-assets.ts
|
|
158
|
+
import * as esbuild2 from "esbuild";
|
|
159
|
+
import { createHash } from "node:crypto";
|
|
160
|
+
import { readFile, stat } from "node:fs/promises";
|
|
161
|
+
import { join as join2, resolve as resolve2, sep } from "node:path";
|
|
162
|
+
|
|
163
|
+
// src/dev/dev-bundler.ts
|
|
164
|
+
import * as esbuild from "esbuild";
|
|
165
|
+
import { join, resolve } from "node:path";
|
|
166
|
+
function createDevBundler(input) {
|
|
167
|
+
const entry = resolve(input.entryPath);
|
|
168
|
+
const base = localBundleOptions(entry);
|
|
169
|
+
const options = {
|
|
170
|
+
...base,
|
|
171
|
+
sourcemap: "external",
|
|
172
|
+
sourcesContent: false,
|
|
173
|
+
metafile: true,
|
|
174
|
+
// Never written (write: false); only anchors source map paths at the root.
|
|
175
|
+
outfile: join(input.root, "__kody_dev_bundle.js")
|
|
176
|
+
};
|
|
177
|
+
const paths = { workingDir: base.absWorkingDir, displayRoot: input.root };
|
|
178
|
+
let disposed = false;
|
|
179
|
+
let context3 = null;
|
|
180
|
+
return {
|
|
181
|
+
build: async () => {
|
|
182
|
+
let result;
|
|
183
|
+
try {
|
|
184
|
+
if (disposed) throw new Error("kody dev bundler was already disposed.");
|
|
185
|
+
context3 ??= esbuild.context(options);
|
|
186
|
+
result = await (await context3).rebuild();
|
|
187
|
+
} catch (error) {
|
|
188
|
+
throw createBundleError(input.entryPath, error, paths);
|
|
189
|
+
}
|
|
190
|
+
return readBundle(result, base.absWorkingDir, input.entryPath);
|
|
191
|
+
},
|
|
192
|
+
dispose: async () => {
|
|
193
|
+
disposed = true;
|
|
194
|
+
const current = context3;
|
|
195
|
+
context3 = null;
|
|
196
|
+
await (await current?.catch(() => null))?.dispose();
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function bundleInputs(metafile, workingDir) {
|
|
201
|
+
return Object.keys(metafile?.inputs ?? {}).filter((path) => !path.includes("node_modules") && !path.includes(":")).map((path) => resolve(workingDir, path));
|
|
202
|
+
}
|
|
203
|
+
function readBundle(result, workingDir, entryPath) {
|
|
204
|
+
const outputs = result.outputFiles ?? [];
|
|
205
|
+
const js = outputs.find((file) => file.path.endsWith(".js"));
|
|
206
|
+
const map = outputs.find((file) => file.path.endsWith(".js.map"));
|
|
207
|
+
if (!js) {
|
|
208
|
+
throw new BundleError(`Could not bundle ${entryPath}: esbuild produced no output.`, []);
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
code: js.text.replace(/\n\/\/# sourceMappingURL=\S+\s*$/, "\n"),
|
|
212
|
+
sourceMap: map?.text ?? null,
|
|
213
|
+
inputs: bundleInputs(result.metafile, workingDir)
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/dev/dev-assets.ts
|
|
218
|
+
var devAssetsPathPrefix = "/_assets/";
|
|
219
|
+
var devVersionAssetName = "__version.json";
|
|
220
|
+
var clientModuleHashLength = 16;
|
|
221
|
+
function createDevClientBundler(input) {
|
|
222
|
+
const externals = input.externals ?? [];
|
|
223
|
+
const workingDir = process.cwd();
|
|
224
|
+
let disposed = false;
|
|
225
|
+
let context3 = null;
|
|
226
|
+
const createContext = () => esbuild2.context({
|
|
227
|
+
absWorkingDir: workingDir,
|
|
228
|
+
entryPoints: [resolve2(input.entryPath)],
|
|
229
|
+
bundle: true,
|
|
230
|
+
write: false,
|
|
231
|
+
metafile: true,
|
|
232
|
+
format: "esm",
|
|
233
|
+
platform: "browser",
|
|
234
|
+
target: "es2022",
|
|
235
|
+
sourcemap: "inline",
|
|
236
|
+
logLevel: "silent",
|
|
237
|
+
outfile: join2(input.root, "_assets", "client.js"),
|
|
238
|
+
plugins: externals.length > 0 ? [clientExternalsPlugin(externals)] : []
|
|
239
|
+
});
|
|
240
|
+
return {
|
|
241
|
+
build: async () => {
|
|
242
|
+
try {
|
|
243
|
+
if (disposed) throw new Error("kody dev bundler was already disposed.");
|
|
244
|
+
context3 ??= createContext();
|
|
245
|
+
const result = await (await context3).rebuild();
|
|
246
|
+
const code = result.outputFiles.find((file) => file.path.endsWith(".js"))?.text ?? "";
|
|
247
|
+
const hash = createHash("sha256").update(code).digest("base64url").slice(0, clientModuleHashLength);
|
|
248
|
+
return {
|
|
249
|
+
name: `client.${hash}.js`,
|
|
250
|
+
code,
|
|
251
|
+
inputs: bundleInputs(result.metafile, workingDir)
|
|
252
|
+
};
|
|
253
|
+
} catch (error) {
|
|
254
|
+
throw createBundleError(input.entryPath, error, {
|
|
255
|
+
workingDir,
|
|
256
|
+
displayRoot: input.root
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
dispose: async () => {
|
|
261
|
+
disposed = true;
|
|
262
|
+
const current = context3;
|
|
263
|
+
context3 = null;
|
|
264
|
+
await (await current?.catch(() => null))?.dispose();
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function clientExternalsPlugin(externals) {
|
|
269
|
+
return {
|
|
270
|
+
name: "kody-dev-client-externals",
|
|
271
|
+
setup(build) {
|
|
272
|
+
build.onResolve(
|
|
273
|
+
{ filter: /^[^./]/ },
|
|
274
|
+
(args) => externals.some(
|
|
275
|
+
(external) => args.path === external || args.path.startsWith(`${external}/`)
|
|
276
|
+
) ? { path: args.path, external: true } : void 0
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
function parseDevAssetPath(pathname) {
|
|
282
|
+
if (!pathname.startsWith(devAssetsPathPrefix)) return null;
|
|
283
|
+
const encoded = pathname.slice(devAssetsPathPrefix.length);
|
|
284
|
+
if (!encoded) return null;
|
|
285
|
+
try {
|
|
286
|
+
return decodeURIComponent(encoded);
|
|
287
|
+
} catch {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function resolveDevAssetFile(assetsDir, relativePath) {
|
|
292
|
+
if (relativePath.includes("\0") || relativePath.startsWith("/")) return null;
|
|
293
|
+
const root = resolve2(assetsDir);
|
|
294
|
+
const file = resolve2(root, relativePath);
|
|
295
|
+
return file.startsWith(`${root}${sep}`) ? file : null;
|
|
296
|
+
}
|
|
297
|
+
async function serveDevAsset(input) {
|
|
298
|
+
const { req, res } = input;
|
|
299
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
300
|
+
res.writeHead(405, { allow: "GET, HEAD", "cache-control": "no-store" });
|
|
301
|
+
res.end("Method not allowed\n");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (input.relativePath === devVersionAssetName) {
|
|
305
|
+
send(req, res, "application/json; charset=utf-8", JSON.stringify({
|
|
306
|
+
clientModuleUrl: input.clientModuleUrl,
|
|
307
|
+
assetBasePath: `${input.appBasePath}/_assets`,
|
|
308
|
+
publishedCommit: null
|
|
309
|
+
}));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (input.client && input.relativePath === input.client.name) {
|
|
313
|
+
send(req, res, "text/javascript; charset=utf-8", input.client.code);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const file = input.assetsDir ? resolveDevAssetFile(input.assetsDir, input.relativePath) : null;
|
|
317
|
+
const body = file ? await readAssetFile(file) : null;
|
|
318
|
+
if (!file || !body) {
|
|
319
|
+
res.writeHead(404, {
|
|
320
|
+
"content-type": "text/plain; charset=utf-8",
|
|
321
|
+
"cache-control": "no-store"
|
|
322
|
+
});
|
|
323
|
+
res.end("Not found\n");
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const contentType = inferDevAssetContentType(file);
|
|
327
|
+
send(req, res, contentType, body, contentType.startsWith("text/javascript") ? { "service-worker-allowed": `${input.appBasePath}/` } : {});
|
|
328
|
+
}
|
|
329
|
+
async function readAssetFile(file) {
|
|
330
|
+
try {
|
|
331
|
+
if (!(await stat(file)).isFile()) return null;
|
|
332
|
+
return await readFile(file);
|
|
333
|
+
} catch {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function send(req, res, contentType, body, extraHeaders = {}) {
|
|
338
|
+
const bytes = typeof body === "string" ? Buffer.from(body, "utf8") : body;
|
|
339
|
+
res.writeHead(200, {
|
|
340
|
+
"content-type": contentType,
|
|
341
|
+
"content-length": String(bytes.length),
|
|
342
|
+
"cache-control": "no-store",
|
|
343
|
+
"x-content-type-options": "nosniff",
|
|
344
|
+
...extraHeaders
|
|
345
|
+
});
|
|
346
|
+
res.end(req.method === "HEAD" ? void 0 : bytes);
|
|
347
|
+
}
|
|
348
|
+
var contentTypesByExtension = {
|
|
349
|
+
avif: "image/avif",
|
|
350
|
+
css: "text/css; charset=utf-8",
|
|
351
|
+
csv: "text/csv; charset=utf-8",
|
|
352
|
+
gif: "image/gif",
|
|
353
|
+
htm: "text/html; charset=utf-8",
|
|
354
|
+
html: "text/html; charset=utf-8",
|
|
355
|
+
ico: "image/x-icon",
|
|
356
|
+
jpeg: "image/jpeg",
|
|
357
|
+
jpg: "image/jpeg",
|
|
358
|
+
js: "text/javascript; charset=utf-8",
|
|
359
|
+
json: "application/json; charset=utf-8",
|
|
360
|
+
map: "application/json; charset=utf-8",
|
|
361
|
+
md: "text/markdown; charset=utf-8",
|
|
362
|
+
mjs: "text/javascript; charset=utf-8",
|
|
363
|
+
mp3: "audio/mpeg",
|
|
364
|
+
mp4: "video/mp4",
|
|
365
|
+
oga: "audio/ogg",
|
|
366
|
+
ogg: "audio/ogg",
|
|
367
|
+
ogv: "video/ogg",
|
|
368
|
+
otf: "font/otf",
|
|
369
|
+
pdf: "application/pdf",
|
|
370
|
+
png: "image/png",
|
|
371
|
+
svg: "image/svg+xml; charset=utf-8",
|
|
372
|
+
ttf: "font/ttf",
|
|
373
|
+
txt: "text/plain; charset=utf-8",
|
|
374
|
+
wasm: "application/wasm",
|
|
375
|
+
wav: "audio/wav",
|
|
376
|
+
webm: "video/webm",
|
|
377
|
+
webmanifest: "application/manifest+json; charset=utf-8",
|
|
378
|
+
webp: "image/webp",
|
|
379
|
+
woff: "font/woff",
|
|
380
|
+
woff2: "font/woff2",
|
|
381
|
+
xml: "application/xml; charset=utf-8"
|
|
382
|
+
};
|
|
383
|
+
function inferDevAssetContentType(path) {
|
|
384
|
+
const name = path.split(/[\\/]/).pop() ?? "";
|
|
385
|
+
const separator = name.lastIndexOf(".");
|
|
386
|
+
const extension = separator <= 0 ? "" : name.slice(separator + 1).toLowerCase();
|
|
387
|
+
return contentTypesByExtension[extension] ?? "application/octet-stream";
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// src/dev/dev-events.ts
|
|
391
|
+
var logLevels = /* @__PURE__ */ new Set(["log", "info", "warn", "error", "debug"]);
|
|
392
|
+
function parseWorkerdStdoutLine(line) {
|
|
393
|
+
if (line.startsWith(appLogMarker)) {
|
|
394
|
+
try {
|
|
395
|
+
const parsed = JSON.parse(line.slice(appLogMarker.length));
|
|
396
|
+
const level = typeof parsed.level === "string" && logLevels.has(parsed.level) ? parsed.level : "log";
|
|
397
|
+
return {
|
|
398
|
+
type: "log",
|
|
399
|
+
source: "app",
|
|
400
|
+
level,
|
|
401
|
+
message: typeof parsed.message === "string" ? parsed.message : String(parsed.message),
|
|
402
|
+
request: readRequestTag(parsed.request)
|
|
403
|
+
};
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return { type: "log", source: "app", level: "log", message: line, request: null };
|
|
408
|
+
}
|
|
409
|
+
function readRequestTag(value) {
|
|
410
|
+
if (!value || typeof value !== "object") return null;
|
|
411
|
+
const record = value;
|
|
412
|
+
if (typeof record.id !== "string") return null;
|
|
413
|
+
return {
|
|
414
|
+
id: record.id,
|
|
415
|
+
method: typeof record.method === "string" ? record.method : "GET",
|
|
416
|
+
path: typeof record.path === "string" ? record.path : "/"
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function runtimeErrorFix(message) {
|
|
420
|
+
const dynamicRequire = /Dynamic require of "([^"]+)" is not supported/.exec(message);
|
|
421
|
+
if (dynamicRequire) {
|
|
422
|
+
return `A CommonJS dependency calls require("${dynamicRequire[1]}"), which an ESM Worker cannot run (hosted apps fail the same way). It likely resolved to its Node build: import the package\u2019s Workers/edge entry (an exports subpath or condition like \`edge\`, \`worker\`, or \`workerd\`) or an ESM build instead.`;
|
|
423
|
+
}
|
|
424
|
+
return void 0;
|
|
425
|
+
}
|
|
426
|
+
function formatRequestTag(request) {
|
|
427
|
+
return request ? `[${request.method} ${request.path} #${request.id}]` : "[app]";
|
|
428
|
+
}
|
|
429
|
+
function formatDevEvent(event) {
|
|
430
|
+
switch (event.type) {
|
|
431
|
+
case "rebuild_start":
|
|
432
|
+
return {
|
|
433
|
+
stream: "stderr",
|
|
434
|
+
text: event.files.length > 0 ? `Rebuilding (${event.files.join(", ")})\u2026` : "Building\u2026"
|
|
435
|
+
};
|
|
436
|
+
case "rebuild_ok":
|
|
437
|
+
return {
|
|
438
|
+
stream: "stderr",
|
|
439
|
+
text: event.restarted ? `Ready in ${event.ms}ms (build ${event.buildId})` : `No bundle changes (${event.ms}ms)`
|
|
440
|
+
};
|
|
441
|
+
case "build_error":
|
|
442
|
+
return {
|
|
443
|
+
stream: "stderr",
|
|
444
|
+
text: `Build failed${event.diagnostics.length > 0 ? `:
|
|
445
|
+
${event.diagnostics.map(formatBundleDiagnostic).join("\n")}` : `: ${event.message}`}
|
|
446
|
+
The last good build is paused until this is fixed; requests get the error page.`
|
|
447
|
+
};
|
|
448
|
+
case "runtime_error":
|
|
449
|
+
return {
|
|
450
|
+
stream: "stderr",
|
|
451
|
+
text: `${formatRequestTag(event.request)} Uncaught ${event.stack || event.message}${event.fix ? `
|
|
452
|
+
fix: ${event.fix}` : ""}`
|
|
453
|
+
};
|
|
454
|
+
case "log": {
|
|
455
|
+
const tag = event.source === "app" ? formatRequestTag(event.request) : `[${event.source}]`;
|
|
456
|
+
const level = event.level === "log" || event.level === "info" ? "" : `${event.level}: `;
|
|
457
|
+
const stream = event.level === "error" || event.level === "warn" ? "stderr" : "stdout";
|
|
458
|
+
return { stream, text: `${tag} ${level}${event.message}` };
|
|
459
|
+
}
|
|
460
|
+
default: {
|
|
461
|
+
const exhaustive = event;
|
|
462
|
+
throw new Error(`Unhandled dev event: ${JSON.stringify(exhaustive)}`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/dev/dev-port.ts
|
|
468
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
469
|
+
var devPortRange = { min: 4100, max: 4999 };
|
|
470
|
+
var fallbackAttempts = 20;
|
|
471
|
+
function defaultDevPort(kodyId) {
|
|
472
|
+
const span = devPortRange.max - devPortRange.min + 1;
|
|
473
|
+
const hash = createHash2("sha256").update(kodyId).digest();
|
|
474
|
+
return devPortRange.min + hash.readUInt32BE(0) % span;
|
|
475
|
+
}
|
|
476
|
+
function parseDevPort(raw) {
|
|
477
|
+
const port = /^\d+$/.test(raw.trim()) ? Number(raw) : Number.NaN;
|
|
478
|
+
if (!Number.isInteger(port) || port > 65535) {
|
|
479
|
+
throw new StructuredCliError({
|
|
480
|
+
code: "invalid_port",
|
|
481
|
+
message: `--port must be an integer from 0 to 65535 (got "${raw}").`,
|
|
482
|
+
fix: "Pass --port 0 for any free port, or omit --port to use this package\u2019s stable default."
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
return port;
|
|
486
|
+
}
|
|
487
|
+
async function listenDevServer(server, input) {
|
|
488
|
+
const attempts = input.explicit || input.port === 0 ? 1 : fallbackAttempts;
|
|
489
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
490
|
+
const port = input.port === 0 ? 0 : wrapPort(input.port + attempt);
|
|
491
|
+
try {
|
|
492
|
+
await listenOnce(server, input.host, port);
|
|
493
|
+
return server.address().port;
|
|
494
|
+
} catch (error) {
|
|
495
|
+
if (!isAddressInUse(error)) throw error;
|
|
496
|
+
if (input.explicit) throw portInUseError(input.host, port);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
throw new StructuredCliError({
|
|
500
|
+
code: "dev_port_in_use",
|
|
501
|
+
message: `Ports ${input.port}\u2013${wrapPort(input.port + attempts - 1)} on ${input.host} are all in use.`,
|
|
502
|
+
fix: "Stop old `kody dev` processes, or pass --port 0 for any free port."
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
function portInUseError(host, port) {
|
|
506
|
+
return new StructuredCliError({
|
|
507
|
+
code: "dev_port_in_use",
|
|
508
|
+
message: `Port ${port} on ${host} is already in use.`,
|
|
509
|
+
fix: `Find the owner with \`lsof -nP -iTCP:${port} -sTCP:LISTEN\` and stop it, pass another --port, or omit --port to use this package\u2019s default (it moves to the next free port automatically).`
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function wrapPort(port) {
|
|
513
|
+
return port > 65535 ? devPortRange.min + (port - 65536) : port;
|
|
514
|
+
}
|
|
515
|
+
function listenOnce(server, host, port) {
|
|
516
|
+
return new Promise((resolve8, reject) => {
|
|
517
|
+
const onError = (error) => {
|
|
518
|
+
server.off("listening", onListening);
|
|
519
|
+
reject(error);
|
|
520
|
+
};
|
|
521
|
+
const onListening = () => {
|
|
522
|
+
server.off("error", onError);
|
|
523
|
+
resolve8();
|
|
524
|
+
};
|
|
525
|
+
server.once("error", onError);
|
|
526
|
+
server.once("listening", onListening);
|
|
527
|
+
server.listen(port, host);
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
function isAddressInUse(error) {
|
|
531
|
+
return error?.code === "EADDRINUSE";
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// src/dev/dev-registry.ts
|
|
535
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
536
|
+
import { mkdir, readFile as readFile3, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
537
|
+
import { join as join4 } from "node:path";
|
|
538
|
+
|
|
539
|
+
// src/dev/process-reaper.ts
|
|
540
|
+
import { spawn } from "node:child_process";
|
|
541
|
+
import { readdir, readFile as readFile2, rm, stat as stat2, writeFile } from "node:fs/promises";
|
|
542
|
+
import { tmpdir } from "node:os";
|
|
543
|
+
import { join as join3 } from "node:path";
|
|
544
|
+
import psList from "ps-list";
|
|
545
|
+
var devWorkDirPrefix = "kody-dev-";
|
|
546
|
+
var ownerFileName = "owner.json";
|
|
547
|
+
var legacyDirMinAgeMs = 10 * 6e4;
|
|
548
|
+
var reaperScript = `
|
|
549
|
+
pids=" "
|
|
550
|
+
while IFS= read -r line; do
|
|
551
|
+
case "$line" in
|
|
552
|
+
+*) pids="$pids\${line#+} " ;;
|
|
553
|
+
-*) pids="$(printf '%s' "$pids" | sed "s/ \${line#-} / /g")" ;;
|
|
554
|
+
esac
|
|
555
|
+
done
|
|
556
|
+
for entry in $pids; do
|
|
557
|
+
case "$entry" in
|
|
558
|
+
g*) /bin/kill -s KILL -- -\${entry#g} 2>/dev/null ;;
|
|
559
|
+
*) /bin/kill -s KILL -- $entry 2>/dev/null ;;
|
|
560
|
+
esac
|
|
561
|
+
done
|
|
562
|
+
rm -rf "$1"
|
|
563
|
+
`;
|
|
564
|
+
async function startProcessReaper(input) {
|
|
565
|
+
await writeFile(
|
|
566
|
+
join3(input.workDir, ownerFileName),
|
|
567
|
+
JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
568
|
+
);
|
|
569
|
+
let reaper;
|
|
570
|
+
try {
|
|
571
|
+
reaper = spawn("/bin/sh", ["-c", reaperScript, "kody-dev-reaper", input.workDir], {
|
|
572
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
573
|
+
detached: true
|
|
574
|
+
});
|
|
575
|
+
reaper.on("error", () => {
|
|
576
|
+
reaper = void 0;
|
|
577
|
+
});
|
|
578
|
+
reaper.stdin?.on("error", () => {
|
|
579
|
+
});
|
|
580
|
+
reaper.unref();
|
|
581
|
+
reaper.stdin?.unref?.();
|
|
582
|
+
} catch {
|
|
583
|
+
reaper = void 0;
|
|
584
|
+
}
|
|
585
|
+
const send2 = (line) => {
|
|
586
|
+
if (reaper?.stdin?.writable) reaper.stdin.write(`${line}
|
|
587
|
+
`);
|
|
588
|
+
};
|
|
589
|
+
return {
|
|
590
|
+
track: (pid) => send2(`+${pid}`),
|
|
591
|
+
untrack: (pid) => send2(`-${pid}`),
|
|
592
|
+
trackGroup: (pid) => send2(`+g${pid}`),
|
|
593
|
+
untrackGroup: (pid) => send2(`-g${pid}`),
|
|
594
|
+
close: () => {
|
|
595
|
+
reaper?.stdin?.end();
|
|
596
|
+
reaper = void 0;
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
async function sweepStaleDevDirs(input = {}) {
|
|
601
|
+
const tmp = input.tmp ?? tmpdir();
|
|
602
|
+
const now = input.now ?? Date.now();
|
|
603
|
+
const isAlive = input.isAlive ?? isProcessAlive;
|
|
604
|
+
const kill = input.kill ?? ((pid) => process.kill(pid, "SIGKILL"));
|
|
605
|
+
const removed = [];
|
|
606
|
+
const killed = [];
|
|
607
|
+
let entries;
|
|
608
|
+
try {
|
|
609
|
+
entries = (await readdir(tmp)).filter((name) => name.startsWith(devWorkDirPrefix));
|
|
610
|
+
} catch {
|
|
611
|
+
return { removed, killed };
|
|
612
|
+
}
|
|
613
|
+
if (entries.length === 0) return { removed, killed };
|
|
614
|
+
let processes = [];
|
|
615
|
+
try {
|
|
616
|
+
processes = await (input.listProcesses ?? psList)();
|
|
617
|
+
} catch {
|
|
618
|
+
processes = [];
|
|
619
|
+
}
|
|
620
|
+
const byPid = new Map(processes.map((item) => [item.pid, item]));
|
|
621
|
+
for (const name of entries) {
|
|
622
|
+
const dir = join3(tmp, name);
|
|
623
|
+
try {
|
|
624
|
+
const owner = await readOwnerPid(dir);
|
|
625
|
+
if (owner !== null && isAlive(owner)) continue;
|
|
626
|
+
const workerds = processes.filter(
|
|
627
|
+
(item) => /workerd/.test(item.name) && (item.cmd ?? "").includes(`${dir}/`)
|
|
628
|
+
);
|
|
629
|
+
if (owner === null) {
|
|
630
|
+
const info = await stat2(dir);
|
|
631
|
+
if (now - info.mtimeMs < legacyDirMinAgeMs) continue;
|
|
632
|
+
if (workerds.some((item) => !isOrphaned(item, byPid, isAlive))) continue;
|
|
633
|
+
}
|
|
634
|
+
for (const item of workerds) {
|
|
635
|
+
try {
|
|
636
|
+
kill(item.pid);
|
|
637
|
+
killed.push(item.pid);
|
|
638
|
+
} catch {
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
await rm(dir, { recursive: true, force: true });
|
|
642
|
+
removed.push(dir);
|
|
643
|
+
} catch {
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return { removed, killed };
|
|
647
|
+
}
|
|
648
|
+
function isOrphaned(item, byPid, isAlive) {
|
|
649
|
+
if (item.ppid === void 0) return false;
|
|
650
|
+
if (item.ppid <= 1 || !isAlive(item.ppid)) return true;
|
|
651
|
+
const parent = byPid.get(item.ppid);
|
|
652
|
+
return parent !== void 0 && !/node|kody/i.test(`${parent.name} ${parent.cmd ?? ""}`);
|
|
653
|
+
}
|
|
654
|
+
async function readOwnerPid(dir) {
|
|
655
|
+
try {
|
|
656
|
+
const parsed = JSON.parse(await readFile2(join3(dir, ownerFileName), "utf8"));
|
|
657
|
+
return typeof parsed.pid === "number" ? parsed.pid : null;
|
|
658
|
+
} catch {
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
function isProcessAlive(pid) {
|
|
663
|
+
try {
|
|
664
|
+
process.kill(pid, 0);
|
|
665
|
+
return true;
|
|
666
|
+
} catch (error) {
|
|
667
|
+
return error.code === "EPERM";
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/dev/dev-registry.ts
|
|
672
|
+
var devStatusPath = "/__kody/dev/status";
|
|
673
|
+
function devRegistryPath(root, target, cacheDir = userCacheDir()) {
|
|
674
|
+
const key = createHash3("sha256").update(`${root}\0${target}`).digest("hex").slice(0, 16);
|
|
675
|
+
return join4(cacheDir, "dev-servers", `${key}.json`);
|
|
676
|
+
}
|
|
677
|
+
async function writeDevRegistration(registration, cacheDir) {
|
|
678
|
+
const path = devRegistryPath(registration.root, registration.target, cacheDir);
|
|
679
|
+
await mkdir(join4(path, ".."), { recursive: true });
|
|
680
|
+
await writeFile2(path, `${JSON.stringify(registration, null, 2)}
|
|
681
|
+
`);
|
|
682
|
+
return path;
|
|
683
|
+
}
|
|
684
|
+
async function removeDevRegistration(root, pid, target, cacheDir) {
|
|
685
|
+
const path = devRegistryPath(root, target, cacheDir);
|
|
686
|
+
try {
|
|
687
|
+
const current = await readRegistration(path);
|
|
688
|
+
if (!current || current.pid !== pid) return;
|
|
689
|
+
await rm2(path, { force: true });
|
|
690
|
+
} catch {
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
async function findRunningDevServer(input) {
|
|
694
|
+
const path = devRegistryPath(input.root, input.target, input.cacheDir);
|
|
695
|
+
const registration = await readRegistration(path);
|
|
696
|
+
if (!registration || registration.root !== input.root) return null;
|
|
697
|
+
if (!isProcessAlive(registration.pid)) {
|
|
698
|
+
const current = await readRegistration(path);
|
|
699
|
+
if (current && current.pid === registration.pid) {
|
|
700
|
+
await rm2(path, { force: true });
|
|
701
|
+
}
|
|
702
|
+
return null;
|
|
703
|
+
}
|
|
704
|
+
if (registration.target !== input.target) return null;
|
|
705
|
+
try {
|
|
706
|
+
const response = await (input.fetchFn ?? fetch)(
|
|
707
|
+
new URL(devStatusPath, registration.origin),
|
|
708
|
+
{ signal: AbortSignal.timeout(input.timeoutMs ?? 1500) }
|
|
709
|
+
);
|
|
710
|
+
if (!response.ok) return null;
|
|
711
|
+
const status = await response.json();
|
|
712
|
+
if (status.package?.root !== input.root || status.target !== input.target) return null;
|
|
713
|
+
return { registration, status };
|
|
714
|
+
} catch {
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
async function readRegistration(path) {
|
|
719
|
+
try {
|
|
720
|
+
const parsed = JSON.parse(await readFile3(path, "utf8"));
|
|
721
|
+
if (typeof parsed.pid !== "number" || typeof parsed.origin !== "string" || typeof parsed.root !== "string" || parsed.target !== "mock" && parsed.target !== "prod") {
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
return {
|
|
725
|
+
pid: parsed.pid,
|
|
726
|
+
origin: parsed.origin,
|
|
727
|
+
port: typeof parsed.port === "number" ? parsed.port : Number(new URL(parsed.origin).port),
|
|
728
|
+
basePath: typeof parsed.basePath === "string" ? parsed.basePath : "",
|
|
729
|
+
target: parsed.target,
|
|
730
|
+
root: parsed.root
|
|
731
|
+
};
|
|
732
|
+
} catch {
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// node_modules/chokidar/esm/index.js
|
|
738
|
+
import { stat as statcb } from "fs";
|
|
739
|
+
import { stat as stat5, readdir as readdir3 } from "fs/promises";
|
|
740
|
+
import { EventEmitter } from "events";
|
|
741
|
+
import * as sysPath2 from "path";
|
|
742
|
+
|
|
743
|
+
// node_modules/readdirp/esm/index.js
|
|
744
|
+
import { stat as stat3, lstat, readdir as readdir2, realpath } from "node:fs/promises";
|
|
745
|
+
import { Readable } from "node:stream";
|
|
746
|
+
import { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from "node:path";
|
|
747
|
+
var EntryTypes = {
|
|
748
|
+
FILE_TYPE: "files",
|
|
749
|
+
DIR_TYPE: "directories",
|
|
750
|
+
FILE_DIR_TYPE: "files_directories",
|
|
751
|
+
EVERYTHING_TYPE: "all"
|
|
752
|
+
};
|
|
753
|
+
var defaultOptions = {
|
|
754
|
+
root: ".",
|
|
755
|
+
fileFilter: (_entryInfo) => true,
|
|
756
|
+
directoryFilter: (_entryInfo) => true,
|
|
757
|
+
type: EntryTypes.FILE_TYPE,
|
|
758
|
+
lstat: false,
|
|
759
|
+
depth: 2147483648,
|
|
760
|
+
alwaysStat: false,
|
|
761
|
+
highWaterMark: 4096
|
|
762
|
+
};
|
|
763
|
+
Object.freeze(defaultOptions);
|
|
764
|
+
var RECURSIVE_ERROR_CODE = "READDIRP_RECURSIVE_ERROR";
|
|
765
|
+
var NORMAL_FLOW_ERRORS = /* @__PURE__ */ new Set(["ENOENT", "EPERM", "EACCES", "ELOOP", RECURSIVE_ERROR_CODE]);
|
|
766
|
+
var ALL_TYPES = [
|
|
767
|
+
EntryTypes.DIR_TYPE,
|
|
768
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
769
|
+
EntryTypes.FILE_DIR_TYPE,
|
|
770
|
+
EntryTypes.FILE_TYPE
|
|
771
|
+
];
|
|
772
|
+
var DIR_TYPES = /* @__PURE__ */ new Set([
|
|
773
|
+
EntryTypes.DIR_TYPE,
|
|
774
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
775
|
+
EntryTypes.FILE_DIR_TYPE
|
|
776
|
+
]);
|
|
777
|
+
var FILE_TYPES = /* @__PURE__ */ new Set([
|
|
778
|
+
EntryTypes.EVERYTHING_TYPE,
|
|
779
|
+
EntryTypes.FILE_DIR_TYPE,
|
|
780
|
+
EntryTypes.FILE_TYPE
|
|
781
|
+
]);
|
|
782
|
+
var isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code);
|
|
783
|
+
var wantBigintFsStats = process.platform === "win32";
|
|
784
|
+
var emptyFn = (_entryInfo) => true;
|
|
785
|
+
var normalizeFilter = (filter) => {
|
|
786
|
+
if (filter === void 0)
|
|
787
|
+
return emptyFn;
|
|
788
|
+
if (typeof filter === "function")
|
|
789
|
+
return filter;
|
|
790
|
+
if (typeof filter === "string") {
|
|
791
|
+
const fl = filter.trim();
|
|
792
|
+
return (entry) => entry.basename === fl;
|
|
793
|
+
}
|
|
794
|
+
if (Array.isArray(filter)) {
|
|
795
|
+
const trItems = filter.map((item) => item.trim());
|
|
796
|
+
return (entry) => trItems.some((f) => entry.basename === f);
|
|
797
|
+
}
|
|
798
|
+
return emptyFn;
|
|
799
|
+
};
|
|
800
|
+
var ReaddirpStream = class extends Readable {
|
|
801
|
+
constructor(options = {}) {
|
|
802
|
+
super({
|
|
803
|
+
objectMode: true,
|
|
804
|
+
autoDestroy: true,
|
|
805
|
+
highWaterMark: options.highWaterMark
|
|
806
|
+
});
|
|
807
|
+
const opts = { ...defaultOptions, ...options };
|
|
808
|
+
const { root, type } = opts;
|
|
809
|
+
this._fileFilter = normalizeFilter(opts.fileFilter);
|
|
810
|
+
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
811
|
+
const statMethod = opts.lstat ? lstat : stat3;
|
|
812
|
+
if (wantBigintFsStats) {
|
|
813
|
+
this._stat = (path) => statMethod(path, { bigint: true });
|
|
814
|
+
} else {
|
|
815
|
+
this._stat = statMethod;
|
|
816
|
+
}
|
|
817
|
+
this._maxDepth = opts.depth ?? defaultOptions.depth;
|
|
818
|
+
this._wantsDir = type ? DIR_TYPES.has(type) : false;
|
|
819
|
+
this._wantsFile = type ? FILE_TYPES.has(type) : false;
|
|
820
|
+
this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
|
|
821
|
+
this._root = presolve(root);
|
|
822
|
+
this._isDirent = !opts.alwaysStat;
|
|
823
|
+
this._statsProp = this._isDirent ? "dirent" : "stats";
|
|
824
|
+
this._rdOptions = { encoding: "utf8", withFileTypes: this._isDirent };
|
|
825
|
+
this.parents = [this._exploreDir(root, 1)];
|
|
826
|
+
this.reading = false;
|
|
827
|
+
this.parent = void 0;
|
|
828
|
+
}
|
|
829
|
+
async _read(batch) {
|
|
830
|
+
if (this.reading)
|
|
831
|
+
return;
|
|
832
|
+
this.reading = true;
|
|
833
|
+
try {
|
|
834
|
+
while (!this.destroyed && batch > 0) {
|
|
835
|
+
const par = this.parent;
|
|
836
|
+
const fil = par && par.files;
|
|
837
|
+
if (fil && fil.length > 0) {
|
|
838
|
+
const { path, depth } = par;
|
|
839
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path));
|
|
840
|
+
const awaited = await Promise.all(slice);
|
|
841
|
+
for (const entry of awaited) {
|
|
842
|
+
if (!entry)
|
|
843
|
+
continue;
|
|
844
|
+
if (this.destroyed)
|
|
845
|
+
return;
|
|
846
|
+
const entryType = await this._getEntryType(entry);
|
|
847
|
+
if (entryType === "directory" && this._directoryFilter(entry)) {
|
|
848
|
+
if (depth <= this._maxDepth) {
|
|
849
|
+
this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
|
|
850
|
+
}
|
|
851
|
+
if (this._wantsDir) {
|
|
852
|
+
this.push(entry);
|
|
853
|
+
batch--;
|
|
854
|
+
}
|
|
855
|
+
} else if ((entryType === "file" || this._includeAsFile(entry)) && this._fileFilter(entry)) {
|
|
856
|
+
if (this._wantsFile) {
|
|
857
|
+
this.push(entry);
|
|
858
|
+
batch--;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
} else {
|
|
863
|
+
const parent = this.parents.pop();
|
|
864
|
+
if (!parent) {
|
|
865
|
+
this.push(null);
|
|
866
|
+
break;
|
|
867
|
+
}
|
|
868
|
+
this.parent = await parent;
|
|
869
|
+
if (this.destroyed)
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
} catch (error) {
|
|
874
|
+
this.destroy(error);
|
|
875
|
+
} finally {
|
|
876
|
+
this.reading = false;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
async _exploreDir(path, depth) {
|
|
880
|
+
let files;
|
|
881
|
+
try {
|
|
882
|
+
files = await readdir2(path, this._rdOptions);
|
|
883
|
+
} catch (error) {
|
|
884
|
+
this._onError(error);
|
|
885
|
+
}
|
|
886
|
+
return { files, depth, path };
|
|
887
|
+
}
|
|
888
|
+
async _formatEntry(dirent, path) {
|
|
889
|
+
let entry;
|
|
890
|
+
const basename3 = this._isDirent ? dirent.name : dirent;
|
|
891
|
+
try {
|
|
892
|
+
const fullPath = presolve(pjoin(path, basename3));
|
|
893
|
+
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
|
|
894
|
+
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
895
|
+
} catch (err) {
|
|
896
|
+
this._onError(err);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
return entry;
|
|
900
|
+
}
|
|
901
|
+
_onError(err) {
|
|
902
|
+
if (isNormalFlowError(err) && !this.destroyed) {
|
|
903
|
+
this.emit("warn", err);
|
|
904
|
+
} else {
|
|
905
|
+
this.destroy(err);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
async _getEntryType(entry) {
|
|
909
|
+
if (!entry && this._statsProp in entry) {
|
|
910
|
+
return "";
|
|
911
|
+
}
|
|
912
|
+
const stats = entry[this._statsProp];
|
|
913
|
+
if (stats.isFile())
|
|
914
|
+
return "file";
|
|
915
|
+
if (stats.isDirectory())
|
|
916
|
+
return "directory";
|
|
917
|
+
if (stats && stats.isSymbolicLink()) {
|
|
918
|
+
const full = entry.fullPath;
|
|
919
|
+
try {
|
|
920
|
+
const entryRealPath = await realpath(full);
|
|
921
|
+
const entryRealPathStats = await lstat(entryRealPath);
|
|
922
|
+
if (entryRealPathStats.isFile()) {
|
|
923
|
+
return "file";
|
|
924
|
+
}
|
|
925
|
+
if (entryRealPathStats.isDirectory()) {
|
|
926
|
+
const len = entryRealPath.length;
|
|
927
|
+
if (full.startsWith(entryRealPath) && full.substr(len, 1) === psep) {
|
|
928
|
+
const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
|
|
929
|
+
recursiveError.code = RECURSIVE_ERROR_CODE;
|
|
930
|
+
return this._onError(recursiveError);
|
|
931
|
+
}
|
|
932
|
+
return "directory";
|
|
933
|
+
}
|
|
934
|
+
} catch (error) {
|
|
935
|
+
this._onError(error);
|
|
936
|
+
return "";
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
_includeAsFile(entry) {
|
|
941
|
+
const stats = entry && entry[this._statsProp];
|
|
942
|
+
return stats && this._wantsEverything && !stats.isDirectory();
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
function readdirp(root, options = {}) {
|
|
946
|
+
let type = options.entryType || options.type;
|
|
947
|
+
if (type === "both")
|
|
948
|
+
type = EntryTypes.FILE_DIR_TYPE;
|
|
949
|
+
if (type)
|
|
950
|
+
options.type = type;
|
|
951
|
+
if (!root) {
|
|
952
|
+
throw new Error("readdirp: root argument is required. Usage: readdirp(root, options)");
|
|
953
|
+
} else if (typeof root !== "string") {
|
|
954
|
+
throw new TypeError("readdirp: root argument must be a string. Usage: readdirp(root, options)");
|
|
955
|
+
} else if (type && !ALL_TYPES.includes(type)) {
|
|
956
|
+
throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(", ")}`);
|
|
957
|
+
}
|
|
958
|
+
options.root = root;
|
|
959
|
+
return new ReaddirpStream(options);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// node_modules/chokidar/esm/handler.js
|
|
963
|
+
import { watchFile, unwatchFile, watch as fs_watch } from "fs";
|
|
964
|
+
import { open, stat as stat4, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
|
|
965
|
+
import * as sysPath from "path";
|
|
966
|
+
import { type as osType } from "os";
|
|
967
|
+
var STR_DATA = "data";
|
|
968
|
+
var STR_END = "end";
|
|
969
|
+
var STR_CLOSE = "close";
|
|
970
|
+
var EMPTY_FN = () => {
|
|
971
|
+
};
|
|
972
|
+
var pl = process.platform;
|
|
973
|
+
var isWindows = pl === "win32";
|
|
974
|
+
var isMacos = pl === "darwin";
|
|
975
|
+
var isLinux = pl === "linux";
|
|
976
|
+
var isFreeBSD = pl === "freebsd";
|
|
977
|
+
var isIBMi = osType() === "OS400";
|
|
978
|
+
var EVENTS = {
|
|
979
|
+
ALL: "all",
|
|
980
|
+
READY: "ready",
|
|
981
|
+
ADD: "add",
|
|
982
|
+
CHANGE: "change",
|
|
983
|
+
ADD_DIR: "addDir",
|
|
984
|
+
UNLINK: "unlink",
|
|
985
|
+
UNLINK_DIR: "unlinkDir",
|
|
986
|
+
RAW: "raw",
|
|
987
|
+
ERROR: "error"
|
|
988
|
+
};
|
|
989
|
+
var EV = EVENTS;
|
|
990
|
+
var THROTTLE_MODE_WATCH = "watch";
|
|
991
|
+
var statMethods = { lstat: lstat2, stat: stat4 };
|
|
992
|
+
var KEY_LISTENERS = "listeners";
|
|
993
|
+
var KEY_ERR = "errHandlers";
|
|
994
|
+
var KEY_RAW = "rawEmitters";
|
|
995
|
+
var HANDLER_KEYS = [KEY_LISTENERS, KEY_ERR, KEY_RAW];
|
|
996
|
+
var binaryExtensions = /* @__PURE__ */ new Set([
|
|
997
|
+
"3dm",
|
|
998
|
+
"3ds",
|
|
999
|
+
"3g2",
|
|
1000
|
+
"3gp",
|
|
1001
|
+
"7z",
|
|
1002
|
+
"a",
|
|
1003
|
+
"aac",
|
|
1004
|
+
"adp",
|
|
1005
|
+
"afdesign",
|
|
1006
|
+
"afphoto",
|
|
1007
|
+
"afpub",
|
|
1008
|
+
"ai",
|
|
1009
|
+
"aif",
|
|
1010
|
+
"aiff",
|
|
1011
|
+
"alz",
|
|
1012
|
+
"ape",
|
|
1013
|
+
"apk",
|
|
1014
|
+
"appimage",
|
|
1015
|
+
"ar",
|
|
1016
|
+
"arj",
|
|
1017
|
+
"asf",
|
|
1018
|
+
"au",
|
|
1019
|
+
"avi",
|
|
1020
|
+
"bak",
|
|
1021
|
+
"baml",
|
|
1022
|
+
"bh",
|
|
1023
|
+
"bin",
|
|
1024
|
+
"bk",
|
|
1025
|
+
"bmp",
|
|
1026
|
+
"btif",
|
|
1027
|
+
"bz2",
|
|
1028
|
+
"bzip2",
|
|
1029
|
+
"cab",
|
|
1030
|
+
"caf",
|
|
1031
|
+
"cgm",
|
|
1032
|
+
"class",
|
|
1033
|
+
"cmx",
|
|
1034
|
+
"cpio",
|
|
1035
|
+
"cr2",
|
|
1036
|
+
"cur",
|
|
1037
|
+
"dat",
|
|
1038
|
+
"dcm",
|
|
1039
|
+
"deb",
|
|
1040
|
+
"dex",
|
|
1041
|
+
"djvu",
|
|
1042
|
+
"dll",
|
|
1043
|
+
"dmg",
|
|
1044
|
+
"dng",
|
|
1045
|
+
"doc",
|
|
1046
|
+
"docm",
|
|
1047
|
+
"docx",
|
|
1048
|
+
"dot",
|
|
1049
|
+
"dotm",
|
|
1050
|
+
"dra",
|
|
1051
|
+
"DS_Store",
|
|
1052
|
+
"dsk",
|
|
1053
|
+
"dts",
|
|
1054
|
+
"dtshd",
|
|
1055
|
+
"dvb",
|
|
1056
|
+
"dwg",
|
|
1057
|
+
"dxf",
|
|
1058
|
+
"ecelp4800",
|
|
1059
|
+
"ecelp7470",
|
|
1060
|
+
"ecelp9600",
|
|
1061
|
+
"egg",
|
|
1062
|
+
"eol",
|
|
1063
|
+
"eot",
|
|
1064
|
+
"epub",
|
|
1065
|
+
"exe",
|
|
1066
|
+
"f4v",
|
|
1067
|
+
"fbs",
|
|
1068
|
+
"fh",
|
|
1069
|
+
"fla",
|
|
1070
|
+
"flac",
|
|
1071
|
+
"flatpak",
|
|
1072
|
+
"fli",
|
|
1073
|
+
"flv",
|
|
1074
|
+
"fpx",
|
|
1075
|
+
"fst",
|
|
1076
|
+
"fvt",
|
|
1077
|
+
"g3",
|
|
1078
|
+
"gh",
|
|
1079
|
+
"gif",
|
|
1080
|
+
"graffle",
|
|
1081
|
+
"gz",
|
|
1082
|
+
"gzip",
|
|
1083
|
+
"h261",
|
|
1084
|
+
"h263",
|
|
1085
|
+
"h264",
|
|
1086
|
+
"icns",
|
|
1087
|
+
"ico",
|
|
1088
|
+
"ief",
|
|
1089
|
+
"img",
|
|
1090
|
+
"ipa",
|
|
1091
|
+
"iso",
|
|
1092
|
+
"jar",
|
|
1093
|
+
"jpeg",
|
|
1094
|
+
"jpg",
|
|
1095
|
+
"jpgv",
|
|
1096
|
+
"jpm",
|
|
1097
|
+
"jxr",
|
|
1098
|
+
"key",
|
|
1099
|
+
"ktx",
|
|
1100
|
+
"lha",
|
|
1101
|
+
"lib",
|
|
1102
|
+
"lvp",
|
|
1103
|
+
"lz",
|
|
1104
|
+
"lzh",
|
|
1105
|
+
"lzma",
|
|
1106
|
+
"lzo",
|
|
1107
|
+
"m3u",
|
|
1108
|
+
"m4a",
|
|
1109
|
+
"m4v",
|
|
1110
|
+
"mar",
|
|
1111
|
+
"mdi",
|
|
1112
|
+
"mht",
|
|
1113
|
+
"mid",
|
|
1114
|
+
"midi",
|
|
1115
|
+
"mj2",
|
|
1116
|
+
"mka",
|
|
1117
|
+
"mkv",
|
|
1118
|
+
"mmr",
|
|
1119
|
+
"mng",
|
|
1120
|
+
"mobi",
|
|
1121
|
+
"mov",
|
|
1122
|
+
"movie",
|
|
1123
|
+
"mp3",
|
|
1124
|
+
"mp4",
|
|
1125
|
+
"mp4a",
|
|
1126
|
+
"mpeg",
|
|
1127
|
+
"mpg",
|
|
1128
|
+
"mpga",
|
|
1129
|
+
"mxu",
|
|
1130
|
+
"nef",
|
|
1131
|
+
"npx",
|
|
1132
|
+
"numbers",
|
|
1133
|
+
"nupkg",
|
|
1134
|
+
"o",
|
|
1135
|
+
"odp",
|
|
1136
|
+
"ods",
|
|
1137
|
+
"odt",
|
|
1138
|
+
"oga",
|
|
1139
|
+
"ogg",
|
|
1140
|
+
"ogv",
|
|
1141
|
+
"otf",
|
|
1142
|
+
"ott",
|
|
1143
|
+
"pages",
|
|
1144
|
+
"pbm",
|
|
1145
|
+
"pcx",
|
|
1146
|
+
"pdb",
|
|
1147
|
+
"pdf",
|
|
1148
|
+
"pea",
|
|
1149
|
+
"pgm",
|
|
1150
|
+
"pic",
|
|
1151
|
+
"png",
|
|
1152
|
+
"pnm",
|
|
1153
|
+
"pot",
|
|
1154
|
+
"potm",
|
|
1155
|
+
"potx",
|
|
1156
|
+
"ppa",
|
|
1157
|
+
"ppam",
|
|
1158
|
+
"ppm",
|
|
1159
|
+
"pps",
|
|
1160
|
+
"ppsm",
|
|
1161
|
+
"ppsx",
|
|
1162
|
+
"ppt",
|
|
1163
|
+
"pptm",
|
|
1164
|
+
"pptx",
|
|
1165
|
+
"psd",
|
|
1166
|
+
"pya",
|
|
1167
|
+
"pyc",
|
|
1168
|
+
"pyo",
|
|
1169
|
+
"pyv",
|
|
1170
|
+
"qt",
|
|
1171
|
+
"rar",
|
|
1172
|
+
"ras",
|
|
1173
|
+
"raw",
|
|
1174
|
+
"resources",
|
|
1175
|
+
"rgb",
|
|
1176
|
+
"rip",
|
|
1177
|
+
"rlc",
|
|
1178
|
+
"rmf",
|
|
1179
|
+
"rmvb",
|
|
1180
|
+
"rpm",
|
|
1181
|
+
"rtf",
|
|
1182
|
+
"rz",
|
|
1183
|
+
"s3m",
|
|
1184
|
+
"s7z",
|
|
1185
|
+
"scpt",
|
|
1186
|
+
"sgi",
|
|
1187
|
+
"shar",
|
|
1188
|
+
"snap",
|
|
1189
|
+
"sil",
|
|
1190
|
+
"sketch",
|
|
1191
|
+
"slk",
|
|
1192
|
+
"smv",
|
|
1193
|
+
"snk",
|
|
1194
|
+
"so",
|
|
1195
|
+
"stl",
|
|
1196
|
+
"suo",
|
|
1197
|
+
"sub",
|
|
1198
|
+
"swf",
|
|
1199
|
+
"tar",
|
|
1200
|
+
"tbz",
|
|
1201
|
+
"tbz2",
|
|
1202
|
+
"tga",
|
|
1203
|
+
"tgz",
|
|
1204
|
+
"thmx",
|
|
1205
|
+
"tif",
|
|
1206
|
+
"tiff",
|
|
1207
|
+
"tlz",
|
|
1208
|
+
"ttc",
|
|
1209
|
+
"ttf",
|
|
1210
|
+
"txz",
|
|
1211
|
+
"udf",
|
|
1212
|
+
"uvh",
|
|
1213
|
+
"uvi",
|
|
1214
|
+
"uvm",
|
|
1215
|
+
"uvp",
|
|
1216
|
+
"uvs",
|
|
1217
|
+
"uvu",
|
|
1218
|
+
"viv",
|
|
1219
|
+
"vob",
|
|
1220
|
+
"war",
|
|
1221
|
+
"wav",
|
|
1222
|
+
"wax",
|
|
1223
|
+
"wbmp",
|
|
1224
|
+
"wdp",
|
|
1225
|
+
"weba",
|
|
1226
|
+
"webm",
|
|
1227
|
+
"webp",
|
|
1228
|
+
"whl",
|
|
1229
|
+
"wim",
|
|
1230
|
+
"wm",
|
|
1231
|
+
"wma",
|
|
1232
|
+
"wmv",
|
|
1233
|
+
"wmx",
|
|
1234
|
+
"woff",
|
|
1235
|
+
"woff2",
|
|
1236
|
+
"wrm",
|
|
1237
|
+
"wvx",
|
|
1238
|
+
"xbm",
|
|
1239
|
+
"xif",
|
|
1240
|
+
"xla",
|
|
1241
|
+
"xlam",
|
|
1242
|
+
"xls",
|
|
1243
|
+
"xlsb",
|
|
1244
|
+
"xlsm",
|
|
1245
|
+
"xlsx",
|
|
1246
|
+
"xlt",
|
|
1247
|
+
"xltm",
|
|
1248
|
+
"xltx",
|
|
1249
|
+
"xm",
|
|
1250
|
+
"xmind",
|
|
1251
|
+
"xpi",
|
|
1252
|
+
"xpm",
|
|
1253
|
+
"xwd",
|
|
1254
|
+
"xz",
|
|
1255
|
+
"z",
|
|
1256
|
+
"zip",
|
|
1257
|
+
"zipx"
|
|
1258
|
+
]);
|
|
1259
|
+
var isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());
|
|
1260
|
+
var foreach = (val, fn) => {
|
|
1261
|
+
if (val instanceof Set) {
|
|
1262
|
+
val.forEach(fn);
|
|
1263
|
+
} else {
|
|
1264
|
+
fn(val);
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
var addAndConvert = (main, prop, item) => {
|
|
1268
|
+
let container = main[prop];
|
|
1269
|
+
if (!(container instanceof Set)) {
|
|
1270
|
+
main[prop] = container = /* @__PURE__ */ new Set([container]);
|
|
1271
|
+
}
|
|
1272
|
+
container.add(item);
|
|
1273
|
+
};
|
|
1274
|
+
var clearItem = (cont) => (key) => {
|
|
1275
|
+
const set = cont[key];
|
|
1276
|
+
if (set instanceof Set) {
|
|
1277
|
+
set.clear();
|
|
1278
|
+
} else {
|
|
1279
|
+
delete cont[key];
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
var delFromSet = (main, prop, item) => {
|
|
1283
|
+
const container = main[prop];
|
|
1284
|
+
if (container instanceof Set) {
|
|
1285
|
+
container.delete(item);
|
|
1286
|
+
} else if (container === item) {
|
|
1287
|
+
delete main[prop];
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
|
|
1291
|
+
var FsWatchInstances = /* @__PURE__ */ new Map();
|
|
1292
|
+
function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
|
|
1293
|
+
const handleEvent = (rawEvent, evPath) => {
|
|
1294
|
+
listener(path);
|
|
1295
|
+
emitRaw(rawEvent, evPath, { watchedPath: path });
|
|
1296
|
+
if (evPath && path !== evPath) {
|
|
1297
|
+
fsWatchBroadcast(sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath));
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
try {
|
|
1301
|
+
return fs_watch(path, {
|
|
1302
|
+
persistent: options.persistent
|
|
1303
|
+
}, handleEvent);
|
|
1304
|
+
} catch (error) {
|
|
1305
|
+
errHandler(error);
|
|
1306
|
+
return void 0;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
|
|
1310
|
+
const cont = FsWatchInstances.get(fullPath);
|
|
1311
|
+
if (!cont)
|
|
1312
|
+
return;
|
|
1313
|
+
foreach(cont[listenerType], (listener) => {
|
|
1314
|
+
listener(val1, val2, val3);
|
|
1315
|
+
});
|
|
1316
|
+
};
|
|
1317
|
+
var setFsWatchListener = (path, fullPath, options, handlers) => {
|
|
1318
|
+
const { listener, errHandler, rawEmitter } = handlers;
|
|
1319
|
+
let cont = FsWatchInstances.get(fullPath);
|
|
1320
|
+
let watcher;
|
|
1321
|
+
if (!options.persistent) {
|
|
1322
|
+
watcher = createFsWatchInstance(path, options, listener, errHandler, rawEmitter);
|
|
1323
|
+
if (!watcher)
|
|
1324
|
+
return;
|
|
1325
|
+
return watcher.close.bind(watcher);
|
|
1326
|
+
}
|
|
1327
|
+
if (cont) {
|
|
1328
|
+
addAndConvert(cont, KEY_LISTENERS, listener);
|
|
1329
|
+
addAndConvert(cont, KEY_ERR, errHandler);
|
|
1330
|
+
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
1331
|
+
} else {
|
|
1332
|
+
watcher = createFsWatchInstance(
|
|
1333
|
+
path,
|
|
1334
|
+
options,
|
|
1335
|
+
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
1336
|
+
errHandler,
|
|
1337
|
+
// no need to use broadcast here
|
|
1338
|
+
fsWatchBroadcast.bind(null, fullPath, KEY_RAW)
|
|
1339
|
+
);
|
|
1340
|
+
if (!watcher)
|
|
1341
|
+
return;
|
|
1342
|
+
watcher.on(EV.ERROR, async (error) => {
|
|
1343
|
+
const broadcastErr = fsWatchBroadcast.bind(null, fullPath, KEY_ERR);
|
|
1344
|
+
if (cont)
|
|
1345
|
+
cont.watcherUnusable = true;
|
|
1346
|
+
if (isWindows && error.code === "EPERM") {
|
|
1347
|
+
try {
|
|
1348
|
+
const fd = await open(path, "r");
|
|
1349
|
+
await fd.close();
|
|
1350
|
+
broadcastErr(error);
|
|
1351
|
+
} catch (err) {
|
|
1352
|
+
}
|
|
1353
|
+
} else {
|
|
1354
|
+
broadcastErr(error);
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
cont = {
|
|
1358
|
+
listeners: listener,
|
|
1359
|
+
errHandlers: errHandler,
|
|
1360
|
+
rawEmitters: rawEmitter,
|
|
1361
|
+
watcher
|
|
1362
|
+
};
|
|
1363
|
+
FsWatchInstances.set(fullPath, cont);
|
|
1364
|
+
}
|
|
1365
|
+
return () => {
|
|
1366
|
+
delFromSet(cont, KEY_LISTENERS, listener);
|
|
1367
|
+
delFromSet(cont, KEY_ERR, errHandler);
|
|
1368
|
+
delFromSet(cont, KEY_RAW, rawEmitter);
|
|
1369
|
+
if (isEmptySet(cont.listeners)) {
|
|
1370
|
+
cont.watcher.close();
|
|
1371
|
+
FsWatchInstances.delete(fullPath);
|
|
1372
|
+
HANDLER_KEYS.forEach(clearItem(cont));
|
|
1373
|
+
cont.watcher = void 0;
|
|
1374
|
+
Object.freeze(cont);
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
};
|
|
1378
|
+
var FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
1379
|
+
var setFsWatchFileListener = (path, fullPath, options, handlers) => {
|
|
1380
|
+
const { listener, rawEmitter } = handlers;
|
|
1381
|
+
let cont = FsWatchFileInstances.get(fullPath);
|
|
1382
|
+
const copts = cont && cont.options;
|
|
1383
|
+
if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
|
|
1384
|
+
unwatchFile(fullPath);
|
|
1385
|
+
cont = void 0;
|
|
1386
|
+
}
|
|
1387
|
+
if (cont) {
|
|
1388
|
+
addAndConvert(cont, KEY_LISTENERS, listener);
|
|
1389
|
+
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
1390
|
+
} else {
|
|
1391
|
+
cont = {
|
|
1392
|
+
listeners: listener,
|
|
1393
|
+
rawEmitters: rawEmitter,
|
|
1394
|
+
options,
|
|
1395
|
+
watcher: watchFile(fullPath, options, (curr, prev) => {
|
|
1396
|
+
foreach(cont.rawEmitters, (rawEmitter2) => {
|
|
1397
|
+
rawEmitter2(EV.CHANGE, fullPath, { curr, prev });
|
|
1398
|
+
});
|
|
1399
|
+
const currmtime = curr.mtimeMs;
|
|
1400
|
+
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
1401
|
+
foreach(cont.listeners, (listener2) => listener2(path, curr));
|
|
1402
|
+
}
|
|
1403
|
+
})
|
|
1404
|
+
};
|
|
1405
|
+
FsWatchFileInstances.set(fullPath, cont);
|
|
1406
|
+
}
|
|
1407
|
+
return () => {
|
|
1408
|
+
delFromSet(cont, KEY_LISTENERS, listener);
|
|
1409
|
+
delFromSet(cont, KEY_RAW, rawEmitter);
|
|
1410
|
+
if (isEmptySet(cont.listeners)) {
|
|
1411
|
+
FsWatchFileInstances.delete(fullPath);
|
|
1412
|
+
unwatchFile(fullPath);
|
|
1413
|
+
cont.options = cont.watcher = void 0;
|
|
1414
|
+
Object.freeze(cont);
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1417
|
+
};
|
|
1418
|
+
var NodeFsHandler = class {
|
|
1419
|
+
constructor(fsW) {
|
|
1420
|
+
this.fsw = fsW;
|
|
1421
|
+
this._boundHandleError = (error) => fsW._handleError(error);
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* Watch file for changes with fs_watchFile or fs_watch.
|
|
1425
|
+
* @param path to file or dir
|
|
1426
|
+
* @param listener on fs change
|
|
1427
|
+
* @returns closer for the watcher instance
|
|
1428
|
+
*/
|
|
1429
|
+
_watchWithNodeFs(path, listener) {
|
|
1430
|
+
const opts = this.fsw.options;
|
|
1431
|
+
const directory = sysPath.dirname(path);
|
|
1432
|
+
const basename3 = sysPath.basename(path);
|
|
1433
|
+
const parent = this.fsw._getWatchedDir(directory);
|
|
1434
|
+
parent.add(basename3);
|
|
1435
|
+
const absolutePath = sysPath.resolve(path);
|
|
1436
|
+
const options = {
|
|
1437
|
+
persistent: opts.persistent
|
|
1438
|
+
};
|
|
1439
|
+
if (!listener)
|
|
1440
|
+
listener = EMPTY_FN;
|
|
1441
|
+
let closer;
|
|
1442
|
+
if (opts.usePolling) {
|
|
1443
|
+
const enableBin = opts.interval !== opts.binaryInterval;
|
|
1444
|
+
options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
|
|
1445
|
+
closer = setFsWatchFileListener(path, absolutePath, options, {
|
|
1446
|
+
listener,
|
|
1447
|
+
rawEmitter: this.fsw._emitRaw
|
|
1448
|
+
});
|
|
1449
|
+
} else {
|
|
1450
|
+
closer = setFsWatchListener(path, absolutePath, options, {
|
|
1451
|
+
listener,
|
|
1452
|
+
errHandler: this._boundHandleError,
|
|
1453
|
+
rawEmitter: this.fsw._emitRaw
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
return closer;
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Watch a file and emit add event if warranted.
|
|
1460
|
+
* @returns closer for the watcher instance
|
|
1461
|
+
*/
|
|
1462
|
+
_handleFile(file, stats, initialAdd) {
|
|
1463
|
+
if (this.fsw.closed) {
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
const dirname4 = sysPath.dirname(file);
|
|
1467
|
+
const basename3 = sysPath.basename(file);
|
|
1468
|
+
const parent = this.fsw._getWatchedDir(dirname4);
|
|
1469
|
+
let prevStats = stats;
|
|
1470
|
+
if (parent.has(basename3))
|
|
1471
|
+
return;
|
|
1472
|
+
const listener = async (path, newStats) => {
|
|
1473
|
+
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
1474
|
+
return;
|
|
1475
|
+
if (!newStats || newStats.mtimeMs === 0) {
|
|
1476
|
+
try {
|
|
1477
|
+
const newStats2 = await stat4(file);
|
|
1478
|
+
if (this.fsw.closed)
|
|
1479
|
+
return;
|
|
1480
|
+
const at = newStats2.atimeMs;
|
|
1481
|
+
const mt = newStats2.mtimeMs;
|
|
1482
|
+
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
1483
|
+
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
1484
|
+
}
|
|
1485
|
+
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
1486
|
+
this.fsw._closeFile(path);
|
|
1487
|
+
prevStats = newStats2;
|
|
1488
|
+
const closer2 = this._watchWithNodeFs(file, listener);
|
|
1489
|
+
if (closer2)
|
|
1490
|
+
this.fsw._addPathCloser(path, closer2);
|
|
1491
|
+
} else {
|
|
1492
|
+
prevStats = newStats2;
|
|
1493
|
+
}
|
|
1494
|
+
} catch (error) {
|
|
1495
|
+
this.fsw._remove(dirname4, basename3);
|
|
1496
|
+
}
|
|
1497
|
+
} else if (parent.has(basename3)) {
|
|
1498
|
+
const at = newStats.atimeMs;
|
|
1499
|
+
const mt = newStats.mtimeMs;
|
|
1500
|
+
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
1501
|
+
this.fsw._emit(EV.CHANGE, file, newStats);
|
|
1502
|
+
}
|
|
1503
|
+
prevStats = newStats;
|
|
1504
|
+
}
|
|
1505
|
+
};
|
|
1506
|
+
const closer = this._watchWithNodeFs(file, listener);
|
|
1507
|
+
if (!(initialAdd && this.fsw.options.ignoreInitial) && this.fsw._isntIgnored(file)) {
|
|
1508
|
+
if (!this.fsw._throttle(EV.ADD, file, 0))
|
|
1509
|
+
return;
|
|
1510
|
+
this.fsw._emit(EV.ADD, file, stats);
|
|
1511
|
+
}
|
|
1512
|
+
return closer;
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Handle symlinks encountered while reading a dir.
|
|
1516
|
+
* @param entry returned by readdirp
|
|
1517
|
+
* @param directory path of dir being read
|
|
1518
|
+
* @param path of this item
|
|
1519
|
+
* @param item basename of this item
|
|
1520
|
+
* @returns true if no more processing is needed for this entry.
|
|
1521
|
+
*/
|
|
1522
|
+
async _handleSymlink(entry, directory, path, item) {
|
|
1523
|
+
if (this.fsw.closed) {
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
const full = entry.fullPath;
|
|
1527
|
+
const dir = this.fsw._getWatchedDir(directory);
|
|
1528
|
+
if (!this.fsw.options.followSymlinks) {
|
|
1529
|
+
this.fsw._incrReadyCount();
|
|
1530
|
+
let linkPath;
|
|
1531
|
+
try {
|
|
1532
|
+
linkPath = await fsrealpath(path);
|
|
1533
|
+
} catch (e) {
|
|
1534
|
+
this.fsw._emitReady();
|
|
1535
|
+
return true;
|
|
1536
|
+
}
|
|
1537
|
+
if (this.fsw.closed)
|
|
1538
|
+
return;
|
|
1539
|
+
if (dir.has(item)) {
|
|
1540
|
+
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
1541
|
+
this.fsw._symlinkPaths.set(full, linkPath);
|
|
1542
|
+
this.fsw._emit(EV.CHANGE, path, entry.stats);
|
|
1543
|
+
}
|
|
1544
|
+
} else {
|
|
1545
|
+
dir.add(item);
|
|
1546
|
+
this.fsw._symlinkPaths.set(full, linkPath);
|
|
1547
|
+
this.fsw._emit(EV.ADD, path, entry.stats);
|
|
1548
|
+
}
|
|
1549
|
+
this.fsw._emitReady();
|
|
1550
|
+
return true;
|
|
1551
|
+
}
|
|
1552
|
+
if (this.fsw._symlinkPaths.has(full)) {
|
|
1553
|
+
return true;
|
|
1554
|
+
}
|
|
1555
|
+
this.fsw._symlinkPaths.set(full, true);
|
|
1556
|
+
}
|
|
1557
|
+
_handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
|
|
1558
|
+
directory = sysPath.join(directory, "");
|
|
1559
|
+
throttler = this.fsw._throttle("readdir", directory, 1e3);
|
|
1560
|
+
if (!throttler)
|
|
1561
|
+
return;
|
|
1562
|
+
const previous = this.fsw._getWatchedDir(wh.path);
|
|
1563
|
+
const current = /* @__PURE__ */ new Set();
|
|
1564
|
+
let stream = this.fsw._readdirp(directory, {
|
|
1565
|
+
fileFilter: (entry) => wh.filterPath(entry),
|
|
1566
|
+
directoryFilter: (entry) => wh.filterDir(entry)
|
|
1567
|
+
});
|
|
1568
|
+
if (!stream)
|
|
1569
|
+
return;
|
|
1570
|
+
stream.on(STR_DATA, async (entry) => {
|
|
1571
|
+
if (this.fsw.closed) {
|
|
1572
|
+
stream = void 0;
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
const item = entry.path;
|
|
1576
|
+
let path = sysPath.join(directory, item);
|
|
1577
|
+
current.add(item);
|
|
1578
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item)) {
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
if (this.fsw.closed) {
|
|
1582
|
+
stream = void 0;
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
if (item === target || !target && !previous.has(item)) {
|
|
1586
|
+
this.fsw._incrReadyCount();
|
|
1587
|
+
path = sysPath.join(dir, sysPath.relative(dir, path));
|
|
1588
|
+
this._addToNodeFs(path, initialAdd, wh, depth + 1);
|
|
1589
|
+
}
|
|
1590
|
+
}).on(EV.ERROR, this._boundHandleError);
|
|
1591
|
+
return new Promise((resolve8, reject) => {
|
|
1592
|
+
if (!stream)
|
|
1593
|
+
return reject();
|
|
1594
|
+
stream.once(STR_END, () => {
|
|
1595
|
+
if (this.fsw.closed) {
|
|
1596
|
+
stream = void 0;
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
const wasThrottled = throttler ? throttler.clear() : false;
|
|
1600
|
+
resolve8(void 0);
|
|
1601
|
+
previous.getChildren().filter((item) => {
|
|
1602
|
+
return item !== directory && !current.has(item);
|
|
1603
|
+
}).forEach((item) => {
|
|
1604
|
+
this.fsw._remove(directory, item);
|
|
1605
|
+
});
|
|
1606
|
+
stream = void 0;
|
|
1607
|
+
if (wasThrottled)
|
|
1608
|
+
this._handleRead(directory, false, wh, target, dir, depth, throttler);
|
|
1609
|
+
});
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
/**
|
|
1613
|
+
* Read directory to add / remove files from `@watched` list and re-read it on change.
|
|
1614
|
+
* @param dir fs path
|
|
1615
|
+
* @param stats
|
|
1616
|
+
* @param initialAdd
|
|
1617
|
+
* @param depth relative to user-supplied path
|
|
1618
|
+
* @param target child path targeted for watch
|
|
1619
|
+
* @param wh Common watch helpers for this path
|
|
1620
|
+
* @param realpath
|
|
1621
|
+
* @returns closer for the watcher instance.
|
|
1622
|
+
*/
|
|
1623
|
+
async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath2) {
|
|
1624
|
+
const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
|
|
1625
|
+
const tracked = parentDir.has(sysPath.basename(dir));
|
|
1626
|
+
if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
|
|
1627
|
+
this.fsw._emit(EV.ADD_DIR, dir, stats);
|
|
1628
|
+
}
|
|
1629
|
+
parentDir.add(sysPath.basename(dir));
|
|
1630
|
+
this.fsw._getWatchedDir(dir);
|
|
1631
|
+
let throttler;
|
|
1632
|
+
let closer;
|
|
1633
|
+
const oDepth = this.fsw.options.depth;
|
|
1634
|
+
if ((oDepth == null || depth <= oDepth) && !this.fsw._symlinkPaths.has(realpath2)) {
|
|
1635
|
+
if (!target) {
|
|
1636
|
+
await this._handleRead(dir, initialAdd, wh, target, dir, depth, throttler);
|
|
1637
|
+
if (this.fsw.closed)
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
closer = this._watchWithNodeFs(dir, (dirPath, stats2) => {
|
|
1641
|
+
if (stats2 && stats2.mtimeMs === 0)
|
|
1642
|
+
return;
|
|
1643
|
+
this._handleRead(dirPath, false, wh, target, dir, depth, throttler);
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
return closer;
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Handle added file, directory, or glob pattern.
|
|
1650
|
+
* Delegates call to _handleFile / _handleDir after checks.
|
|
1651
|
+
* @param path to file or ir
|
|
1652
|
+
* @param initialAdd was the file added at watch instantiation?
|
|
1653
|
+
* @param priorWh depth relative to user-supplied path
|
|
1654
|
+
* @param depth Child path actually targeted for watch
|
|
1655
|
+
* @param target Child path actually targeted for watch
|
|
1656
|
+
*/
|
|
1657
|
+
async _addToNodeFs(path, initialAdd, priorWh, depth, target) {
|
|
1658
|
+
const ready = this.fsw._emitReady;
|
|
1659
|
+
if (this.fsw._isIgnored(path) || this.fsw.closed) {
|
|
1660
|
+
ready();
|
|
1661
|
+
return false;
|
|
1662
|
+
}
|
|
1663
|
+
const wh = this.fsw._getWatchHelpers(path);
|
|
1664
|
+
if (priorWh) {
|
|
1665
|
+
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
1666
|
+
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
1667
|
+
}
|
|
1668
|
+
try {
|
|
1669
|
+
const stats = await statMethods[wh.statMethod](wh.watchPath);
|
|
1670
|
+
if (this.fsw.closed)
|
|
1671
|
+
return;
|
|
1672
|
+
if (this.fsw._isIgnored(wh.watchPath, stats)) {
|
|
1673
|
+
ready();
|
|
1674
|
+
return false;
|
|
1675
|
+
}
|
|
1676
|
+
const follow = this.fsw.options.followSymlinks;
|
|
1677
|
+
let closer;
|
|
1678
|
+
if (stats.isDirectory()) {
|
|
1679
|
+
const absPath = sysPath.resolve(path);
|
|
1680
|
+
const targetPath = follow ? await fsrealpath(path) : path;
|
|
1681
|
+
if (this.fsw.closed)
|
|
1682
|
+
return;
|
|
1683
|
+
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
1684
|
+
if (this.fsw.closed)
|
|
1685
|
+
return;
|
|
1686
|
+
if (absPath !== targetPath && targetPath !== void 0) {
|
|
1687
|
+
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
1688
|
+
}
|
|
1689
|
+
} else if (stats.isSymbolicLink()) {
|
|
1690
|
+
const targetPath = follow ? await fsrealpath(path) : path;
|
|
1691
|
+
if (this.fsw.closed)
|
|
1692
|
+
return;
|
|
1693
|
+
const parent = sysPath.dirname(wh.watchPath);
|
|
1694
|
+
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
1695
|
+
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
1696
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
|
|
1697
|
+
if (this.fsw.closed)
|
|
1698
|
+
return;
|
|
1699
|
+
if (targetPath !== void 0) {
|
|
1700
|
+
this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);
|
|
1701
|
+
}
|
|
1702
|
+
} else {
|
|
1703
|
+
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
1704
|
+
}
|
|
1705
|
+
ready();
|
|
1706
|
+
if (closer)
|
|
1707
|
+
this.fsw._addPathCloser(path, closer);
|
|
1708
|
+
return false;
|
|
1709
|
+
} catch (error) {
|
|
1710
|
+
if (this.fsw._handleError(error)) {
|
|
1711
|
+
ready();
|
|
1712
|
+
return path;
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
};
|
|
1717
|
+
|
|
1718
|
+
// node_modules/chokidar/esm/index.js
|
|
1719
|
+
var SLASH = "/";
|
|
1720
|
+
var SLASH_SLASH = "//";
|
|
1721
|
+
var ONE_DOT = ".";
|
|
1722
|
+
var TWO_DOTS = "..";
|
|
1723
|
+
var STRING_TYPE = "string";
|
|
1724
|
+
var BACK_SLASH_RE = /\\/g;
|
|
1725
|
+
var DOUBLE_SLASH_RE = /\/\//;
|
|
1726
|
+
var DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
|
|
1727
|
+
var REPLACER_RE = /^\.[/\\]/;
|
|
1728
|
+
function arrify(item) {
|
|
1729
|
+
return Array.isArray(item) ? item : [item];
|
|
1730
|
+
}
|
|
1731
|
+
var isMatcherObject = (matcher) => typeof matcher === "object" && matcher !== null && !(matcher instanceof RegExp);
|
|
1732
|
+
function createPattern(matcher) {
|
|
1733
|
+
if (typeof matcher === "function")
|
|
1734
|
+
return matcher;
|
|
1735
|
+
if (typeof matcher === "string")
|
|
1736
|
+
return (string) => matcher === string;
|
|
1737
|
+
if (matcher instanceof RegExp)
|
|
1738
|
+
return (string) => matcher.test(string);
|
|
1739
|
+
if (typeof matcher === "object" && matcher !== null) {
|
|
1740
|
+
return (string) => {
|
|
1741
|
+
if (matcher.path === string)
|
|
1742
|
+
return true;
|
|
1743
|
+
if (matcher.recursive) {
|
|
1744
|
+
const relative5 = sysPath2.relative(matcher.path, string);
|
|
1745
|
+
if (!relative5) {
|
|
1746
|
+
return false;
|
|
1747
|
+
}
|
|
1748
|
+
return !relative5.startsWith("..") && !sysPath2.isAbsolute(relative5);
|
|
1749
|
+
}
|
|
1750
|
+
return false;
|
|
1751
|
+
};
|
|
1752
|
+
}
|
|
1753
|
+
return () => false;
|
|
1754
|
+
}
|
|
1755
|
+
function normalizePath(path) {
|
|
1756
|
+
if (typeof path !== "string")
|
|
1757
|
+
throw new Error("string expected");
|
|
1758
|
+
path = sysPath2.normalize(path);
|
|
1759
|
+
path = path.replace(/\\/g, "/");
|
|
1760
|
+
let prepend = false;
|
|
1761
|
+
if (path.startsWith("//"))
|
|
1762
|
+
prepend = true;
|
|
1763
|
+
const DOUBLE_SLASH_RE2 = /\/\//;
|
|
1764
|
+
while (path.match(DOUBLE_SLASH_RE2))
|
|
1765
|
+
path = path.replace(DOUBLE_SLASH_RE2, "/");
|
|
1766
|
+
if (prepend)
|
|
1767
|
+
path = "/" + path;
|
|
1768
|
+
return path;
|
|
1769
|
+
}
|
|
1770
|
+
function matchPatterns(patterns, testString, stats) {
|
|
1771
|
+
const path = normalizePath(testString);
|
|
1772
|
+
for (let index = 0; index < patterns.length; index++) {
|
|
1773
|
+
const pattern = patterns[index];
|
|
1774
|
+
if (pattern(path, stats)) {
|
|
1775
|
+
return true;
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
return false;
|
|
1779
|
+
}
|
|
1780
|
+
function anymatch(matchers, testString) {
|
|
1781
|
+
if (matchers == null) {
|
|
1782
|
+
throw new TypeError("anymatch: specify first argument");
|
|
1783
|
+
}
|
|
1784
|
+
const matchersArray = arrify(matchers);
|
|
1785
|
+
const patterns = matchersArray.map((matcher) => createPattern(matcher));
|
|
1786
|
+
if (testString == null) {
|
|
1787
|
+
return (testString2, stats) => {
|
|
1788
|
+
return matchPatterns(patterns, testString2, stats);
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
return matchPatterns(patterns, testString);
|
|
1792
|
+
}
|
|
1793
|
+
var unifyPaths = (paths_) => {
|
|
1794
|
+
const paths = arrify(paths_).flat();
|
|
1795
|
+
if (!paths.every((p) => typeof p === STRING_TYPE)) {
|
|
1796
|
+
throw new TypeError(`Non-string provided as watch path: ${paths}`);
|
|
1797
|
+
}
|
|
1798
|
+
return paths.map(normalizePathToUnix);
|
|
1799
|
+
};
|
|
1800
|
+
var toUnix = (string) => {
|
|
1801
|
+
let str = string.replace(BACK_SLASH_RE, SLASH);
|
|
1802
|
+
let prepend = false;
|
|
1803
|
+
if (str.startsWith(SLASH_SLASH)) {
|
|
1804
|
+
prepend = true;
|
|
1805
|
+
}
|
|
1806
|
+
while (str.match(DOUBLE_SLASH_RE)) {
|
|
1807
|
+
str = str.replace(DOUBLE_SLASH_RE, SLASH);
|
|
1808
|
+
}
|
|
1809
|
+
if (prepend) {
|
|
1810
|
+
str = SLASH + str;
|
|
1811
|
+
}
|
|
1812
|
+
return str;
|
|
1813
|
+
};
|
|
1814
|
+
var normalizePathToUnix = (path) => toUnix(sysPath2.normalize(toUnix(path)));
|
|
1815
|
+
var normalizeIgnored = (cwd = "") => (path) => {
|
|
1816
|
+
if (typeof path === "string") {
|
|
1817
|
+
return normalizePathToUnix(sysPath2.isAbsolute(path) ? path : sysPath2.join(cwd, path));
|
|
1818
|
+
} else {
|
|
1819
|
+
return path;
|
|
1820
|
+
}
|
|
1821
|
+
};
|
|
1822
|
+
var getAbsolutePath = (path, cwd) => {
|
|
1823
|
+
if (sysPath2.isAbsolute(path)) {
|
|
1824
|
+
return path;
|
|
1825
|
+
}
|
|
1826
|
+
return sysPath2.join(cwd, path);
|
|
1827
|
+
};
|
|
1828
|
+
var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
1829
|
+
var DirEntry = class {
|
|
1830
|
+
constructor(dir, removeWatcher) {
|
|
1831
|
+
this.path = dir;
|
|
1832
|
+
this._removeWatcher = removeWatcher;
|
|
1833
|
+
this.items = /* @__PURE__ */ new Set();
|
|
1834
|
+
}
|
|
1835
|
+
add(item) {
|
|
1836
|
+
const { items } = this;
|
|
1837
|
+
if (!items)
|
|
1838
|
+
return;
|
|
1839
|
+
if (item !== ONE_DOT && item !== TWO_DOTS)
|
|
1840
|
+
items.add(item);
|
|
1841
|
+
}
|
|
1842
|
+
async remove(item) {
|
|
1843
|
+
const { items } = this;
|
|
1844
|
+
if (!items)
|
|
1845
|
+
return;
|
|
1846
|
+
items.delete(item);
|
|
1847
|
+
if (items.size > 0)
|
|
1848
|
+
return;
|
|
1849
|
+
const dir = this.path;
|
|
1850
|
+
try {
|
|
1851
|
+
await readdir3(dir);
|
|
1852
|
+
} catch (err) {
|
|
1853
|
+
if (this._removeWatcher) {
|
|
1854
|
+
this._removeWatcher(sysPath2.dirname(dir), sysPath2.basename(dir));
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
has(item) {
|
|
1859
|
+
const { items } = this;
|
|
1860
|
+
if (!items)
|
|
1861
|
+
return;
|
|
1862
|
+
return items.has(item);
|
|
1863
|
+
}
|
|
1864
|
+
getChildren() {
|
|
1865
|
+
const { items } = this;
|
|
1866
|
+
if (!items)
|
|
1867
|
+
return [];
|
|
1868
|
+
return [...items.values()];
|
|
1869
|
+
}
|
|
1870
|
+
dispose() {
|
|
1871
|
+
this.items.clear();
|
|
1872
|
+
this.path = "";
|
|
1873
|
+
this._removeWatcher = EMPTY_FN;
|
|
1874
|
+
this.items = EMPTY_SET;
|
|
1875
|
+
Object.freeze(this);
|
|
1876
|
+
}
|
|
1877
|
+
};
|
|
1878
|
+
var STAT_METHOD_F = "stat";
|
|
1879
|
+
var STAT_METHOD_L = "lstat";
|
|
1880
|
+
var WatchHelper = class {
|
|
1881
|
+
constructor(path, follow, fsw) {
|
|
1882
|
+
this.fsw = fsw;
|
|
1883
|
+
const watchPath = path;
|
|
1884
|
+
this.path = path = path.replace(REPLACER_RE, "");
|
|
1885
|
+
this.watchPath = watchPath;
|
|
1886
|
+
this.fullWatchPath = sysPath2.resolve(watchPath);
|
|
1887
|
+
this.dirParts = [];
|
|
1888
|
+
this.dirParts.forEach((parts) => {
|
|
1889
|
+
if (parts.length > 1)
|
|
1890
|
+
parts.pop();
|
|
1891
|
+
});
|
|
1892
|
+
this.followSymlinks = follow;
|
|
1893
|
+
this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
|
|
1894
|
+
}
|
|
1895
|
+
entryPath(entry) {
|
|
1896
|
+
return sysPath2.join(this.watchPath, sysPath2.relative(this.watchPath, entry.fullPath));
|
|
1897
|
+
}
|
|
1898
|
+
filterPath(entry) {
|
|
1899
|
+
const { stats } = entry;
|
|
1900
|
+
if (stats && stats.isSymbolicLink())
|
|
1901
|
+
return this.filterDir(entry);
|
|
1902
|
+
const resolvedPath = this.entryPath(entry);
|
|
1903
|
+
return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);
|
|
1904
|
+
}
|
|
1905
|
+
filterDir(entry) {
|
|
1906
|
+
return this.fsw._isntIgnored(this.entryPath(entry), entry.stats);
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
var FSWatcher = class extends EventEmitter {
|
|
1910
|
+
// Not indenting methods for history sake; for now.
|
|
1911
|
+
constructor(_opts = {}) {
|
|
1912
|
+
super();
|
|
1913
|
+
this.closed = false;
|
|
1914
|
+
this._closers = /* @__PURE__ */ new Map();
|
|
1915
|
+
this._ignoredPaths = /* @__PURE__ */ new Set();
|
|
1916
|
+
this._throttled = /* @__PURE__ */ new Map();
|
|
1917
|
+
this._streams = /* @__PURE__ */ new Set();
|
|
1918
|
+
this._symlinkPaths = /* @__PURE__ */ new Map();
|
|
1919
|
+
this._watched = /* @__PURE__ */ new Map();
|
|
1920
|
+
this._pendingWrites = /* @__PURE__ */ new Map();
|
|
1921
|
+
this._pendingUnlinks = /* @__PURE__ */ new Map();
|
|
1922
|
+
this._readyCount = 0;
|
|
1923
|
+
this._readyEmitted = false;
|
|
1924
|
+
const awf = _opts.awaitWriteFinish;
|
|
1925
|
+
const DEF_AWF = { stabilityThreshold: 2e3, pollInterval: 100 };
|
|
1926
|
+
const opts = {
|
|
1927
|
+
// Defaults
|
|
1928
|
+
persistent: true,
|
|
1929
|
+
ignoreInitial: false,
|
|
1930
|
+
ignorePermissionErrors: false,
|
|
1931
|
+
interval: 100,
|
|
1932
|
+
binaryInterval: 300,
|
|
1933
|
+
followSymlinks: true,
|
|
1934
|
+
usePolling: false,
|
|
1935
|
+
// useAsync: false,
|
|
1936
|
+
atomic: true,
|
|
1937
|
+
// NOTE: overwritten later (depends on usePolling)
|
|
1938
|
+
..._opts,
|
|
1939
|
+
// Change format
|
|
1940
|
+
ignored: _opts.ignored ? arrify(_opts.ignored) : arrify([]),
|
|
1941
|
+
awaitWriteFinish: awf === true ? DEF_AWF : typeof awf === "object" ? { ...DEF_AWF, ...awf } : false
|
|
1942
|
+
};
|
|
1943
|
+
if (isIBMi)
|
|
1944
|
+
opts.usePolling = true;
|
|
1945
|
+
if (opts.atomic === void 0)
|
|
1946
|
+
opts.atomic = !opts.usePolling;
|
|
1947
|
+
const envPoll = process.env.CHOKIDAR_USEPOLLING;
|
|
1948
|
+
if (envPoll !== void 0) {
|
|
1949
|
+
const envLower = envPoll.toLowerCase();
|
|
1950
|
+
if (envLower === "false" || envLower === "0")
|
|
1951
|
+
opts.usePolling = false;
|
|
1952
|
+
else if (envLower === "true" || envLower === "1")
|
|
1953
|
+
opts.usePolling = true;
|
|
1954
|
+
else
|
|
1955
|
+
opts.usePolling = !!envLower;
|
|
1956
|
+
}
|
|
1957
|
+
const envInterval = process.env.CHOKIDAR_INTERVAL;
|
|
1958
|
+
if (envInterval)
|
|
1959
|
+
opts.interval = Number.parseInt(envInterval, 10);
|
|
1960
|
+
let readyCalls = 0;
|
|
1961
|
+
this._emitReady = () => {
|
|
1962
|
+
readyCalls++;
|
|
1963
|
+
if (readyCalls >= this._readyCount) {
|
|
1964
|
+
this._emitReady = EMPTY_FN;
|
|
1965
|
+
this._readyEmitted = true;
|
|
1966
|
+
process.nextTick(() => this.emit(EVENTS.READY));
|
|
1967
|
+
}
|
|
1968
|
+
};
|
|
1969
|
+
this._emitRaw = (...args) => this.emit(EVENTS.RAW, ...args);
|
|
1970
|
+
this._boundRemove = this._remove.bind(this);
|
|
1971
|
+
this.options = opts;
|
|
1972
|
+
this._nodeFsHandler = new NodeFsHandler(this);
|
|
1973
|
+
Object.freeze(opts);
|
|
1974
|
+
}
|
|
1975
|
+
_addIgnoredPath(matcher) {
|
|
1976
|
+
if (isMatcherObject(matcher)) {
|
|
1977
|
+
for (const ignored of this._ignoredPaths) {
|
|
1978
|
+
if (isMatcherObject(ignored) && ignored.path === matcher.path && ignored.recursive === matcher.recursive) {
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
this._ignoredPaths.add(matcher);
|
|
1984
|
+
}
|
|
1985
|
+
_removeIgnoredPath(matcher) {
|
|
1986
|
+
this._ignoredPaths.delete(matcher);
|
|
1987
|
+
if (typeof matcher === "string") {
|
|
1988
|
+
for (const ignored of this._ignoredPaths) {
|
|
1989
|
+
if (isMatcherObject(ignored) && ignored.path === matcher) {
|
|
1990
|
+
this._ignoredPaths.delete(ignored);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
// Public methods
|
|
1996
|
+
/**
|
|
1997
|
+
* Adds paths to be watched on an existing FSWatcher instance.
|
|
1998
|
+
* @param paths_ file or file list. Other arguments are unused
|
|
1999
|
+
*/
|
|
2000
|
+
add(paths_, _origAdd, _internal) {
|
|
2001
|
+
const { cwd } = this.options;
|
|
2002
|
+
this.closed = false;
|
|
2003
|
+
this._closePromise = void 0;
|
|
2004
|
+
let paths = unifyPaths(paths_);
|
|
2005
|
+
if (cwd) {
|
|
2006
|
+
paths = paths.map((path) => {
|
|
2007
|
+
const absPath = getAbsolutePath(path, cwd);
|
|
2008
|
+
return absPath;
|
|
2009
|
+
});
|
|
2010
|
+
}
|
|
2011
|
+
paths.forEach((path) => {
|
|
2012
|
+
this._removeIgnoredPath(path);
|
|
2013
|
+
});
|
|
2014
|
+
this._userIgnored = void 0;
|
|
2015
|
+
if (!this._readyCount)
|
|
2016
|
+
this._readyCount = 0;
|
|
2017
|
+
this._readyCount += paths.length;
|
|
2018
|
+
Promise.all(paths.map(async (path) => {
|
|
2019
|
+
const res = await this._nodeFsHandler._addToNodeFs(path, !_internal, void 0, 0, _origAdd);
|
|
2020
|
+
if (res)
|
|
2021
|
+
this._emitReady();
|
|
2022
|
+
return res;
|
|
2023
|
+
})).then((results) => {
|
|
2024
|
+
if (this.closed)
|
|
2025
|
+
return;
|
|
2026
|
+
results.forEach((item) => {
|
|
2027
|
+
if (item)
|
|
2028
|
+
this.add(sysPath2.dirname(item), sysPath2.basename(_origAdd || item));
|
|
2029
|
+
});
|
|
2030
|
+
});
|
|
2031
|
+
return this;
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Close watchers or start ignoring events from specified paths.
|
|
2035
|
+
*/
|
|
2036
|
+
unwatch(paths_) {
|
|
2037
|
+
if (this.closed)
|
|
2038
|
+
return this;
|
|
2039
|
+
const paths = unifyPaths(paths_);
|
|
2040
|
+
const { cwd } = this.options;
|
|
2041
|
+
paths.forEach((path) => {
|
|
2042
|
+
if (!sysPath2.isAbsolute(path) && !this._closers.has(path)) {
|
|
2043
|
+
if (cwd)
|
|
2044
|
+
path = sysPath2.join(cwd, path);
|
|
2045
|
+
path = sysPath2.resolve(path);
|
|
2046
|
+
}
|
|
2047
|
+
this._closePath(path);
|
|
2048
|
+
this._addIgnoredPath(path);
|
|
2049
|
+
if (this._watched.has(path)) {
|
|
2050
|
+
this._addIgnoredPath({
|
|
2051
|
+
path,
|
|
2052
|
+
recursive: true
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
this._userIgnored = void 0;
|
|
2056
|
+
});
|
|
2057
|
+
return this;
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* Close watchers and remove all listeners from watched paths.
|
|
2061
|
+
*/
|
|
2062
|
+
close() {
|
|
2063
|
+
if (this._closePromise) {
|
|
2064
|
+
return this._closePromise;
|
|
2065
|
+
}
|
|
2066
|
+
this.closed = true;
|
|
2067
|
+
this.removeAllListeners();
|
|
2068
|
+
const closers = [];
|
|
2069
|
+
this._closers.forEach((closerList) => closerList.forEach((closer) => {
|
|
2070
|
+
const promise = closer();
|
|
2071
|
+
if (promise instanceof Promise)
|
|
2072
|
+
closers.push(promise);
|
|
2073
|
+
}));
|
|
2074
|
+
this._streams.forEach((stream) => stream.destroy());
|
|
2075
|
+
this._userIgnored = void 0;
|
|
2076
|
+
this._readyCount = 0;
|
|
2077
|
+
this._readyEmitted = false;
|
|
2078
|
+
this._watched.forEach((dirent) => dirent.dispose());
|
|
2079
|
+
this._closers.clear();
|
|
2080
|
+
this._watched.clear();
|
|
2081
|
+
this._streams.clear();
|
|
2082
|
+
this._symlinkPaths.clear();
|
|
2083
|
+
this._throttled.clear();
|
|
2084
|
+
this._closePromise = closers.length ? Promise.all(closers).then(() => void 0) : Promise.resolve();
|
|
2085
|
+
return this._closePromise;
|
|
2086
|
+
}
|
|
2087
|
+
/**
|
|
2088
|
+
* Expose list of watched paths
|
|
2089
|
+
* @returns for chaining
|
|
2090
|
+
*/
|
|
2091
|
+
getWatched() {
|
|
2092
|
+
const watchList = {};
|
|
2093
|
+
this._watched.forEach((entry, dir) => {
|
|
2094
|
+
const key = this.options.cwd ? sysPath2.relative(this.options.cwd, dir) : dir;
|
|
2095
|
+
const index = key || ONE_DOT;
|
|
2096
|
+
watchList[index] = entry.getChildren().sort();
|
|
2097
|
+
});
|
|
2098
|
+
return watchList;
|
|
2099
|
+
}
|
|
2100
|
+
emitWithAll(event, args) {
|
|
2101
|
+
this.emit(event, ...args);
|
|
2102
|
+
if (event !== EVENTS.ERROR)
|
|
2103
|
+
this.emit(EVENTS.ALL, event, ...args);
|
|
2104
|
+
}
|
|
2105
|
+
// Common helpers
|
|
2106
|
+
// --------------
|
|
2107
|
+
/**
|
|
2108
|
+
* Normalize and emit events.
|
|
2109
|
+
* Calling _emit DOES NOT MEAN emit() would be called!
|
|
2110
|
+
* @param event Type of event
|
|
2111
|
+
* @param path File or directory path
|
|
2112
|
+
* @param stats arguments to be passed with event
|
|
2113
|
+
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
2114
|
+
*/
|
|
2115
|
+
async _emit(event, path, stats) {
|
|
2116
|
+
if (this.closed)
|
|
2117
|
+
return;
|
|
2118
|
+
const opts = this.options;
|
|
2119
|
+
if (isWindows)
|
|
2120
|
+
path = sysPath2.normalize(path);
|
|
2121
|
+
if (opts.cwd)
|
|
2122
|
+
path = sysPath2.relative(opts.cwd, path);
|
|
2123
|
+
const args = [path];
|
|
2124
|
+
if (stats != null)
|
|
2125
|
+
args.push(stats);
|
|
2126
|
+
const awf = opts.awaitWriteFinish;
|
|
2127
|
+
let pw;
|
|
2128
|
+
if (awf && (pw = this._pendingWrites.get(path))) {
|
|
2129
|
+
pw.lastChange = /* @__PURE__ */ new Date();
|
|
2130
|
+
return this;
|
|
2131
|
+
}
|
|
2132
|
+
if (opts.atomic) {
|
|
2133
|
+
if (event === EVENTS.UNLINK) {
|
|
2134
|
+
this._pendingUnlinks.set(path, [event, ...args]);
|
|
2135
|
+
setTimeout(() => {
|
|
2136
|
+
this._pendingUnlinks.forEach((entry, path2) => {
|
|
2137
|
+
this.emit(...entry);
|
|
2138
|
+
this.emit(EVENTS.ALL, ...entry);
|
|
2139
|
+
this._pendingUnlinks.delete(path2);
|
|
2140
|
+
});
|
|
2141
|
+
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
2142
|
+
return this;
|
|
2143
|
+
}
|
|
2144
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path)) {
|
|
2145
|
+
event = EVENTS.CHANGE;
|
|
2146
|
+
this._pendingUnlinks.delete(path);
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
2150
|
+
const awfEmit = (err, stats2) => {
|
|
2151
|
+
if (err) {
|
|
2152
|
+
event = EVENTS.ERROR;
|
|
2153
|
+
args[0] = err;
|
|
2154
|
+
this.emitWithAll(event, args);
|
|
2155
|
+
} else if (stats2) {
|
|
2156
|
+
if (args.length > 1) {
|
|
2157
|
+
args[1] = stats2;
|
|
2158
|
+
} else {
|
|
2159
|
+
args.push(stats2);
|
|
2160
|
+
}
|
|
2161
|
+
this.emitWithAll(event, args);
|
|
2162
|
+
}
|
|
2163
|
+
};
|
|
2164
|
+
this._awaitWriteFinish(path, awf.stabilityThreshold, event, awfEmit);
|
|
2165
|
+
return this;
|
|
2166
|
+
}
|
|
2167
|
+
if (event === EVENTS.CHANGE) {
|
|
2168
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path, 50);
|
|
2169
|
+
if (isThrottled)
|
|
2170
|
+
return this;
|
|
2171
|
+
}
|
|
2172
|
+
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
2173
|
+
const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path) : path;
|
|
2174
|
+
let stats2;
|
|
2175
|
+
try {
|
|
2176
|
+
stats2 = await stat5(fullPath);
|
|
2177
|
+
} catch (err) {
|
|
2178
|
+
}
|
|
2179
|
+
if (!stats2 || this.closed)
|
|
2180
|
+
return;
|
|
2181
|
+
args.push(stats2);
|
|
2182
|
+
}
|
|
2183
|
+
this.emitWithAll(event, args);
|
|
2184
|
+
return this;
|
|
2185
|
+
}
|
|
2186
|
+
/**
|
|
2187
|
+
* Common handler for errors
|
|
2188
|
+
* @returns The error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
2189
|
+
*/
|
|
2190
|
+
_handleError(error) {
|
|
2191
|
+
const code = error && error.code;
|
|
2192
|
+
if (error && code !== "ENOENT" && code !== "ENOTDIR" && (!this.options.ignorePermissionErrors || code !== "EPERM" && code !== "EACCES")) {
|
|
2193
|
+
this.emit(EVENTS.ERROR, error);
|
|
2194
|
+
}
|
|
2195
|
+
return error || this.closed;
|
|
2196
|
+
}
|
|
2197
|
+
/**
|
|
2198
|
+
* Helper utility for throttling
|
|
2199
|
+
* @param actionType type being throttled
|
|
2200
|
+
* @param path being acted upon
|
|
2201
|
+
* @param timeout duration of time to suppress duplicate actions
|
|
2202
|
+
* @returns tracking object or false if action should be suppressed
|
|
2203
|
+
*/
|
|
2204
|
+
_throttle(actionType, path, timeout) {
|
|
2205
|
+
if (!this._throttled.has(actionType)) {
|
|
2206
|
+
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
2207
|
+
}
|
|
2208
|
+
const action = this._throttled.get(actionType);
|
|
2209
|
+
if (!action)
|
|
2210
|
+
throw new Error("invalid throttle");
|
|
2211
|
+
const actionPath = action.get(path);
|
|
2212
|
+
if (actionPath) {
|
|
2213
|
+
actionPath.count++;
|
|
2214
|
+
return false;
|
|
2215
|
+
}
|
|
2216
|
+
let timeoutObject;
|
|
2217
|
+
const clear = () => {
|
|
2218
|
+
const item = action.get(path);
|
|
2219
|
+
const count = item ? item.count : 0;
|
|
2220
|
+
action.delete(path);
|
|
2221
|
+
clearTimeout(timeoutObject);
|
|
2222
|
+
if (item)
|
|
2223
|
+
clearTimeout(item.timeoutObject);
|
|
2224
|
+
return count;
|
|
2225
|
+
};
|
|
2226
|
+
timeoutObject = setTimeout(clear, timeout);
|
|
2227
|
+
const thr = { timeoutObject, clear, count: 0 };
|
|
2228
|
+
action.set(path, thr);
|
|
2229
|
+
return thr;
|
|
2230
|
+
}
|
|
2231
|
+
_incrReadyCount() {
|
|
2232
|
+
return this._readyCount++;
|
|
2233
|
+
}
|
|
2234
|
+
/**
|
|
2235
|
+
* Awaits write operation to finish.
|
|
2236
|
+
* Polls a newly created file for size variations. When files size does not change for 'threshold' milliseconds calls callback.
|
|
2237
|
+
* @param path being acted upon
|
|
2238
|
+
* @param threshold Time in milliseconds a file size must be fixed before acknowledging write OP is finished
|
|
2239
|
+
* @param event
|
|
2240
|
+
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
2241
|
+
*/
|
|
2242
|
+
_awaitWriteFinish(path, threshold, event, awfEmit) {
|
|
2243
|
+
const awf = this.options.awaitWriteFinish;
|
|
2244
|
+
if (typeof awf !== "object")
|
|
2245
|
+
return;
|
|
2246
|
+
const pollInterval = awf.pollInterval;
|
|
2247
|
+
let timeoutHandler;
|
|
2248
|
+
let fullPath = path;
|
|
2249
|
+
if (this.options.cwd && !sysPath2.isAbsolute(path)) {
|
|
2250
|
+
fullPath = sysPath2.join(this.options.cwd, path);
|
|
2251
|
+
}
|
|
2252
|
+
const now = /* @__PURE__ */ new Date();
|
|
2253
|
+
const writes = this._pendingWrites;
|
|
2254
|
+
function awaitWriteFinishFn(prevStat) {
|
|
2255
|
+
statcb(fullPath, (err, curStat) => {
|
|
2256
|
+
if (err || !writes.has(path)) {
|
|
2257
|
+
if (err && err.code !== "ENOENT")
|
|
2258
|
+
awfEmit(err);
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
const now2 = Number(/* @__PURE__ */ new Date());
|
|
2262
|
+
if (prevStat && curStat.size !== prevStat.size) {
|
|
2263
|
+
writes.get(path).lastChange = now2;
|
|
2264
|
+
}
|
|
2265
|
+
const pw = writes.get(path);
|
|
2266
|
+
const df = now2 - pw.lastChange;
|
|
2267
|
+
if (df >= threshold) {
|
|
2268
|
+
writes.delete(path);
|
|
2269
|
+
awfEmit(void 0, curStat);
|
|
2270
|
+
} else {
|
|
2271
|
+
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
2272
|
+
}
|
|
2273
|
+
});
|
|
2274
|
+
}
|
|
2275
|
+
if (!writes.has(path)) {
|
|
2276
|
+
writes.set(path, {
|
|
2277
|
+
lastChange: now,
|
|
2278
|
+
cancelWait: () => {
|
|
2279
|
+
writes.delete(path);
|
|
2280
|
+
clearTimeout(timeoutHandler);
|
|
2281
|
+
return event;
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
/**
|
|
2288
|
+
* Determines whether user has asked to ignore this path.
|
|
2289
|
+
*/
|
|
2290
|
+
_isIgnored(path, stats) {
|
|
2291
|
+
if (this.options.atomic && DOT_RE.test(path))
|
|
2292
|
+
return true;
|
|
2293
|
+
if (!this._userIgnored) {
|
|
2294
|
+
const { cwd } = this.options;
|
|
2295
|
+
const ign = this.options.ignored;
|
|
2296
|
+
const ignored = (ign || []).map(normalizeIgnored(cwd));
|
|
2297
|
+
const ignoredPaths = [...this._ignoredPaths];
|
|
2298
|
+
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
2299
|
+
this._userIgnored = anymatch(list, void 0);
|
|
2300
|
+
}
|
|
2301
|
+
return this._userIgnored(path, stats);
|
|
2302
|
+
}
|
|
2303
|
+
_isntIgnored(path, stat7) {
|
|
2304
|
+
return !this._isIgnored(path, stat7);
|
|
2305
|
+
}
|
|
2306
|
+
/**
|
|
2307
|
+
* Provides a set of common helpers and properties relating to symlink handling.
|
|
2308
|
+
* @param path file or directory pattern being watched
|
|
2309
|
+
*/
|
|
2310
|
+
_getWatchHelpers(path) {
|
|
2311
|
+
return new WatchHelper(path, this.options.followSymlinks, this);
|
|
2312
|
+
}
|
|
2313
|
+
// Directory helpers
|
|
2314
|
+
// -----------------
|
|
2315
|
+
/**
|
|
2316
|
+
* Provides directory tracking objects
|
|
2317
|
+
* @param directory path of the directory
|
|
2318
|
+
*/
|
|
2319
|
+
_getWatchedDir(directory) {
|
|
2320
|
+
const dir = sysPath2.resolve(directory);
|
|
2321
|
+
if (!this._watched.has(dir))
|
|
2322
|
+
this._watched.set(dir, new DirEntry(dir, this._boundRemove));
|
|
2323
|
+
return this._watched.get(dir);
|
|
2324
|
+
}
|
|
2325
|
+
// File helpers
|
|
2326
|
+
// ------------
|
|
2327
|
+
/**
|
|
2328
|
+
* Check for read permissions: https://stackoverflow.com/a/11781404/1358405
|
|
2329
|
+
*/
|
|
2330
|
+
_hasReadPermissions(stats) {
|
|
2331
|
+
if (this.options.ignorePermissionErrors)
|
|
2332
|
+
return true;
|
|
2333
|
+
return Boolean(Number(stats.mode) & 256);
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* Handles emitting unlink events for
|
|
2337
|
+
* files and directories, and via recursion, for
|
|
2338
|
+
* files and directories within directories that are unlinked
|
|
2339
|
+
* @param directory within which the following item is located
|
|
2340
|
+
* @param item base path of item/directory
|
|
2341
|
+
*/
|
|
2342
|
+
_remove(directory, item, isDirectory) {
|
|
2343
|
+
const path = sysPath2.join(directory, item);
|
|
2344
|
+
const fullPath = sysPath2.resolve(path);
|
|
2345
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);
|
|
2346
|
+
if (!this._throttle("remove", path, 100))
|
|
2347
|
+
return;
|
|
2348
|
+
if (!isDirectory && this._watched.size === 1) {
|
|
2349
|
+
this.add(directory, item, true);
|
|
2350
|
+
}
|
|
2351
|
+
const wp = this._getWatchedDir(path);
|
|
2352
|
+
const nestedDirectoryChildren = wp.getChildren();
|
|
2353
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path, nested));
|
|
2354
|
+
const parent = this._getWatchedDir(directory);
|
|
2355
|
+
const wasTracked = parent.has(item);
|
|
2356
|
+
parent.remove(item);
|
|
2357
|
+
if (this._symlinkPaths.has(fullPath)) {
|
|
2358
|
+
this._symlinkPaths.delete(fullPath);
|
|
2359
|
+
}
|
|
2360
|
+
let relPath = path;
|
|
2361
|
+
if (this.options.cwd)
|
|
2362
|
+
relPath = sysPath2.relative(this.options.cwd, path);
|
|
2363
|
+
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
2364
|
+
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
2365
|
+
if (event === EVENTS.ADD)
|
|
2366
|
+
return;
|
|
2367
|
+
}
|
|
2368
|
+
this._watched.delete(path);
|
|
2369
|
+
this._watched.delete(fullPath);
|
|
2370
|
+
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
2371
|
+
if (wasTracked && !this._isIgnored(path))
|
|
2372
|
+
this._emit(eventName, path);
|
|
2373
|
+
this._closePath(path);
|
|
2374
|
+
}
|
|
2375
|
+
/**
|
|
2376
|
+
* Closes all watchers for a path
|
|
2377
|
+
*/
|
|
2378
|
+
_closePath(path) {
|
|
2379
|
+
this._closeFile(path);
|
|
2380
|
+
const dir = sysPath2.dirname(path);
|
|
2381
|
+
this._getWatchedDir(dir).remove(sysPath2.basename(path));
|
|
2382
|
+
}
|
|
2383
|
+
/**
|
|
2384
|
+
* Closes only file-specific watchers
|
|
2385
|
+
*/
|
|
2386
|
+
_closeFile(path) {
|
|
2387
|
+
const closers = this._closers.get(path);
|
|
2388
|
+
if (!closers)
|
|
2389
|
+
return;
|
|
2390
|
+
closers.forEach((closer) => closer());
|
|
2391
|
+
this._closers.delete(path);
|
|
2392
|
+
}
|
|
2393
|
+
_addPathCloser(path, closer) {
|
|
2394
|
+
if (!closer)
|
|
2395
|
+
return;
|
|
2396
|
+
let list = this._closers.get(path);
|
|
2397
|
+
if (!list) {
|
|
2398
|
+
list = [];
|
|
2399
|
+
this._closers.set(path, list);
|
|
2400
|
+
}
|
|
2401
|
+
list.push(closer);
|
|
2402
|
+
}
|
|
2403
|
+
_readdirp(root, opts) {
|
|
2404
|
+
if (this.closed)
|
|
2405
|
+
return;
|
|
2406
|
+
const options = { type: EVENTS.ALL, alwaysStat: true, lstat: true, ...opts, depth: 0 };
|
|
2407
|
+
let stream = readdirp(root, options);
|
|
2408
|
+
this._streams.add(stream);
|
|
2409
|
+
stream.once(STR_CLOSE, () => {
|
|
2410
|
+
stream = void 0;
|
|
2411
|
+
});
|
|
2412
|
+
stream.once(STR_END, () => {
|
|
2413
|
+
if (stream) {
|
|
2414
|
+
this._streams.delete(stream);
|
|
2415
|
+
stream = void 0;
|
|
2416
|
+
}
|
|
2417
|
+
});
|
|
2418
|
+
return stream;
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
function watch(paths, options = {}) {
|
|
2422
|
+
const watcher = new FSWatcher(options);
|
|
2423
|
+
watcher.add(paths);
|
|
2424
|
+
return watcher;
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
// src/dev/dev-watcher.ts
|
|
2428
|
+
import { stat as stat6 } from "node:fs/promises";
|
|
2429
|
+
import { relative as relative3, sep as sep2 } from "node:path";
|
|
2430
|
+
var defaultPollIntervalMs = 1e3;
|
|
2431
|
+
function createDevWatcher(input) {
|
|
2432
|
+
const ignorePrefixes = (input.ignore ?? []).map(normalizePrefix).filter(Boolean);
|
|
2433
|
+
const ignored = (path) => shouldIgnoreWatchPath(relative3(input.root, path), ignorePrefixes);
|
|
2434
|
+
const watcher = watch(input.root, {
|
|
2435
|
+
ignoreInitial: true,
|
|
2436
|
+
ignored,
|
|
2437
|
+
atomic: true
|
|
2438
|
+
});
|
|
2439
|
+
watcher.on("all", (_event, path) => {
|
|
2440
|
+
if (!ignored(path)) input.onChange(path);
|
|
2441
|
+
});
|
|
2442
|
+
let readySettled = false;
|
|
2443
|
+
const ready = new Promise((resolve8, reject) => {
|
|
2444
|
+
watcher.once("ready", () => {
|
|
2445
|
+
readySettled = true;
|
|
2446
|
+
resolve8();
|
|
2447
|
+
});
|
|
2448
|
+
watcher.on("error", (error) => {
|
|
2449
|
+
if (readySettled) return;
|
|
2450
|
+
readySettled = true;
|
|
2451
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
2452
|
+
});
|
|
2453
|
+
});
|
|
2454
|
+
let tracked = /* @__PURE__ */ new Map();
|
|
2455
|
+
let trackGeneration = 0;
|
|
2456
|
+
let polling = false;
|
|
2457
|
+
const poll = async () => {
|
|
2458
|
+
if (polling) return;
|
|
2459
|
+
polling = true;
|
|
2460
|
+
const map = tracked;
|
|
2461
|
+
const generation = trackGeneration;
|
|
2462
|
+
try {
|
|
2463
|
+
for (const [path, previous] of [...map]) {
|
|
2464
|
+
if (tracked !== map || generation !== trackGeneration) return;
|
|
2465
|
+
const next = await fingerprint(path);
|
|
2466
|
+
if (tracked !== map || generation !== trackGeneration) return;
|
|
2467
|
+
if (!map.has(path)) continue;
|
|
2468
|
+
if (previous !== null && next !== previous) {
|
|
2469
|
+
if (tracked !== map || generation !== trackGeneration) return;
|
|
2470
|
+
if (input.onChange(path) === false) continue;
|
|
2471
|
+
if (tracked !== map || generation !== trackGeneration) return;
|
|
2472
|
+
map.set(path, next);
|
|
2473
|
+
} else if (previous === null) {
|
|
2474
|
+
map.set(path, next);
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
} finally {
|
|
2478
|
+
polling = false;
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
const timer = setInterval(() => void poll(), input.pollIntervalMs ?? defaultPollIntervalMs);
|
|
2482
|
+
timer.unref();
|
|
2483
|
+
return {
|
|
2484
|
+
ready,
|
|
2485
|
+
rebaseline: async () => {
|
|
2486
|
+
const generation = ++trackGeneration;
|
|
2487
|
+
const previousTracked = tracked;
|
|
2488
|
+
tracked = /* @__PURE__ */ new Map();
|
|
2489
|
+
const next = /* @__PURE__ */ new Map();
|
|
2490
|
+
for (const path of previousTracked.keys()) {
|
|
2491
|
+
next.set(path, await fingerprint(path));
|
|
2492
|
+
if (generation !== trackGeneration) return;
|
|
2493
|
+
}
|
|
2494
|
+
if (generation !== trackGeneration) return;
|
|
2495
|
+
tracked = next;
|
|
2496
|
+
trackGeneration++;
|
|
2497
|
+
},
|
|
2498
|
+
track: async (paths, options) => {
|
|
2499
|
+
const ignoreChanges = new Set(options?.ignoreChanges ?? []);
|
|
2500
|
+
const generation = ++trackGeneration;
|
|
2501
|
+
const previousTracked = tracked;
|
|
2502
|
+
tracked = /* @__PURE__ */ new Map();
|
|
2503
|
+
const changedDuringBuild = [];
|
|
2504
|
+
const next = /* @__PURE__ */ new Map();
|
|
2505
|
+
for (const path of paths) {
|
|
2506
|
+
if (ignored(path)) continue;
|
|
2507
|
+
const current = await fingerprint(path);
|
|
2508
|
+
if (generation !== trackGeneration) return [];
|
|
2509
|
+
const previous = previousTracked.get(path);
|
|
2510
|
+
if (previous !== void 0 && previous !== null && current !== previous && !ignoreChanges.has(path)) {
|
|
2511
|
+
changedDuringBuild.push(path);
|
|
2512
|
+
}
|
|
2513
|
+
next.set(path, current);
|
|
2514
|
+
}
|
|
2515
|
+
if (generation !== trackGeneration) return [];
|
|
2516
|
+
tracked = next;
|
|
2517
|
+
trackGeneration++;
|
|
2518
|
+
return changedDuringBuild;
|
|
2519
|
+
},
|
|
2520
|
+
close: async () => {
|
|
2521
|
+
clearInterval(timer);
|
|
2522
|
+
trackGeneration++;
|
|
2523
|
+
tracked = /* @__PURE__ */ new Map();
|
|
2524
|
+
await watcher.close();
|
|
2525
|
+
}
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
async function fingerprint(path) {
|
|
2529
|
+
try {
|
|
2530
|
+
const info = await stat6(path);
|
|
2531
|
+
return `${info.ino}:${info.size}:${info.mtimeMs}`;
|
|
2532
|
+
} catch {
|
|
2533
|
+
return "missing";
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
function normalizePrefix(prefix) {
|
|
2537
|
+
return prefix.replace(/^\.\//, "").replace(/\/+$/, "").split("/").join(sep2);
|
|
2538
|
+
}
|
|
2539
|
+
function shouldIgnoreWatchPath(relativePath, ignorePrefixes = []) {
|
|
2540
|
+
if (!relativePath || relativePath === ".") return false;
|
|
2541
|
+
if (relativePath.startsWith("..")) return true;
|
|
2542
|
+
const segments = relativePath.split(sep2);
|
|
2543
|
+
if (segments.some((segment) => segment === "node_modules" || segment.startsWith("."))) {
|
|
2544
|
+
return true;
|
|
2545
|
+
}
|
|
2546
|
+
if (relativePath.endsWith(".map")) return true;
|
|
2547
|
+
return ignorePrefixes.some(
|
|
2548
|
+
(prefix) => relativePath === prefix || relativePath.startsWith(`${prefix}${sep2}`)
|
|
2549
|
+
);
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
// src/dev/error-page.ts
|
|
2553
|
+
function wantsHtml(req) {
|
|
2554
|
+
const accept = req.headers.accept ?? "";
|
|
2555
|
+
return /\btext\/html\b/.test(accept);
|
|
2556
|
+
}
|
|
2557
|
+
function sendDevError(req, res, status, view, options = {}) {
|
|
2558
|
+
if (res.headersSent) {
|
|
2559
|
+
res.end();
|
|
2560
|
+
return;
|
|
2561
|
+
}
|
|
2562
|
+
const headers = {
|
|
2563
|
+
"cache-control": "no-store",
|
|
2564
|
+
"x-kody-dev-error": view.kind
|
|
2565
|
+
};
|
|
2566
|
+
if (wantsHtml(req)) {
|
|
2567
|
+
res.writeHead(status, { ...headers, "content-type": "text/html; charset=utf-8" });
|
|
2568
|
+
res.end(renderDevErrorPage(view) + (options.htmlTail ?? ""));
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
res.writeHead(status, { ...headers, "content-type": "text/plain; charset=utf-8" });
|
|
2572
|
+
res.end(`kody dev ${view.kind} error${view.request ? ` (${view.request})` : ""}
|
|
2573
|
+
|
|
2574
|
+
${view.detail}
|
|
2575
|
+
`);
|
|
2576
|
+
}
|
|
2577
|
+
function renderDevErrorPage(view) {
|
|
2578
|
+
const label = view.kind === "build" ? "Build error" : "Runtime error";
|
|
2579
|
+
return `<!doctype html>
|
|
2580
|
+
<html lang="en">
|
|
2581
|
+
<head>
|
|
2582
|
+
<meta charset="utf-8">
|
|
2583
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2584
|
+
<title>${escapeHtml(label)} \xB7 kody dev</title>
|
|
2585
|
+
<style>
|
|
2586
|
+
:root { color-scheme: dark; }
|
|
2587
|
+
body { margin: 0; font: 14px/1.5 ui-sans-serif, system-ui, sans-serif; background: #1a1015; color: #f5e9ee; }
|
|
2588
|
+
main { max-width: 960px; margin: 0 auto; padding: 32px 24px; }
|
|
2589
|
+
.label { display: inline-block; font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: #ff8fab; }
|
|
2590
|
+
h1 { font-size: 20px; margin: 8px 0 20px; word-break: break-word; }
|
|
2591
|
+
pre { margin: 0; padding: 16px; overflow: auto; background: #0e080b; border: 1px solid #3a2530; border-radius: 8px; font: 13px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre; }
|
|
2592
|
+
p { color: #bfa9b3; font-size: 13px; }
|
|
2593
|
+
</style>
|
|
2594
|
+
</head>
|
|
2595
|
+
<body>
|
|
2596
|
+
<main>
|
|
2597
|
+
<span class="label">${escapeHtml(label)}</span>
|
|
2598
|
+
<h1>${escapeHtml(view.title)}</h1>${view.request ? `
|
|
2599
|
+
<p>${escapeHtml(view.request)}</p>` : ""}
|
|
2600
|
+
<pre>${escapeHtml(view.detail)}</pre>
|
|
2601
|
+
<p>Served by <code>kody dev</code> only; published apps never see this page.</p>
|
|
2602
|
+
</main>
|
|
2603
|
+
</body>
|
|
2604
|
+
</html>
|
|
2605
|
+
`;
|
|
2606
|
+
}
|
|
2607
|
+
function escapeHtml(value) {
|
|
2608
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2611
|
+
// src/dev/live-reload.ts
|
|
2612
|
+
var liveReloadEventsPath = "/__kody/dev/events";
|
|
2613
|
+
var liveReloadClientPath = "/__kody/dev/client.js";
|
|
2614
|
+
var liveReloadScriptTag = `<script type="module" src="${liveReloadClientPath}"></script>`;
|
|
2615
|
+
var keepAliveMs = 15e3;
|
|
2616
|
+
function createLiveReloadHub(input) {
|
|
2617
|
+
const clients = /* @__PURE__ */ new Set();
|
|
2618
|
+
const send2 = (res, event, data) => {
|
|
2619
|
+
res.write(`event: ${event}
|
|
2620
|
+
data: ${JSON.stringify(data)}
|
|
2621
|
+
|
|
2622
|
+
`);
|
|
2623
|
+
};
|
|
2624
|
+
const keepAlive = setInterval(() => {
|
|
2625
|
+
for (const res of clients) res.write(": keep-alive\n\n");
|
|
2626
|
+
}, keepAliveMs);
|
|
2627
|
+
keepAlive.unref();
|
|
2628
|
+
return {
|
|
2629
|
+
handleEvents: (req, res) => {
|
|
2630
|
+
res.writeHead(200, {
|
|
2631
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
2632
|
+
"cache-control": "no-store",
|
|
2633
|
+
connection: "keep-alive"
|
|
2634
|
+
});
|
|
2635
|
+
res.write("retry: 500\n\n");
|
|
2636
|
+
send2(res, "hello", input.state());
|
|
2637
|
+
clients.add(res);
|
|
2638
|
+
req.on("close", () => clients.delete(res));
|
|
2639
|
+
},
|
|
2640
|
+
broadcast: (message) => {
|
|
2641
|
+
for (const res of clients) send2(res, message.event, message.data);
|
|
2642
|
+
},
|
|
2643
|
+
clientCount: () => clients.size,
|
|
2644
|
+
close: () => {
|
|
2645
|
+
clearInterval(keepAlive);
|
|
2646
|
+
for (const res of clients) res.end();
|
|
2647
|
+
clients.clear();
|
|
2648
|
+
}
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
function injectLiveReloadScript(html) {
|
|
2652
|
+
if (html.includes(liveReloadClientPath)) return html;
|
|
2653
|
+
for (const pattern of [/<\/head\s*>/i, /<\/body\s*>/i]) {
|
|
2654
|
+
const match = pattern.exec(html);
|
|
2655
|
+
if (match) {
|
|
2656
|
+
return `${html.slice(0, match.index)}${liveReloadScriptTag}${html.slice(match.index)}`;
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
return `${html}${liveReloadScriptTag}`;
|
|
2660
|
+
}
|
|
2661
|
+
function shouldInjectLiveReload(input) {
|
|
2662
|
+
if (input.method === "HEAD") return false;
|
|
2663
|
+
if (!/\btext\/html\b/i.test(input.accept ?? "")) return false;
|
|
2664
|
+
if (input.status === 204 || input.status === 304 || input.status < 200) return false;
|
|
2665
|
+
const contentType = String(input.headers["content-type"] ?? "");
|
|
2666
|
+
if (!/^\s*text\/html\b/i.test(contentType)) return false;
|
|
2667
|
+
const encoding = String(input.headers["content-encoding"] ?? "identity").trim().toLowerCase();
|
|
2668
|
+
return encoding === "" || encoding === "identity";
|
|
2669
|
+
}
|
|
2670
|
+
var liveReloadClientSource = `
|
|
2671
|
+
const eventsUrl = ${JSON.stringify(liveReloadEventsPath)};
|
|
2672
|
+
let buildId = null;
|
|
2673
|
+
let overlay = null;
|
|
2674
|
+
|
|
2675
|
+
function hideOverlay() {
|
|
2676
|
+
overlay?.remove();
|
|
2677
|
+
overlay = null;
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
function showOverlay(view) {
|
|
2681
|
+
if (globalThis.__kodyDevErrorPage) return;
|
|
2682
|
+
hideOverlay();
|
|
2683
|
+
const host = document.createElement('kody-dev-overlay');
|
|
2684
|
+
const root = host.attachShadow({ mode: 'open' });
|
|
2685
|
+
const label = view.kind === 'build' ? 'Build error' : 'Runtime error';
|
|
2686
|
+
// Keep CSS out of the text tree (page-text readers / textContent walkers).
|
|
2687
|
+
const sheet = new CSSStyleSheet();
|
|
2688
|
+
sheet.replaceSync(\`
|
|
2689
|
+
.backdrop { position: fixed; inset: 0; z-index: 2147483647; background: rgba(10, 4, 7, .82); display: flex; align-items: flex-start; justify-content: center; overflow: auto; font: 14px/1.5 ui-sans-serif, system-ui, sans-serif; }
|
|
2690
|
+
.panel { margin: 48px 16px; max-width: 960px; width: 100%; background: #1a1015; color: #f5e9ee; border: 1px solid #3a2530; border-radius: 10px; padding: 20px 24px; box-shadow: 0 20px 60px rgba(0,0,0,.5); }
|
|
2691
|
+
.top { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
|
|
2692
|
+
.label { font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: #ff8fab; }
|
|
2693
|
+
button { all: unset; cursor: pointer; color: #bfa9b3; font-size: 20px; line-height: 1; padding: 4px 8px; border-radius: 6px; }
|
|
2694
|
+
button:hover, button:focus-visible { background: #2a1a22; color: #fff; }
|
|
2695
|
+
h1 { font-size: 18px; margin: 8px 0 4px; word-break: break-word; }
|
|
2696
|
+
.request { color: #bfa9b3; font-size: 13px; margin: 0 0 12px; }
|
|
2697
|
+
pre { margin: 12px 0 0; padding: 14px; overflow: auto; background: #0e080b; border: 1px solid #3a2530; border-radius: 8px; font: 13px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre; }
|
|
2698
|
+
.hint { color: #bfa9b3; font-size: 12px; margin-top: 12px; }
|
|
2699
|
+
\`);
|
|
2700
|
+
root.adoptedStyleSheets = [sheet];
|
|
2701
|
+
root.innerHTML = \`
|
|
2702
|
+
<div class="backdrop" role="alertdialog" aria-modal="true" aria-labelledby="kody-dev-title">
|
|
2703
|
+
<div class="panel">
|
|
2704
|
+
<div class="top"><span class="label"></span><button type="button" aria-label="Dismiss">\xD7</button></div>
|
|
2705
|
+
<h1 id="kody-dev-title"></h1>
|
|
2706
|
+
<p class="request"></p>
|
|
2707
|
+
<pre></pre>
|
|
2708
|
+
<p class="hint">Fix it and save. The page reloads on the next good build. Esc dismisses.</p>
|
|
2709
|
+
</div>
|
|
2710
|
+
</div>\`;
|
|
2711
|
+
root.querySelector('.label').textContent = label;
|
|
2712
|
+
root.querySelector('h1').textContent = view.title;
|
|
2713
|
+
const request = root.querySelector('.request');
|
|
2714
|
+
if (view.request) request.textContent = view.request;
|
|
2715
|
+
else request.remove();
|
|
2716
|
+
root.querySelector('pre').textContent = view.detail;
|
|
2717
|
+
root.querySelector('button').addEventListener('click', hideOverlay);
|
|
2718
|
+
document.documentElement.appendChild(host);
|
|
2719
|
+
overlay = host;
|
|
2720
|
+
root.querySelector('button').focus();
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
addEventListener('keydown', (event) => {
|
|
2724
|
+
if (event.key === 'Escape') hideOverlay();
|
|
2725
|
+
});
|
|
2726
|
+
|
|
2727
|
+
function connect() {
|
|
2728
|
+
const source = new EventSource(eventsUrl);
|
|
2729
|
+
source.addEventListener('hello', (event) => {
|
|
2730
|
+
const state = JSON.parse(event.data);
|
|
2731
|
+
if (buildId !== null && state.buildId !== null && state.buildId !== buildId) {
|
|
2732
|
+
location.reload();
|
|
2733
|
+
return;
|
|
2734
|
+
}
|
|
2735
|
+
buildId = state.buildId;
|
|
2736
|
+
if (state.error) showOverlay(state.error);
|
|
2737
|
+
});
|
|
2738
|
+
source.addEventListener('reload', () => location.reload());
|
|
2739
|
+
source.addEventListener('build_error', (event) => showOverlay(JSON.parse(event.data)));
|
|
2740
|
+
source.addEventListener('runtime_error', (event) => showOverlay(JSON.parse(event.data)));
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
connect();
|
|
2744
|
+
`.trimStart();
|
|
2745
|
+
|
|
2746
|
+
// src/dev/prebuild.ts
|
|
2747
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2748
|
+
var outputTailLines = 20;
|
|
2749
|
+
var PrebuildError = class extends Error {
|
|
2750
|
+
constructor(message) {
|
|
2751
|
+
super(message);
|
|
2752
|
+
this.name = "PrebuildError";
|
|
2753
|
+
}
|
|
2754
|
+
};
|
|
2755
|
+
async function runPrebuild(input) {
|
|
2756
|
+
if (input.signal?.aborted) {
|
|
2757
|
+
throw new PrebuildError("kody.dev.prebuild aborted before start");
|
|
2758
|
+
}
|
|
2759
|
+
const env = { ...process.env, KODY_DEV: "1" };
|
|
2760
|
+
delete env[apiTokenEnvVar];
|
|
2761
|
+
const child = spawn2("sh", ["-c", input.command], {
|
|
2762
|
+
cwd: input.cwd,
|
|
2763
|
+
env,
|
|
2764
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2765
|
+
detached: true
|
|
2766
|
+
});
|
|
2767
|
+
if (child.pid) input.onSpawn?.(child.pid);
|
|
2768
|
+
const tail = [];
|
|
2769
|
+
const collect = (stream) => (line) => {
|
|
2770
|
+
tail.push(line);
|
|
2771
|
+
if (tail.length > outputTailLines) tail.shift();
|
|
2772
|
+
input.onLine?.(line, stream);
|
|
2773
|
+
};
|
|
2774
|
+
forEachLine(child.stdout, collect("stdout"));
|
|
2775
|
+
forEachLine(child.stderr, collect("stderr"));
|
|
2776
|
+
const killChild = () => {
|
|
2777
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
2778
|
+
const pid = child.pid;
|
|
2779
|
+
if (pid === void 0) return;
|
|
2780
|
+
try {
|
|
2781
|
+
process.kill(-pid, "SIGKILL");
|
|
2782
|
+
} catch {
|
|
2783
|
+
try {
|
|
2784
|
+
child.kill("SIGKILL");
|
|
2785
|
+
} catch {
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
};
|
|
2789
|
+
if (input.signal) {
|
|
2790
|
+
if (input.signal.aborted) killChild();
|
|
2791
|
+
else input.signal.addEventListener("abort", killChild, { once: true });
|
|
2792
|
+
}
|
|
2793
|
+
try {
|
|
2794
|
+
const result = await new Promise(
|
|
2795
|
+
(resolve8, reject) => {
|
|
2796
|
+
child.once("error", reject);
|
|
2797
|
+
child.once("close", (code, signal) => resolve8({ code, signal }));
|
|
2798
|
+
}
|
|
2799
|
+
);
|
|
2800
|
+
if (input.signal?.aborted) {
|
|
2801
|
+
throw new PrebuildError("kody.dev.prebuild aborted");
|
|
2802
|
+
}
|
|
2803
|
+
if (result.code === 0) return;
|
|
2804
|
+
const how = result.signal ? `signal ${result.signal}` : `exit ${result.code}`;
|
|
2805
|
+
throw new PrebuildError(
|
|
2806
|
+
[`kody.dev.prebuild failed (${how}): ${input.command}`, ...tail.map((line) => ` ${line}`)].join(
|
|
2807
|
+
"\n"
|
|
2808
|
+
)
|
|
2809
|
+
);
|
|
2810
|
+
} finally {
|
|
2811
|
+
input.signal?.removeEventListener("abort", killChild);
|
|
2812
|
+
if (child.pid) input.onExit?.(child.pid);
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
function forEachLine(stream, onLine) {
|
|
2816
|
+
if (!stream) return;
|
|
2817
|
+
let buffered = "";
|
|
2818
|
+
stream.on("data", (chunk) => {
|
|
2819
|
+
buffered += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
2820
|
+
const lines = buffered.split("\n");
|
|
2821
|
+
buffered = lines.pop() ?? "";
|
|
2822
|
+
for (const line of lines) {
|
|
2823
|
+
if (line.length > 0) onLine(line);
|
|
2824
|
+
}
|
|
2825
|
+
});
|
|
2826
|
+
stream.on("end", () => {
|
|
2827
|
+
if (buffered.length > 0) onLine(buffered);
|
|
2828
|
+
buffered = "";
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
// node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
|
|
2833
|
+
var comma = ",".charCodeAt(0);
|
|
2834
|
+
var semicolon = ";".charCodeAt(0);
|
|
2835
|
+
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
2836
|
+
var intToChar = new Uint8Array(64);
|
|
2837
|
+
var charToInt = new Uint8Array(128);
|
|
2838
|
+
for (let i = 0; i < chars.length; i++) {
|
|
2839
|
+
const c = chars.charCodeAt(i);
|
|
2840
|
+
intToChar[i] = c;
|
|
2841
|
+
charToInt[c] = i;
|
|
2842
|
+
}
|
|
2843
|
+
function decodeInteger(reader) {
|
|
2844
|
+
let value = 0;
|
|
2845
|
+
let shift = 0;
|
|
2846
|
+
let integer = 0;
|
|
2847
|
+
do {
|
|
2848
|
+
const c = reader.next();
|
|
2849
|
+
integer = charToInt[c];
|
|
2850
|
+
value |= (integer & 31) << shift;
|
|
2851
|
+
shift += 5;
|
|
2852
|
+
} while (integer & 32);
|
|
2853
|
+
return value;
|
|
2854
|
+
}
|
|
2855
|
+
function decodeSign(num) {
|
|
2856
|
+
return num & 1 ? -2147483648 | -(num >>> 1) : num >>> 1;
|
|
2857
|
+
}
|
|
2858
|
+
function hasMoreVlq(reader, max) {
|
|
2859
|
+
if (reader.pos >= max) return false;
|
|
2860
|
+
return reader.peek() !== comma;
|
|
2861
|
+
}
|
|
2862
|
+
var bufLength = 1024 * 16;
|
|
2863
|
+
var StringReader = class {
|
|
2864
|
+
constructor(buffer) {
|
|
2865
|
+
this.pos = 0;
|
|
2866
|
+
this.buffer = buffer;
|
|
2867
|
+
}
|
|
2868
|
+
next() {
|
|
2869
|
+
return this.buffer.charCodeAt(this.pos++);
|
|
2870
|
+
}
|
|
2871
|
+
peek() {
|
|
2872
|
+
return this.buffer.charCodeAt(this.pos);
|
|
2873
|
+
}
|
|
2874
|
+
indexOf(char) {
|
|
2875
|
+
const { buffer, pos } = this;
|
|
2876
|
+
const idx = buffer.indexOf(char, pos);
|
|
2877
|
+
return idx === -1 ? buffer.length : idx;
|
|
2878
|
+
}
|
|
2879
|
+
};
|
|
2880
|
+
function decode(mappings) {
|
|
2881
|
+
const { length } = mappings;
|
|
2882
|
+
const reader = new StringReader(mappings);
|
|
2883
|
+
const decoded = [];
|
|
2884
|
+
let genColumn = 0;
|
|
2885
|
+
let sourcesIndex = 0;
|
|
2886
|
+
let sourceLine = 0;
|
|
2887
|
+
let sourceColumn = 0;
|
|
2888
|
+
let namesIndex = 0;
|
|
2889
|
+
do {
|
|
2890
|
+
const semi = reader.indexOf(";");
|
|
2891
|
+
const line = [];
|
|
2892
|
+
let sorted = true;
|
|
2893
|
+
let lastCol = 0;
|
|
2894
|
+
genColumn = 0;
|
|
2895
|
+
while (reader.pos < semi) {
|
|
2896
|
+
let seg;
|
|
2897
|
+
genColumn += decodeSign(decodeInteger(reader));
|
|
2898
|
+
if (genColumn < lastCol) sorted = false;
|
|
2899
|
+
lastCol = genColumn;
|
|
2900
|
+
if (hasMoreVlq(reader, semi)) {
|
|
2901
|
+
sourcesIndex += decodeSign(decodeInteger(reader));
|
|
2902
|
+
sourceLine += decodeSign(decodeInteger(reader));
|
|
2903
|
+
sourceColumn += decodeSign(decodeInteger(reader));
|
|
2904
|
+
if (hasMoreVlq(reader, semi)) {
|
|
2905
|
+
namesIndex += decodeSign(decodeInteger(reader));
|
|
2906
|
+
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex];
|
|
2907
|
+
} else {
|
|
2908
|
+
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn];
|
|
2909
|
+
}
|
|
2910
|
+
} else {
|
|
2911
|
+
seg = [genColumn];
|
|
2912
|
+
}
|
|
2913
|
+
line.push(seg);
|
|
2914
|
+
reader.pos++;
|
|
2915
|
+
}
|
|
2916
|
+
if (!sorted) sort(line);
|
|
2917
|
+
decoded.push(line);
|
|
2918
|
+
reader.pos = semi + 1;
|
|
2919
|
+
} while (reader.pos <= length);
|
|
2920
|
+
return decoded;
|
|
2921
|
+
}
|
|
2922
|
+
function sort(line) {
|
|
2923
|
+
line.sort(sortComparator);
|
|
2924
|
+
}
|
|
2925
|
+
function sortComparator(a, b) {
|
|
2926
|
+
return a[0] - b[0];
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
// node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs
|
|
2930
|
+
var schemeRegex = /^[\w+.-]+:\/\//;
|
|
2931
|
+
var urlRegex = /^([\w+.-]+:)\/\/([^@/#?]*@)?([^:/#?]*)(:\d+)?(\/[^#?]*)?(\?[^#]*)?(#.*)?/;
|
|
2932
|
+
var fileRegex = /^file:(?:\/\/((?![a-z]:)[^/#?]*)?)?(\/?[^#?]*)(\?[^#]*)?(#.*)?/i;
|
|
2933
|
+
function isAbsoluteUrl(input) {
|
|
2934
|
+
return schemeRegex.test(input);
|
|
2935
|
+
}
|
|
2936
|
+
function isSchemeRelativeUrl(input) {
|
|
2937
|
+
return input.startsWith("//");
|
|
2938
|
+
}
|
|
2939
|
+
function isAbsolutePath(input) {
|
|
2940
|
+
return input.startsWith("/");
|
|
2941
|
+
}
|
|
2942
|
+
function isFileUrl(input) {
|
|
2943
|
+
return input.startsWith("file:");
|
|
2944
|
+
}
|
|
2945
|
+
function isRelative(input) {
|
|
2946
|
+
return /^[.?#]/.test(input);
|
|
2947
|
+
}
|
|
2948
|
+
function parseAbsoluteUrl(input) {
|
|
2949
|
+
const match = urlRegex.exec(input);
|
|
2950
|
+
return makeUrl(match[1], match[2] || "", match[3], match[4] || "", match[5] || "/", match[6] || "", match[7] || "");
|
|
2951
|
+
}
|
|
2952
|
+
function parseFileUrl(input) {
|
|
2953
|
+
const match = fileRegex.exec(input);
|
|
2954
|
+
const path = match[2];
|
|
2955
|
+
return makeUrl("file:", "", match[1] || "", "", isAbsolutePath(path) ? path : "/" + path, match[3] || "", match[4] || "");
|
|
2956
|
+
}
|
|
2957
|
+
function makeUrl(scheme, user, host, port, path, query, hash) {
|
|
2958
|
+
return {
|
|
2959
|
+
scheme,
|
|
2960
|
+
user,
|
|
2961
|
+
host,
|
|
2962
|
+
port,
|
|
2963
|
+
path,
|
|
2964
|
+
query,
|
|
2965
|
+
hash,
|
|
2966
|
+
type: 7
|
|
2967
|
+
};
|
|
2968
|
+
}
|
|
2969
|
+
function parseUrl(input) {
|
|
2970
|
+
if (isSchemeRelativeUrl(input)) {
|
|
2971
|
+
const url2 = parseAbsoluteUrl("http:" + input);
|
|
2972
|
+
url2.scheme = "";
|
|
2973
|
+
url2.type = 6;
|
|
2974
|
+
return url2;
|
|
2975
|
+
}
|
|
2976
|
+
if (isAbsolutePath(input)) {
|
|
2977
|
+
const url2 = parseAbsoluteUrl("http://foo.com" + input);
|
|
2978
|
+
url2.scheme = "";
|
|
2979
|
+
url2.host = "";
|
|
2980
|
+
url2.type = 5;
|
|
2981
|
+
return url2;
|
|
2982
|
+
}
|
|
2983
|
+
if (isFileUrl(input))
|
|
2984
|
+
return parseFileUrl(input);
|
|
2985
|
+
if (isAbsoluteUrl(input))
|
|
2986
|
+
return parseAbsoluteUrl(input);
|
|
2987
|
+
const url = parseAbsoluteUrl("http://foo.com/" + input);
|
|
2988
|
+
url.scheme = "";
|
|
2989
|
+
url.host = "";
|
|
2990
|
+
url.type = input ? input.startsWith("?") ? 3 : input.startsWith("#") ? 2 : 4 : 1;
|
|
2991
|
+
return url;
|
|
2992
|
+
}
|
|
2993
|
+
function stripPathFilename(path) {
|
|
2994
|
+
if (path.endsWith("/.."))
|
|
2995
|
+
return path;
|
|
2996
|
+
const index = path.lastIndexOf("/");
|
|
2997
|
+
return path.slice(0, index + 1);
|
|
2998
|
+
}
|
|
2999
|
+
function mergePaths(url, base) {
|
|
3000
|
+
normalizePath2(base, base.type);
|
|
3001
|
+
if (url.path === "/") {
|
|
3002
|
+
url.path = base.path;
|
|
3003
|
+
} else {
|
|
3004
|
+
url.path = stripPathFilename(base.path) + url.path;
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
function normalizePath2(url, type) {
|
|
3008
|
+
const rel = type <= 4;
|
|
3009
|
+
const pieces = url.path.split("/");
|
|
3010
|
+
let pointer = 1;
|
|
3011
|
+
let positive = 0;
|
|
3012
|
+
let addTrailingSlash = false;
|
|
3013
|
+
for (let i = 1; i < pieces.length; i++) {
|
|
3014
|
+
const piece = pieces[i];
|
|
3015
|
+
if (!piece) {
|
|
3016
|
+
addTrailingSlash = true;
|
|
3017
|
+
continue;
|
|
3018
|
+
}
|
|
3019
|
+
addTrailingSlash = false;
|
|
3020
|
+
if (piece === ".")
|
|
3021
|
+
continue;
|
|
3022
|
+
if (piece === "..") {
|
|
3023
|
+
if (positive) {
|
|
3024
|
+
addTrailingSlash = true;
|
|
3025
|
+
positive--;
|
|
3026
|
+
pointer--;
|
|
3027
|
+
} else if (rel) {
|
|
3028
|
+
pieces[pointer++] = piece;
|
|
3029
|
+
}
|
|
3030
|
+
continue;
|
|
3031
|
+
}
|
|
3032
|
+
pieces[pointer++] = piece;
|
|
3033
|
+
positive++;
|
|
3034
|
+
}
|
|
3035
|
+
let path = "";
|
|
3036
|
+
for (let i = 1; i < pointer; i++) {
|
|
3037
|
+
path += "/" + pieces[i];
|
|
3038
|
+
}
|
|
3039
|
+
if (!path || addTrailingSlash && !path.endsWith("/..")) {
|
|
3040
|
+
path += "/";
|
|
3041
|
+
}
|
|
3042
|
+
url.path = path;
|
|
3043
|
+
}
|
|
3044
|
+
function resolve5(input, base) {
|
|
3045
|
+
if (!input && !base)
|
|
3046
|
+
return "";
|
|
3047
|
+
const url = parseUrl(input);
|
|
3048
|
+
let inputType = url.type;
|
|
3049
|
+
if (base && inputType !== 7) {
|
|
3050
|
+
const baseUrl = parseUrl(base);
|
|
3051
|
+
const baseType = baseUrl.type;
|
|
3052
|
+
switch (inputType) {
|
|
3053
|
+
case 1:
|
|
3054
|
+
url.hash = baseUrl.hash;
|
|
3055
|
+
// fall through
|
|
3056
|
+
case 2:
|
|
3057
|
+
url.query = baseUrl.query;
|
|
3058
|
+
// fall through
|
|
3059
|
+
case 3:
|
|
3060
|
+
case 4:
|
|
3061
|
+
mergePaths(url, baseUrl);
|
|
3062
|
+
// fall through
|
|
3063
|
+
case 5:
|
|
3064
|
+
url.user = baseUrl.user;
|
|
3065
|
+
url.host = baseUrl.host;
|
|
3066
|
+
url.port = baseUrl.port;
|
|
3067
|
+
// fall through
|
|
3068
|
+
case 6:
|
|
3069
|
+
url.scheme = baseUrl.scheme;
|
|
3070
|
+
}
|
|
3071
|
+
if (baseType > inputType)
|
|
3072
|
+
inputType = baseType;
|
|
3073
|
+
}
|
|
3074
|
+
normalizePath2(url, inputType);
|
|
3075
|
+
const queryHash = url.query + url.hash;
|
|
3076
|
+
switch (inputType) {
|
|
3077
|
+
// This is impossible, because of the empty checks at the start of the function.
|
|
3078
|
+
// case UrlType.Empty:
|
|
3079
|
+
case 2:
|
|
3080
|
+
case 3:
|
|
3081
|
+
return queryHash;
|
|
3082
|
+
case 4: {
|
|
3083
|
+
const path = url.path.slice(1);
|
|
3084
|
+
if (!path)
|
|
3085
|
+
return queryHash || ".";
|
|
3086
|
+
if (isRelative(base || input) && !isRelative(path)) {
|
|
3087
|
+
return "./" + path + queryHash;
|
|
3088
|
+
}
|
|
3089
|
+
return path + queryHash;
|
|
3090
|
+
}
|
|
3091
|
+
case 5:
|
|
3092
|
+
return url.path + queryHash;
|
|
3093
|
+
default:
|
|
3094
|
+
return url.scheme + "//" + url.user + url.host + url.port + url.path + queryHash;
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3098
|
+
// node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs
|
|
3099
|
+
function stripFilename(path) {
|
|
3100
|
+
if (!path) return "";
|
|
3101
|
+
const index = path.lastIndexOf("/");
|
|
3102
|
+
return path.slice(0, index + 1);
|
|
3103
|
+
}
|
|
3104
|
+
function resolver(mapUrl, sourceRoot) {
|
|
3105
|
+
const from = stripFilename(mapUrl);
|
|
3106
|
+
const prefix = sourceRoot ? sourceRoot + "/" : "";
|
|
3107
|
+
return (source) => resolve5(prefix + (source || ""), from);
|
|
3108
|
+
}
|
|
3109
|
+
var COLUMN = 0;
|
|
3110
|
+
var SOURCES_INDEX = 1;
|
|
3111
|
+
var SOURCE_LINE = 2;
|
|
3112
|
+
var SOURCE_COLUMN = 3;
|
|
3113
|
+
var NAMES_INDEX = 4;
|
|
3114
|
+
function maybeSort(mappings, owned) {
|
|
3115
|
+
const unsortedIndex = nextUnsortedSegmentLine(mappings, 0);
|
|
3116
|
+
if (unsortedIndex === mappings.length) return mappings;
|
|
3117
|
+
if (!owned) mappings = mappings.slice();
|
|
3118
|
+
for (let i = unsortedIndex; i < mappings.length; i = nextUnsortedSegmentLine(mappings, i + 1)) {
|
|
3119
|
+
mappings[i] = sortSegments(mappings[i], owned);
|
|
3120
|
+
}
|
|
3121
|
+
return mappings;
|
|
3122
|
+
}
|
|
3123
|
+
function nextUnsortedSegmentLine(mappings, start) {
|
|
3124
|
+
for (let i = start; i < mappings.length; i++) {
|
|
3125
|
+
if (!isSorted(mappings[i])) return i;
|
|
3126
|
+
}
|
|
3127
|
+
return mappings.length;
|
|
3128
|
+
}
|
|
3129
|
+
function isSorted(line) {
|
|
3130
|
+
for (let j = 1; j < line.length; j++) {
|
|
3131
|
+
if (line[j][COLUMN] < line[j - 1][COLUMN]) {
|
|
3132
|
+
return false;
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
return true;
|
|
3136
|
+
}
|
|
3137
|
+
function sortSegments(line, owned) {
|
|
3138
|
+
if (!owned) line = line.slice();
|
|
3139
|
+
return line.sort(sortComparator2);
|
|
3140
|
+
}
|
|
3141
|
+
function sortComparator2(a, b) {
|
|
3142
|
+
return a[COLUMN] - b[COLUMN];
|
|
3143
|
+
}
|
|
3144
|
+
var found = false;
|
|
3145
|
+
function binarySearch(haystack, needle, low, high) {
|
|
3146
|
+
while (low <= high) {
|
|
3147
|
+
const mid = low + (high - low >> 1);
|
|
3148
|
+
const cmp = haystack[mid][COLUMN] - needle;
|
|
3149
|
+
if (cmp === 0) {
|
|
3150
|
+
found = true;
|
|
3151
|
+
return mid;
|
|
3152
|
+
}
|
|
3153
|
+
if (cmp < 0) {
|
|
3154
|
+
low = mid + 1;
|
|
3155
|
+
} else {
|
|
3156
|
+
high = mid - 1;
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
found = false;
|
|
3160
|
+
return low - 1;
|
|
3161
|
+
}
|
|
3162
|
+
function upperBound(haystack, needle, index) {
|
|
3163
|
+
for (let i = index + 1; i < haystack.length; index = i++) {
|
|
3164
|
+
if (haystack[i][COLUMN] !== needle) break;
|
|
3165
|
+
}
|
|
3166
|
+
return index;
|
|
3167
|
+
}
|
|
3168
|
+
function lowerBound(haystack, needle, index) {
|
|
3169
|
+
for (let i = index - 1; i >= 0; index = i--) {
|
|
3170
|
+
if (haystack[i][COLUMN] !== needle) break;
|
|
3171
|
+
}
|
|
3172
|
+
return index;
|
|
3173
|
+
}
|
|
3174
|
+
function memoizedState() {
|
|
3175
|
+
return {
|
|
3176
|
+
lastKey: -1,
|
|
3177
|
+
lastNeedle: -1,
|
|
3178
|
+
lastIndex: -1
|
|
3179
|
+
};
|
|
3180
|
+
}
|
|
3181
|
+
function memoizedBinarySearch(haystack, needle, state, key) {
|
|
3182
|
+
const { lastKey, lastNeedle, lastIndex } = state;
|
|
3183
|
+
let low = 0;
|
|
3184
|
+
let high = haystack.length - 1;
|
|
3185
|
+
if (key === lastKey) {
|
|
3186
|
+
if (needle === lastNeedle) {
|
|
3187
|
+
found = lastIndex !== -1 && haystack[lastIndex][COLUMN] === needle;
|
|
3188
|
+
return lastIndex;
|
|
3189
|
+
}
|
|
3190
|
+
if (needle >= lastNeedle) {
|
|
3191
|
+
low = lastIndex === -1 ? 0 : lastIndex;
|
|
3192
|
+
} else {
|
|
3193
|
+
high = lastIndex;
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3196
|
+
state.lastKey = key;
|
|
3197
|
+
state.lastNeedle = needle;
|
|
3198
|
+
return state.lastIndex = binarySearch(haystack, needle, low, high);
|
|
3199
|
+
}
|
|
3200
|
+
function parse2(map) {
|
|
3201
|
+
return typeof map === "string" ? JSON.parse(map) : map;
|
|
3202
|
+
}
|
|
3203
|
+
var LINE_GTR_ZERO = "`line` must be greater than 0 (lines start at line 1)";
|
|
3204
|
+
var COL_GTR_EQ_ZERO = "`column` must be greater than or equal to 0 (columns start at column 0)";
|
|
3205
|
+
var LEAST_UPPER_BOUND = -1;
|
|
3206
|
+
var GREATEST_LOWER_BOUND = 1;
|
|
3207
|
+
var TraceMap = class {
|
|
3208
|
+
constructor(map, mapUrl) {
|
|
3209
|
+
const isString = typeof map === "string";
|
|
3210
|
+
if (!isString && map._decodedMemo) return map;
|
|
3211
|
+
const parsed = parse2(map);
|
|
3212
|
+
const { version, file, names, sourceRoot, sources, sourcesContent } = parsed;
|
|
3213
|
+
this.version = version;
|
|
3214
|
+
this.file = file;
|
|
3215
|
+
this.names = names || [];
|
|
3216
|
+
this.sourceRoot = sourceRoot;
|
|
3217
|
+
this.sources = sources;
|
|
3218
|
+
this.sourcesContent = sourcesContent;
|
|
3219
|
+
this.ignoreList = parsed.ignoreList || parsed.x_google_ignoreList || void 0;
|
|
3220
|
+
const resolve8 = resolver(mapUrl, sourceRoot);
|
|
3221
|
+
this.resolvedSources = sources.map(resolve8);
|
|
3222
|
+
const { mappings } = parsed;
|
|
3223
|
+
if (typeof mappings === "string") {
|
|
3224
|
+
this._encoded = mappings;
|
|
3225
|
+
this._decoded = void 0;
|
|
3226
|
+
} else if (Array.isArray(mappings)) {
|
|
3227
|
+
this._encoded = void 0;
|
|
3228
|
+
this._decoded = maybeSort(mappings, isString);
|
|
3229
|
+
} else if (parsed.sections) {
|
|
3230
|
+
throw new Error(`TraceMap passed sectioned source map, please use FlattenMap export instead`);
|
|
3231
|
+
} else {
|
|
3232
|
+
throw new Error(`invalid source map: ${JSON.stringify(parsed)}`);
|
|
3233
|
+
}
|
|
3234
|
+
this._decodedMemo = memoizedState();
|
|
3235
|
+
this._bySources = void 0;
|
|
3236
|
+
this._bySourceMemos = void 0;
|
|
3237
|
+
}
|
|
3238
|
+
};
|
|
3239
|
+
function cast(map) {
|
|
3240
|
+
return map;
|
|
3241
|
+
}
|
|
3242
|
+
function decodedMappings(map) {
|
|
3243
|
+
var _a;
|
|
3244
|
+
return (_a = cast(map))._decoded || (_a._decoded = decode(cast(map)._encoded));
|
|
3245
|
+
}
|
|
3246
|
+
function originalPositionFor(map, needle) {
|
|
3247
|
+
let { line, column, bias } = needle;
|
|
3248
|
+
line--;
|
|
3249
|
+
if (line < 0) throw new Error(LINE_GTR_ZERO);
|
|
3250
|
+
if (column < 0) throw new Error(COL_GTR_EQ_ZERO);
|
|
3251
|
+
const decoded = decodedMappings(map);
|
|
3252
|
+
if (line >= decoded.length) return OMapping(null, null, null, null);
|
|
3253
|
+
const segments = decoded[line];
|
|
3254
|
+
const index = traceSegmentInternal(
|
|
3255
|
+
segments,
|
|
3256
|
+
cast(map)._decodedMemo,
|
|
3257
|
+
line,
|
|
3258
|
+
column,
|
|
3259
|
+
bias || GREATEST_LOWER_BOUND
|
|
3260
|
+
);
|
|
3261
|
+
if (index === -1) return OMapping(null, null, null, null);
|
|
3262
|
+
const segment = segments[index];
|
|
3263
|
+
if (segment.length === 1) return OMapping(null, null, null, null);
|
|
3264
|
+
const { names, resolvedSources } = map;
|
|
3265
|
+
return OMapping(
|
|
3266
|
+
resolvedSources[segment[SOURCES_INDEX]],
|
|
3267
|
+
segment[SOURCE_LINE] + 1,
|
|
3268
|
+
segment[SOURCE_COLUMN],
|
|
3269
|
+
segment.length === 5 ? names[segment[NAMES_INDEX]] : null
|
|
3270
|
+
);
|
|
3271
|
+
}
|
|
3272
|
+
function OMapping(source, line, column, name) {
|
|
3273
|
+
return { source, line, column, name };
|
|
3274
|
+
}
|
|
3275
|
+
function traceSegmentInternal(segments, memo, line, column, bias) {
|
|
3276
|
+
let index = memoizedBinarySearch(segments, column, memo, line);
|
|
3277
|
+
if (found) {
|
|
3278
|
+
index = (bias === LEAST_UPPER_BOUND ? upperBound : lowerBound)(segments, column, index);
|
|
3279
|
+
} else if (bias === LEAST_UPPER_BOUND) index++;
|
|
3280
|
+
if (index === -1 || index === segments.length) return -1;
|
|
3281
|
+
return index;
|
|
3282
|
+
}
|
|
3283
|
+
|
|
3284
|
+
// src/dev/stack-trace.ts
|
|
3285
|
+
var internalModules = /* @__PURE__ */ new Set([
|
|
3286
|
+
"__kody_local_entry.js",
|
|
3287
|
+
"kody:runtime",
|
|
3288
|
+
".__kody_runtime__.js",
|
|
3289
|
+
"runtime.js",
|
|
3290
|
+
"entry.js"
|
|
3291
|
+
]);
|
|
3292
|
+
var framePattern = /^(\s*at\s+)(?:(.*?)\s+\()?([^()\s]+?):(\d+):(\d+)\)?\s*$/;
|
|
3293
|
+
function createStackMapper(input) {
|
|
3294
|
+
let map = null;
|
|
3295
|
+
if (input.sourceMap) {
|
|
3296
|
+
try {
|
|
3297
|
+
map = new TraceMap(input.sourceMap);
|
|
3298
|
+
} catch {
|
|
3299
|
+
map = null;
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
return (stack) => {
|
|
3303
|
+
const out = [];
|
|
3304
|
+
for (const line of stack.split("\n")) {
|
|
3305
|
+
if (!/^\s*at\s/.test(line)) {
|
|
3306
|
+
out.push(line);
|
|
3307
|
+
continue;
|
|
3308
|
+
}
|
|
3309
|
+
const match = framePattern.exec(line);
|
|
3310
|
+
if (!match) continue;
|
|
3311
|
+
const [, lead, fn, file, lineText, colText] = match;
|
|
3312
|
+
const module = file.replace(/^file:\/\/\//, "").replace(/^\//, "");
|
|
3313
|
+
if (internalModules.has(module)) continue;
|
|
3314
|
+
const fnPart = fn ? `${fn} (` : "";
|
|
3315
|
+
const close = fn ? ")" : "";
|
|
3316
|
+
if (map && module === input.userModule) {
|
|
3317
|
+
const original = originalPositionFor(map, {
|
|
3318
|
+
line: Number(lineText),
|
|
3319
|
+
column: Number(colText) - 1
|
|
3320
|
+
});
|
|
3321
|
+
if (original.source && original.line !== null) {
|
|
3322
|
+
const name = fn ?? original.name;
|
|
3323
|
+
out.push(
|
|
3324
|
+
`${lead}${name ? `${name} (` : ""}${original.source}:${original.line}:${(original.column ?? 0) + 1}${name ? ")" : ""}`
|
|
3325
|
+
);
|
|
3326
|
+
continue;
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
out.push(`${lead}${fnPart}${file}:${lineText}:${colText}${close}`);
|
|
3330
|
+
}
|
|
3331
|
+
return out.join("\n");
|
|
3332
|
+
};
|
|
3333
|
+
}
|
|
3334
|
+
|
|
3335
|
+
// src/dev/workerd-process.ts
|
|
3336
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
3337
|
+
import { createInterface } from "node:readline";
|
|
3338
|
+
var workerdStartTimeoutMs = 3e4;
|
|
3339
|
+
async function startWorkerd(input) {
|
|
3340
|
+
const child = spawn3(input.workerdPath, ["serve", input.configPath, "--control-fd=3"], {
|
|
3341
|
+
cwd: input.cwd,
|
|
3342
|
+
env: input.env,
|
|
3343
|
+
stdio: ["ignore", "pipe", "pipe", "pipe"]
|
|
3344
|
+
});
|
|
3345
|
+
if (child.pid !== void 0) input.onSpawn?.(child.pid);
|
|
3346
|
+
let startupStderr = "";
|
|
3347
|
+
let listening = false;
|
|
3348
|
+
forEachLine2(child.stdout, input.onStdoutLine);
|
|
3349
|
+
forEachLine2(child.stderr, (line) => {
|
|
3350
|
+
if (!listening) startupStderr += `${line}
|
|
3351
|
+
`;
|
|
3352
|
+
input.onStderrLine(line);
|
|
3353
|
+
});
|
|
3354
|
+
const exited = new Promise((resolve8) => {
|
|
3355
|
+
child.once("exit", (code, signal) => resolve8(`workerd exit ${signal ?? code}`));
|
|
3356
|
+
child.once("error", (error) => resolve8(`workerd error: ${error.message}`));
|
|
3357
|
+
});
|
|
3358
|
+
const port = await waitForListen(child, exited, () => {
|
|
3359
|
+
const trimmed = redact(startupStderr.trim());
|
|
3360
|
+
return trimmed ? `
|
|
3361
|
+
${trimmed}` : "";
|
|
3362
|
+
});
|
|
3363
|
+
listening = true;
|
|
3364
|
+
return {
|
|
3365
|
+
pid: child.pid ?? -1,
|
|
3366
|
+
port,
|
|
3367
|
+
exited,
|
|
3368
|
+
kill: async () => {
|
|
3369
|
+
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
3370
|
+
child.kill("SIGKILL");
|
|
3371
|
+
await Promise.race([exited, new Promise((resolve8) => setTimeout(resolve8, 1e3))]);
|
|
3372
|
+
}
|
|
3373
|
+
};
|
|
3374
|
+
}
|
|
3375
|
+
function forEachLine2(stream, onLine) {
|
|
3376
|
+
if (!stream) return;
|
|
3377
|
+
const lines = createInterface({ input: stream, crlfDelay: Infinity });
|
|
3378
|
+
lines.on("line", (line) => {
|
|
3379
|
+
if (line.length > 0) onLine(line);
|
|
3380
|
+
});
|
|
3381
|
+
}
|
|
3382
|
+
function waitForListen(child, exited, stderr) {
|
|
3383
|
+
return new Promise((resolve8, reject) => {
|
|
3384
|
+
const control = child.stdio[3];
|
|
3385
|
+
let buffered = "";
|
|
3386
|
+
let settled = false;
|
|
3387
|
+
const settle = (fn) => {
|
|
3388
|
+
if (settled) return;
|
|
3389
|
+
settled = true;
|
|
3390
|
+
clearTimeout(timer);
|
|
3391
|
+
fn();
|
|
3392
|
+
};
|
|
3393
|
+
const timer = setTimeout(() => {
|
|
3394
|
+
settle(() => {
|
|
3395
|
+
child.kill("SIGKILL");
|
|
3396
|
+
reject(
|
|
3397
|
+
new Error(`workerd did not start within ${workerdStartTimeoutMs / 1e3}s.${stderr()}`)
|
|
3398
|
+
);
|
|
3399
|
+
});
|
|
3400
|
+
}, workerdStartTimeoutMs);
|
|
3401
|
+
control?.on("data", (chunk) => {
|
|
3402
|
+
buffered += chunk.toString("utf8");
|
|
3403
|
+
const lines = buffered.split("\n");
|
|
3404
|
+
buffered = lines.pop() ?? "";
|
|
3405
|
+
for (const line of lines) {
|
|
3406
|
+
const port = parseListenPort(line);
|
|
3407
|
+
if (port !== null) settle(() => resolve8(port));
|
|
3408
|
+
}
|
|
3409
|
+
});
|
|
3410
|
+
void exited.then((detail) => {
|
|
3411
|
+
settle(() => reject(new Error(`workerd failed to start (${detail})${stderr()}`)));
|
|
3412
|
+
});
|
|
3413
|
+
});
|
|
3414
|
+
}
|
|
3415
|
+
function parseListenPort(line) {
|
|
3416
|
+
if (!line.trim()) return null;
|
|
3417
|
+
try {
|
|
3418
|
+
const event = JSON.parse(line);
|
|
3419
|
+
return event.event === "listen" && event.socket === "http" && typeof event.port === "number" ? event.port : null;
|
|
3420
|
+
} catch {
|
|
3421
|
+
return null;
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
function cleanWorkerdStderrLine(line) {
|
|
3425
|
+
const trimmed = line.trim();
|
|
3426
|
+
if (!trimmed) return null;
|
|
3427
|
+
if (/^stack:(\s+\S+@[0-9a-fx]+)+\s*$/i.test(trimmed)) return null;
|
|
3428
|
+
const prefixed = /^workerd\/\S+:\d+: (?:error|warning|info|debug): (.*)$/.exec(trimmed);
|
|
3429
|
+
const body = prefixed ? prefixed[1] : trimmed;
|
|
3430
|
+
return body.replace(/\S+\.(?:c\+\+|h):\d+: failed: /g, "").replace(/(?:remote\.)?jsg\.(\w*Error)(?=:)/g, "$1");
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3433
|
+
// src/package-app-dev.ts
|
|
3434
|
+
var defaultDebounceMs = 30;
|
|
3435
|
+
var prebuildSettleMs = 150;
|
|
3436
|
+
var failedPrebuildAbsorbMs = 1500;
|
|
3437
|
+
var rebuildGateMs = 1e4;
|
|
3438
|
+
var drainTimeoutMs = 1e4;
|
|
3439
|
+
async function startPackageAppDev(options) {
|
|
3440
|
+
if (!options.manifest.app?.entry) {
|
|
3441
|
+
throw new Error(
|
|
3442
|
+
'This package has no kody.app.entry. Add `"kody": { "app": { "entry": "./app/router.ts" } }` or use `kody dev job|webhook|emit|check`.'
|
|
3443
|
+
);
|
|
3444
|
+
}
|
|
3445
|
+
assertTokenSafeApiUrl(options.apiUrl);
|
|
3446
|
+
const host = options.host ?? "127.0.0.1";
|
|
3447
|
+
const appBasePath = normalizeBasePath(options.appBasePath ?? "");
|
|
3448
|
+
const root = options.manifest.root;
|
|
3449
|
+
const runSecret = randomBytes(32).toString("hex");
|
|
3450
|
+
const emit = (event) => options.onEvent?.(event);
|
|
3451
|
+
const liveReload = options.liveReload ?? options.watch !== false;
|
|
3452
|
+
const errorPageTail = liveReload ? `<script>globalThis.__kodyDevErrorPage = true</script>${liveReloadScriptTag}` : "";
|
|
3453
|
+
void sweepStaleDevDirs().catch(() => {
|
|
3454
|
+
});
|
|
3455
|
+
const workDir = await mkdtemp(join7(tmpdir2(), devWorkDirPrefix));
|
|
3456
|
+
const reaper = await startProcessReaper({ workDir });
|
|
3457
|
+
const workerdPath = options.workerdPath ?? await ensureWorkerdBinary({
|
|
3458
|
+
onDownload: (url) => options.onStatus?.(`Downloading workerd from ${url}\u2026`)
|
|
3459
|
+
});
|
|
3460
|
+
const client = {
|
|
3461
|
+
apiUrl: options.apiUrl,
|
|
3462
|
+
token: options.token,
|
|
3463
|
+
fetchFn: options.fetchFn,
|
|
3464
|
+
...options.org ? { org: options.org } : {}
|
|
3465
|
+
};
|
|
3466
|
+
const generations = /* @__PURE__ */ new Set();
|
|
3467
|
+
let active;
|
|
3468
|
+
let buildFailure = null;
|
|
3469
|
+
let generationCounter = 0;
|
|
3470
|
+
let requestCounter = 0;
|
|
3471
|
+
let closed = false;
|
|
3472
|
+
let buildLoop = null;
|
|
3473
|
+
let rebuildAgain = false;
|
|
3474
|
+
const changedFiles = /* @__PURE__ */ new Set();
|
|
3475
|
+
let debouncePending = false;
|
|
3476
|
+
let debounceFlush = null;
|
|
3477
|
+
let resolveDebounceFlush = null;
|
|
3478
|
+
let debounceTimer;
|
|
3479
|
+
const liveReloadHub = createLiveReloadHub({
|
|
3480
|
+
state: () => ({
|
|
3481
|
+
buildId: active?.id ?? null,
|
|
3482
|
+
error: buildFailure ? buildErrorView(buildFailure) : null
|
|
3483
|
+
})
|
|
3484
|
+
});
|
|
3485
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3486
|
+
let lastRebuild = null;
|
|
3487
|
+
let lastRuntimeError = null;
|
|
3488
|
+
let buildFailureAt = startedAt;
|
|
3489
|
+
let markStarted = () => {
|
|
3490
|
+
};
|
|
3491
|
+
const started = new Promise((resolve8) => {
|
|
3492
|
+
markStarted = resolve8;
|
|
3493
|
+
});
|
|
3494
|
+
const killAllSync = () => {
|
|
3495
|
+
for (const generation of generations) {
|
|
3496
|
+
try {
|
|
3497
|
+
process.kill(generation.workerd.pid, "SIGKILL");
|
|
3498
|
+
} catch {
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
try {
|
|
3502
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
3503
|
+
} catch {
|
|
3504
|
+
}
|
|
3505
|
+
};
|
|
3506
|
+
process.once("exit", killAllSync);
|
|
3507
|
+
const proxy = createServer((req, res) => {
|
|
3508
|
+
void handleRequest(req, res);
|
|
3509
|
+
});
|
|
3510
|
+
try {
|
|
3511
|
+
await listenDevServer(proxy, {
|
|
3512
|
+
host,
|
|
3513
|
+
port: options.port ?? options.defaultPort ?? 0,
|
|
3514
|
+
explicit: options.port !== void 0
|
|
3515
|
+
});
|
|
3516
|
+
} catch (error) {
|
|
3517
|
+
process.off("exit", killAllSync);
|
|
3518
|
+
reaper.close();
|
|
3519
|
+
await rm3(workDir, { recursive: true, force: true });
|
|
3520
|
+
throw error;
|
|
3521
|
+
}
|
|
3522
|
+
const address = proxy.address();
|
|
3523
|
+
const hostedUrl = `http://${host}:${address.port}`;
|
|
3524
|
+
try {
|
|
3525
|
+
await options.onListening?.({
|
|
3526
|
+
port: address.port,
|
|
3527
|
+
hostedUrl,
|
|
3528
|
+
appBasePath
|
|
3529
|
+
});
|
|
3530
|
+
} catch (error) {
|
|
3531
|
+
process.off("exit", killAllSync);
|
|
3532
|
+
reaper.close();
|
|
3533
|
+
proxy.closeAllConnections();
|
|
3534
|
+
await new Promise((resolve8) => {
|
|
3535
|
+
proxy.close(() => resolve8());
|
|
3536
|
+
});
|
|
3537
|
+
await rm3(workDir, { recursive: true, force: true });
|
|
3538
|
+
throw error;
|
|
3539
|
+
}
|
|
3540
|
+
const contextFor = (client2) => buildLocalPackageContext({
|
|
3541
|
+
kodyId: options.manifest.kodyId,
|
|
3542
|
+
packageId: `local:${options.manifest.kodyId}`,
|
|
3543
|
+
hostedUrl,
|
|
3544
|
+
appBasePath,
|
|
3545
|
+
clientModuleUrl: client2 ? clientModuleUrlFor(client2) : null
|
|
3546
|
+
});
|
|
3547
|
+
const clientModuleUrlFor = (client2) => `${hostedUrl}${appBasePath}/_assets/${client2.name}`;
|
|
3548
|
+
const mockFetchGateway = await resolveMockFetchGateway({
|
|
3549
|
+
target: options.target,
|
|
3550
|
+
packageRoot: root,
|
|
3551
|
+
passthroughHosts: options.passthroughHosts,
|
|
3552
|
+
allowPrivateNetwork: options.allowPrivateNetwork,
|
|
3553
|
+
onStatus: options.onStatus
|
|
3554
|
+
});
|
|
3555
|
+
const bridge = await startLocalRuntimeBridge({
|
|
3556
|
+
runSecret,
|
|
3557
|
+
forwardCall: async (call, signal) => {
|
|
3558
|
+
const mocked = tryHandleMockGatewayFetchCall({
|
|
3559
|
+
gateway: mockFetchGateway,
|
|
3560
|
+
path: call.path,
|
|
3561
|
+
args: call.args,
|
|
3562
|
+
signal
|
|
3563
|
+
});
|
|
3564
|
+
if (mocked) return await mocked;
|
|
3565
|
+
return await callCapabilityProxy({
|
|
3566
|
+
...client,
|
|
3567
|
+
...call,
|
|
3568
|
+
signal
|
|
3569
|
+
});
|
|
3570
|
+
},
|
|
3571
|
+
forwardGatewayFetchStream: async ({ request, response, signal }) => {
|
|
3572
|
+
await forwardLocalGatewayFetchStream({
|
|
3573
|
+
request,
|
|
3574
|
+
response,
|
|
3575
|
+
signal,
|
|
3576
|
+
client,
|
|
3577
|
+
...mockFetchGateway ? { mockFetchGateway } : {}
|
|
3578
|
+
});
|
|
3579
|
+
}
|
|
3580
|
+
});
|
|
3581
|
+
const app = options.manifest.app;
|
|
3582
|
+
const entryPath = resolvePackagePath(options.manifest, app.entry);
|
|
3583
|
+
const bundler = createDevBundler({ entryPath, root });
|
|
3584
|
+
const clientBundler = app.client ? createDevClientBundler({
|
|
3585
|
+
entryPath: resolvePackagePath(options.manifest, app.client),
|
|
3586
|
+
root,
|
|
3587
|
+
externals: app.clientExternals
|
|
3588
|
+
}) : null;
|
|
3589
|
+
const assetsDir = app.assets ? resolve6(root, app.assets) : null;
|
|
3590
|
+
const prebuild = options.manifest.dev?.prebuild;
|
|
3591
|
+
let prebuildRunning = false;
|
|
3592
|
+
let prebuildSettledAt = 0;
|
|
3593
|
+
let ignoreWatcherUntil = 0;
|
|
3594
|
+
let prebuildAbort;
|
|
3595
|
+
const workerdEnv = { ...process.env, [runSecretEnvVar]: runSecret };
|
|
3596
|
+
delete workerdEnv[apiTokenEnvVar];
|
|
3597
|
+
function requestBuild(paths) {
|
|
3598
|
+
if (closed) return Promise.resolve();
|
|
3599
|
+
for (const path of paths) changedFiles.add(path);
|
|
3600
|
+
if (buildLoop) {
|
|
3601
|
+
rebuildAgain = true;
|
|
3602
|
+
return buildLoop;
|
|
3603
|
+
}
|
|
3604
|
+
buildLoop = (async () => {
|
|
3605
|
+
do {
|
|
3606
|
+
rebuildAgain = false;
|
|
3607
|
+
const files = [...changedFiles];
|
|
3608
|
+
changedFiles.clear();
|
|
3609
|
+
await buildOnce(files);
|
|
3610
|
+
} while (rebuildAgain && !closed);
|
|
3611
|
+
})().finally(() => {
|
|
3612
|
+
buildLoop = null;
|
|
3613
|
+
});
|
|
3614
|
+
return buildLoop;
|
|
3615
|
+
}
|
|
3616
|
+
async function buildOnce(files) {
|
|
3617
|
+
const started2 = performance.now();
|
|
3618
|
+
emit({ type: "rebuild_start", files: files.map((file) => relative4(root, file) || file) });
|
|
3619
|
+
const assetsChanged = assetsDir !== null && files.some((file) => file === assetsDir || file.startsWith(`${assetsDir}${sep3}`));
|
|
3620
|
+
try {
|
|
3621
|
+
if (prebuild) {
|
|
3622
|
+
await runPrebuildStep(prebuild);
|
|
3623
|
+
}
|
|
3624
|
+
const [bundle, client2] = await Promise.all([
|
|
3625
|
+
bundler.build(),
|
|
3626
|
+
clientBundler ? clientBundler.build() : Promise.resolve(null)
|
|
3627
|
+
]);
|
|
3628
|
+
const midBuildEdits = await watcher?.track(
|
|
3629
|
+
[...bundle.inputs, ...client2?.inputs ?? [], options.manifest.packageJsonPath],
|
|
3630
|
+
{ ignoreChanges: files }
|
|
3631
|
+
) ?? [];
|
|
3632
|
+
if (midBuildEdits.length > 0) {
|
|
3633
|
+
for (const path of midBuildEdits) changedFiles.add(path);
|
|
3634
|
+
rebuildAgain = true;
|
|
3635
|
+
}
|
|
3636
|
+
if (active && !buildFailure && bundle.code === active.code && client2?.name === active.client?.name) {
|
|
3637
|
+
lastRebuild = { ms: elapsed(started2), at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3638
|
+
emit({
|
|
3639
|
+
type: "rebuild_ok",
|
|
3640
|
+
ms: lastRebuild.ms,
|
|
3641
|
+
buildId: active.id,
|
|
3642
|
+
restarted: false
|
|
3643
|
+
});
|
|
3644
|
+
if (assetsChanged || prebuild) {
|
|
3645
|
+
liveReloadHub.broadcast({ event: "reload", data: { buildId: active.id } });
|
|
3646
|
+
}
|
|
3647
|
+
return;
|
|
3648
|
+
}
|
|
3649
|
+
const generation = await startGeneration(bundle, client2);
|
|
3650
|
+
if (closed) {
|
|
3651
|
+
await stopGeneration(generation);
|
|
3652
|
+
return;
|
|
3653
|
+
}
|
|
3654
|
+
const previous = active;
|
|
3655
|
+
active = generation;
|
|
3656
|
+
buildFailure = null;
|
|
3657
|
+
if (previous) retireGeneration(previous);
|
|
3658
|
+
lastRuntimeError = null;
|
|
3659
|
+
lastRebuild = { ms: elapsed(started2), at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3660
|
+
emit({ type: "rebuild_ok", ms: lastRebuild.ms, buildId: generation.id, restarted: true });
|
|
3661
|
+
liveReloadHub.broadcast({ event: "reload", data: { buildId: generation.id } });
|
|
3662
|
+
} catch (error) {
|
|
3663
|
+
buildFailure = toBuildFailure(error);
|
|
3664
|
+
buildFailureAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3665
|
+
const [first] = buildFailure.diagnostics;
|
|
3666
|
+
emit({
|
|
3667
|
+
type: "build_error",
|
|
3668
|
+
ms: elapsed(started2),
|
|
3669
|
+
message: buildFailure.message,
|
|
3670
|
+
file: first?.file ?? null,
|
|
3671
|
+
line: first?.line ?? null,
|
|
3672
|
+
col: first?.col ?? null,
|
|
3673
|
+
diagnostics: buildFailure.diagnostics
|
|
3674
|
+
});
|
|
3675
|
+
liveReloadHub.broadcast({ event: "build_error", data: buildErrorView(buildFailure) });
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
let clearWatcherPending = null;
|
|
3679
|
+
async function runPrebuildStep(command) {
|
|
3680
|
+
prebuildRunning = true;
|
|
3681
|
+
prebuildAbort = new AbortController();
|
|
3682
|
+
let failed = false;
|
|
3683
|
+
try {
|
|
3684
|
+
await runPrebuild({
|
|
3685
|
+
command,
|
|
3686
|
+
cwd: root,
|
|
3687
|
+
signal: prebuildAbort.signal,
|
|
3688
|
+
onSpawn: (pid) => reaper.trackGroup(pid),
|
|
3689
|
+
onExit: (pid) => reaper.untrackGroup(pid),
|
|
3690
|
+
onLine: (line, stream) => emit({
|
|
3691
|
+
type: "log",
|
|
3692
|
+
source: "prebuild",
|
|
3693
|
+
level: stream === "stderr" ? "warn" : "log",
|
|
3694
|
+
message: redact(line),
|
|
3695
|
+
request: null
|
|
3696
|
+
})
|
|
3697
|
+
});
|
|
3698
|
+
} catch (error) {
|
|
3699
|
+
failed = true;
|
|
3700
|
+
throw error;
|
|
3701
|
+
} finally {
|
|
3702
|
+
prebuildAbort = void 0;
|
|
3703
|
+
prebuildRunning = false;
|
|
3704
|
+
prebuildSettledAt = performance.now();
|
|
3705
|
+
await watcher?.rebaseline();
|
|
3706
|
+
if (failed) {
|
|
3707
|
+
changedFiles.clear();
|
|
3708
|
+
rebuildAgain = false;
|
|
3709
|
+
clearWatcherPending?.();
|
|
3710
|
+
ignoreWatcherUntil = performance.now() + failedPrebuildAbsorbMs;
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
async function startGeneration(bundle, client2) {
|
|
3715
|
+
const id = ++generationCounter;
|
|
3716
|
+
const dir = join7(workDir, `g${id}`);
|
|
3717
|
+
await writeWorkerFiles({
|
|
3718
|
+
workDir: dir,
|
|
3719
|
+
code: bundle.code,
|
|
3720
|
+
packageContext: contextFor(client2),
|
|
3721
|
+
packageRoot: root,
|
|
3722
|
+
mockStorage: options.target === "mock",
|
|
3723
|
+
bridgePort: bridge.port,
|
|
3724
|
+
allowPrivateNetwork: options.allowPrivateNetwork,
|
|
3725
|
+
apiUrl: options.apiUrl,
|
|
3726
|
+
token: options.token,
|
|
3727
|
+
org: options.org,
|
|
3728
|
+
fetchFn: options.fetchFn,
|
|
3729
|
+
depResolve: options.depResolve,
|
|
3730
|
+
mockFetchGateway,
|
|
3731
|
+
onStatus: options.onStatus
|
|
3732
|
+
});
|
|
3733
|
+
let trackedPid;
|
|
3734
|
+
let workerd;
|
|
3735
|
+
try {
|
|
3736
|
+
workerd = await startWorkerd({
|
|
3737
|
+
workerdPath,
|
|
3738
|
+
configPath: join7(dir, "config.capnp"),
|
|
3739
|
+
cwd: dir,
|
|
3740
|
+
env: workerdEnv,
|
|
3741
|
+
onSpawn: (pid) => {
|
|
3742
|
+
trackedPid = pid;
|
|
3743
|
+
reaper.track(pid);
|
|
3744
|
+
},
|
|
3745
|
+
onStdoutLine: (line) => emit(parseWorkerdStdoutLine(line)),
|
|
3746
|
+
onStderrLine: (line) => {
|
|
3747
|
+
const cleaned = cleanWorkerdStderrLine(line);
|
|
3748
|
+
if (cleaned === null) return;
|
|
3749
|
+
emit({
|
|
3750
|
+
type: "log",
|
|
3751
|
+
source: "workerd",
|
|
3752
|
+
level: "error",
|
|
3753
|
+
message: redact(cleaned),
|
|
3754
|
+
request: null
|
|
3755
|
+
});
|
|
3756
|
+
}
|
|
3757
|
+
});
|
|
3758
|
+
} catch (error) {
|
|
3759
|
+
if (trackedPid !== void 0) reaper.untrack(trackedPid);
|
|
3760
|
+
await rm3(dir, { recursive: true, force: true }).catch(() => {
|
|
3761
|
+
});
|
|
3762
|
+
throw error;
|
|
3763
|
+
}
|
|
3764
|
+
const generation = {
|
|
3765
|
+
id,
|
|
3766
|
+
dir,
|
|
3767
|
+
workerd,
|
|
3768
|
+
code: bundle.code,
|
|
3769
|
+
client: client2,
|
|
3770
|
+
mapStack: createStackMapper({
|
|
3771
|
+
sourceMap: bundle.sourceMap,
|
|
3772
|
+
userModule: localWorkerModuleNames.user
|
|
3773
|
+
}),
|
|
3774
|
+
inflight: 0,
|
|
3775
|
+
retired: false,
|
|
3776
|
+
stopped: false
|
|
3777
|
+
};
|
|
3778
|
+
generations.add(generation);
|
|
3779
|
+
void workerd.exited.then((detail) => {
|
|
3780
|
+
reaper.untrack(workerd.pid);
|
|
3781
|
+
if (closed || generation.retired || active !== generation) return;
|
|
3782
|
+
active = void 0;
|
|
3783
|
+
buildFailure = {
|
|
3784
|
+
message: `workerd stopped unexpectedly (${detail}). Save a file to restart it.`,
|
|
3785
|
+
diagnostics: []
|
|
3786
|
+
};
|
|
3787
|
+
options.onError?.(buildFailure.message);
|
|
3788
|
+
});
|
|
3789
|
+
return generation;
|
|
3790
|
+
}
|
|
3791
|
+
function retireGeneration(generation) {
|
|
3792
|
+
generation.retired = true;
|
|
3793
|
+
if (generation.inflight === 0) {
|
|
3794
|
+
void stopGeneration(generation);
|
|
3795
|
+
return;
|
|
3796
|
+
}
|
|
3797
|
+
setTimeout(() => void stopGeneration(generation), drainTimeoutMs).unref();
|
|
3798
|
+
}
|
|
3799
|
+
async function stopGeneration(generation) {
|
|
3800
|
+
if (generation.stopped) return;
|
|
3801
|
+
generation.stopped = true;
|
|
3802
|
+
generation.retired = true;
|
|
3803
|
+
await generation.workerd.kill();
|
|
3804
|
+
reaper.untrack(generation.workerd.pid);
|
|
3805
|
+
generations.delete(generation);
|
|
3806
|
+
await rm3(generation.dir, { recursive: true, force: true });
|
|
3807
|
+
}
|
|
3808
|
+
async function waitForBuild() {
|
|
3809
|
+
const flush = debounceFlush;
|
|
3810
|
+
if (flush) await flush;
|
|
3811
|
+
const pending = buildLoop ?? (active || buildFailure ? null : started);
|
|
3812
|
+
if (!pending) return;
|
|
3813
|
+
await Promise.race([
|
|
3814
|
+
pending.catch(() => {
|
|
3815
|
+
}),
|
|
3816
|
+
new Promise((resolve8) => setTimeout(resolve8, rebuildGateMs).unref())
|
|
3817
|
+
]);
|
|
3818
|
+
}
|
|
3819
|
+
function status() {
|
|
3820
|
+
const [first] = buildFailure?.diagnostics ?? [];
|
|
3821
|
+
return {
|
|
3822
|
+
ok: buildFailure === null && active !== void 0,
|
|
3823
|
+
state: buildLoop || debouncePending ? "building" : buildFailure ? "build_error" : active ? "ready" : "starting",
|
|
3824
|
+
buildId: active?.id ?? null,
|
|
3825
|
+
url: `${hostedUrl}${appBasePath || "/"}`,
|
|
3826
|
+
port: address.port,
|
|
3827
|
+
basePath: appBasePath,
|
|
3828
|
+
target: options.target,
|
|
3829
|
+
package: {
|
|
3830
|
+
name: options.manifest.name,
|
|
3831
|
+
kodyId: options.manifest.kodyId,
|
|
3832
|
+
root
|
|
3833
|
+
},
|
|
3834
|
+
pid: process.pid,
|
|
3835
|
+
startedAt,
|
|
3836
|
+
lastRebuild,
|
|
3837
|
+
lastError: buildFailure ? {
|
|
3838
|
+
kind: "build",
|
|
3839
|
+
message: buildFailure.message,
|
|
3840
|
+
file: first?.file ?? null,
|
|
3841
|
+
line: first?.line ?? null,
|
|
3842
|
+
col: first?.col ?? null,
|
|
3843
|
+
at: buildFailureAt
|
|
3844
|
+
} : lastRuntimeError
|
|
3845
|
+
};
|
|
3846
|
+
}
|
|
3847
|
+
function handleDevRoute(req, res) {
|
|
3848
|
+
const pathname = stripMount(new URL(req.url ?? "/", hostedUrl).pathname, appBasePath);
|
|
3849
|
+
if (pathname === devStatusPath) {
|
|
3850
|
+
res.writeHead(200, {
|
|
3851
|
+
"content-type": "application/json; charset=utf-8",
|
|
3852
|
+
"cache-control": "no-store"
|
|
3853
|
+
});
|
|
3854
|
+
res.end(`${JSON.stringify(status(), null, 2)}
|
|
3855
|
+
`);
|
|
3856
|
+
return true;
|
|
3857
|
+
}
|
|
3858
|
+
if (!liveReload) return false;
|
|
3859
|
+
if (pathname === liveReloadEventsPath) {
|
|
3860
|
+
liveReloadHub.handleEvents(req, res);
|
|
3861
|
+
return true;
|
|
3862
|
+
}
|
|
3863
|
+
if (pathname === liveReloadClientPath) {
|
|
3864
|
+
res.writeHead(200, {
|
|
3865
|
+
"content-type": "text/javascript; charset=utf-8",
|
|
3866
|
+
"cache-control": "no-store"
|
|
3867
|
+
});
|
|
3868
|
+
res.end(liveReloadClientSource);
|
|
3869
|
+
return true;
|
|
3870
|
+
}
|
|
3871
|
+
return false;
|
|
3872
|
+
}
|
|
3873
|
+
async function handleRequest(req, res) {
|
|
3874
|
+
try {
|
|
3875
|
+
if (handleDevRoute(req, res)) return;
|
|
3876
|
+
await waitForBuild();
|
|
3877
|
+
const assetPath = parseDevAssetPath(
|
|
3878
|
+
stripMount(new URL(req.url ?? "/", hostedUrl).pathname, appBasePath)
|
|
3879
|
+
);
|
|
3880
|
+
if (assetPath !== null) {
|
|
3881
|
+
const client2 = active?.client ?? null;
|
|
3882
|
+
await serveDevAsset({
|
|
3883
|
+
req,
|
|
3884
|
+
res,
|
|
3885
|
+
relativePath: assetPath,
|
|
3886
|
+
assetsDir,
|
|
3887
|
+
client: client2,
|
|
3888
|
+
clientModuleUrl: client2 ? clientModuleUrlFor(client2) : null,
|
|
3889
|
+
appBasePath
|
|
3890
|
+
});
|
|
3891
|
+
return;
|
|
3892
|
+
}
|
|
3893
|
+
if (buildFailure) {
|
|
3894
|
+
sendDevError(req, res, 500, buildErrorView(buildFailure), {
|
|
3895
|
+
htmlTail: errorPageTail
|
|
3896
|
+
});
|
|
3897
|
+
return;
|
|
3898
|
+
}
|
|
3899
|
+
const generation = active;
|
|
3900
|
+
if (!generation) {
|
|
3901
|
+
res.writeHead(503, { "content-type": "text/plain; charset=utf-8" });
|
|
3902
|
+
res.end(closed ? "kody dev is shutting down.\n" : "kody dev is still starting.\n");
|
|
3903
|
+
return;
|
|
3904
|
+
}
|
|
3905
|
+
generation.inflight += 1;
|
|
3906
|
+
try {
|
|
3907
|
+
await forwardRequest(generation, req, res);
|
|
3908
|
+
} finally {
|
|
3909
|
+
generation.inflight -= 1;
|
|
3910
|
+
if (generation.retired && generation.inflight === 0) {
|
|
3911
|
+
void stopGeneration(generation);
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
} catch (error) {
|
|
3915
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3916
|
+
options.onError?.(`Dev proxy error: ${message}`);
|
|
3917
|
+
if (!res.headersSent) {
|
|
3918
|
+
res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
|
|
3919
|
+
res.end(`Dev proxy error: ${message}
|
|
3920
|
+
`);
|
|
3921
|
+
} else if (!res.writableEnded) {
|
|
3922
|
+
res.destroy();
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
function forwardRequest(generation, req, res) {
|
|
3927
|
+
const incomingUrl = new URL(req.url ?? "/", hostedUrl);
|
|
3928
|
+
const restPath = stripMount(incomingUrl.pathname, appBasePath);
|
|
3929
|
+
const forwardUrl = new URL(
|
|
3930
|
+
restPath + incomingUrl.search,
|
|
3931
|
+
`http://127.0.0.1:${generation.workerd.port}`
|
|
3932
|
+
);
|
|
3933
|
+
const tag = {
|
|
3934
|
+
id: String(++requestCounter),
|
|
3935
|
+
method: req.method ?? "GET",
|
|
3936
|
+
path: restPath + incomingUrl.search
|
|
3937
|
+
};
|
|
3938
|
+
const headers = {
|
|
3939
|
+
...req.headers,
|
|
3940
|
+
host: `127.0.0.1:${generation.workerd.port}`,
|
|
3941
|
+
[runSecretHeader]: runSecret,
|
|
3942
|
+
[devRequestIdHeader]: tag.id
|
|
3943
|
+
};
|
|
3944
|
+
return new Promise((resolve8, reject) => {
|
|
3945
|
+
const upstream = httpRequest(
|
|
3946
|
+
forwardUrl,
|
|
3947
|
+
{ method: req.method, headers },
|
|
3948
|
+
(upstreamRes) => {
|
|
3949
|
+
if (upstreamRes.headers[devErrorHeader]) {
|
|
3950
|
+
void readStream(upstreamRes).then((raw) => {
|
|
3951
|
+
respondWithRuntimeError(generation, req, res, tag, raw);
|
|
3952
|
+
resolve8();
|
|
3953
|
+
}).catch(reject);
|
|
3954
|
+
return;
|
|
3955
|
+
}
|
|
3956
|
+
const status2 = upstreamRes.statusCode ?? 502;
|
|
3957
|
+
if (status2 >= 500) {
|
|
3958
|
+
emit({
|
|
3959
|
+
type: "log",
|
|
3960
|
+
source: "app",
|
|
3961
|
+
level: "warn",
|
|
3962
|
+
message: `responded HTTP ${status2}`,
|
|
3963
|
+
request: tag
|
|
3964
|
+
});
|
|
3965
|
+
}
|
|
3966
|
+
if (liveReload && shouldInjectLiveReload({
|
|
3967
|
+
method: req.method,
|
|
3968
|
+
accept: req.headers.accept,
|
|
3969
|
+
status: status2,
|
|
3970
|
+
headers: upstreamRes.headers
|
|
3971
|
+
})) {
|
|
3972
|
+
void readStream(upstreamRes).then((html) => {
|
|
3973
|
+
const body = Buffer.from(injectLiveReloadScript(html), "utf8");
|
|
3974
|
+
const headers2 = { ...upstreamRes.headers };
|
|
3975
|
+
delete headers2["transfer-encoding"];
|
|
3976
|
+
headers2["content-length"] = String(body.length);
|
|
3977
|
+
res.writeHead(status2, headers2);
|
|
3978
|
+
res.end(body);
|
|
3979
|
+
resolve8();
|
|
3980
|
+
}).catch(reject);
|
|
3981
|
+
return;
|
|
3982
|
+
}
|
|
3983
|
+
res.writeHead(status2, upstreamRes.headers);
|
|
3984
|
+
upstreamRes.pipe(res);
|
|
3985
|
+
upstreamRes.on("end", () => resolve8());
|
|
3986
|
+
upstreamRes.on("error", reject);
|
|
3987
|
+
}
|
|
3988
|
+
);
|
|
3989
|
+
upstream.on("error", reject);
|
|
3990
|
+
res.on("close", () => {
|
|
3991
|
+
if (!res.writableFinished) upstream.destroy();
|
|
3992
|
+
resolve8();
|
|
3993
|
+
});
|
|
3994
|
+
req.pipe(upstream);
|
|
3995
|
+
});
|
|
3996
|
+
}
|
|
3997
|
+
function respondWithRuntimeError(generation, req, res, tag, raw) {
|
|
3998
|
+
let message = "Uncaught error";
|
|
3999
|
+
let stack = "";
|
|
4000
|
+
try {
|
|
4001
|
+
const parsed = JSON.parse(raw);
|
|
4002
|
+
message = `${parsed.name ?? "Error"}: ${parsed.message ?? ""}`;
|
|
4003
|
+
stack = parsed.stack ?? message;
|
|
4004
|
+
} catch {
|
|
4005
|
+
stack = raw;
|
|
4006
|
+
}
|
|
4007
|
+
const mapped = redact(generation.mapStack(stack));
|
|
4008
|
+
const fix = runtimeErrorFix(message);
|
|
4009
|
+
lastRuntimeError = {
|
|
4010
|
+
kind: "runtime",
|
|
4011
|
+
message: redact(message),
|
|
4012
|
+
stack: mapped,
|
|
4013
|
+
request: tag,
|
|
4014
|
+
...fix ? { fix } : {},
|
|
4015
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4016
|
+
};
|
|
4017
|
+
emit({
|
|
4018
|
+
type: "runtime_error",
|
|
4019
|
+
message: redact(message),
|
|
4020
|
+
stack: mapped,
|
|
4021
|
+
request: tag,
|
|
4022
|
+
...fix ? { fix } : {}
|
|
4023
|
+
});
|
|
4024
|
+
const view = {
|
|
4025
|
+
kind: "runtime",
|
|
4026
|
+
title: redact(message),
|
|
4027
|
+
detail: fix ? `${mapped}
|
|
4028
|
+
|
|
4029
|
+
fix: ${fix}` : mapped,
|
|
4030
|
+
request: `${tag.method} ${tag.path}`
|
|
4031
|
+
};
|
|
4032
|
+
liveReloadHub.broadcast({ event: "runtime_error", data: view });
|
|
4033
|
+
sendDevError(req, res, 500, view, { htmlTail: errorPageTail });
|
|
4034
|
+
}
|
|
4035
|
+
const watcher = options.watch === false ? null : (() => {
|
|
4036
|
+
const pending = /* @__PURE__ */ new Set();
|
|
4037
|
+
clearWatcherPending = () => {
|
|
4038
|
+
pending.clear();
|
|
4039
|
+
clearTimeout(debounceTimer);
|
|
4040
|
+
debouncePending = false;
|
|
4041
|
+
const resolve8 = resolveDebounceFlush;
|
|
4042
|
+
resolveDebounceFlush = null;
|
|
4043
|
+
debounceFlush = null;
|
|
4044
|
+
resolve8?.();
|
|
4045
|
+
};
|
|
4046
|
+
return createDevWatcher({
|
|
4047
|
+
root,
|
|
4048
|
+
ignore: options.manifest.dev?.ignore,
|
|
4049
|
+
onChange: (path) => {
|
|
4050
|
+
if (closed) return;
|
|
4051
|
+
const now = performance.now();
|
|
4052
|
+
if (prebuildRunning || now - prebuildSettledAt < prebuildSettleMs) {
|
|
4053
|
+
return;
|
|
4054
|
+
}
|
|
4055
|
+
if (now < ignoreWatcherUntil) {
|
|
4056
|
+
return false;
|
|
4057
|
+
}
|
|
4058
|
+
if (buildLoop) {
|
|
4059
|
+
changedFiles.add(path);
|
|
4060
|
+
rebuildAgain = true;
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
pending.add(path);
|
|
4064
|
+
debouncePending = true;
|
|
4065
|
+
if (!debounceFlush) {
|
|
4066
|
+
debounceFlush = new Promise((resolve8) => {
|
|
4067
|
+
resolveDebounceFlush = resolve8;
|
|
4068
|
+
});
|
|
4069
|
+
}
|
|
4070
|
+
clearTimeout(debounceTimer);
|
|
4071
|
+
debounceTimer = setTimeout(() => {
|
|
4072
|
+
const paths = [...pending];
|
|
4073
|
+
pending.clear();
|
|
4074
|
+
debouncePending = false;
|
|
4075
|
+
const build = requestBuild(paths);
|
|
4076
|
+
const resolve8 = resolveDebounceFlush;
|
|
4077
|
+
resolveDebounceFlush = null;
|
|
4078
|
+
debounceFlush = null;
|
|
4079
|
+
resolve8?.();
|
|
4080
|
+
void build;
|
|
4081
|
+
}, options.debounceMs ?? defaultDebounceMs);
|
|
4082
|
+
}
|
|
4083
|
+
});
|
|
4084
|
+
})();
|
|
4085
|
+
const close = async () => {
|
|
4086
|
+
if (closed) return;
|
|
4087
|
+
closed = true;
|
|
4088
|
+
process.off("exit", killAllSync);
|
|
4089
|
+
clearTimeout(debounceTimer);
|
|
4090
|
+
debouncePending = false;
|
|
4091
|
+
const resolve8 = resolveDebounceFlush;
|
|
4092
|
+
resolveDebounceFlush = null;
|
|
4093
|
+
debounceFlush = null;
|
|
4094
|
+
resolve8?.();
|
|
4095
|
+
prebuildAbort?.abort();
|
|
4096
|
+
await watcher?.close();
|
|
4097
|
+
await buildLoop?.catch(() => {
|
|
4098
|
+
});
|
|
4099
|
+
await bundler.dispose();
|
|
4100
|
+
await clientBundler?.dispose();
|
|
4101
|
+
await Promise.all([...generations].map((generation) => stopGeneration(generation)));
|
|
4102
|
+
reaper.close();
|
|
4103
|
+
await bridge.close();
|
|
4104
|
+
liveReloadHub.close();
|
|
4105
|
+
proxy.closeAllConnections();
|
|
4106
|
+
await new Promise((resolve9) => {
|
|
4107
|
+
proxy.close(() => resolve9());
|
|
4108
|
+
});
|
|
4109
|
+
await rm3(workDir, { recursive: true, force: true });
|
|
4110
|
+
};
|
|
4111
|
+
try {
|
|
4112
|
+
await Promise.all([requestBuild([]), watcher?.ready]);
|
|
4113
|
+
} catch (error) {
|
|
4114
|
+
await close();
|
|
4115
|
+
throw error;
|
|
4116
|
+
} finally {
|
|
4117
|
+
markStarted();
|
|
4118
|
+
}
|
|
4119
|
+
if (options.watch === false && buildFailure) {
|
|
4120
|
+
const message = buildFailure.message;
|
|
4121
|
+
await close();
|
|
4122
|
+
throw new Error(message);
|
|
4123
|
+
}
|
|
4124
|
+
if (active) options.onStatus?.(`App ready at ${hostedUrl}${appBasePath || "/"}`);
|
|
4125
|
+
const fetchPath = async (path, init2 = {}) => {
|
|
4126
|
+
const url = new URL(joinUrlPath(appBasePath, path), hostedUrl);
|
|
4127
|
+
const response = await fetch(url, {
|
|
4128
|
+
method: init2.method ?? "GET",
|
|
4129
|
+
headers: init2.headers,
|
|
4130
|
+
body: init2.body
|
|
4131
|
+
});
|
|
4132
|
+
const body = await response.text();
|
|
4133
|
+
const headers = {};
|
|
4134
|
+
response.headers.forEach((value, key) => {
|
|
4135
|
+
headers[key] = value;
|
|
4136
|
+
});
|
|
4137
|
+
return { status: response.status, headers, body };
|
|
4138
|
+
};
|
|
4139
|
+
return {
|
|
4140
|
+
url: hostedUrl,
|
|
4141
|
+
port: address.port,
|
|
4142
|
+
hostedUrl,
|
|
4143
|
+
appBasePath,
|
|
4144
|
+
get packageContext() {
|
|
4145
|
+
return contextFor(active?.client ?? null);
|
|
4146
|
+
},
|
|
4147
|
+
fetchPath,
|
|
4148
|
+
status,
|
|
4149
|
+
close
|
|
4150
|
+
};
|
|
4151
|
+
}
|
|
4152
|
+
function elapsed(started) {
|
|
4153
|
+
return Math.round(performance.now() - started);
|
|
4154
|
+
}
|
|
4155
|
+
function toBuildFailure(error) {
|
|
4156
|
+
if (error instanceof BundleError) {
|
|
4157
|
+
return { message: error.message, diagnostics: error.diagnostics };
|
|
4158
|
+
}
|
|
4159
|
+
return {
|
|
4160
|
+
message: redact(error instanceof Error ? error.message : String(error)),
|
|
4161
|
+
diagnostics: []
|
|
4162
|
+
};
|
|
4163
|
+
}
|
|
4164
|
+
function buildErrorView(failure) {
|
|
4165
|
+
const [first] = failure.diagnostics;
|
|
4166
|
+
return {
|
|
4167
|
+
kind: "build",
|
|
4168
|
+
title: first ? `${first.file ?? "bundle"}${first.line !== null ? `:${first.line}:${first.col}` : ""} ${first.message}` : failure.message.split("\n")[0] ?? "Build failed",
|
|
4169
|
+
detail: failure.diagnostics.length > 0 ? failure.diagnostics.map(formatBundleDiagnostic).join("\n\n") : failure.message
|
|
4170
|
+
};
|
|
4171
|
+
}
|
|
4172
|
+
async function readStream(stream) {
|
|
4173
|
+
const chunks = [];
|
|
4174
|
+
for await (const chunk of stream) {
|
|
4175
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
4176
|
+
}
|
|
4177
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
4178
|
+
}
|
|
4179
|
+
async function writeWorkerFiles(input) {
|
|
4180
|
+
await mkdir2(input.workDir, { recursive: true });
|
|
4181
|
+
const packageGraph = await resolveLocalPackageGraph({
|
|
4182
|
+
code: input.code,
|
|
4183
|
+
apiUrl: input.apiUrl,
|
|
4184
|
+
token: input.token,
|
|
4185
|
+
...input.org ? { org: input.org } : {},
|
|
4186
|
+
fetchFn: input.fetchFn,
|
|
4187
|
+
...input.depResolve ? { depResolve: input.depResolve } : {},
|
|
4188
|
+
...input.packageRoot ? { packageRoot: input.packageRoot } : {},
|
|
4189
|
+
onStatus: input.onStatus
|
|
4190
|
+
});
|
|
4191
|
+
const files = { entry: "entry.js", user: "main.js", runtime: "runtime.js" };
|
|
4192
|
+
let packageModules = await writePackageModuleFiles(
|
|
4193
|
+
input.workDir,
|
|
4194
|
+
packageGraph,
|
|
4195
|
+
writeFile3,
|
|
4196
|
+
join7
|
|
4197
|
+
);
|
|
4198
|
+
let gatewayFetchShimModules = packageGraph.modules.map((module) => module.name).filter((name) => name === localExecuteGatewayFetchShimModuleName);
|
|
4199
|
+
if (input.mockFetchGateway) {
|
|
4200
|
+
const installed = await installMockFetchShimModules({
|
|
4201
|
+
workDir: input.workDir,
|
|
4202
|
+
packageModules
|
|
4203
|
+
});
|
|
4204
|
+
packageModules = installed.packageModules;
|
|
4205
|
+
gatewayFetchShimModules = installed.sideEffectModules;
|
|
4206
|
+
}
|
|
4207
|
+
await writeFile3(
|
|
4208
|
+
join7(input.workDir, files.entry),
|
|
4209
|
+
createPackageAppEntrySource({ sideEffectModules: gatewayFetchShimModules })
|
|
4210
|
+
);
|
|
4211
|
+
await writeFile3(join7(input.workDir, files.user), input.code);
|
|
4212
|
+
await writeFile3(
|
|
4213
|
+
join7(input.workDir, files.runtime),
|
|
4214
|
+
createPackageLocalRuntimeModuleSource({
|
|
4215
|
+
packageContext: input.packageContext,
|
|
4216
|
+
mockStorage: input.mockStorage
|
|
4217
|
+
})
|
|
4218
|
+
);
|
|
4219
|
+
await writeFile3(
|
|
4220
|
+
join7(input.workDir, localWorkerModuleNames.runtimeFlatAlias),
|
|
4221
|
+
createRuntimeFlatAliasSource()
|
|
4222
|
+
);
|
|
4223
|
+
await writeFile3(
|
|
4224
|
+
join7(input.workDir, "config.capnp"),
|
|
4225
|
+
createWorkerdConfig({
|
|
4226
|
+
bridgePort: input.bridgePort,
|
|
4227
|
+
files,
|
|
4228
|
+
packageModules,
|
|
4229
|
+
allowPrivateNetwork: input.allowPrivateNetwork
|
|
4230
|
+
})
|
|
4231
|
+
);
|
|
4232
|
+
}
|
|
4233
|
+
function normalizeBasePath(value) {
|
|
4234
|
+
const trimmed = value.trim();
|
|
4235
|
+
if (!trimmed || trimmed === "/") return "";
|
|
4236
|
+
const withSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
4237
|
+
return withSlash.replace(/\/+$/, "");
|
|
4238
|
+
}
|
|
4239
|
+
function stripMount(pathname, appBasePath) {
|
|
4240
|
+
if (!appBasePath) return pathname || "/";
|
|
4241
|
+
if (pathname === appBasePath) return "/";
|
|
4242
|
+
if (pathname.startsWith(`${appBasePath}/`)) {
|
|
4243
|
+
return pathname.slice(appBasePath.length) || "/";
|
|
4244
|
+
}
|
|
4245
|
+
return pathname || "/";
|
|
4246
|
+
}
|
|
4247
|
+
function joinUrlPath(base, path) {
|
|
4248
|
+
const rest = path.startsWith("/") ? path : `/${path}`;
|
|
4249
|
+
if (!base) return rest;
|
|
4250
|
+
return `${base}${rest === "/" ? "" : rest}` || base;
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4253
|
+
// src/dev-command.ts
|
|
4254
|
+
async function resolveDevAuth(input) {
|
|
4255
|
+
if (input.target !== "mock") {
|
|
4256
|
+
return {
|
|
4257
|
+
token: input.token,
|
|
4258
|
+
apiUrl: input.apiUrl,
|
|
4259
|
+
...input.org ? { org: input.org } : {},
|
|
4260
|
+
...input.passthroughHosts?.length ? { passthroughHosts: input.passthroughHosts } : {}
|
|
4261
|
+
};
|
|
4262
|
+
}
|
|
4263
|
+
const ensured = await ensureMockTarget({
|
|
4264
|
+
sessionId: input.sessionId,
|
|
4265
|
+
resetStorage: input.resetStorage,
|
|
4266
|
+
packageId: input.packageId,
|
|
4267
|
+
orgSlug: input.orgSlug ?? input.org
|
|
4268
|
+
});
|
|
4269
|
+
let mockClosed = false;
|
|
4270
|
+
return {
|
|
4271
|
+
token: ensured.token,
|
|
4272
|
+
apiUrl: ensured.apiUrl,
|
|
4273
|
+
...input.org ? { org: input.org } : {},
|
|
4274
|
+
...input.depResolve ? { depResolve: input.depResolve } : {},
|
|
4275
|
+
...input.passthroughHosts?.length ? { passthroughHosts: input.passthroughHosts } : {},
|
|
4276
|
+
mockServer: ensured.server,
|
|
4277
|
+
closeMock: ensured.started ? async () => {
|
|
4278
|
+
if (mockClosed) return;
|
|
4279
|
+
mockClosed = true;
|
|
4280
|
+
await ensured.server.close();
|
|
4281
|
+
} : void 0
|
|
4282
|
+
};
|
|
4283
|
+
}
|
|
4284
|
+
async function runDevServe(input) {
|
|
4285
|
+
const manifest = await loadPackageManifest(input.cwd);
|
|
4286
|
+
if (input.port === void 0) {
|
|
4287
|
+
const running = await findRunningDevServer({ root: manifest.root, target: input.target });
|
|
4288
|
+
if (running) {
|
|
4289
|
+
const { registration } = running;
|
|
4290
|
+
throw new StructuredCliError({
|
|
4291
|
+
code: "dev_already_running",
|
|
4292
|
+
target: input.target,
|
|
4293
|
+
message: `kody dev is already running for ${manifest.name} at ${registration.origin}${registration.basePath || "/"} (pid ${registration.pid}).`,
|
|
4294
|
+
fix: `Reuse it (\`kody dev fetch <path>\` attaches automatically), stop it with \`kill ${registration.pid}\`, or pass --port to start a second server.`
|
|
4295
|
+
});
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
const onEvent = input.json ? (event) => writeJsonEvent(input.io, event) : (event) => writeDevEvent(input.io, event);
|
|
4299
|
+
let registered = false;
|
|
4300
|
+
let server;
|
|
4301
|
+
try {
|
|
4302
|
+
server = await startPackageAppDev({
|
|
4303
|
+
manifest,
|
|
4304
|
+
token: input.auth.token,
|
|
4305
|
+
apiUrl: input.auth.apiUrl,
|
|
4306
|
+
target: input.target,
|
|
4307
|
+
port: input.port,
|
|
4308
|
+
defaultPort: defaultDevPort(manifest.kodyId),
|
|
4309
|
+
appBasePath: input.appBasePath,
|
|
4310
|
+
...input.auth.org ? { org: input.auth.org } : {},
|
|
4311
|
+
...input.auth.depResolve ? { depResolve: input.auth.depResolve } : {},
|
|
4312
|
+
...input.passthroughHosts?.length ? { passthroughHosts: input.passthroughHosts } : input.auth.passthroughHosts?.length ? { passthroughHosts: input.auth.passthroughHosts } : {},
|
|
4313
|
+
allowPrivateNetwork: input.allowPrivateNetwork,
|
|
4314
|
+
liveReload: input.liveReload ?? true,
|
|
4315
|
+
onStatus: (message) => input.io.stderr(`${message}
|
|
4316
|
+
`),
|
|
4317
|
+
onError: (message) => input.io.stderr(`${message}
|
|
4318
|
+
`),
|
|
4319
|
+
onEvent,
|
|
4320
|
+
// Register as soon as the port is bound so a second `kody dev`
|
|
4321
|
+
// during the first build hits `dev_already_running` instead of
|
|
4322
|
+
// stealing the next free port and overwriting the registry.
|
|
4323
|
+
// Explicit `--port` starts a second server on purpose — leave the
|
|
4324
|
+
// primary attach registry alone so fetch keeps talking to it.
|
|
4325
|
+
onListening: async ({ port, hostedUrl, appBasePath }) => {
|
|
4326
|
+
if (input.port !== void 0) return;
|
|
4327
|
+
await writeDevRegistration({
|
|
4328
|
+
pid: process.pid,
|
|
4329
|
+
origin: hostedUrl,
|
|
4330
|
+
port,
|
|
4331
|
+
basePath: appBasePath,
|
|
4332
|
+
target: input.target,
|
|
4333
|
+
root: manifest.root
|
|
4334
|
+
});
|
|
4335
|
+
registered = true;
|
|
4336
|
+
}
|
|
4337
|
+
});
|
|
4338
|
+
} catch (error) {
|
|
4339
|
+
if (registered) {
|
|
4340
|
+
await removeDevRegistration(manifest.root, process.pid, input.target).catch(() => {
|
|
4341
|
+
});
|
|
4342
|
+
}
|
|
4343
|
+
await input.auth.closeMock?.();
|
|
4344
|
+
throw error;
|
|
4345
|
+
}
|
|
4346
|
+
const shutdown = async () => {
|
|
4347
|
+
await server.close();
|
|
4348
|
+
await removeDevRegistration(manifest.root, process.pid, input.target).catch(() => {
|
|
4349
|
+
});
|
|
4350
|
+
if (input.readyFile) await removeOwnedReadyFile(input.readyFile, process.pid);
|
|
4351
|
+
await input.auth.closeMock?.();
|
|
4352
|
+
};
|
|
4353
|
+
try {
|
|
4354
|
+
const status = server.status();
|
|
4355
|
+
const statusUrl = `${server.hostedUrl}${devStatusPath}`;
|
|
4356
|
+
const ready = {
|
|
4357
|
+
url: status.url,
|
|
4358
|
+
port: server.port,
|
|
4359
|
+
basePath: server.appBasePath,
|
|
4360
|
+
pid: process.pid,
|
|
4361
|
+
statusUrl
|
|
4362
|
+
};
|
|
4363
|
+
if (input.readyFile) {
|
|
4364
|
+
await mkdir3(dirname3(input.readyFile), { recursive: true });
|
|
4365
|
+
await writeFile4(input.readyFile, `${JSON.stringify(ready, null, 2)}
|
|
4366
|
+
`);
|
|
4367
|
+
}
|
|
4368
|
+
if (input.json) {
|
|
4369
|
+
input.io.stdout(
|
|
4370
|
+
`${JSON.stringify({
|
|
4371
|
+
type: "ready",
|
|
4372
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4373
|
+
...ready,
|
|
4374
|
+
target: input.target,
|
|
4375
|
+
package: status.package,
|
|
4376
|
+
buildId: status.buildId,
|
|
4377
|
+
state: status.state
|
|
4378
|
+
})}
|
|
4379
|
+
`
|
|
4380
|
+
);
|
|
4381
|
+
} else {
|
|
4382
|
+
input.io.stdout(
|
|
4383
|
+
`kody dev listening on ${status.url}
|
|
4384
|
+
target: ${input.target}
|
|
4385
|
+
package: ${manifest.name} (${manifest.kodyId})
|
|
4386
|
+
status: ${statusUrl}
|
|
4387
|
+
Agents: kody dev fetch /path | job <name> | webhook <name> --body file.json | emit <event> --body file.json | check
|
|
4388
|
+
`
|
|
4389
|
+
);
|
|
4390
|
+
}
|
|
4391
|
+
} catch (error) {
|
|
4392
|
+
await shutdown().catch(() => {
|
|
4393
|
+
});
|
|
4394
|
+
throw error;
|
|
4395
|
+
}
|
|
4396
|
+
if (input.serveOnce) {
|
|
4397
|
+
await shutdown();
|
|
4398
|
+
return 0;
|
|
4399
|
+
}
|
|
4400
|
+
await new Promise((resolveWait) => {
|
|
4401
|
+
const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
4402
|
+
const stop = async () => {
|
|
4403
|
+
for (const signal of signals) process.off(signal, onSig);
|
|
4404
|
+
await shutdown();
|
|
4405
|
+
resolveWait();
|
|
4406
|
+
};
|
|
4407
|
+
const onSig = () => {
|
|
4408
|
+
void stop();
|
|
4409
|
+
};
|
|
4410
|
+
for (const signal of signals) process.on(signal, onSig);
|
|
4411
|
+
});
|
|
4412
|
+
return 0;
|
|
4413
|
+
}
|
|
4414
|
+
async function tryAttachedDevFetch(input) {
|
|
4415
|
+
let root;
|
|
4416
|
+
try {
|
|
4417
|
+
root = (await loadPackageManifest(input.cwd)).root;
|
|
4418
|
+
} catch {
|
|
4419
|
+
return null;
|
|
4420
|
+
}
|
|
4421
|
+
const running = await findRunningDevServer({ root, target: input.target });
|
|
4422
|
+
if (!running) return null;
|
|
4423
|
+
const { registration } = running;
|
|
4424
|
+
const rest = input.path.startsWith("/") ? input.path : `/${input.path}`;
|
|
4425
|
+
const url = new URL(
|
|
4426
|
+
`${registration.basePath}${rest === "/" && registration.basePath ? "" : rest}` || "/",
|
|
4427
|
+
registration.origin
|
|
4428
|
+
);
|
|
4429
|
+
input.io.stderr(`Attached to running kody dev (pid ${registration.pid}) at ${registration.origin}
|
|
4430
|
+
`);
|
|
4431
|
+
const response = await fetch(url, {
|
|
4432
|
+
method: input.method ?? "GET",
|
|
4433
|
+
headers: input.body ? { "content-type": "application/json" } : void 0,
|
|
4434
|
+
body: input.body
|
|
4435
|
+
});
|
|
4436
|
+
const body = await response.text();
|
|
4437
|
+
const headers = {};
|
|
4438
|
+
response.headers.forEach((value, key) => {
|
|
4439
|
+
headers[key] = value;
|
|
4440
|
+
});
|
|
4441
|
+
const result = { status: response.status, headers, body };
|
|
4442
|
+
if (input.json) {
|
|
4443
|
+
input.io.stdout(`${JSON.stringify(result, null, 2)}
|
|
4444
|
+
`);
|
|
4445
|
+
} else {
|
|
4446
|
+
input.io.stdout(`HTTP ${result.status}
|
|
4447
|
+
${result.body}
|
|
4448
|
+
`);
|
|
4449
|
+
}
|
|
4450
|
+
return result.status >= 400 ? 1 : 0;
|
|
4451
|
+
}
|
|
4452
|
+
async function runDevFetch(input) {
|
|
4453
|
+
let server;
|
|
4454
|
+
try {
|
|
4455
|
+
const manifest = await loadPackageManifest(input.cwd);
|
|
4456
|
+
server = await startPackageAppDev({
|
|
4457
|
+
manifest,
|
|
4458
|
+
token: input.auth.token,
|
|
4459
|
+
apiUrl: input.auth.apiUrl,
|
|
4460
|
+
target: input.target,
|
|
4461
|
+
port: input.port,
|
|
4462
|
+
appBasePath: input.appBasePath,
|
|
4463
|
+
...input.auth.org ? { org: input.auth.org } : {},
|
|
4464
|
+
...input.auth.depResolve ? { depResolve: input.auth.depResolve } : {},
|
|
4465
|
+
...input.auth.passthroughHosts?.length ? { passthroughHosts: input.auth.passthroughHosts } : {},
|
|
4466
|
+
allowPrivateNetwork: input.allowPrivateNetwork,
|
|
4467
|
+
watch: false,
|
|
4468
|
+
onStatus: (message) => input.io.stderr(`${message}
|
|
4469
|
+
`),
|
|
4470
|
+
onError: (message) => input.io.stderr(`${message}
|
|
4471
|
+
`),
|
|
4472
|
+
onEvent: (event) => {
|
|
4473
|
+
if (event.type === "log" || event.type === "runtime_error") {
|
|
4474
|
+
input.io.stderr(`${formatDevEvent(event).text}
|
|
4475
|
+
`);
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
});
|
|
4479
|
+
} catch (error) {
|
|
4480
|
+
await input.auth.closeMock?.();
|
|
4481
|
+
if (input.json) {
|
|
4482
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4483
|
+
input.io.stdout(
|
|
4484
|
+
`${JSON.stringify({ type: "build_error", message, ts: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
|
|
4485
|
+
`
|
|
4486
|
+
);
|
|
4487
|
+
}
|
|
4488
|
+
throw error;
|
|
4489
|
+
}
|
|
4490
|
+
try {
|
|
4491
|
+
const result = await server.fetchPath(input.path, {
|
|
4492
|
+
method: input.method,
|
|
4493
|
+
body: input.body,
|
|
4494
|
+
headers: input.body ? { "content-type": "application/json" } : void 0
|
|
4495
|
+
});
|
|
4496
|
+
if (input.json) {
|
|
4497
|
+
input.io.stdout(`${JSON.stringify(result, null, 2)}
|
|
4498
|
+
`);
|
|
4499
|
+
} else {
|
|
4500
|
+
input.io.stdout(`HTTP ${result.status}
|
|
4501
|
+
${result.body}
|
|
4502
|
+
`);
|
|
4503
|
+
}
|
|
4504
|
+
return result.status >= 400 ? 1 : 0;
|
|
4505
|
+
} finally {
|
|
4506
|
+
await server.close();
|
|
4507
|
+
await input.auth.closeMock?.();
|
|
4508
|
+
}
|
|
4509
|
+
}
|
|
4510
|
+
async function runDevJob(input) {
|
|
4511
|
+
const manifest = await loadPackageManifest(input.cwd);
|
|
4512
|
+
const job = manifest.jobs[input.jobName];
|
|
4513
|
+
if (!job) {
|
|
4514
|
+
throw new StructuredCliError({
|
|
4515
|
+
code: "dev_surface_missing",
|
|
4516
|
+
message: `No job named ${JSON.stringify(input.jobName)}.`,
|
|
4517
|
+
where: manifest.packageJsonPath,
|
|
4518
|
+
fix: `Known jobs: ${Object.keys(manifest.jobs).join(", ") || "(none)"}.`,
|
|
4519
|
+
target: input.target
|
|
4520
|
+
});
|
|
4521
|
+
}
|
|
4522
|
+
const modulePath = resolvePackagePath(manifest, job.entry);
|
|
4523
|
+
if (input.dryRun) {
|
|
4524
|
+
return printDevDryRun({
|
|
4525
|
+
target: input.target,
|
|
4526
|
+
surface: `job:${input.jobName}`,
|
|
4527
|
+
modulePath,
|
|
4528
|
+
params: void 0,
|
|
4529
|
+
extra: {
|
|
4530
|
+
schedule: job.schedule,
|
|
4531
|
+
scheduleRaw: job.scheduleRaw,
|
|
4532
|
+
timezone: job.timezone ?? null,
|
|
4533
|
+
enabled: job.enabled ?? null
|
|
4534
|
+
},
|
|
4535
|
+
json: input.json,
|
|
4536
|
+
io: input.io,
|
|
4537
|
+
closeMock: input.auth.closeMock
|
|
4538
|
+
});
|
|
4539
|
+
}
|
|
4540
|
+
return await runPackageModuleOnce({
|
|
4541
|
+
manifest,
|
|
4542
|
+
modulePath,
|
|
4543
|
+
params: void 0,
|
|
4544
|
+
target: input.target,
|
|
4545
|
+
auth: input.auth,
|
|
4546
|
+
json: input.json,
|
|
4547
|
+
allowPrivateNetwork: input.allowPrivateNetwork,
|
|
4548
|
+
io: input.io,
|
|
4549
|
+
surface: `job:${input.jobName}`
|
|
4550
|
+
});
|
|
4551
|
+
}
|
|
4552
|
+
async function runDevWebhook(input) {
|
|
4553
|
+
const manifest = await loadPackageManifest(input.cwd);
|
|
4554
|
+
const webhook = manifest.webhooks[input.webhookName];
|
|
4555
|
+
if (!webhook) {
|
|
4556
|
+
throw new StructuredCliError({
|
|
4557
|
+
code: "dev_surface_missing",
|
|
4558
|
+
message: `No webhook named ${JSON.stringify(input.webhookName)}.`,
|
|
4559
|
+
where: manifest.packageJsonPath,
|
|
4560
|
+
fix: `Known webhooks: ${Object.keys(manifest.webhooks).join(", ") || "(none)"}.`,
|
|
4561
|
+
target: input.target
|
|
4562
|
+
});
|
|
4563
|
+
}
|
|
4564
|
+
const rawBody = await readFile4(resolve7(input.cwd ?? process.cwd(), input.bodyPath), "utf8");
|
|
4565
|
+
let jsonBody = null;
|
|
4566
|
+
try {
|
|
4567
|
+
jsonBody = JSON.parse(rawBody);
|
|
4568
|
+
} catch {
|
|
4569
|
+
jsonBody = null;
|
|
4570
|
+
}
|
|
4571
|
+
const params = webhook.inputMode === "params" ? jsonBody ?? {} : {
|
|
4572
|
+
webhook: {
|
|
4573
|
+
packageKodyId: manifest.kodyId,
|
|
4574
|
+
name: input.webhookName,
|
|
4575
|
+
receivedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4576
|
+
},
|
|
4577
|
+
request: {
|
|
4578
|
+
method: "POST",
|
|
4579
|
+
contentType: "application/json",
|
|
4580
|
+
headers: { "content-type": "application/json" },
|
|
4581
|
+
body: rawBody,
|
|
4582
|
+
json: jsonBody
|
|
4583
|
+
},
|
|
4584
|
+
synthetic: true
|
|
4585
|
+
};
|
|
4586
|
+
const modulePath = resolveExportModulePath(manifest, webhook.export);
|
|
4587
|
+
if (input.dryRun) {
|
|
4588
|
+
return printDevDryRun({
|
|
4589
|
+
target: input.target,
|
|
4590
|
+
surface: `webhook:${input.webhookName}`,
|
|
4591
|
+
modulePath,
|
|
4592
|
+
params,
|
|
4593
|
+
extra: { export: webhook.export, bodyPath: input.bodyPath },
|
|
4594
|
+
json: input.json,
|
|
4595
|
+
io: input.io,
|
|
4596
|
+
closeMock: input.auth.closeMock
|
|
4597
|
+
});
|
|
4598
|
+
}
|
|
4599
|
+
return await runPackageModuleOnce({
|
|
4600
|
+
manifest,
|
|
4601
|
+
modulePath,
|
|
4602
|
+
params,
|
|
4603
|
+
target: input.target,
|
|
4604
|
+
auth: input.auth,
|
|
4605
|
+
json: input.json,
|
|
4606
|
+
allowPrivateNetwork: input.allowPrivateNetwork,
|
|
4607
|
+
io: input.io,
|
|
4608
|
+
surface: `webhook:${input.webhookName}`
|
|
4609
|
+
});
|
|
4610
|
+
}
|
|
4611
|
+
async function runDevEmit(input) {
|
|
4612
|
+
const manifest = await loadPackageManifest(input.cwd);
|
|
4613
|
+
const subscription = manifest.subscriptions[input.event];
|
|
4614
|
+
if (!subscription) {
|
|
4615
|
+
if (input.target !== "mock") {
|
|
4616
|
+
throw new StructuredCliError({
|
|
4617
|
+
code: "dev_surface_missing",
|
|
4618
|
+
message: `No subscription for event ${JSON.stringify(input.event)}.`,
|
|
4619
|
+
where: manifest.packageJsonPath,
|
|
4620
|
+
fix: `Known subscriptions: ${Object.keys(manifest.subscriptions).join(", ") || "(none)"}. On --target mock you can still declare the handler first.`,
|
|
4621
|
+
target: input.target
|
|
4622
|
+
});
|
|
4623
|
+
}
|
|
4624
|
+
throw new StructuredCliError({
|
|
4625
|
+
code: "dev_surface_missing",
|
|
4626
|
+
message: `No subscription for event ${JSON.stringify(input.event)} in package.json#kody.subscriptions.`,
|
|
4627
|
+
where: manifest.packageJsonPath,
|
|
4628
|
+
fix: `Add a handler under kody.subscriptions[${JSON.stringify(input.event)}], then retry. Mock allows any event type once declared.`,
|
|
4629
|
+
target: input.target
|
|
4630
|
+
});
|
|
4631
|
+
}
|
|
4632
|
+
const rawBody = await readFile4(resolve7(input.cwd ?? process.cwd(), input.bodyPath), "utf8");
|
|
4633
|
+
let envelope;
|
|
4634
|
+
try {
|
|
4635
|
+
const parsed = JSON.parse(rawBody);
|
|
4636
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4637
|
+
throw new Error("body must be a JSON object");
|
|
4638
|
+
}
|
|
4639
|
+
envelope = { ...parsed };
|
|
4640
|
+
} catch (error) {
|
|
4641
|
+
throw new StructuredCliError({
|
|
4642
|
+
code: "package_manifest_invalid",
|
|
4643
|
+
message: `Could not parse event body ${input.bodyPath} as a JSON object.`,
|
|
4644
|
+
fix: "Pass --body event.json with the platform delivery envelope fields.",
|
|
4645
|
+
details: { cause: error instanceof Error ? error.message : String(error) },
|
|
4646
|
+
target: input.target
|
|
4647
|
+
});
|
|
4648
|
+
}
|
|
4649
|
+
if (typeof envelope.event !== "string") {
|
|
4650
|
+
envelope.event = input.event;
|
|
4651
|
+
}
|
|
4652
|
+
envelope.synthetic = true;
|
|
4653
|
+
if (envelope.idempotency_key === void 0) {
|
|
4654
|
+
envelope.idempotency_key = `synthetic:local:${Date.now()}`;
|
|
4655
|
+
}
|
|
4656
|
+
const modulePath = resolvePackagePath(manifest, subscription.handler);
|
|
4657
|
+
if (input.dryRun) {
|
|
4658
|
+
return printDevDryRun({
|
|
4659
|
+
target: input.target,
|
|
4660
|
+
surface: `subscription:${input.event}`,
|
|
4661
|
+
modulePath,
|
|
4662
|
+
params: envelope,
|
|
4663
|
+
extra: { handler: subscription.handler, bodyPath: input.bodyPath },
|
|
4664
|
+
json: input.json,
|
|
4665
|
+
io: input.io,
|
|
4666
|
+
closeMock: input.auth.closeMock
|
|
4667
|
+
});
|
|
4668
|
+
}
|
|
4669
|
+
return await runPackageModuleOnce({
|
|
4670
|
+
manifest,
|
|
4671
|
+
modulePath,
|
|
4672
|
+
params: envelope,
|
|
4673
|
+
target: input.target,
|
|
4674
|
+
auth: input.auth,
|
|
4675
|
+
json: input.json,
|
|
4676
|
+
allowPrivateNetwork: input.allowPrivateNetwork,
|
|
4677
|
+
io: input.io,
|
|
4678
|
+
surface: `subscription:${input.event}`
|
|
4679
|
+
});
|
|
4680
|
+
}
|
|
4681
|
+
async function runDevCheck(input) {
|
|
4682
|
+
const manifest = await loadPackageManifest(input.cwd);
|
|
4683
|
+
const checks = [];
|
|
4684
|
+
const credentialSignals = [];
|
|
4685
|
+
const push = (name, ok, detail) => {
|
|
4686
|
+
checks.push({ name, ok, detail });
|
|
4687
|
+
};
|
|
4688
|
+
const bundleAndScan = async (label, modulePath) => {
|
|
4689
|
+
try {
|
|
4690
|
+
const bundled = await bundleLocalUserModule(modulePath);
|
|
4691
|
+
push(label, true, label.startsWith("app.entry") ? modulePath : void 0);
|
|
4692
|
+
credentialSignals.push(...await detectCredentialedFetchInSource(bundled, label));
|
|
4693
|
+
} catch (error) {
|
|
4694
|
+
push(label, false, error instanceof Error ? error.message : String(error));
|
|
4695
|
+
}
|
|
4696
|
+
};
|
|
4697
|
+
push("package.json", true, manifest.name);
|
|
4698
|
+
push("kody.id", Boolean(manifest.kodyId), manifest.kodyId);
|
|
4699
|
+
try {
|
|
4700
|
+
await readFile4(resolve7(manifest.root, "README.md"));
|
|
4701
|
+
push("README.md", true);
|
|
4702
|
+
} catch {
|
|
4703
|
+
push("README.md", false, "Publish requires README.md");
|
|
4704
|
+
}
|
|
4705
|
+
try {
|
|
4706
|
+
await readFile4(resolve7(manifest.root, "AGENTS.md"));
|
|
4707
|
+
push("AGENTS.md", true);
|
|
4708
|
+
} catch {
|
|
4709
|
+
push("AGENTS.md", false, "Publish requires AGENTS.md");
|
|
4710
|
+
}
|
|
4711
|
+
if (manifest.app?.entry) {
|
|
4712
|
+
await bundleAndScan(
|
|
4713
|
+
"app.entry bundle",
|
|
4714
|
+
resolvePackagePath(manifest, manifest.app.entry)
|
|
4715
|
+
);
|
|
4716
|
+
} else {
|
|
4717
|
+
push("app.entry", true, "none (ok)");
|
|
4718
|
+
}
|
|
4719
|
+
for (const [name, job] of Object.entries(manifest.jobs)) {
|
|
4720
|
+
await bundleAndScan(`job:${name}`, resolvePackagePath(manifest, job.entry));
|
|
4721
|
+
}
|
|
4722
|
+
for (const [name, webhook] of Object.entries(manifest.webhooks)) {
|
|
4723
|
+
await bundleAndScan(
|
|
4724
|
+
`webhook:${name}`,
|
|
4725
|
+
resolveExportModulePath(manifest, webhook.export)
|
|
4726
|
+
);
|
|
4727
|
+
}
|
|
4728
|
+
for (const [topic, sub] of Object.entries(manifest.subscriptions)) {
|
|
4729
|
+
await bundleAndScan(
|
|
4730
|
+
`subscription:${topic}`,
|
|
4731
|
+
resolvePackagePath(manifest, sub.handler)
|
|
4732
|
+
);
|
|
4733
|
+
}
|
|
4734
|
+
for (const [key, rel] of Object.entries(manifest.exports)) {
|
|
4735
|
+
await bundleAndScan(`export:${key}`, resolvePackagePath(manifest, rel));
|
|
4736
|
+
}
|
|
4737
|
+
let hasMocksFile = false;
|
|
4738
|
+
try {
|
|
4739
|
+
await access(join8(manifest.root, packageMocksRelativePath));
|
|
4740
|
+
hasMocksFile = true;
|
|
4741
|
+
} catch {
|
|
4742
|
+
hasMocksFile = false;
|
|
4743
|
+
}
|
|
4744
|
+
const mocksDetail = mocksSuggestionDetail({
|
|
4745
|
+
signals: credentialSignals,
|
|
4746
|
+
hasMocksFile
|
|
4747
|
+
});
|
|
4748
|
+
if (mocksDetail) {
|
|
4749
|
+
push(packageMocksRelativePath, true, mocksDetail);
|
|
4750
|
+
}
|
|
4751
|
+
const failed = checks.filter((check) => !check.ok);
|
|
4752
|
+
if (input.json) {
|
|
4753
|
+
input.io.stdout(
|
|
4754
|
+
`${JSON.stringify(
|
|
4755
|
+
{
|
|
4756
|
+
target: input.target,
|
|
4757
|
+
checks,
|
|
4758
|
+
ok: failed.length === 0,
|
|
4759
|
+
...credentialSignals.length > 0 ? {
|
|
4760
|
+
credentialedFetch: {
|
|
4761
|
+
detected: true,
|
|
4762
|
+
hasMocksFile,
|
|
4763
|
+
signals: credentialSignals
|
|
4764
|
+
}
|
|
4765
|
+
} : {}
|
|
4766
|
+
},
|
|
4767
|
+
null,
|
|
4768
|
+
2
|
|
4769
|
+
)}
|
|
4770
|
+
`
|
|
4771
|
+
);
|
|
4772
|
+
} else {
|
|
4773
|
+
for (const check of checks) {
|
|
4774
|
+
const mark = check.ok ? "ok" : "FAIL";
|
|
4775
|
+
input.io.stdout(
|
|
4776
|
+
`${mark.padEnd(4)} ${check.name}${check.detail ? ` \u2014 ${check.detail}` : ""}
|
|
4777
|
+
`
|
|
4778
|
+
);
|
|
4779
|
+
}
|
|
4780
|
+
input.io.stdout(
|
|
4781
|
+
failed.length === 0 ? `kody dev check passed (${checks.length} checks, target ${input.target})
|
|
4782
|
+
` : `kody dev check failed: ${failed.length}/${checks.length}
|
|
4783
|
+
`
|
|
4784
|
+
);
|
|
4785
|
+
}
|
|
4786
|
+
return failed.length === 0 ? 0 : 1;
|
|
4787
|
+
}
|
|
4788
|
+
async function runPackageModuleOnce(input) {
|
|
4789
|
+
const packageContext = buildLocalPackageContext({
|
|
4790
|
+
kodyId: input.manifest.kodyId,
|
|
4791
|
+
hostedUrl: input.auth.apiUrl.startsWith("http") ? "http://127.0.0.1:0" : "http://127.0.0.1:0",
|
|
4792
|
+
appBasePath: ""
|
|
4793
|
+
});
|
|
4794
|
+
try {
|
|
4795
|
+
const result = await runLocalPackageModule({
|
|
4796
|
+
modulePath: input.modulePath,
|
|
4797
|
+
params: input.params,
|
|
4798
|
+
token: input.auth.token,
|
|
4799
|
+
apiUrl: input.auth.apiUrl,
|
|
4800
|
+
target: input.target,
|
|
4801
|
+
packageContext,
|
|
4802
|
+
packageRoot: input.manifest.root,
|
|
4803
|
+
...input.auth.org ? { org: input.auth.org } : {},
|
|
4804
|
+
...input.auth.depResolve ? { depResolve: input.auth.depResolve } : {},
|
|
4805
|
+
...input.auth.passthroughHosts?.length ? { passthroughHosts: input.auth.passthroughHosts } : {},
|
|
4806
|
+
allowPrivateNetwork: input.allowPrivateNetwork,
|
|
4807
|
+
onStatus: (message) => input.io.stderr(`${message}
|
|
4808
|
+
`)
|
|
4809
|
+
});
|
|
4810
|
+
input.io.stdout(formatToolResult(result, input.json === true));
|
|
4811
|
+
return result.isError ? 1 : 0;
|
|
4812
|
+
} finally {
|
|
4813
|
+
await input.auth.closeMock?.();
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4816
|
+
async function printDevDryRun(input) {
|
|
4817
|
+
try {
|
|
4818
|
+
const plan = {
|
|
4819
|
+
dryRun: true,
|
|
4820
|
+
wouldInvoke: true,
|
|
4821
|
+
target: input.target,
|
|
4822
|
+
surface: input.surface,
|
|
4823
|
+
modulePath: input.modulePath,
|
|
4824
|
+
params: input.params ?? null,
|
|
4825
|
+
...input.extra ?? {},
|
|
4826
|
+
note: input.target === "prod" ? "No module run and no prod side effects. Drop --dry-run to invoke once against prod CapabilityProxy." : "No module run. Drop --dry-run to invoke against the selected target."
|
|
4827
|
+
};
|
|
4828
|
+
if (input.json) {
|
|
4829
|
+
input.io.stdout(`${JSON.stringify(plan, null, 2)}
|
|
4830
|
+
`);
|
|
4831
|
+
} else {
|
|
4832
|
+
input.io.stdout(
|
|
4833
|
+
`dry-run: would invoke ${input.surface} on --target ${input.target}
|
|
4834
|
+
module: ${input.modulePath}
|
|
4835
|
+
params: ${JSON.stringify(input.params ?? null, null, 2)}
|
|
4836
|
+
` + (input.extra ? `detail: ${JSON.stringify(input.extra, null, 2)}
|
|
4837
|
+
` : "") + `${plan.note}
|
|
4838
|
+
`
|
|
4839
|
+
);
|
|
4840
|
+
}
|
|
4841
|
+
return 0;
|
|
4842
|
+
} finally {
|
|
4843
|
+
await input.closeMock?.();
|
|
4844
|
+
}
|
|
4845
|
+
}
|
|
4846
|
+
function writeJsonEvent(io, event) {
|
|
4847
|
+
io.stdout(`${JSON.stringify({ ...event, ts: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
4848
|
+
`);
|
|
4849
|
+
}
|
|
4850
|
+
function writeDevEvent(io, event) {
|
|
4851
|
+
const { stream, text } = formatDevEvent(event);
|
|
4852
|
+
io[stream](`${text}
|
|
4853
|
+
`);
|
|
4854
|
+
}
|
|
4855
|
+
function parseDevSubcommand(positionals) {
|
|
4856
|
+
const raw = positionals[0];
|
|
4857
|
+
if (!raw || raw.startsWith("-")) {
|
|
4858
|
+
return { subcommand: "serve", rest: positionals };
|
|
4859
|
+
}
|
|
4860
|
+
switch (raw) {
|
|
4861
|
+
case "serve":
|
|
4862
|
+
case "fetch":
|
|
4863
|
+
case "job":
|
|
4864
|
+
case "webhook":
|
|
4865
|
+
case "emit":
|
|
4866
|
+
case "check":
|
|
4867
|
+
return { subcommand: raw, rest: positionals.slice(1) };
|
|
4868
|
+
default:
|
|
4869
|
+
throw new Error(
|
|
4870
|
+
`Unknown kody dev subcommand ${JSON.stringify(raw)}. Use serve | fetch | job | webhook | emit | check.`
|
|
4871
|
+
);
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
async function removeOwnedReadyFile(path, ownerPid) {
|
|
4875
|
+
try {
|
|
4876
|
+
const parsed = JSON.parse(await readFile4(path, "utf8"));
|
|
4877
|
+
if (parsed.pid !== ownerPid) return;
|
|
4878
|
+
await rm4(path, { force: true });
|
|
4879
|
+
} catch {
|
|
4880
|
+
}
|
|
4881
|
+
}
|
|
4882
|
+
export {
|
|
4883
|
+
parseDevPort,
|
|
4884
|
+
parseDevSubcommand,
|
|
4885
|
+
removeOwnedReadyFile,
|
|
4886
|
+
resolveDevAuth,
|
|
4887
|
+
runDevCheck,
|
|
4888
|
+
runDevEmit,
|
|
4889
|
+
runDevFetch,
|
|
4890
|
+
runDevJob,
|
|
4891
|
+
runDevServe,
|
|
4892
|
+
runDevWebhook,
|
|
4893
|
+
tryAttachedDevFetch
|
|
4894
|
+
};
|
|
4895
|
+
/*! Bundled license information:
|
|
4896
|
+
|
|
4897
|
+
chokidar/esm/index.js:
|
|
4898
|
+
(*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) *)
|
|
4899
|
+
*/
|
|
4900
|
+
//# sourceMappingURL=dev-command-3LX234N6.js.map
|