@neat.is/core 0.4.2 → 0.4.4
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-NTQHMXWE.js → chunk-G3YGPWJL.js} +2 -2
- package/dist/{chunk-CB2UK4EH.js → chunk-J4YBTD24.js} +44 -3
- package/dist/{chunk-CB2UK4EH.js.map → chunk-J4YBTD24.js.map} +1 -1
- package/dist/{chunk-D5PIJFBE.js → chunk-YJOA7BBF.js} +2 -2
- package/dist/{chunk-KYRIQIPG.js → chunk-ZVNP3ZDH.js} +97 -25
- package/dist/chunk-ZVNP3ZDH.js.map +1 -0
- package/dist/cli.cjs +307 -125
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +215 -105
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +136 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +136 -23
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-QTX2YQJZ.js → otel-grpc-FIERFRGD.js} +3 -3
- package/dist/server.cjs +95 -23
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/dist/chunk-KYRIQIPG.js.map +0 -1
- /package/dist/{chunk-NTQHMXWE.js.map → chunk-G3YGPWJL.js.map} +0 -0
- /package/dist/{chunk-D5PIJFBE.js.map → chunk-YJOA7BBF.js.map} +0 -0
- /package/dist/{otel-grpc-QTX2YQJZ.js.map → otel-grpc-FIERFRGD.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -41,15 +41,15 @@ import {
|
|
|
41
41
|
setStatus,
|
|
42
42
|
startPersistLoop,
|
|
43
43
|
startStalenessLoop
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-G3YGPWJL.js";
|
|
45
45
|
import {
|
|
46
46
|
startOtelGrpcReceiver
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-YJOA7BBF.js";
|
|
48
48
|
import {
|
|
49
49
|
__dirname,
|
|
50
50
|
__require,
|
|
51
51
|
buildOtelReceiver
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-ZVNP3ZDH.js";
|
|
53
53
|
|
|
54
54
|
// src/cli.ts
|
|
55
55
|
import path8 from "path";
|
|
@@ -694,45 +694,31 @@ import path4 from "path";
|
|
|
694
694
|
// src/installers/templates.ts
|
|
695
695
|
var OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-069). OpenTelemetry auto-instrumentation hook.";
|
|
696
696
|
var OTEL_INIT_CJS = `${OTEL_INIT_HEADER}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
try {
|
|
701
|
-
require('dotenv').config({ path: path.join(__dirname, '.env.neat') })
|
|
702
|
-
} catch (err) {
|
|
703
|
-
// dotenv unavailable \u2014 fall through to process.env.
|
|
704
|
-
}
|
|
697
|
+
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
698
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
699
|
+
|
|
705
700
|
require('@opentelemetry/auto-instrumentations-node/register')
|
|
706
701
|
`;
|
|
707
702
|
var OTEL_INIT_ESM = `${OTEL_INIT_HEADER}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
import { fileURLToPath } from 'node:url'
|
|
711
|
-
import path from 'node:path'
|
|
712
|
-
import dotenv from 'dotenv'
|
|
713
|
-
|
|
714
|
-
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
715
|
-
dotenv.config({ path: path.join(here, '.env.neat') })
|
|
703
|
+
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
704
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
716
705
|
|
|
717
706
|
await import('@opentelemetry/auto-instrumentations-node/register')
|
|
718
707
|
`;
|
|
719
708
|
var OTEL_INIT_TS = `${OTEL_INIT_HEADER}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
import { fileURLToPath } from 'node:url'
|
|
723
|
-
import path from 'node:path'
|
|
724
|
-
import dotenv from 'dotenv'
|
|
725
|
-
|
|
726
|
-
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
727
|
-
dotenv.config({ path: path.join(here, '.env.neat') })
|
|
709
|
+
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
710
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
728
711
|
|
|
729
712
|
await import('@opentelemetry/auto-instrumentations-node/register')
|
|
730
713
|
`;
|
|
731
|
-
function
|
|
714
|
+
function renderNodeOtelInit(template, serviceName, projectName) {
|
|
715
|
+
return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName);
|
|
716
|
+
}
|
|
717
|
+
function renderEnvNeat(serviceName, projectName) {
|
|
732
718
|
return [
|
|
733
719
|
"# Generated by `neat init --apply` (ADR-069).",
|
|
734
|
-
`OTEL_SERVICE_NAME=${
|
|
735
|
-
|
|
720
|
+
`OTEL_SERVICE_NAME=${serviceName}`,
|
|
721
|
+
`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/projects/${projectName}/v1/traces`,
|
|
736
722
|
""
|
|
737
723
|
].join("\n");
|
|
738
724
|
}
|
|
@@ -752,54 +738,34 @@ export async function register() {
|
|
|
752
738
|
}
|
|
753
739
|
`;
|
|
754
740
|
var NEXT_INSTRUMENTATION_NODE_TS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
import path from 'node:path'
|
|
759
|
-
import dotenv from 'dotenv'
|
|
741
|
+
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
742
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
743
|
+
|
|
760
744
|
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
761
745
|
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
|
|
762
746
|
|
|
763
|
-
|
|
764
|
-
dotenv.config({ path: path.join(here, '.env.neat') })
|
|
765
|
-
|
|
766
|
-
const sdk = new NodeSDK({
|
|
767
|
-
serviceName: process.env.OTEL_SERVICE_NAME,
|
|
768
|
-
instrumentations: [getNodeAutoInstrumentations()],
|
|
769
|
-
})
|
|
770
|
-
sdk.start()
|
|
747
|
+
new NodeSDK({ instrumentations: [getNodeAutoInstrumentations()] }).start()
|
|
771
748
|
`;
|
|
772
749
|
var NEXT_INSTRUMENTATION_NODE_JS = `${NEXT_INSTRUMENTATION_HEADER}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
import { fileURLToPath } from 'node:url'
|
|
776
|
-
import path from 'node:path'
|
|
777
|
-
import dotenv from 'dotenv'
|
|
778
|
-
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
779
|
-
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
|
|
750
|
+
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
751
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
780
752
|
|
|
781
|
-
const
|
|
782
|
-
|
|
753
|
+
const { NodeSDK } = require('@opentelemetry/sdk-node')
|
|
754
|
+
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
|
|
783
755
|
|
|
784
|
-
|
|
785
|
-
serviceName: process.env.OTEL_SERVICE_NAME,
|
|
786
|
-
instrumentations: [getNodeAutoInstrumentations()],
|
|
787
|
-
})
|
|
788
|
-
sdk.start()
|
|
756
|
+
new NodeSDK({ instrumentations: [getNodeAutoInstrumentations()] }).start()
|
|
789
757
|
`;
|
|
758
|
+
function renderNextInstrumentationNode(template, serviceName, projectName) {
|
|
759
|
+
return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName);
|
|
760
|
+
}
|
|
790
761
|
var FRAMEWORK_OTEL_INIT_HEADER = "// Generated by `neat init --apply` (ADR-074). OpenTelemetry SDK hook.";
|
|
791
762
|
var FRAMEWORK_OTEL_INIT_TS_BODY = `${FRAMEWORK_OTEL_INIT_HEADER}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
import path from 'node:path'
|
|
796
|
-
import dotenv from 'dotenv'
|
|
763
|
+
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
764
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
765
|
+
|
|
797
766
|
import { NodeSDK } from '@opentelemetry/sdk-node'
|
|
798
767
|
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
|
|
799
768
|
|
|
800
|
-
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
801
|
-
dotenv.config({ path: path.join(here, '.env.neat') })
|
|
802
|
-
|
|
803
769
|
const sdk = new NodeSDK({
|
|
804
770
|
serviceName: process.env.OTEL_SERVICE_NAME,
|
|
805
771
|
instrumentations: [getNodeAutoInstrumentations()],
|
|
@@ -807,14 +773,9 @@ const sdk = new NodeSDK({
|
|
|
807
773
|
sdk.start()
|
|
808
774
|
`;
|
|
809
775
|
var FRAMEWORK_OTEL_INIT_JS_BODY = `${FRAMEWORK_OTEL_INIT_HEADER}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
try {
|
|
814
|
-
require('dotenv').config({ path: path.join(__dirname, '.env.neat') })
|
|
815
|
-
} catch (err) {
|
|
816
|
-
// dotenv unavailable \u2014 fall through to process.env.
|
|
817
|
-
}
|
|
776
|
+
process.env.OTEL_SERVICE_NAME ||= '__SERVICE_NAME__'
|
|
777
|
+
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||= 'http://localhost:4318/projects/__PROJECT__/v1/traces'
|
|
778
|
+
|
|
818
779
|
const { NodeSDK } = require('@opentelemetry/sdk-node')
|
|
819
780
|
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
|
|
820
781
|
|
|
@@ -824,6 +785,9 @@ const sdk = new NodeSDK({
|
|
|
824
785
|
})
|
|
825
786
|
sdk.start()
|
|
826
787
|
`;
|
|
788
|
+
function renderFrameworkOtelInit(template, serviceName, projectName) {
|
|
789
|
+
return template.replace(/__SERVICE_NAME__/g, serviceName).replace(/__PROJECT__/g, projectName);
|
|
790
|
+
}
|
|
827
791
|
var REMIX_OTEL_SERVER_TS = FRAMEWORK_OTEL_INIT_TS_BODY;
|
|
828
792
|
var REMIX_OTEL_SERVER_JS = FRAMEWORK_OTEL_INIT_JS_BODY;
|
|
829
793
|
var SVELTEKIT_OTEL_INIT_TS = FRAMEWORK_OTEL_INIT_TS_BODY;
|
|
@@ -886,11 +850,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
886
850
|
var SDK_PACKAGES = [
|
|
887
851
|
{ name: "@opentelemetry/api", version: "^1.9.0" },
|
|
888
852
|
{ name: "@opentelemetry/sdk-node", version: "^0.57.0" },
|
|
889
|
-
{ name: "@opentelemetry/auto-instrumentations-node", version: "^0.55.0" }
|
|
890
|
-
// ADR-069 §5 — dotenv is the fourth dep. The generated otel-init loads
|
|
891
|
-
// .env.neat through it so OTEL_SERVICE_NAME and the endpoint are in scope
|
|
892
|
-
// before the auto-instrumentation hook attaches.
|
|
893
|
-
{ name: "dotenv", version: "^16.4.5" }
|
|
853
|
+
{ name: "@opentelemetry/auto-instrumentations-node", version: "^0.55.0" }
|
|
894
854
|
];
|
|
895
855
|
var OTEL_ENV = {
|
|
896
856
|
// ADR-069 §4 — endpoint moves into the per-package .env.neat (written
|
|
@@ -898,13 +858,36 @@ var OTEL_ENV = {
|
|
|
898
858
|
// patch render: it documents the key/value the user can inspect in the
|
|
899
859
|
// generated .env.neat.
|
|
900
860
|
file: null,
|
|
901
|
-
key: "
|
|
902
|
-
value: "http://localhost:4318"
|
|
861
|
+
key: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
|
|
862
|
+
value: "http://localhost:4318/projects/<project>/v1/traces"
|
|
903
863
|
};
|
|
904
|
-
function
|
|
864
|
+
function serviceNodeName(pkg, serviceDir) {
|
|
865
|
+
return pkg.name ?? path4.basename(serviceDir);
|
|
866
|
+
}
|
|
867
|
+
function projectToken(pkg, serviceDir, project) {
|
|
905
868
|
if (project && project.length > 0) return project;
|
|
906
869
|
return pkg.name ?? path4.basename(serviceDir);
|
|
907
870
|
}
|
|
871
|
+
async function readJsonFile(p) {
|
|
872
|
+
try {
|
|
873
|
+
const raw = await fs4.readFile(p, "utf8");
|
|
874
|
+
return JSON.parse(raw);
|
|
875
|
+
} catch {
|
|
876
|
+
return null;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
async function detectRuntimeKind(pkgRoot, pkg) {
|
|
880
|
+
const deps = allDeps(pkg);
|
|
881
|
+
if ("react-native" in deps || "expo" in deps) return "react-native";
|
|
882
|
+
const appJson = await readJsonFile(path4.join(pkgRoot, "app.json"));
|
|
883
|
+
if (appJson && typeof appJson === "object" && "expo" in appJson) {
|
|
884
|
+
return "react-native";
|
|
885
|
+
}
|
|
886
|
+
if (await exists(path4.join(pkgRoot, "vite.config.js")) || await exists(path4.join(pkgRoot, "vite.config.ts")) || await exists(path4.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
|
|
887
|
+
return "browser-bundle";
|
|
888
|
+
}
|
|
889
|
+
return "node";
|
|
890
|
+
}
|
|
908
891
|
async function readPackageJson(serviceDir) {
|
|
909
892
|
try {
|
|
910
893
|
const raw = await fs4.readFile(path4.join(serviceDir, "package.json"), "utf8");
|
|
@@ -1129,14 +1112,25 @@ function lineIsOtelInjection(line) {
|
|
|
1129
1112
|
if (trimmed.length === 0) return false;
|
|
1130
1113
|
return /(?:require\(|import\s+)['"]\.\/otel-init[^'"]*['"]/.test(trimmed);
|
|
1131
1114
|
}
|
|
1115
|
+
async function detectsSrcLayout(serviceDir) {
|
|
1116
|
+
const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
|
|
1117
|
+
exists(path4.join(serviceDir, "src", "app")),
|
|
1118
|
+
exists(path4.join(serviceDir, "src", "pages")),
|
|
1119
|
+
exists(path4.join(serviceDir, "app")),
|
|
1120
|
+
exists(path4.join(serviceDir, "pages"))
|
|
1121
|
+
]);
|
|
1122
|
+
return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
|
|
1123
|
+
}
|
|
1132
1124
|
async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
|
|
1133
1125
|
const useTs = await isTypeScriptProject(serviceDir);
|
|
1134
|
-
const
|
|
1126
|
+
const srcLayout = await detectsSrcLayout(serviceDir);
|
|
1127
|
+
const baseDir = srcLayout ? path4.join(serviceDir, "src") : serviceDir;
|
|
1128
|
+
const instrumentationFile = path4.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
|
|
1135
1129
|
const instrumentationNodeFile = path4.join(
|
|
1136
|
-
|
|
1130
|
+
baseDir,
|
|
1137
1131
|
useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
|
|
1138
1132
|
);
|
|
1139
|
-
const envNeatFile = path4.join(
|
|
1133
|
+
const envNeatFile = path4.join(baseDir, ".env.neat");
|
|
1140
1134
|
const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
1141
1135
|
const dependencyEdits = [];
|
|
1142
1136
|
for (const sdk of SDK_PACKAGES) {
|
|
@@ -1148,6 +1142,8 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
|
|
|
1148
1142
|
version: sdk.version
|
|
1149
1143
|
});
|
|
1150
1144
|
}
|
|
1145
|
+
const svcName = serviceNodeName(pkg, serviceDir);
|
|
1146
|
+
const projectName = projectToken(pkg, serviceDir, project);
|
|
1151
1147
|
const generatedFiles = [];
|
|
1152
1148
|
if (!await exists(instrumentationFile)) {
|
|
1153
1149
|
generatedFiles.push({
|
|
@@ -1159,14 +1155,18 @@ async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project)
|
|
|
1159
1155
|
if (!await exists(instrumentationNodeFile)) {
|
|
1160
1156
|
generatedFiles.push({
|
|
1161
1157
|
file: instrumentationNodeFile,
|
|
1162
|
-
contents:
|
|
1158
|
+
contents: renderNextInstrumentationNode(
|
|
1159
|
+
useTs ? NEXT_INSTRUMENTATION_NODE_TS : NEXT_INSTRUMENTATION_NODE_JS,
|
|
1160
|
+
svcName,
|
|
1161
|
+
projectName
|
|
1162
|
+
),
|
|
1163
1163
|
skipIfExists: true
|
|
1164
1164
|
});
|
|
1165
1165
|
}
|
|
1166
1166
|
if (!await exists(envNeatFile)) {
|
|
1167
1167
|
generatedFiles.push({
|
|
1168
1168
|
file: envNeatFile,
|
|
1169
|
-
contents: renderEnvNeat(
|
|
1169
|
+
contents: renderEnvNeat(svcName, projectName),
|
|
1170
1170
|
skipIfExists: true
|
|
1171
1171
|
});
|
|
1172
1172
|
}
|
|
@@ -1227,11 +1227,21 @@ async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
|
|
|
1227
1227
|
if (!await exists(envNeatFile)) {
|
|
1228
1228
|
generatedFiles.push({
|
|
1229
1229
|
file: envNeatFile,
|
|
1230
|
-
contents: renderEnvNeat(
|
|
1230
|
+
contents: renderEnvNeat(
|
|
1231
|
+
serviceNodeName(pkg, serviceDir),
|
|
1232
|
+
projectToken(pkg, serviceDir, project)
|
|
1233
|
+
),
|
|
1231
1234
|
skipIfExists: true
|
|
1232
1235
|
});
|
|
1233
1236
|
}
|
|
1234
1237
|
}
|
|
1238
|
+
function renderFrameworkOtelInitForPkg(template, pkg, serviceDir, project) {
|
|
1239
|
+
return renderFrameworkOtelInit(
|
|
1240
|
+
template,
|
|
1241
|
+
serviceNodeName(pkg, serviceDir),
|
|
1242
|
+
projectToken(pkg, serviceDir, project)
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1235
1245
|
function fileImportsOtelHook(raw, specifiers) {
|
|
1236
1246
|
const lines = raw.split(/\r?\n/);
|
|
1237
1247
|
for (const line of lines) {
|
|
@@ -1257,7 +1267,12 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
|
|
|
1257
1267
|
if (!await exists(otelServerFile)) {
|
|
1258
1268
|
generatedFiles.push({
|
|
1259
1269
|
file: otelServerFile,
|
|
1260
|
-
contents:
|
|
1270
|
+
contents: renderFrameworkOtelInitForPkg(
|
|
1271
|
+
useTs ? REMIX_OTEL_SERVER_TS : REMIX_OTEL_SERVER_JS,
|
|
1272
|
+
pkg,
|
|
1273
|
+
serviceDir,
|
|
1274
|
+
project
|
|
1275
|
+
),
|
|
1261
1276
|
skipIfExists: true
|
|
1262
1277
|
});
|
|
1263
1278
|
}
|
|
@@ -1311,7 +1326,12 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
|
|
|
1311
1326
|
if (!await exists(otelInitFile)) {
|
|
1312
1327
|
generatedFiles.push({
|
|
1313
1328
|
file: otelInitFile,
|
|
1314
|
-
contents:
|
|
1329
|
+
contents: renderFrameworkOtelInitForPkg(
|
|
1330
|
+
useTs ? SVELTEKIT_OTEL_INIT_TS : SVELTEKIT_OTEL_INIT_JS,
|
|
1331
|
+
pkg,
|
|
1332
|
+
serviceDir,
|
|
1333
|
+
project
|
|
1334
|
+
),
|
|
1315
1335
|
skipIfExists: true
|
|
1316
1336
|
});
|
|
1317
1337
|
}
|
|
@@ -1374,7 +1394,12 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
|
|
|
1374
1394
|
if (!await exists(otelInitFile)) {
|
|
1375
1395
|
generatedFiles.push({
|
|
1376
1396
|
file: otelInitFile,
|
|
1377
|
-
contents:
|
|
1397
|
+
contents: renderFrameworkOtelInitForPkg(
|
|
1398
|
+
useTs ? NUXT_OTEL_INIT_TS : NUXT_OTEL_INIT_JS,
|
|
1399
|
+
pkg,
|
|
1400
|
+
serviceDir,
|
|
1401
|
+
project
|
|
1402
|
+
),
|
|
1378
1403
|
skipIfExists: true
|
|
1379
1404
|
});
|
|
1380
1405
|
}
|
|
@@ -1430,7 +1455,12 @@ async function planAstro(serviceDir, pkg, manifestPath, project) {
|
|
|
1430
1455
|
if (!await exists(otelInitFile)) {
|
|
1431
1456
|
generatedFiles.push({
|
|
1432
1457
|
file: otelInitFile,
|
|
1433
|
-
contents:
|
|
1458
|
+
contents: renderFrameworkOtelInitForPkg(
|
|
1459
|
+
useTs ? ASTRO_OTEL_INIT_TS : ASTRO_OTEL_INIT_JS,
|
|
1460
|
+
pkg,
|
|
1461
|
+
serviceDir,
|
|
1462
|
+
project
|
|
1463
|
+
),
|
|
1434
1464
|
skipIfExists: true
|
|
1435
1465
|
});
|
|
1436
1466
|
}
|
|
@@ -1522,6 +1552,10 @@ async function plan(serviceDir, opts) {
|
|
|
1522
1552
|
return planAstro(serviceDir, pkg, manifestPath, project);
|
|
1523
1553
|
}
|
|
1524
1554
|
}
|
|
1555
|
+
const runtimeKind = await detectRuntimeKind(serviceDir, pkg);
|
|
1556
|
+
if (runtimeKind !== "node") {
|
|
1557
|
+
return { ...empty, runtimeKind };
|
|
1558
|
+
}
|
|
1525
1559
|
const entryFile = await resolveEntry(serviceDir, pkg);
|
|
1526
1560
|
if (!entryFile) {
|
|
1527
1561
|
return { ...empty, libOnly: true };
|
|
@@ -1556,18 +1590,20 @@ async function plan(serviceDir, opts) {
|
|
|
1556
1590
|
} catch {
|
|
1557
1591
|
return { ...empty, libOnly: true };
|
|
1558
1592
|
}
|
|
1593
|
+
const svcName = serviceNodeName(pkg, serviceDir);
|
|
1594
|
+
const projectName = projectToken(pkg, serviceDir, project);
|
|
1559
1595
|
const generatedFiles = [];
|
|
1560
1596
|
if (!await exists(otelInitFile)) {
|
|
1561
1597
|
generatedFiles.push({
|
|
1562
1598
|
file: otelInitFile,
|
|
1563
|
-
contents: otelInitContents(flavor),
|
|
1599
|
+
contents: renderNodeOtelInit(otelInitContents(flavor), svcName, projectName),
|
|
1564
1600
|
skipIfExists: true
|
|
1565
1601
|
});
|
|
1566
1602
|
}
|
|
1567
1603
|
if (!await exists(envNeatFile)) {
|
|
1568
1604
|
generatedFiles.push({
|
|
1569
1605
|
file: envNeatFile,
|
|
1570
|
-
contents: renderEnvNeat(
|
|
1606
|
+
contents: renderEnvNeat(svcName, projectName),
|
|
1571
1607
|
skipIfExists: true
|
|
1572
1608
|
});
|
|
1573
1609
|
}
|
|
@@ -1592,9 +1628,13 @@ function isAllowedWritePath(serviceDir, target) {
|
|
|
1592
1628
|
if (base === "package.json") return true;
|
|
1593
1629
|
if (base === ".env.neat") return true;
|
|
1594
1630
|
if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
1595
|
-
if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
|
|
1596
|
-
if (/^next\.config\.(?:js|mjs|ts)$/.test(base)) return true;
|
|
1597
1631
|
const relPosix = rel.split(path4.sep).join("/");
|
|
1632
|
+
if (/^instrumentation(?:\.node)?\.(?:js|cjs|mjs|ts)$/.test(base)) {
|
|
1633
|
+
if (relPosix === base) return true;
|
|
1634
|
+
if (relPosix === `src/${base}`) return true;
|
|
1635
|
+
return false;
|
|
1636
|
+
}
|
|
1637
|
+
if (/^next\.config\.(?:js|mjs|ts)$/.test(base)) return true;
|
|
1598
1638
|
if (relPosix === "app/otel.server.ts" || relPosix === "app/otel.server.js") return true;
|
|
1599
1639
|
if (/^app\/entry\.server\.(?:tsx?|jsx?)$/.test(relPosix)) return true;
|
|
1600
1640
|
if (relPosix === "src/otel-init.ts" || relPosix === "src/otel-init.js") return true;
|
|
@@ -1620,6 +1660,22 @@ async function apply(installPlan) {
|
|
|
1620
1660
|
writtenFiles: []
|
|
1621
1661
|
};
|
|
1622
1662
|
}
|
|
1663
|
+
if (installPlan.runtimeKind === "browser-bundle") {
|
|
1664
|
+
return {
|
|
1665
|
+
serviceDir,
|
|
1666
|
+
outcome: "browser-bundle",
|
|
1667
|
+
reason: "browser bundle; Node OTel SDK cannot run here",
|
|
1668
|
+
writtenFiles: []
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
if (installPlan.runtimeKind === "react-native") {
|
|
1672
|
+
return {
|
|
1673
|
+
serviceDir,
|
|
1674
|
+
outcome: "react-native",
|
|
1675
|
+
reason: "React Native / Expo target; Node OTel SDK cannot run here",
|
|
1676
|
+
writtenFiles: []
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1623
1679
|
if (installPlan.dependencyEdits.length === 0 && installPlan.entrypointEdits.length === 0 && (installPlan.generatedFiles?.length ?? 0) === 0 && installPlan.nextConfigEdit === void 0) {
|
|
1624
1680
|
return {
|
|
1625
1681
|
serviceDir,
|
|
@@ -2103,11 +2159,13 @@ async function applyInstallersOver(services, project) {
|
|
|
2103
2159
|
let instrumented = 0;
|
|
2104
2160
|
let already = 0;
|
|
2105
2161
|
let libOnly = 0;
|
|
2162
|
+
let browserBundle = 0;
|
|
2163
|
+
let reactNative = 0;
|
|
2106
2164
|
for (const svc of services) {
|
|
2107
2165
|
const installer = await pickInstaller(svc.dir);
|
|
2108
2166
|
if (!installer) continue;
|
|
2109
2167
|
const plan3 = await installer.plan(svc.dir, { project });
|
|
2110
|
-
if (isEmptyPlan(plan3) && !plan3.libOnly) {
|
|
2168
|
+
if (isEmptyPlan(plan3) && !plan3.libOnly && plan3.runtimeKind === void 0) {
|
|
2111
2169
|
already++;
|
|
2112
2170
|
continue;
|
|
2113
2171
|
}
|
|
@@ -2115,8 +2173,15 @@ async function applyInstallersOver(services, project) {
|
|
|
2115
2173
|
if (outcome.outcome === "instrumented") instrumented++;
|
|
2116
2174
|
else if (outcome.outcome === "already-instrumented") already++;
|
|
2117
2175
|
else if (outcome.outcome === "lib-only") libOnly++;
|
|
2176
|
+
else if (outcome.outcome === "browser-bundle") {
|
|
2177
|
+
browserBundle++;
|
|
2178
|
+
console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
2179
|
+
} else if (outcome.outcome === "react-native") {
|
|
2180
|
+
reactNative++;
|
|
2181
|
+
console.log(`skipping ${svc.dir}: React Native target; browser-OTel support lands in a future release.`);
|
|
2182
|
+
}
|
|
2118
2183
|
}
|
|
2119
|
-
return { instrumented, alreadyInstrumented: already, libOnly };
|
|
2184
|
+
return { instrumented, alreadyInstrumented: already, libOnly, browserBundle, reactNative };
|
|
2120
2185
|
}
|
|
2121
2186
|
async function promptYesNo(question) {
|
|
2122
2187
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -2320,13 +2385,15 @@ async function runOrchestrator(opts) {
|
|
|
2320
2385
|
if (gi.action !== "unchanged") {
|
|
2321
2386
|
console.log(`${gi.action} .gitignore (neat-out/)`);
|
|
2322
2387
|
}
|
|
2388
|
+
let currentProjectName = opts.project;
|
|
2323
2389
|
try {
|
|
2324
|
-
await addProject({
|
|
2390
|
+
const entry2 = await addProject({
|
|
2325
2391
|
name: opts.project,
|
|
2326
2392
|
path: opts.scanPath,
|
|
2327
2393
|
languages,
|
|
2328
2394
|
status: "active"
|
|
2329
2395
|
});
|
|
2396
|
+
currentProjectName = entry2.name;
|
|
2330
2397
|
} catch (err) {
|
|
2331
2398
|
if (!(err instanceof ProjectNameCollisionError)) throw err;
|
|
2332
2399
|
console.error(`neat: ${err.message}`);
|
|
@@ -2334,6 +2401,20 @@ async function runOrchestrator(opts) {
|
|
|
2334
2401
|
result.exitCode = 1;
|
|
2335
2402
|
return result;
|
|
2336
2403
|
}
|
|
2404
|
+
const siblings = await listProjects();
|
|
2405
|
+
const paused = [];
|
|
2406
|
+
for (const p of siblings) {
|
|
2407
|
+
if (p.name !== currentProjectName && p.status === "active") {
|
|
2408
|
+
await setStatus(p.name, "paused");
|
|
2409
|
+
paused.push(p.name);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
if (paused.length > 0) {
|
|
2413
|
+
const plural = paused.length === 1 ? "" : "s";
|
|
2414
|
+
console.log(
|
|
2415
|
+
`neat: paused ${paused.length} sibling project${plural}; run \`neat resume <name>\` to bring one back active.`
|
|
2416
|
+
);
|
|
2417
|
+
}
|
|
2337
2418
|
if (!runApply) {
|
|
2338
2419
|
result.steps.apply.skipped = true;
|
|
2339
2420
|
console.log("skipped instrumentation (--no-instrument)");
|
|
@@ -3004,7 +3085,7 @@ async function runSync(opts) {
|
|
|
3004
3085
|
snapshotPath = target;
|
|
3005
3086
|
}
|
|
3006
3087
|
const skipApply = opts.dryRun || opts.noInstrument;
|
|
3007
|
-
const applyTally = skipApply ? { instrumented: 0, alreadyInstrumented: 0, libOnly: 0 } : await applyInstallersOver(persisted.services, entry2.name);
|
|
3088
|
+
const applyTally = skipApply ? { instrumented: 0, alreadyInstrumented: 0, libOnly: 0, browserBundle: 0, reactNative: 0 } : await applyInstallersOver(persisted.services, entry2.name);
|
|
3008
3089
|
const warnings = [];
|
|
3009
3090
|
let daemonState = "skipped";
|
|
3010
3091
|
let exitCode = 0;
|
|
@@ -3352,7 +3433,7 @@ async function buildPatchSections(services, project) {
|
|
|
3352
3433
|
const installer = await pickInstaller(svc.dir);
|
|
3353
3434
|
if (!installer) continue;
|
|
3354
3435
|
const plan3 = await installer.plan(svc.dir, { project });
|
|
3355
|
-
if (isEmptyPlan(plan3) && !plan3.libOnly) continue;
|
|
3436
|
+
if (isEmptyPlan(plan3) && !plan3.libOnly && plan3.runtimeKind === void 0) continue;
|
|
3356
3437
|
sections.push({ installer: installer.name, plan: plan3 });
|
|
3357
3438
|
}
|
|
3358
3439
|
return sections;
|
|
@@ -3396,13 +3477,15 @@ async function runInit(opts) {
|
|
|
3396
3477
|
written.push(gitignoreResult.file);
|
|
3397
3478
|
}
|
|
3398
3479
|
const languages = [...new Set(services.map((s) => s.node.language))].sort();
|
|
3480
|
+
let currentProjectName = opts.project;
|
|
3399
3481
|
try {
|
|
3400
|
-
await addProject({
|
|
3482
|
+
const entry2 = await addProject({
|
|
3401
3483
|
name: opts.project,
|
|
3402
3484
|
path: opts.scanPath,
|
|
3403
3485
|
languages,
|
|
3404
3486
|
status: "active"
|
|
3405
3487
|
});
|
|
3488
|
+
currentProjectName = entry2.name;
|
|
3406
3489
|
} catch (err) {
|
|
3407
3490
|
if (err instanceof ProjectNameCollisionError) {
|
|
3408
3491
|
console.error(`neat init: ${err.message}`);
|
|
@@ -3411,11 +3494,27 @@ async function runInit(opts) {
|
|
|
3411
3494
|
}
|
|
3412
3495
|
throw err;
|
|
3413
3496
|
}
|
|
3497
|
+
const siblings = await listProjects();
|
|
3498
|
+
const paused = [];
|
|
3499
|
+
for (const p of siblings) {
|
|
3500
|
+
if (p.name !== currentProjectName && p.status === "active") {
|
|
3501
|
+
await setStatus(p.name, "paused");
|
|
3502
|
+
paused.push(p.name);
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
if (paused.length > 0) {
|
|
3506
|
+
const plural = paused.length === 1 ? "" : "s";
|
|
3507
|
+
console.log(
|
|
3508
|
+
`neat: paused ${paused.length} sibling project${plural}; run \`neat resume <name>\` to bring one back active.`
|
|
3509
|
+
);
|
|
3510
|
+
}
|
|
3414
3511
|
if (!opts.noInstall) {
|
|
3415
3512
|
if (opts.apply) {
|
|
3416
3513
|
let instrumented = 0;
|
|
3417
3514
|
let alreadyInstrumented = 0;
|
|
3418
3515
|
let libOnly = 0;
|
|
3516
|
+
let browserBundle = 0;
|
|
3517
|
+
let reactNative = 0;
|
|
3419
3518
|
for (const section of sections) {
|
|
3420
3519
|
const installer = INSTALLERS.find((i) => i.name === section.installer);
|
|
3421
3520
|
if (!installer) continue;
|
|
@@ -3427,13 +3526,24 @@ async function runInit(opts) {
|
|
|
3427
3526
|
alreadyInstrumented++;
|
|
3428
3527
|
} else if (outcome.outcome === "lib-only") {
|
|
3429
3528
|
libOnly++;
|
|
3529
|
+
} else if (outcome.outcome === "browser-bundle") {
|
|
3530
|
+
browserBundle++;
|
|
3531
|
+
console.log(`skipping ${section.plan.serviceDir}: browser bundle; browser-OTel support lands in a future release.`);
|
|
3532
|
+
} else if (outcome.outcome === "react-native") {
|
|
3533
|
+
reactNative++;
|
|
3534
|
+
console.log(`skipping ${section.plan.serviceDir}: React Native target; browser-OTel support lands in a future release.`);
|
|
3430
3535
|
}
|
|
3431
3536
|
}
|
|
3432
3537
|
if (sections.length > 0) {
|
|
3433
3538
|
console.log("");
|
|
3434
|
-
|
|
3435
|
-
`
|
|
3436
|
-
|
|
3539
|
+
const parts = [
|
|
3540
|
+
`instrumented ${instrumented}`,
|
|
3541
|
+
`already-instrumented ${alreadyInstrumented}`,
|
|
3542
|
+
`lib-only ${libOnly}`
|
|
3543
|
+
];
|
|
3544
|
+
if (browserBundle > 0) parts.push(`browser-bundle ${browserBundle}`);
|
|
3545
|
+
if (reactNative > 0) parts.push(`react-native ${reactNative}`);
|
|
3546
|
+
console.log(`apply: ${parts.join(", ")}`);
|
|
3437
3547
|
console.log("Run `npm install` (or your language equivalent) to refresh lockfiles.");
|
|
3438
3548
|
}
|
|
3439
3549
|
} else {
|
|
@@ -3923,7 +4033,7 @@ async function runQueryVerb(cmd, parsed) {
|
|
|
3923
4033
|
}
|
|
3924
4034
|
}
|
|
3925
4035
|
var entry = process.argv[1] ?? "";
|
|
3926
|
-
if (/[\\/]cli\.(?:cjs|js)$/.test(entry) || entry.endsWith("/cli") || entry.endsWith("/neat")) {
|
|
4036
|
+
if (/[\\/]cli\.(?:cjs|js)$/.test(entry) || entry.endsWith("/cli") || entry.endsWith("/neat") || entry.endsWith("/neat.is")) {
|
|
3927
4037
|
main().catch((err) => {
|
|
3928
4038
|
console.error(err);
|
|
3929
4039
|
process.exit(1);
|