@neat.is/core 0.3.5 → 0.3.7
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/{chunk-4ASCXBZF.js → chunk-75IBA4UL.js} +34 -5
- package/dist/chunk-75IBA4UL.js.map +1 -0
- package/dist/{chunk-G3PDTGOW.js → chunk-CY67YKNO.js} +2 -2
- package/dist/{chunk-KCEZSFU2.js → chunk-EDHOCFOG.js} +87 -15
- package/dist/chunk-EDHOCFOG.js.map +1 -0
- package/dist/{chunk-BUB3ASD5.js → chunk-ZU2RQRCN.js} +3 -3
- package/dist/chunk-ZU2RQRCN.js.map +1 -0
- package/dist/cli.cjs +433 -65
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +400 -62
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +119 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +200 -18
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +83 -3
- package/dist/neatd.js.map +1 -1
- package/dist/{otel-grpc-J4O2SIBZ.js → otel-grpc-PM4SWPZE.js} +3 -3
- package/dist/server.cjs +34 -5
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-4ASCXBZF.js.map +0 -1
- package/dist/chunk-BUB3ASD5.js.map +0 -1
- package/dist/chunk-KCEZSFU2.js.map +0 -1
- /package/dist/{chunk-G3PDTGOW.js.map → chunk-CY67YKNO.js.map} +0 -0
- /package/dist/{otel-grpc-J4O2SIBZ.js.map → otel-grpc-PM4SWPZE.js.map} +0 -0
package/dist/cli.cjs
CHANGED
|
@@ -258,8 +258,7 @@ function parseOtlpRequest(body) {
|
|
|
258
258
|
}
|
|
259
259
|
return out;
|
|
260
260
|
}
|
|
261
|
-
function
|
|
262
|
-
if (exportTraceServiceRequestType) return exportTraceServiceRequestType;
|
|
261
|
+
function loadProtoRoot() {
|
|
263
262
|
const here = import_node_path35.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
264
263
|
const protoRoot = import_node_path35.default.resolve(here, "..", "proto");
|
|
265
264
|
const root = new import_protobufjs.default.Root();
|
|
@@ -268,11 +267,32 @@ function loadProtobufDecoder() {
|
|
|
268
267
|
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
|
|
269
268
|
{ keepCase: true }
|
|
270
269
|
);
|
|
270
|
+
return root;
|
|
271
|
+
}
|
|
272
|
+
function loadProtobufDecoder() {
|
|
273
|
+
if (exportTraceServiceRequestType) return exportTraceServiceRequestType;
|
|
274
|
+
const root = loadProtoRoot();
|
|
271
275
|
exportTraceServiceRequestType = root.lookupType(
|
|
272
276
|
"opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest"
|
|
273
277
|
);
|
|
274
278
|
return exportTraceServiceRequestType;
|
|
275
279
|
}
|
|
280
|
+
function loadProtobufResponseEncoder() {
|
|
281
|
+
if (exportTraceServiceResponseType) return exportTraceServiceResponseType;
|
|
282
|
+
const root = loadProtoRoot();
|
|
283
|
+
exportTraceServiceResponseType = root.lookupType(
|
|
284
|
+
"opentelemetry.proto.collector.trace.v1.ExportTraceServiceResponse"
|
|
285
|
+
);
|
|
286
|
+
return exportTraceServiceResponseType;
|
|
287
|
+
}
|
|
288
|
+
function encodeProtobufResponseBody() {
|
|
289
|
+
if (cachedProtobufResponseBody) return cachedProtobufResponseBody;
|
|
290
|
+
const Type = loadProtobufResponseEncoder();
|
|
291
|
+
const msg = Type.create({});
|
|
292
|
+
const encoded = Type.encode(msg).finish();
|
|
293
|
+
cachedProtobufResponseBody = Buffer.from(encoded);
|
|
294
|
+
return cachedProtobufResponseBody;
|
|
295
|
+
}
|
|
276
296
|
async function decodeProtobufBody(buf) {
|
|
277
297
|
const Type = loadProtobufDecoder();
|
|
278
298
|
const decoded = Type.decode(buf).toJSON();
|
|
@@ -319,7 +339,9 @@ async function buildOtelReceiver(opts) {
|
|
|
319
339
|
app.post("/v1/traces", async (req, reply) => {
|
|
320
340
|
const ct = (req.headers["content-type"] ?? "").toString().split(";")[0].trim().toLowerCase();
|
|
321
341
|
let body;
|
|
342
|
+
let responseFlavor;
|
|
322
343
|
if (ct === "application/x-protobuf") {
|
|
344
|
+
responseFlavor = "protobuf";
|
|
323
345
|
try {
|
|
324
346
|
body = await decodeProtobufBody(req.body);
|
|
325
347
|
} catch (err) {
|
|
@@ -328,6 +350,7 @@ async function buildOtelReceiver(opts) {
|
|
|
328
350
|
});
|
|
329
351
|
}
|
|
330
352
|
} else if (!ct || ct === "application/json") {
|
|
353
|
+
responseFlavor = "json";
|
|
331
354
|
body = req.body ?? {};
|
|
332
355
|
} else {
|
|
333
356
|
return reply.code(415).send({ error: `unsupported content-type: ${ct}` });
|
|
@@ -345,7 +368,11 @@ async function buildOtelReceiver(opts) {
|
|
|
345
368
|
}
|
|
346
369
|
}
|
|
347
370
|
enqueue(spans);
|
|
348
|
-
|
|
371
|
+
if (responseFlavor === "protobuf") {
|
|
372
|
+
const buf = encodeProtobufResponseBody();
|
|
373
|
+
return reply.code(200).header("content-type", "application/x-protobuf").send(buf);
|
|
374
|
+
}
|
|
375
|
+
return reply.code(200).header("content-type", "application/json").send({ partialSuccess: {} });
|
|
349
376
|
});
|
|
350
377
|
const decorated = app;
|
|
351
378
|
decorated.flushPending = async () => {
|
|
@@ -355,7 +382,7 @@ async function buildOtelReceiver(opts) {
|
|
|
355
382
|
};
|
|
356
383
|
return decorated;
|
|
357
384
|
}
|
|
358
|
-
var import_node_path35, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType;
|
|
385
|
+
var import_node_path35, import_node_url2, import_fastify2, import_protobufjs, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
|
|
359
386
|
var init_otel = __esm({
|
|
360
387
|
"src/otel.ts"() {
|
|
361
388
|
"use strict";
|
|
@@ -365,6 +392,8 @@ var init_otel = __esm({
|
|
|
365
392
|
import_fastify2 = __toESM(require("fastify"), 1);
|
|
366
393
|
import_protobufjs = __toESM(require("protobufjs"), 1);
|
|
367
394
|
exportTraceServiceRequestType = null;
|
|
395
|
+
exportTraceServiceResponseType = null;
|
|
396
|
+
cachedProtobufResponseBody = null;
|
|
368
397
|
}
|
|
369
398
|
});
|
|
370
399
|
|
|
@@ -1663,7 +1692,7 @@ function buildErrorEventForReceiver(span) {
|
|
|
1663
1692
|
service: span.service,
|
|
1664
1693
|
traceId: span.traceId,
|
|
1665
1694
|
spanId: span.spanId,
|
|
1666
|
-
errorMessage: span.exception?.message ??
|
|
1695
|
+
errorMessage: span.exception?.message ?? "unknown error",
|
|
1667
1696
|
...span.exception?.type ? { exceptionType: span.exception.type } : {},
|
|
1668
1697
|
...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
|
|
1669
1698
|
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
@@ -1755,7 +1784,7 @@ async function handleSpan(ctx, span) {
|
|
|
1755
1784
|
service: span.service,
|
|
1756
1785
|
traceId: span.traceId,
|
|
1757
1786
|
spanId: span.spanId,
|
|
1758
|
-
errorMessage: span.exception?.message ??
|
|
1787
|
+
errorMessage: span.exception?.message ?? "unknown error",
|
|
1759
1788
|
...span.exception?.type ? { exceptionType: span.exception.type } : {},
|
|
1760
1789
|
...span.exception?.stacktrace ? { exceptionStacktrace: span.exception.stacktrace } : {},
|
|
1761
1790
|
...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
|
|
@@ -5911,15 +5940,69 @@ init_cjs_shims();
|
|
|
5911
5940
|
init_cjs_shims();
|
|
5912
5941
|
var import_node_fs23 = require("fs");
|
|
5913
5942
|
var import_node_path38 = __toESM(require("path"), 1);
|
|
5943
|
+
|
|
5944
|
+
// src/installers/templates.ts
|
|
5945
|
+
init_cjs_shims();
|
|
5946
|
+
var OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-069). OpenTelemetry auto-instrumentation hook.";
|
|
5947
|
+
var OTEL_INIT_CJS = `${OTEL_INIT_HEADER}
|
|
5948
|
+
// Loads .env.neat (OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT) before
|
|
5949
|
+
// the auto-instrumentation hook attaches. Configure via the env file.
|
|
5950
|
+
const path = require('node:path')
|
|
5951
|
+
try {
|
|
5952
|
+
require('dotenv').config({ path: path.join(__dirname, '.env.neat') })
|
|
5953
|
+
} catch (err) {
|
|
5954
|
+
// dotenv unavailable \u2014 fall through to process.env.
|
|
5955
|
+
}
|
|
5956
|
+
require('@opentelemetry/auto-instrumentations-node/register')
|
|
5957
|
+
`;
|
|
5958
|
+
var OTEL_INIT_ESM = `${OTEL_INIT_HEADER}
|
|
5959
|
+
// Loads .env.neat (OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT) before
|
|
5960
|
+
// the auto-instrumentation hook attaches. Configure via the env file.
|
|
5961
|
+
import { fileURLToPath } from 'node:url'
|
|
5962
|
+
import path from 'node:path'
|
|
5963
|
+
import dotenv from 'dotenv'
|
|
5964
|
+
|
|
5965
|
+
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
5966
|
+
dotenv.config({ path: path.join(here, '.env.neat') })
|
|
5967
|
+
|
|
5968
|
+
await import('@opentelemetry/auto-instrumentations-node/register')
|
|
5969
|
+
`;
|
|
5970
|
+
var OTEL_INIT_TS = `${OTEL_INIT_HEADER}
|
|
5971
|
+
// Loads .env.neat (OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT) before
|
|
5972
|
+
// the auto-instrumentation hook attaches. Configure via the env file.
|
|
5973
|
+
import { fileURLToPath } from 'node:url'
|
|
5974
|
+
import path from 'node:path'
|
|
5975
|
+
import dotenv from 'dotenv'
|
|
5976
|
+
|
|
5977
|
+
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
5978
|
+
dotenv.config({ path: path.join(here, '.env.neat') })
|
|
5979
|
+
|
|
5980
|
+
await import('@opentelemetry/auto-instrumentations-node/register')
|
|
5981
|
+
`;
|
|
5982
|
+
function renderEnvNeat(serviceName) {
|
|
5983
|
+
return [
|
|
5984
|
+
"# Generated by `neat init --apply` (ADR-069).",
|
|
5985
|
+
`OTEL_SERVICE_NAME=${serviceName}`,
|
|
5986
|
+
"OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318",
|
|
5987
|
+
""
|
|
5988
|
+
].join("\n");
|
|
5989
|
+
}
|
|
5990
|
+
|
|
5991
|
+
// src/installers/javascript.ts
|
|
5914
5992
|
var SDK_PACKAGES = [
|
|
5915
5993
|
{ name: "@opentelemetry/api", version: "^1.9.0" },
|
|
5916
5994
|
{ name: "@opentelemetry/sdk-node", version: "^0.57.0" },
|
|
5917
|
-
{ name: "@opentelemetry/auto-instrumentations-node", version: "^0.55.0" }
|
|
5995
|
+
{ name: "@opentelemetry/auto-instrumentations-node", version: "^0.55.0" },
|
|
5996
|
+
// ADR-069 §5 — dotenv is the fourth dep. The generated otel-init loads
|
|
5997
|
+
// .env.neat through it so OTEL_SERVICE_NAME and the endpoint are in scope
|
|
5998
|
+
// before the auto-instrumentation hook attaches.
|
|
5999
|
+
{ name: "dotenv", version: "^16.4.5" }
|
|
5918
6000
|
];
|
|
5919
|
-
var AUTO_INSTRUMENT_REQUIRE = "--require @opentelemetry/auto-instrumentations-node/register";
|
|
5920
6001
|
var OTEL_ENV = {
|
|
5921
|
-
//
|
|
5922
|
-
//
|
|
6002
|
+
// ADR-069 §4 — endpoint moves into the per-package .env.neat (written
|
|
6003
|
+
// by the apply phase). The envEdits surface stays for the dry-run
|
|
6004
|
+
// patch render: it documents the key/value the user can inspect in the
|
|
6005
|
+
// generated .env.neat.
|
|
5923
6006
|
file: null,
|
|
5924
6007
|
key: "OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
5925
6008
|
value: "http://localhost:4318"
|
|
@@ -5932,16 +6015,134 @@ async function readPackageJson(serviceDir) {
|
|
|
5932
6015
|
return null;
|
|
5933
6016
|
}
|
|
5934
6017
|
}
|
|
6018
|
+
async function exists2(p) {
|
|
6019
|
+
try {
|
|
6020
|
+
await import_node_fs23.promises.stat(p);
|
|
6021
|
+
return true;
|
|
6022
|
+
} catch {
|
|
6023
|
+
return false;
|
|
6024
|
+
}
|
|
6025
|
+
}
|
|
5935
6026
|
async function detect(serviceDir) {
|
|
5936
6027
|
const pkg = await readPackageJson(serviceDir);
|
|
5937
6028
|
return pkg !== null && typeof pkg.name === "string";
|
|
5938
6029
|
}
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
6030
|
+
var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
|
|
6031
|
+
var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
|
|
6032
|
+
var SRC_INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `src/index${ext}`);
|
|
6033
|
+
var SRC_NAMED_CANDIDATES = ["server", "main", "app"].flatMap(
|
|
6034
|
+
(name) => INDEX_EXTENSIONS.map((ext) => `src/${name}${ext}`)
|
|
6035
|
+
);
|
|
6036
|
+
var SCRIPT_LAUNCHERS = /* @__PURE__ */ new Set([
|
|
6037
|
+
"node",
|
|
6038
|
+
"ts-node",
|
|
6039
|
+
"tsx",
|
|
6040
|
+
"ts-node-dev",
|
|
6041
|
+
"nodemon",
|
|
6042
|
+
"npx",
|
|
6043
|
+
"pnpm",
|
|
6044
|
+
"yarn",
|
|
6045
|
+
"npm",
|
|
6046
|
+
"cross-env",
|
|
6047
|
+
"dotenv",
|
|
6048
|
+
"--"
|
|
6049
|
+
]);
|
|
6050
|
+
function looksLikeEntryPath(token) {
|
|
6051
|
+
if (token.length === 0) return false;
|
|
6052
|
+
if (token.startsWith("-")) return false;
|
|
6053
|
+
if (token.includes("=")) return false;
|
|
6054
|
+
if (token.includes("/")) return true;
|
|
6055
|
+
return /\.(?:m?[jt]sx?|c[jt]s)$/.test(token);
|
|
6056
|
+
}
|
|
6057
|
+
function scriptHasShellChain(script) {
|
|
6058
|
+
return /(?:&&|\|\||;|\|(?!\|))/.test(script);
|
|
6059
|
+
}
|
|
6060
|
+
function entryFromScript(script) {
|
|
6061
|
+
if (!script) return void 0;
|
|
6062
|
+
if (scriptHasShellChain(script)) return void 0;
|
|
6063
|
+
const tokens = script.split(/\s+/).filter((t) => t.length > 0);
|
|
6064
|
+
for (const token of tokens) {
|
|
6065
|
+
const lower = token.toLowerCase();
|
|
6066
|
+
if (SCRIPT_LAUNCHERS.has(lower)) continue;
|
|
6067
|
+
const cleaned = token.startsWith("./") ? token.slice(2) : token;
|
|
6068
|
+
if (looksLikeEntryPath(cleaned)) return cleaned;
|
|
6069
|
+
}
|
|
6070
|
+
return void 0;
|
|
6071
|
+
}
|
|
6072
|
+
async function resolveEntry(serviceDir, pkg) {
|
|
6073
|
+
if (typeof pkg.main === "string" && pkg.main.length > 0) {
|
|
6074
|
+
const candidate = import_node_path38.default.resolve(serviceDir, pkg.main);
|
|
6075
|
+
if (await exists2(candidate)) return candidate;
|
|
6076
|
+
}
|
|
6077
|
+
if (pkg.bin) {
|
|
6078
|
+
let binEntry;
|
|
6079
|
+
if (typeof pkg.bin === "string") {
|
|
6080
|
+
binEntry = pkg.bin;
|
|
6081
|
+
} else if (pkg.name && typeof pkg.bin[pkg.name] === "string") {
|
|
6082
|
+
binEntry = pkg.bin[pkg.name];
|
|
6083
|
+
} else {
|
|
6084
|
+
const first = Object.values(pkg.bin)[0];
|
|
6085
|
+
if (typeof first === "string") binEntry = first;
|
|
6086
|
+
}
|
|
6087
|
+
if (binEntry) {
|
|
6088
|
+
const candidate = import_node_path38.default.resolve(serviceDir, binEntry);
|
|
6089
|
+
if (await exists2(candidate)) return candidate;
|
|
6090
|
+
}
|
|
6091
|
+
}
|
|
6092
|
+
const startEntry = entryFromScript(pkg.scripts?.start);
|
|
6093
|
+
if (startEntry) {
|
|
6094
|
+
const candidate = import_node_path38.default.resolve(serviceDir, startEntry);
|
|
6095
|
+
if (await exists2(candidate)) return candidate;
|
|
6096
|
+
}
|
|
6097
|
+
const devEntry = entryFromScript(pkg.scripts?.dev);
|
|
6098
|
+
if (devEntry) {
|
|
6099
|
+
const candidate = import_node_path38.default.resolve(serviceDir, devEntry);
|
|
6100
|
+
if (await exists2(candidate)) return candidate;
|
|
6101
|
+
}
|
|
6102
|
+
for (const rel of SRC_INDEX_CANDIDATES) {
|
|
6103
|
+
const candidate = import_node_path38.default.join(serviceDir, rel);
|
|
6104
|
+
if (await exists2(candidate)) return candidate;
|
|
6105
|
+
}
|
|
6106
|
+
for (const rel of SRC_NAMED_CANDIDATES) {
|
|
6107
|
+
const candidate = import_node_path38.default.join(serviceDir, rel);
|
|
6108
|
+
if (await exists2(candidate)) return candidate;
|
|
6109
|
+
}
|
|
6110
|
+
for (const name of INDEX_CANDIDATES) {
|
|
6111
|
+
const candidate = import_node_path38.default.join(serviceDir, name);
|
|
6112
|
+
if (await exists2(candidate)) return candidate;
|
|
5943
6113
|
}
|
|
5944
|
-
return
|
|
6114
|
+
return null;
|
|
6115
|
+
}
|
|
6116
|
+
function dispatchEntry(entryFile, pkg) {
|
|
6117
|
+
const ext = import_node_path38.default.extname(entryFile).toLowerCase();
|
|
6118
|
+
if (ext === ".ts" || ext === ".tsx") return "ts";
|
|
6119
|
+
if (ext === ".mjs") return "esm";
|
|
6120
|
+
if (ext === ".cjs") return "cjs";
|
|
6121
|
+
return pkg.type === "module" ? "esm" : "cjs";
|
|
6122
|
+
}
|
|
6123
|
+
function otelInitFilename(flavor) {
|
|
6124
|
+
if (flavor === "ts") return "otel-init.ts";
|
|
6125
|
+
if (flavor === "esm") return "otel-init.mjs";
|
|
6126
|
+
return "otel-init.cjs";
|
|
6127
|
+
}
|
|
6128
|
+
function otelInitContents(flavor) {
|
|
6129
|
+
if (flavor === "ts") return OTEL_INIT_TS;
|
|
6130
|
+
if (flavor === "esm") return OTEL_INIT_ESM;
|
|
6131
|
+
return OTEL_INIT_CJS;
|
|
6132
|
+
}
|
|
6133
|
+
function injectionLine(flavor, entryFile, otelInitFile) {
|
|
6134
|
+
let rel = import_node_path38.default.relative(import_node_path38.default.dirname(entryFile), otelInitFile);
|
|
6135
|
+
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
6136
|
+
rel = rel.split(import_node_path38.default.sep).join("/");
|
|
6137
|
+
if (flavor === "cjs") return `require('${rel}')`;
|
|
6138
|
+
if (flavor === "esm") return `import '${rel}'`;
|
|
6139
|
+
const tsRel = rel.replace(/\.ts$/, "");
|
|
6140
|
+
return `import '${tsRel}'`;
|
|
6141
|
+
}
|
|
6142
|
+
function lineIsOtelInjection(line) {
|
|
6143
|
+
const trimmed = line.trim();
|
|
6144
|
+
if (trimmed.length === 0) return false;
|
|
6145
|
+
return /(?:require\(|import\s+)['"]\.\/otel-init[^'"]*['"]/.test(trimmed);
|
|
5945
6146
|
}
|
|
5946
6147
|
async function plan(serviceDir) {
|
|
5947
6148
|
const pkg = await readPackageJson(serviceDir);
|
|
@@ -5951,9 +6152,17 @@ async function plan(serviceDir) {
|
|
|
5951
6152
|
serviceDir,
|
|
5952
6153
|
dependencyEdits: [],
|
|
5953
6154
|
entrypointEdits: [],
|
|
5954
|
-
envEdits: []
|
|
6155
|
+
envEdits: [],
|
|
6156
|
+
generatedFiles: []
|
|
5955
6157
|
};
|
|
5956
6158
|
if (!pkg) return empty;
|
|
6159
|
+
const entryFile = await resolveEntry(serviceDir, pkg);
|
|
6160
|
+
if (!entryFile) {
|
|
6161
|
+
return { ...empty, libOnly: true };
|
|
6162
|
+
}
|
|
6163
|
+
const flavor = dispatchEntry(entryFile, pkg);
|
|
6164
|
+
const otelInitFile = import_node_path38.default.join(import_node_path38.default.dirname(entryFile), otelInitFilename(flavor));
|
|
6165
|
+
const envNeatFile = import_node_path38.default.join(serviceDir, ".env.neat");
|
|
5957
6166
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
5958
6167
|
const dependencyEdits = [];
|
|
5959
6168
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -5966,14 +6175,37 @@ async function plan(serviceDir) {
|
|
|
5966
6175
|
});
|
|
5967
6176
|
}
|
|
5968
6177
|
const entrypointEdits = [];
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
const
|
|
5972
|
-
|
|
5973
|
-
|
|
6178
|
+
try {
|
|
6179
|
+
const raw = await import_node_fs23.promises.readFile(entryFile, "utf8");
|
|
6180
|
+
const lines = raw.split(/\r?\n/);
|
|
6181
|
+
const firstReal = lines[0]?.startsWith("#!") ? lines[1] ?? "" : lines[0] ?? "";
|
|
6182
|
+
if (!lineIsOtelInjection(firstReal)) {
|
|
6183
|
+
const inject = injectionLine(flavor, entryFile, otelInitFile);
|
|
6184
|
+
entrypointEdits.push({
|
|
6185
|
+
file: entryFile,
|
|
6186
|
+
before: firstReal,
|
|
6187
|
+
after: inject
|
|
6188
|
+
});
|
|
5974
6189
|
}
|
|
6190
|
+
} catch {
|
|
6191
|
+
return { ...empty, libOnly: true };
|
|
6192
|
+
}
|
|
6193
|
+
const generatedFiles = [];
|
|
6194
|
+
if (!await exists2(otelInitFile)) {
|
|
6195
|
+
generatedFiles.push({
|
|
6196
|
+
file: otelInitFile,
|
|
6197
|
+
contents: otelInitContents(flavor),
|
|
6198
|
+
skipIfExists: true
|
|
6199
|
+
});
|
|
5975
6200
|
}
|
|
5976
|
-
if (
|
|
6201
|
+
if (!await exists2(envNeatFile)) {
|
|
6202
|
+
generatedFiles.push({
|
|
6203
|
+
file: envNeatFile,
|
|
6204
|
+
contents: renderEnvNeat(pkg.name ?? import_node_path38.default.basename(serviceDir)),
|
|
6205
|
+
skipIfExists: true
|
|
6206
|
+
});
|
|
6207
|
+
}
|
|
6208
|
+
if (dependencyEdits.length === 0 && entrypointEdits.length === 0 && generatedFiles.length === 0) {
|
|
5977
6209
|
return empty;
|
|
5978
6210
|
}
|
|
5979
6211
|
return {
|
|
@@ -5981,53 +6213,129 @@ async function plan(serviceDir) {
|
|
|
5981
6213
|
serviceDir,
|
|
5982
6214
|
dependencyEdits,
|
|
5983
6215
|
entrypointEdits,
|
|
5984
|
-
envEdits: [OTEL_ENV]
|
|
6216
|
+
envEdits: [OTEL_ENV],
|
|
6217
|
+
generatedFiles,
|
|
6218
|
+
entryFile,
|
|
6219
|
+
libOnly: false
|
|
5985
6220
|
};
|
|
5986
6221
|
}
|
|
6222
|
+
function isAllowedWritePath(serviceDir, target) {
|
|
6223
|
+
const rel = import_node_path38.default.relative(serviceDir, target);
|
|
6224
|
+
if (rel.startsWith("..")) return false;
|
|
6225
|
+
const base = import_node_path38.default.basename(target);
|
|
6226
|
+
if (base === "package.json") return true;
|
|
6227
|
+
if (base === ".env.neat") return true;
|
|
6228
|
+
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
6229
|
+
return false;
|
|
6230
|
+
}
|
|
6231
|
+
async function writeAtomic(file, contents) {
|
|
6232
|
+
const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
|
|
6233
|
+
await import_node_fs23.promises.writeFile(tmp, contents, "utf8");
|
|
6234
|
+
await import_node_fs23.promises.rename(tmp, file);
|
|
6235
|
+
}
|
|
5987
6236
|
async function apply(installPlan) {
|
|
5988
|
-
const
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
6237
|
+
const { serviceDir } = installPlan;
|
|
6238
|
+
if (installPlan.libOnly) {
|
|
6239
|
+
return {
|
|
6240
|
+
serviceDir,
|
|
6241
|
+
outcome: "lib-only",
|
|
6242
|
+
reason: "no resolvable entry point",
|
|
6243
|
+
writtenFiles: []
|
|
6244
|
+
};
|
|
6245
|
+
}
|
|
6246
|
+
if (installPlan.dependencyEdits.length === 0 && installPlan.entrypointEdits.length === 0 && (installPlan.generatedFiles?.length ?? 0) === 0) {
|
|
6247
|
+
return {
|
|
6248
|
+
serviceDir,
|
|
6249
|
+
outcome: "already-instrumented",
|
|
6250
|
+
writtenFiles: []
|
|
6251
|
+
};
|
|
6252
|
+
}
|
|
6253
|
+
const allTargets = /* @__PURE__ */ new Set();
|
|
6254
|
+
for (const d of installPlan.dependencyEdits) allTargets.add(d.file);
|
|
6255
|
+
for (const e of installPlan.entrypointEdits) allTargets.add(e.file);
|
|
6256
|
+
for (const g of installPlan.generatedFiles ?? []) allTargets.add(g.file);
|
|
6257
|
+
for (const target of allTargets) {
|
|
6258
|
+
const isEntryEdit = installPlan.entrypointEdits.some((e) => e.file === target);
|
|
6259
|
+
if (isEntryEdit) continue;
|
|
6260
|
+
if (!isAllowedWritePath(serviceDir, target)) {
|
|
6261
|
+
throw new Error(
|
|
6262
|
+
`javascript installer: refusing to write outside the allowed path set (ADR-069 \xA77): ${target}`
|
|
6263
|
+
);
|
|
6264
|
+
}
|
|
6265
|
+
}
|
|
5992
6266
|
const originals = /* @__PURE__ */ new Map();
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
6267
|
+
const createdFiles = [];
|
|
6268
|
+
for (const target of allTargets) {
|
|
6269
|
+
if (await exists2(target)) {
|
|
6270
|
+
try {
|
|
6271
|
+
originals.set(target, await import_node_fs23.promises.readFile(target, "utf8"));
|
|
6272
|
+
} catch {
|
|
6273
|
+
}
|
|
5997
6274
|
}
|
|
5998
6275
|
}
|
|
6276
|
+
const writtenFiles = [];
|
|
5999
6277
|
try {
|
|
6000
|
-
|
|
6001
|
-
|
|
6278
|
+
const manifestTargets = installPlan.dependencyEdits.reduce((acc, e) => {
|
|
6279
|
+
acc.add(e.file);
|
|
6280
|
+
return acc;
|
|
6281
|
+
}, /* @__PURE__ */ new Set());
|
|
6282
|
+
for (const file of manifestTargets) {
|
|
6283
|
+
const raw = originals.get(file);
|
|
6284
|
+
if (raw === void 0) {
|
|
6285
|
+
throw new Error(`javascript installer: cannot read ${file} during apply`);
|
|
6286
|
+
}
|
|
6002
6287
|
const pkg = JSON.parse(raw);
|
|
6003
6288
|
pkg.dependencies = pkg.dependencies ?? {};
|
|
6004
6289
|
for (const dep of installPlan.dependencyEdits) {
|
|
6005
6290
|
if (dep.file !== file) continue;
|
|
6006
6291
|
if (dep.kind === "add") {
|
|
6007
|
-
|
|
6292
|
+
if (!(dep.name in (pkg.dependencies ?? {}))) {
|
|
6293
|
+
pkg.dependencies[dep.name] = dep.version;
|
|
6294
|
+
}
|
|
6008
6295
|
} else {
|
|
6009
6296
|
delete pkg.dependencies[dep.name];
|
|
6010
6297
|
}
|
|
6011
6298
|
}
|
|
6012
|
-
for (const ep of installPlan.entrypointEdits) {
|
|
6013
|
-
if (ep.file !== file) continue;
|
|
6014
|
-
pkg.scripts = pkg.scripts ?? {};
|
|
6015
|
-
if (pkg.scripts.start === ep.before) {
|
|
6016
|
-
pkg.scripts.start = ep.after;
|
|
6017
|
-
}
|
|
6018
|
-
}
|
|
6019
6299
|
const newRaw = JSON.stringify(pkg, null, 2) + "\n";
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6300
|
+
await writeAtomic(file, newRaw);
|
|
6301
|
+
writtenFiles.push(file);
|
|
6302
|
+
}
|
|
6303
|
+
for (const gen of installPlan.generatedFiles ?? []) {
|
|
6304
|
+
if (gen.skipIfExists && await exists2(gen.file)) {
|
|
6305
|
+
continue;
|
|
6306
|
+
}
|
|
6307
|
+
await writeAtomic(gen.file, gen.contents);
|
|
6308
|
+
if (!originals.has(gen.file)) createdFiles.push(gen.file);
|
|
6309
|
+
writtenFiles.push(gen.file);
|
|
6310
|
+
}
|
|
6311
|
+
for (const ep of installPlan.entrypointEdits) {
|
|
6312
|
+
const raw = originals.get(ep.file);
|
|
6313
|
+
if (raw === void 0) {
|
|
6314
|
+
throw new Error(`javascript installer: cannot read entry ${ep.file} during apply`);
|
|
6315
|
+
}
|
|
6316
|
+
const lines = raw.split(/\r?\n/);
|
|
6317
|
+
const hasShebang = lines[0]?.startsWith("#!") ?? false;
|
|
6318
|
+
const insertAt = hasShebang ? 1 : 0;
|
|
6319
|
+
const firstReal = lines[insertAt] ?? "";
|
|
6320
|
+
if (lineIsOtelInjection(firstReal)) continue;
|
|
6321
|
+
lines.splice(insertAt, 0, ep.after);
|
|
6322
|
+
const newRaw = lines.join("\n");
|
|
6323
|
+
await writeAtomic(ep.file, newRaw);
|
|
6324
|
+
writtenFiles.push(ep.file);
|
|
6023
6325
|
}
|
|
6024
6326
|
} catch (err) {
|
|
6025
|
-
await rollback(installPlan, originals);
|
|
6327
|
+
await rollback(installPlan, originals, createdFiles);
|
|
6026
6328
|
throw err;
|
|
6027
6329
|
}
|
|
6330
|
+
return {
|
|
6331
|
+
serviceDir,
|
|
6332
|
+
outcome: "instrumented",
|
|
6333
|
+
writtenFiles
|
|
6334
|
+
};
|
|
6028
6335
|
}
|
|
6029
|
-
async function rollback(installPlan, originals) {
|
|
6336
|
+
async function rollback(installPlan, originals, createdFiles) {
|
|
6030
6337
|
const restored = [];
|
|
6338
|
+
const removed = [];
|
|
6031
6339
|
for (const [file, raw] of originals.entries()) {
|
|
6032
6340
|
try {
|
|
6033
6341
|
await import_node_fs23.promises.writeFile(file, raw, "utf8");
|
|
@@ -6035,6 +6343,13 @@ async function rollback(installPlan, originals) {
|
|
|
6035
6343
|
} catch {
|
|
6036
6344
|
}
|
|
6037
6345
|
}
|
|
6346
|
+
for (const file of createdFiles) {
|
|
6347
|
+
try {
|
|
6348
|
+
await import_node_fs23.promises.unlink(file);
|
|
6349
|
+
removed.push(file);
|
|
6350
|
+
} catch {
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6038
6353
|
const lines = [
|
|
6039
6354
|
"# neat-rollback.patch",
|
|
6040
6355
|
"",
|
|
@@ -6042,6 +6357,7 @@ async function rollback(installPlan, originals) {
|
|
|
6042
6357
|
"# Files listed below were restored to their pre-apply contents.",
|
|
6043
6358
|
"",
|
|
6044
6359
|
...restored.map((f) => `restored: ${f}`),
|
|
6360
|
+
...removed.map((f) => `removed: ${f}`),
|
|
6045
6361
|
""
|
|
6046
6362
|
];
|
|
6047
6363
|
const rollbackPath = import_node_path38.default.join(installPlan.serviceDir, "neat-rollback.patch");
|
|
@@ -6067,7 +6383,7 @@ var OTEL_ENV2 = {
|
|
|
6067
6383
|
key: "OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
6068
6384
|
value: "http://localhost:4318"
|
|
6069
6385
|
};
|
|
6070
|
-
async function
|
|
6386
|
+
async function exists3(p) {
|
|
6071
6387
|
try {
|
|
6072
6388
|
await import_node_fs24.promises.stat(p);
|
|
6073
6389
|
return true;
|
|
@@ -6078,7 +6394,7 @@ async function exists2(p) {
|
|
|
6078
6394
|
async function detect2(serviceDir) {
|
|
6079
6395
|
const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
|
|
6080
6396
|
for (const m of markers) {
|
|
6081
|
-
if (await
|
|
6397
|
+
if (await exists3(import_node_path39.default.join(serviceDir, m))) return true;
|
|
6082
6398
|
}
|
|
6083
6399
|
return false;
|
|
6084
6400
|
}
|
|
@@ -6089,7 +6405,7 @@ function reqPackageName(line) {
|
|
|
6089
6405
|
}
|
|
6090
6406
|
async function planRequirementsTxtEdits(serviceDir) {
|
|
6091
6407
|
const file = import_node_path39.default.join(serviceDir, "requirements.txt");
|
|
6092
|
-
if (!await
|
|
6408
|
+
if (!await exists3(file)) return null;
|
|
6093
6409
|
const raw = await import_node_fs24.promises.readFile(file, "utf8");
|
|
6094
6410
|
const presentNames = new Set(
|
|
6095
6411
|
raw.split(/\r?\n/).map(reqPackageName).filter((n) => n.length > 0)
|
|
@@ -6099,7 +6415,7 @@ async function planRequirementsTxtEdits(serviceDir) {
|
|
|
6099
6415
|
}
|
|
6100
6416
|
async function planProcfileEdits(serviceDir) {
|
|
6101
6417
|
const procfile = import_node_path39.default.join(serviceDir, "Procfile");
|
|
6102
|
-
if (!await
|
|
6418
|
+
if (!await exists3(procfile)) return [];
|
|
6103
6419
|
const raw = await import_node_fs24.promises.readFile(procfile, "utf8");
|
|
6104
6420
|
const edits = [];
|
|
6105
6421
|
for (const line of raw.split(/\r?\n/)) {
|
|
@@ -6166,10 +6482,13 @@ async function applyProcfile(procfile, edits, original) {
|
|
|
6166
6482
|
await import_node_fs24.promises.rename(tmp, procfile);
|
|
6167
6483
|
}
|
|
6168
6484
|
async function apply2(installPlan) {
|
|
6485
|
+
const { serviceDir } = installPlan;
|
|
6169
6486
|
const touched = /* @__PURE__ */ new Set();
|
|
6170
6487
|
for (const e of installPlan.dependencyEdits) touched.add(e.file);
|
|
6171
6488
|
for (const e of installPlan.entrypointEdits) touched.add(e.file);
|
|
6172
|
-
if (touched.size === 0)
|
|
6489
|
+
if (touched.size === 0) {
|
|
6490
|
+
return { serviceDir, outcome: "already-instrumented", writtenFiles: [] };
|
|
6491
|
+
}
|
|
6173
6492
|
const originals = /* @__PURE__ */ new Map();
|
|
6174
6493
|
for (const file of touched) {
|
|
6175
6494
|
try {
|
|
@@ -6177,6 +6496,7 @@ async function apply2(installPlan) {
|
|
|
6177
6496
|
} catch {
|
|
6178
6497
|
}
|
|
6179
6498
|
}
|
|
6499
|
+
const writtenFiles = [];
|
|
6180
6500
|
try {
|
|
6181
6501
|
for (const file of touched) {
|
|
6182
6502
|
const raw = originals.get(file);
|
|
@@ -6186,16 +6506,23 @@ async function apply2(installPlan) {
|
|
|
6186
6506
|
const base = import_node_path39.default.basename(file);
|
|
6187
6507
|
if (base === "requirements.txt") {
|
|
6188
6508
|
const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
|
|
6189
|
-
if (edits.length > 0)
|
|
6509
|
+
if (edits.length > 0) {
|
|
6510
|
+
await applyRequirementsTxt(file, edits, raw);
|
|
6511
|
+
writtenFiles.push(file);
|
|
6512
|
+
}
|
|
6190
6513
|
} else if (base === "Procfile") {
|
|
6191
6514
|
const edits = installPlan.entrypointEdits.filter((e) => e.file === file);
|
|
6192
|
-
if (edits.length > 0)
|
|
6515
|
+
if (edits.length > 0) {
|
|
6516
|
+
await applyProcfile(file, edits, raw);
|
|
6517
|
+
writtenFiles.push(file);
|
|
6518
|
+
}
|
|
6193
6519
|
}
|
|
6194
6520
|
}
|
|
6195
6521
|
} catch (err) {
|
|
6196
6522
|
await rollback2(installPlan, originals);
|
|
6197
6523
|
throw err;
|
|
6198
6524
|
}
|
|
6525
|
+
return { serviceDir, outcome: "instrumented", writtenFiles };
|
|
6199
6526
|
}
|
|
6200
6527
|
async function rollback2(installPlan, originals) {
|
|
6201
6528
|
const restored = [];
|
|
@@ -6228,7 +6555,7 @@ var pythonInstaller = {
|
|
|
6228
6555
|
// src/installers/shared.ts
|
|
6229
6556
|
init_cjs_shims();
|
|
6230
6557
|
function isEmptyPlan(plan3) {
|
|
6231
|
-
return plan3.dependencyEdits.length === 0 && plan3.entrypointEdits.length === 0 && plan3.envEdits.length === 0;
|
|
6558
|
+
return plan3.dependencyEdits.length === 0 && plan3.entrypointEdits.length === 0 && plan3.envEdits.length === 0 && (plan3.generatedFiles?.length ?? 0) === 0;
|
|
6232
6559
|
}
|
|
6233
6560
|
|
|
6234
6561
|
// src/installers/index.ts
|
|
@@ -6270,8 +6597,18 @@ function renderPatch(sections) {
|
|
|
6270
6597
|
const { installer, plan: plan3 } = section;
|
|
6271
6598
|
lines.push(`## ${installer} (${plan3.language}) \u2014 ${plan3.serviceDir}`);
|
|
6272
6599
|
lines.push("");
|
|
6600
|
+
if (plan3.libOnly) {
|
|
6601
|
+
lines.push("### skipped \u2014 no resolvable entry point (lib-only)");
|
|
6602
|
+
lines.push("");
|
|
6603
|
+
continue;
|
|
6604
|
+
}
|
|
6605
|
+
if (plan3.entryFile) {
|
|
6606
|
+
lines.push(`entry: ${plan3.entryFile}`);
|
|
6607
|
+
lines.push("");
|
|
6608
|
+
}
|
|
6273
6609
|
if (plan3.dependencyEdits.length > 0) {
|
|
6274
6610
|
lines.push("### dependencies");
|
|
6611
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
6275
6612
|
for (const dep of plan3.dependencyEdits) {
|
|
6276
6613
|
const base = dep.file.split(/[\\/]/).pop() ?? dep.file;
|
|
6277
6614
|
if (FORBIDDEN_LOCKFILES.has(base)) {
|
|
@@ -6279,24 +6616,41 @@ function renderPatch(sections) {
|
|
|
6279
6616
|
`installer "${installer}" produced a dependency edit against a lockfile (${dep.file}); lockfiles must never be touched (ADR-047).`
|
|
6280
6617
|
);
|
|
6281
6618
|
}
|
|
6282
|
-
|
|
6619
|
+
const existing = byFile.get(dep.file) ?? [];
|
|
6620
|
+
existing.push(dep);
|
|
6621
|
+
byFile.set(dep.file, existing);
|
|
6622
|
+
}
|
|
6623
|
+
for (const [file, deps] of byFile) {
|
|
6624
|
+
lines.push(`--- ${file}`);
|
|
6625
|
+
for (const dep of deps) {
|
|
6626
|
+
lines.push(`+ "${dep.name}": "${dep.version}"`);
|
|
6627
|
+
}
|
|
6628
|
+
}
|
|
6629
|
+
lines.push("");
|
|
6630
|
+
}
|
|
6631
|
+
if (plan3.generatedFiles && plan3.generatedFiles.length > 0) {
|
|
6632
|
+
lines.push("### generated files");
|
|
6633
|
+
for (const gen of plan3.generatedFiles) {
|
|
6634
|
+
lines.push(`--- (new file) ${gen.file}`);
|
|
6635
|
+
for (const ln of gen.contents.split(/\r?\n/)) {
|
|
6636
|
+
lines.push(`+ ${ln}`);
|
|
6637
|
+
}
|
|
6283
6638
|
}
|
|
6284
6639
|
lines.push("");
|
|
6285
6640
|
}
|
|
6286
6641
|
if (plan3.entrypointEdits.length > 0) {
|
|
6287
|
-
lines.push("###
|
|
6642
|
+
lines.push("### entry-point injection");
|
|
6288
6643
|
for (const e of plan3.entrypointEdits) {
|
|
6289
|
-
lines.push(
|
|
6290
|
-
lines.push(
|
|
6291
|
-
lines.push(`
|
|
6644
|
+
lines.push(`--- ${e.file}`);
|
|
6645
|
+
lines.push(`+ ${e.after}`);
|
|
6646
|
+
lines.push(` ${e.before}`);
|
|
6292
6647
|
}
|
|
6293
6648
|
lines.push("");
|
|
6294
6649
|
}
|
|
6295
6650
|
if (plan3.envEdits.length > 0) {
|
|
6296
|
-
lines.push("### env");
|
|
6651
|
+
lines.push("### env (written to <package-dir>/.env.neat)");
|
|
6297
6652
|
for (const env of plan3.envEdits) {
|
|
6298
|
-
|
|
6299
|
-
lines.push(`- ${env.key}=${env.value} \u2192 ${target}`);
|
|
6653
|
+
lines.push(`- ${env.key}=${env.value}`);
|
|
6300
6654
|
}
|
|
6301
6655
|
lines.push("");
|
|
6302
6656
|
}
|
|
@@ -7045,7 +7399,7 @@ async function buildPatchSections(services) {
|
|
|
7045
7399
|
const installer = await pickInstaller(svc.dir);
|
|
7046
7400
|
if (!installer) continue;
|
|
7047
7401
|
const plan3 = await installer.plan(svc.dir);
|
|
7048
|
-
if (isEmptyPlan(plan3)) continue;
|
|
7402
|
+
if (isEmptyPlan(plan3) && !plan3.libOnly) continue;
|
|
7049
7403
|
sections.push({ installer: installer.name, plan: plan3 });
|
|
7050
7404
|
}
|
|
7051
7405
|
return sections;
|
|
@@ -7098,14 +7452,28 @@ async function runInit(opts) {
|
|
|
7098
7452
|
}
|
|
7099
7453
|
if (!opts.noInstall) {
|
|
7100
7454
|
if (opts.apply) {
|
|
7455
|
+
let instrumented = 0;
|
|
7456
|
+
let alreadyInstrumented = 0;
|
|
7457
|
+
let libOnly = 0;
|
|
7101
7458
|
for (const section of sections) {
|
|
7102
7459
|
const installer = INSTALLERS.find((i) => i.name === section.installer);
|
|
7103
7460
|
if (!installer) continue;
|
|
7104
|
-
await installer.apply(section.plan);
|
|
7461
|
+
const outcome = await installer.apply(section.plan);
|
|
7462
|
+
if (outcome.outcome === "instrumented") {
|
|
7463
|
+
instrumented++;
|
|
7464
|
+
for (const f of outcome.writtenFiles) written.push(f);
|
|
7465
|
+
} else if (outcome.outcome === "already-instrumented") {
|
|
7466
|
+
alreadyInstrumented++;
|
|
7467
|
+
} else if (outcome.outcome === "lib-only") {
|
|
7468
|
+
libOnly++;
|
|
7469
|
+
}
|
|
7105
7470
|
}
|
|
7106
7471
|
if (sections.length > 0) {
|
|
7107
7472
|
console.log("");
|
|
7108
|
-
console.log(
|
|
7473
|
+
console.log(
|
|
7474
|
+
`apply: instrumented ${instrumented}, already-instrumented ${alreadyInstrumented}, lib-only ${libOnly}`
|
|
7475
|
+
);
|
|
7476
|
+
console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
|
|
7109
7477
|
}
|
|
7110
7478
|
} else {
|
|
7111
7479
|
await import_node_fs25.promises.writeFile(patchPath, patch, "utf8");
|