@egintegrations/telemetry 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -10
- package/dist/cli.js +544 -0
- package/package.json +16 -5
- package/src/cli/standards/platforms.json +202 -0
- package/src/cli/standards/system_types.json +130 -0
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @egintegrations/telemetry
|
|
2
2
|
|
|
3
3
|
Official JavaScript/TypeScript telemetry SDK for EGIntegrations client engines.
|
|
4
4
|
|
|
@@ -6,19 +6,132 @@ Official JavaScript/TypeScript telemetry SDK for EGIntegrations client engines.
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# npm
|
|
9
|
-
npm install @
|
|
9
|
+
npm install @egintegrations/telemetry
|
|
10
10
|
|
|
11
11
|
# pnpm
|
|
12
|
-
pnpm add @
|
|
12
|
+
pnpm add @egintegrations/telemetry
|
|
13
13
|
|
|
14
14
|
# yarn
|
|
15
|
-
yarn add @
|
|
15
|
+
yarn add @egintegrations/telemetry
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
## Quick
|
|
18
|
+
## Quick Setup with CLI
|
|
19
|
+
|
|
20
|
+
The fastest way to integrate telemetry into your project:
|
|
21
|
+
|
|
22
|
+
### Interactive Setup
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx @egintegrations/telemetry init
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The CLI will:
|
|
29
|
+
1. Detect your framework (Next.js, Express, Fastify, or generic)
|
|
30
|
+
2. Prompt for engine name, SKU version, and Control Center URL
|
|
31
|
+
3. Generate configuration file (`.egi/telemetry.json`)
|
|
32
|
+
4. Create integration code (`lib/telemetry.ts` for Next.js, `src/telemetry.ts` for Express)
|
|
33
|
+
5. Add environment variables (`.env.local` or `.env`)
|
|
34
|
+
6. Optionally generate status endpoint boilerplate
|
|
35
|
+
|
|
36
|
+
### Non-Interactive Setup
|
|
37
|
+
|
|
38
|
+
For CI/CD or automated setups:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx @egintegrations/telemetry init \
|
|
42
|
+
--engine my-engine \
|
|
43
|
+
--sku 1.0.0 \
|
|
44
|
+
--url https://control-center.egintegrations.com \
|
|
45
|
+
--token your-auth-token
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### CLI Options
|
|
49
|
+
|
|
50
|
+
| Option | Description | Default |
|
|
51
|
+
|--------|-------------|---------|
|
|
52
|
+
| `--engine <name>` | Engine name (required) | - |
|
|
53
|
+
| `--sku <version>` | SKU version (required) | - |
|
|
54
|
+
| `--url <url>` | Control Center URL (required) | - |
|
|
55
|
+
| `--token <token>` | Auth token (optional) | - |
|
|
56
|
+
| `--modules <modules>` | Comma-separated module list | - |
|
|
57
|
+
| `--output <path>` | Config file location | `.egi/telemetry.json` |
|
|
58
|
+
| `--framework <type>` | Force framework type (nextjs, express, fastify, generic) | Auto-detected |
|
|
59
|
+
| `--no-interactive` | Skip prompts, fail on missing args | Interactive mode |
|
|
60
|
+
| `--no-codegen` | Only generate config file | Code generation enabled |
|
|
61
|
+
| `--status-endpoint` | Generate status endpoint boilerplate | false |
|
|
62
|
+
|
|
63
|
+
### Framework-Specific Examples
|
|
64
|
+
|
|
65
|
+
#### Next.js Project
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
cd my-nextjs-app
|
|
69
|
+
npx @egintegrations/telemetry init
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Generated Files:**
|
|
73
|
+
- `.egi/telemetry.json` - Configuration
|
|
74
|
+
- `lib/telemetry.ts` - Telemetry client singleton
|
|
75
|
+
- `.env.local` - Environment variables
|
|
76
|
+
- `app/api/engine-status/route.ts` - Status endpoint (if requested)
|
|
77
|
+
|
|
78
|
+
**Usage in Next.js:**
|
|
79
|
+
```typescript
|
|
80
|
+
// app/api/data/route.ts
|
|
81
|
+
import telemetry from '@/lib/telemetry';
|
|
82
|
+
|
|
83
|
+
export async function GET() {
|
|
84
|
+
await telemetry.sendMetrics({
|
|
85
|
+
metrics: { api_calls: 1 }
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return Response.json({ ok: true });
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Express Project
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cd my-express-app
|
|
96
|
+
npx @egintegrations/telemetry init
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Generated Files:**
|
|
100
|
+
- `.egi/telemetry.json` - Configuration
|
|
101
|
+
- `src/telemetry.ts` - Telemetry client
|
|
102
|
+
- `.env` - Environment variables
|
|
103
|
+
- `src/routes/status.ts` - Status endpoint (if requested)
|
|
104
|
+
|
|
105
|
+
**Usage in Express:**
|
|
106
|
+
```typescript
|
|
107
|
+
// src/index.ts
|
|
108
|
+
import express from 'express';
|
|
109
|
+
import telemetry from './telemetry';
|
|
110
|
+
|
|
111
|
+
const app = express();
|
|
112
|
+
|
|
113
|
+
// Auto-track requests
|
|
114
|
+
app.use(telemetry.middleware());
|
|
115
|
+
|
|
116
|
+
// Use in routes
|
|
117
|
+
app.get('/data', async (req, res) => {
|
|
118
|
+
await telemetry.sendMetrics({ metrics: { requests: 1 } });
|
|
119
|
+
res.json({ ok: true });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
app.listen(3000, async () => {
|
|
123
|
+
await telemetry.register();
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Manual Setup (Advanced)
|
|
128
|
+
|
|
129
|
+
If you prefer manual setup instead of using the CLI:
|
|
130
|
+
|
|
131
|
+
### Quick Start
|
|
19
132
|
|
|
20
133
|
```typescript
|
|
21
|
-
import { TelemetryClient } from '@
|
|
134
|
+
import { TelemetryClient } from '@egintegrations/telemetry';
|
|
22
135
|
|
|
23
136
|
// Initialize the client
|
|
24
137
|
const telemetry = new TelemetryClient({
|
|
@@ -57,11 +170,11 @@ await telemetry.sendMetrics({
|
|
|
57
170
|
});
|
|
58
171
|
```
|
|
59
172
|
|
|
60
|
-
|
|
173
|
+
### Express/Fastify Integration
|
|
61
174
|
|
|
62
175
|
```typescript
|
|
63
176
|
import express from 'express';
|
|
64
|
-
import { TelemetryClient } from '@
|
|
177
|
+
import { TelemetryClient } from '@egintegrations/telemetry';
|
|
65
178
|
|
|
66
179
|
const app = express();
|
|
67
180
|
const telemetry = new TelemetryClient({
|
|
@@ -76,7 +189,7 @@ app.use(telemetry.middleware());
|
|
|
76
189
|
app.listen(3000);
|
|
77
190
|
```
|
|
78
191
|
|
|
79
|
-
|
|
192
|
+
### Automatic Health Checks
|
|
80
193
|
|
|
81
194
|
```typescript
|
|
82
195
|
// Report health every 60 seconds
|
|
@@ -86,7 +199,7 @@ const healthCheckTimer = telemetry.startHealthCheck(60000);
|
|
|
86
199
|
clearInterval(healthCheckTimer);
|
|
87
200
|
```
|
|
88
201
|
|
|
89
|
-
|
|
202
|
+
### Graceful Shutdown
|
|
90
203
|
|
|
91
204
|
```typescript
|
|
92
205
|
process.on('SIGTERM', async () => {
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli.ts
|
|
27
|
+
var import_commander = require("commander");
|
|
28
|
+
|
|
29
|
+
// src/cli/detector.ts
|
|
30
|
+
var fs = __toESM(require("fs"));
|
|
31
|
+
var path = __toESM(require("path"));
|
|
32
|
+
async function detectFramework(cwd) {
|
|
33
|
+
const pkgJsonPath = path.join(cwd, "package.json");
|
|
34
|
+
if (!fs.existsSync(pkgJsonPath)) {
|
|
35
|
+
return { type: "generic", rootDir: cwd };
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const pkgContent = fs.readFileSync(pkgJsonPath, "utf-8");
|
|
39
|
+
const pkg = JSON.parse(pkgContent);
|
|
40
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
41
|
+
if (deps.next) {
|
|
42
|
+
const hasAppDir = fs.existsSync(path.join(cwd, "app"));
|
|
43
|
+
return {
|
|
44
|
+
type: "nextjs",
|
|
45
|
+
version: deps.next,
|
|
46
|
+
appDir: hasAppDir,
|
|
47
|
+
rootDir: cwd
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (deps.express) {
|
|
51
|
+
return {
|
|
52
|
+
type: "express",
|
|
53
|
+
version: deps.express,
|
|
54
|
+
rootDir: cwd
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (deps.fastify) {
|
|
58
|
+
return {
|
|
59
|
+
type: "fastify",
|
|
60
|
+
version: deps.fastify,
|
|
61
|
+
rootDir: cwd
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return { type: "generic", rootDir: cwd };
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.warn("Failed to parse package.json, assuming generic project");
|
|
67
|
+
return { type: "generic", rootDir: cwd };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/cli/prompts.ts
|
|
72
|
+
var import_prompts2 = __toESM(require("prompts"));
|
|
73
|
+
|
|
74
|
+
// src/cli/utils.ts
|
|
75
|
+
var fs2 = __toESM(require("fs"));
|
|
76
|
+
var path2 = __toESM(require("path"));
|
|
77
|
+
var import_prompts = __toESM(require("prompts"));
|
|
78
|
+
async function writeFile(file) {
|
|
79
|
+
const dir = path2.dirname(file.path);
|
|
80
|
+
await fs2.promises.mkdir(dir, { recursive: true });
|
|
81
|
+
if (fs2.existsSync(file.path) && file.overwrite !== true) {
|
|
82
|
+
const { confirm } = await (0, import_prompts.default)({
|
|
83
|
+
type: "confirm",
|
|
84
|
+
name: "confirm",
|
|
85
|
+
message: `File ${file.path} already exists. Overwrite?`,
|
|
86
|
+
initial: false
|
|
87
|
+
});
|
|
88
|
+
if (!confirm) {
|
|
89
|
+
console.log(`\u23ED\uFE0F Skipped: ${file.path}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (file.path.endsWith(".env") || file.path.endsWith(".env.local")) {
|
|
94
|
+
await appendEnvFile(file.path, file.content);
|
|
95
|
+
} else {
|
|
96
|
+
await fs2.promises.writeFile(file.path, file.content, "utf-8");
|
|
97
|
+
console.log(`\u2713 Created: ${file.path}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function appendEnvFile(envPath, content) {
|
|
101
|
+
let existing = "";
|
|
102
|
+
if (fs2.existsSync(envPath)) {
|
|
103
|
+
existing = await fs2.promises.readFile(envPath, "utf-8");
|
|
104
|
+
}
|
|
105
|
+
if (existing.includes("EGI_ENGINE_NAME")) {
|
|
106
|
+
console.log(`\u26A0\uFE0F ${envPath} already contains EGI variables, skipping`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const separator = existing.trim() ? "\n\n" : "";
|
|
110
|
+
const updated = existing + separator + content;
|
|
111
|
+
await fs2.promises.writeFile(envPath, updated, "utf-8");
|
|
112
|
+
console.log(`\u2713 Updated: ${envPath}`);
|
|
113
|
+
}
|
|
114
|
+
function loadSkuStandards() {
|
|
115
|
+
const standardsDir = path2.join(__dirname, "standards");
|
|
116
|
+
const systemTypesPath = path2.join(standardsDir, "system_types.json");
|
|
117
|
+
const platformsPath = path2.join(standardsDir, "platforms.json");
|
|
118
|
+
const systemTypes = JSON.parse(
|
|
119
|
+
fs2.readFileSync(systemTypesPath, "utf-8")
|
|
120
|
+
);
|
|
121
|
+
const platforms = JSON.parse(
|
|
122
|
+
fs2.readFileSync(platformsPath, "utf-8")
|
|
123
|
+
);
|
|
124
|
+
return { systemTypes, platforms };
|
|
125
|
+
}
|
|
126
|
+
function buildSku(version, systemType, platform, date) {
|
|
127
|
+
const skuDate = date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
|
|
128
|
+
return `${version}.${systemType}.${platform}.${skuDate}`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/cli/prompts.ts
|
|
132
|
+
async function gatherOptions(cliArgs, detected) {
|
|
133
|
+
const questions = [];
|
|
134
|
+
if (!cliArgs.engine) {
|
|
135
|
+
questions.push({
|
|
136
|
+
type: "text",
|
|
137
|
+
name: "engine",
|
|
138
|
+
message: "Engine name (e.g., hs-and-c):",
|
|
139
|
+
validate: (v) => v.length > 0 || "Engine name is required"
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
let skuComponents = {};
|
|
143
|
+
if (!cliArgs.sku) {
|
|
144
|
+
const standards = loadSkuStandards();
|
|
145
|
+
questions.push({
|
|
146
|
+
type: "text",
|
|
147
|
+
name: "skuVersion",
|
|
148
|
+
message: "Version (MAJOR.MINOR.PATCH, e.g., 1.0.0):",
|
|
149
|
+
validate: (v) => /^\d+\.\d+\.\d+$/.test(v) || "Invalid version format (expected: X.Y.Z)"
|
|
150
|
+
});
|
|
151
|
+
questions.push({
|
|
152
|
+
type: "select",
|
|
153
|
+
name: "skuType",
|
|
154
|
+
message: "System Type:",
|
|
155
|
+
choices: standards.systemTypes.map((t) => ({
|
|
156
|
+
title: `${t.code} - ${t.label}`,
|
|
157
|
+
description: t.description,
|
|
158
|
+
value: t.code
|
|
159
|
+
}))
|
|
160
|
+
});
|
|
161
|
+
questions.push({
|
|
162
|
+
type: "select",
|
|
163
|
+
name: "skuPlatform",
|
|
164
|
+
message: "Platform:",
|
|
165
|
+
choices: standards.platforms.map((p) => ({
|
|
166
|
+
title: `${p.code} - ${p.label}`,
|
|
167
|
+
description: p.description,
|
|
168
|
+
value: p.code
|
|
169
|
+
}))
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (!cliArgs.url) {
|
|
173
|
+
questions.push({
|
|
174
|
+
type: "text",
|
|
175
|
+
name: "url",
|
|
176
|
+
message: "Control Center URL:",
|
|
177
|
+
initial: "https://control-center.egintegrations.com"
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (cliArgs.token === void 0) {
|
|
181
|
+
questions.push({
|
|
182
|
+
type: "password",
|
|
183
|
+
name: "token",
|
|
184
|
+
message: "Auth token (optional, press Enter to skip):"
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (!cliArgs.framework) {
|
|
188
|
+
const frameworkChoices = [
|
|
189
|
+
{ title: `${detected.type} (detected)`, value: detected.type },
|
|
190
|
+
{ title: "Next.js", value: "nextjs", disabled: detected.type === "nextjs" },
|
|
191
|
+
{ title: "Express", value: "express", disabled: detected.type === "express" },
|
|
192
|
+
{ title: "Fastify", value: "fastify", disabled: detected.type === "fastify" },
|
|
193
|
+
{ title: "Generic/Other", value: "generic", disabled: detected.type === "generic" }
|
|
194
|
+
].filter((choice) => !choice.disabled);
|
|
195
|
+
if (frameworkChoices.length > 1) {
|
|
196
|
+
questions.push({
|
|
197
|
+
type: "select",
|
|
198
|
+
name: "framework",
|
|
199
|
+
message: "Select framework:",
|
|
200
|
+
choices: frameworkChoices,
|
|
201
|
+
initial: 0
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (cliArgs.codegen !== false && detected.type !== "generic") {
|
|
206
|
+
questions.push({
|
|
207
|
+
type: "confirm",
|
|
208
|
+
name: "statusEndpoint",
|
|
209
|
+
message: "Generate status endpoint boilerplate?",
|
|
210
|
+
initial: true
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
const answers = await (0, import_prompts2.default)(questions, {
|
|
214
|
+
onCancel: () => {
|
|
215
|
+
console.log("\n\u274C Setup cancelled");
|
|
216
|
+
process.exit(0);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
let finalSku = cliArgs.sku;
|
|
220
|
+
if (!finalSku && answers.skuVersion && answers.skuType && answers.skuPlatform) {
|
|
221
|
+
finalSku = buildSku(answers.skuVersion, answers.skuType, answers.skuPlatform);
|
|
222
|
+
console.log(`
|
|
223
|
+
\u{1F4E6} Generated SKU: ${finalSku}`);
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
engine: cliArgs.engine || answers.engine,
|
|
227
|
+
sku: finalSku,
|
|
228
|
+
url: cliArgs.url || answers.url,
|
|
229
|
+
token: cliArgs.token || answers.token || void 0,
|
|
230
|
+
modules: cliArgs.modules,
|
|
231
|
+
output: cliArgs.output || ".egi/telemetry.json",
|
|
232
|
+
framework: cliArgs.framework || answers.framework || detected.type,
|
|
233
|
+
codegen: cliArgs.codegen !== false,
|
|
234
|
+
statusEndpoint: cliArgs.statusEndpoint || answers.statusEndpoint || false
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/cli/generators/nextjs.ts
|
|
239
|
+
var path3 = __toESM(require("path"));
|
|
240
|
+
|
|
241
|
+
// src/cli/generators/templates.ts
|
|
242
|
+
function generateConfigFile(ctx) {
|
|
243
|
+
return JSON.stringify({
|
|
244
|
+
engine: ctx.engine,
|
|
245
|
+
sku: ctx.sku,
|
|
246
|
+
url: ctx.url,
|
|
247
|
+
...ctx.token && { token: ctx.token },
|
|
248
|
+
...ctx.modules && { modules: ctx.modules }
|
|
249
|
+
}, null, 2);
|
|
250
|
+
}
|
|
251
|
+
function generateNextjsTelemetryClient(ctx) {
|
|
252
|
+
return `import { TelemetryClient } from '@egintegrations/telemetry';
|
|
253
|
+
|
|
254
|
+
const telemetry = new TelemetryClient({
|
|
255
|
+
engineName: process.env.EGI_ENGINE_NAME || '${ctx.engine}',
|
|
256
|
+
skuVersion: process.env.EGI_SKU_VERSION || '${ctx.sku}',
|
|
257
|
+
controlCenterUrl: process.env.EGI_CONTROL_CENTER_URL || '${ctx.url}',
|
|
258
|
+
${ctx.token ? `authToken: process.env.EGI_AUTH_TOKEN,` : "// authToken: process.env.EGI_AUTH_TOKEN, // Optional"}
|
|
259
|
+
enabled: process.env.NODE_ENV === 'production'
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Auto-register on first import in production
|
|
263
|
+
if (process.env.NODE_ENV === 'production') {
|
|
264
|
+
telemetry.register().catch(err => {
|
|
265
|
+
console.error('[EGI Telemetry] Failed to register:', err);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export default telemetry;
|
|
270
|
+
`;
|
|
271
|
+
}
|
|
272
|
+
function generateNextjsStatusEndpoint() {
|
|
273
|
+
return `import { NextResponse } from 'next/server';
|
|
274
|
+
import telemetry from '@/lib/telemetry';
|
|
275
|
+
|
|
276
|
+
export async function GET() {
|
|
277
|
+
try {
|
|
278
|
+
await telemetry.reportStatus({
|
|
279
|
+
status: 'healthy',
|
|
280
|
+
metadata: {
|
|
281
|
+
timestamp: new Date().toISOString(),
|
|
282
|
+
uptime: process.uptime()
|
|
283
|
+
},
|
|
284
|
+
metrics: {},
|
|
285
|
+
message: 'Engine is healthy'
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
return NextResponse.json({
|
|
289
|
+
status: 'ok',
|
|
290
|
+
timestamp: new Date().toISOString()
|
|
291
|
+
});
|
|
292
|
+
} catch (error) {
|
|
293
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
294
|
+
return NextResponse.json(
|
|
295
|
+
{ status: 'error', message: errorMessage },
|
|
296
|
+
{ status: 500 }
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
`;
|
|
301
|
+
}
|
|
302
|
+
function generateExpressTelemetryClient(ctx) {
|
|
303
|
+
return `import { TelemetryClient } from '@egintegrations/telemetry';
|
|
304
|
+
|
|
305
|
+
const telemetry = new TelemetryClient({
|
|
306
|
+
engineName: process.env.EGI_ENGINE_NAME || '${ctx.engine}',
|
|
307
|
+
skuVersion: process.env.EGI_SKU_VERSION || '${ctx.sku}',
|
|
308
|
+
controlCenterUrl: process.env.EGI_CONTROL_CENTER_URL || '${ctx.url}',
|
|
309
|
+
${ctx.token ? `authToken: process.env.EGI_AUTH_TOKEN,` : "// authToken: process.env.EGI_AUTH_TOKEN, // Optional"}
|
|
310
|
+
enabled: process.env.NODE_ENV === 'production'
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
export default telemetry;
|
|
314
|
+
`;
|
|
315
|
+
}
|
|
316
|
+
function generateExpressStatusRoute() {
|
|
317
|
+
return `import express from 'express';
|
|
318
|
+
import telemetry from '../telemetry';
|
|
319
|
+
|
|
320
|
+
const router = express.Router();
|
|
321
|
+
|
|
322
|
+
router.get('/engine-status', async (req, res) => {
|
|
323
|
+
try {
|
|
324
|
+
await telemetry.reportStatus({
|
|
325
|
+
status: 'healthy',
|
|
326
|
+
metadata: {
|
|
327
|
+
uptime: process.uptime(),
|
|
328
|
+
memory: process.memoryUsage()
|
|
329
|
+
},
|
|
330
|
+
metrics: {},
|
|
331
|
+
message: 'Engine is healthy'
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
res.json({
|
|
335
|
+
status: 'ok',
|
|
336
|
+
timestamp: new Date().toISOString()
|
|
337
|
+
});
|
|
338
|
+
} catch (error) {
|
|
339
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
340
|
+
res.status(500).json({
|
|
341
|
+
status: 'error',
|
|
342
|
+
message: errorMessage
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
export default router;
|
|
348
|
+
`;
|
|
349
|
+
}
|
|
350
|
+
function generateEnvVars(ctx) {
|
|
351
|
+
const lines = [
|
|
352
|
+
"# EGI Telemetry Configuration",
|
|
353
|
+
`EGI_ENGINE_NAME=${ctx.engine}`,
|
|
354
|
+
`EGI_SKU_VERSION=${ctx.sku}`,
|
|
355
|
+
`EGI_CONTROL_CENTER_URL=${ctx.url}`
|
|
356
|
+
];
|
|
357
|
+
if (ctx.token) {
|
|
358
|
+
lines.push(`EGI_AUTH_TOKEN=${ctx.token}`);
|
|
359
|
+
}
|
|
360
|
+
return lines.join("\n");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/cli/generators/nextjs.ts
|
|
364
|
+
var nextjsGenerator = {
|
|
365
|
+
name: "Next.js",
|
|
366
|
+
supports: (ctx) => ctx.framework.type === "nextjs",
|
|
367
|
+
async generate(ctx) {
|
|
368
|
+
const files = [];
|
|
369
|
+
files.push({
|
|
370
|
+
path: path3.join(ctx.rootDir, ctx.options.output),
|
|
371
|
+
content: generateConfigFile(ctx.options)
|
|
372
|
+
});
|
|
373
|
+
if (ctx.options.codegen) {
|
|
374
|
+
const libDir = path3.join(ctx.rootDir, "lib");
|
|
375
|
+
files.push({
|
|
376
|
+
path: path3.join(libDir, "telemetry.ts"),
|
|
377
|
+
content: generateNextjsTelemetryClient(ctx.options),
|
|
378
|
+
overwrite: false
|
|
379
|
+
// Ask if exists
|
|
380
|
+
});
|
|
381
|
+
if (ctx.framework.appDir && ctx.options.statusEndpoint) {
|
|
382
|
+
files.push({
|
|
383
|
+
path: path3.join(ctx.rootDir, "app/api/engine-status/route.ts"),
|
|
384
|
+
content: generateNextjsStatusEndpoint(),
|
|
385
|
+
overwrite: false
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
files.push({
|
|
389
|
+
path: path3.join(ctx.rootDir, ".env.local"),
|
|
390
|
+
content: generateEnvVars(ctx.options),
|
|
391
|
+
overwrite: false
|
|
392
|
+
// Append, don't replace
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
return files;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// src/cli/generators/express.ts
|
|
400
|
+
var path4 = __toESM(require("path"));
|
|
401
|
+
var fs3 = __toESM(require("fs"));
|
|
402
|
+
var expressGenerator = {
|
|
403
|
+
name: "Express",
|
|
404
|
+
supports: (ctx) => ctx.framework.type === "express" || ctx.framework.type === "fastify",
|
|
405
|
+
async generate(ctx) {
|
|
406
|
+
const files = [];
|
|
407
|
+
files.push({
|
|
408
|
+
path: path4.join(ctx.rootDir, ctx.options.output),
|
|
409
|
+
content: generateConfigFile(ctx.options)
|
|
410
|
+
});
|
|
411
|
+
if (ctx.options.codegen) {
|
|
412
|
+
const hasSrcDir = fs3.existsSync(path4.join(ctx.rootDir, "src"));
|
|
413
|
+
const baseDir = hasSrcDir ? "src" : ".";
|
|
414
|
+
files.push({
|
|
415
|
+
path: path4.join(ctx.rootDir, baseDir, "telemetry.ts"),
|
|
416
|
+
content: generateExpressTelemetryClient(ctx.options),
|
|
417
|
+
overwrite: false
|
|
418
|
+
});
|
|
419
|
+
if (ctx.options.statusEndpoint) {
|
|
420
|
+
const routesDir = path4.join(ctx.rootDir, baseDir, "routes");
|
|
421
|
+
files.push({
|
|
422
|
+
path: path4.join(routesDir, "status.ts"),
|
|
423
|
+
content: generateExpressStatusRoute(),
|
|
424
|
+
overwrite: false
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
files.push({
|
|
428
|
+
path: path4.join(ctx.rootDir, ".env"),
|
|
429
|
+
content: generateEnvVars(ctx.options),
|
|
430
|
+
overwrite: false
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
return files;
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
// src/cli/generators/generic.ts
|
|
438
|
+
var path5 = __toESM(require("path"));
|
|
439
|
+
var genericGenerator = {
|
|
440
|
+
name: "Generic Node.js",
|
|
441
|
+
supports: () => true,
|
|
442
|
+
// Fallback generator, always supports
|
|
443
|
+
async generate(ctx) {
|
|
444
|
+
const files = [];
|
|
445
|
+
files.push({
|
|
446
|
+
path: path5.join(ctx.rootDir, ctx.options.output),
|
|
447
|
+
content: generateConfigFile(ctx.options)
|
|
448
|
+
});
|
|
449
|
+
console.log(`
|
|
450
|
+
\u26A0\uFE0F Generic project detected - only config file generated.
|
|
451
|
+
|
|
452
|
+
To use the telemetry SDK, manually:
|
|
453
|
+
1. Import the SDK: import { TelemetryClient } from '@egintegrations/telemetry';
|
|
454
|
+
2. Create a client with your config
|
|
455
|
+
3. Call telemetry.register() on startup
|
|
456
|
+
|
|
457
|
+
Example:
|
|
458
|
+
import { TelemetryClient } from '@egintegrations/telemetry';
|
|
459
|
+
|
|
460
|
+
const telemetry = new TelemetryClient({
|
|
461
|
+
engineName: '${ctx.options.engine}',
|
|
462
|
+
skuVersion: '${ctx.options.sku}',
|
|
463
|
+
controlCenterUrl: '${ctx.options.url}',
|
|
464
|
+
${ctx.options.token ? `authToken: '${ctx.options.token}',` : ""}
|
|
465
|
+
enabled: true
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
await telemetry.register();
|
|
469
|
+
|
|
470
|
+
See README for full documentation.
|
|
471
|
+
`);
|
|
472
|
+
return files;
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
// src/cli/generators/index.ts
|
|
477
|
+
var generators = [
|
|
478
|
+
nextjsGenerator,
|
|
479
|
+
expressGenerator,
|
|
480
|
+
genericGenerator
|
|
481
|
+
// Always last as fallback
|
|
482
|
+
];
|
|
483
|
+
async function runGenerator(ctx) {
|
|
484
|
+
const generator = generators.find((g) => g.supports(ctx));
|
|
485
|
+
if (!generator) {
|
|
486
|
+
throw new Error(`No generator found for framework: ${ctx.framework.type}`);
|
|
487
|
+
}
|
|
488
|
+
console.log(`
|
|
489
|
+
\u{1F3A8} Using ${generator.name} generator
|
|
490
|
+
`);
|
|
491
|
+
return generator.generate(ctx);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/cli/commands.ts
|
|
495
|
+
async function initCommand(options) {
|
|
496
|
+
console.log("\u{1F680} EGI Telemetry SDK Initialization\n");
|
|
497
|
+
try {
|
|
498
|
+
const cwd = process.cwd();
|
|
499
|
+
console.log("\u{1F50D} Detecting framework...");
|
|
500
|
+
const detected = await detectFramework(cwd);
|
|
501
|
+
console.log(` Found: ${detected.type}${detected.version ? ` v${detected.version}` : ""}`);
|
|
502
|
+
if (detected.type === "nextjs" && detected.appDir) {
|
|
503
|
+
console.log(" Next.js App Router detected");
|
|
504
|
+
}
|
|
505
|
+
console.log("");
|
|
506
|
+
const telemetryOptions = await gatherOptions(options, detected);
|
|
507
|
+
console.log("");
|
|
508
|
+
const ctx = {
|
|
509
|
+
options: telemetryOptions,
|
|
510
|
+
framework: detected,
|
|
511
|
+
rootDir: cwd
|
|
512
|
+
};
|
|
513
|
+
const files = await runGenerator(ctx);
|
|
514
|
+
for (const file of files) {
|
|
515
|
+
await writeFile(file);
|
|
516
|
+
}
|
|
517
|
+
console.log("\n\u2705 Telemetry SDK initialized successfully!\n");
|
|
518
|
+
if (telemetryOptions.codegen && detected.type !== "generic") {
|
|
519
|
+
console.log("\u{1F4DD} Next steps:");
|
|
520
|
+
console.log(" 1. Review the generated files");
|
|
521
|
+
console.log(" 2. Import telemetry in your code:");
|
|
522
|
+
if (detected.type === "nextjs") {
|
|
523
|
+
console.log(" import telemetry from '@/lib/telemetry';");
|
|
524
|
+
} else {
|
|
525
|
+
console.log(" import telemetry from './telemetry';");
|
|
526
|
+
}
|
|
527
|
+
console.log(" 3. Start using telemetry.sendMetrics() in your app");
|
|
528
|
+
console.log("");
|
|
529
|
+
}
|
|
530
|
+
} catch (error) {
|
|
531
|
+
if (error instanceof Error) {
|
|
532
|
+
console.error("\n\u274C Error:", error.message);
|
|
533
|
+
} else {
|
|
534
|
+
console.error("\n\u274C Unknown error occurred");
|
|
535
|
+
}
|
|
536
|
+
process.exit(1);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// src/cli.ts
|
|
541
|
+
var program = new import_commander.Command();
|
|
542
|
+
program.name("egi-telemetry").description("EGIntegrations Telemetry SDK CLI").version("0.2.0");
|
|
543
|
+
program.command("init").description("Initialize telemetry in your project").option("--engine <name>", "Engine name").option("--sku <version>", "SKU version").option("--url <url>", "Control Center URL").option("--token <token>", "Auth token (optional)").option("--modules <modules>", "Comma-separated module list").option("--output <path>", "Config output path", ".egi/telemetry.json").option("--framework <type>", "Framework (nextjs, express, fastify, generic)").option("--no-interactive", "Skip prompts, fail if args missing").option("--no-codegen", "Only generate config, skip integration code").option("--status-endpoint", "Generate status endpoint boilerplate").action(initCommand);
|
|
544
|
+
program.parse();
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@egintegrations/telemetry",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Telemetry SDK for EGIntegrations client engines",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"egi-telemetry": "./dist/cli.js"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"require": "./dist/index.js",
|
|
@@ -13,9 +16,12 @@
|
|
|
13
16
|
}
|
|
14
17
|
},
|
|
15
18
|
"scripts": {
|
|
16
|
-
"build": "
|
|
19
|
+
"build": "npm run build:lib && npm run build:cli",
|
|
20
|
+
"build:lib": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
21
|
+
"build:cli": "tsup src/cli.ts --format cjs --no-dts --shims",
|
|
17
22
|
"prepublishOnly": "npm run build",
|
|
18
|
-
"test": "echo \"No tests yet\""
|
|
23
|
+
"test": "echo \"No tests yet\"",
|
|
24
|
+
"dev:cli": "tsx src/cli.ts"
|
|
19
25
|
},
|
|
20
26
|
"keywords": [
|
|
21
27
|
"telemetry",
|
|
@@ -26,15 +32,20 @@
|
|
|
26
32
|
"author": "EGIntegrations",
|
|
27
33
|
"license": "PROPRIETARY",
|
|
28
34
|
"dependencies": {
|
|
29
|
-
"axios": "^1.6.0"
|
|
35
|
+
"axios": "^1.6.0",
|
|
36
|
+
"commander": "^12.0.0",
|
|
37
|
+
"prompts": "^2.4.2"
|
|
30
38
|
},
|
|
31
39
|
"devDependencies": {
|
|
32
40
|
"@types/node": "^20.0.0",
|
|
41
|
+
"@types/prompts": "^2.4.9",
|
|
33
42
|
"tsup": "^8.0.0",
|
|
43
|
+
"tsx": "^4.0.0",
|
|
34
44
|
"typescript": "^5.3.0"
|
|
35
45
|
},
|
|
36
46
|
"files": [
|
|
37
47
|
"dist",
|
|
38
|
-
"README.md"
|
|
48
|
+
"README.md",
|
|
49
|
+
"src/cli/standards"
|
|
39
50
|
]
|
|
40
51
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"code": "EKS",
|
|
4
|
+
"label": "AWS EKS (Kubernetes)",
|
|
5
|
+
"description": "Amazon Elastic Kubernetes Service",
|
|
6
|
+
"provider": "AWS",
|
|
7
|
+
"type": "managed-kubernetes",
|
|
8
|
+
"notes": "Preferred for production containerized workloads on AWS"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"code": "GKE",
|
|
12
|
+
"label": "Google Kubernetes Engine",
|
|
13
|
+
"description": "Google's managed Kubernetes service",
|
|
14
|
+
"provider": "GCP",
|
|
15
|
+
"type": "managed-kubernetes",
|
|
16
|
+
"notes": "Preferred for production containerized workloads on GCP"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"code": "AKS",
|
|
20
|
+
"label": "Azure Kubernetes Service",
|
|
21
|
+
"description": "Microsoft Azure managed Kubernetes",
|
|
22
|
+
"provider": "Azure",
|
|
23
|
+
"type": "managed-kubernetes",
|
|
24
|
+
"notes": "Preferred for production containerized workloads on Azure"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"code": "K8S",
|
|
28
|
+
"label": "Kubernetes (generic)",
|
|
29
|
+
"description": "Self-managed or other Kubernetes distributions",
|
|
30
|
+
"provider": "Various",
|
|
31
|
+
"type": "kubernetes",
|
|
32
|
+
"notes": "Use for DigitalOcean, Linode, self-hosted K8s"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"code": "DK",
|
|
36
|
+
"label": "Docker (generic container build/runtime)",
|
|
37
|
+
"description": "Docker containers, docker-compose",
|
|
38
|
+
"provider": "Various",
|
|
39
|
+
"type": "container",
|
|
40
|
+
"notes": "For Docker-based deployments without orchestration"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"code": "DKO",
|
|
44
|
+
"label": "Docker Offline (air-gapped/off-net)",
|
|
45
|
+
"description": "Docker in air-gapped or offline environments",
|
|
46
|
+
"provider": "Various",
|
|
47
|
+
"type": "container",
|
|
48
|
+
"notes": "For secure/isolated environments"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"code": "LM",
|
|
52
|
+
"label": "AWS Lambda",
|
|
53
|
+
"description": "AWS serverless functions",
|
|
54
|
+
"provider": "AWS",
|
|
55
|
+
"type": "serverless",
|
|
56
|
+
"notes": "For event-driven, serverless workloads"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"code": "CF",
|
|
60
|
+
"label": "Cloudflare (Workers/Pages)",
|
|
61
|
+
"description": "Cloudflare Workers and Pages",
|
|
62
|
+
"provider": "Cloudflare",
|
|
63
|
+
"type": "edge-compute",
|
|
64
|
+
"notes": "Edge compute and static hosting"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"code": "VE",
|
|
68
|
+
"label": "Vercel",
|
|
69
|
+
"description": "Vercel serverless platform",
|
|
70
|
+
"provider": "Vercel",
|
|
71
|
+
"type": "serverless",
|
|
72
|
+
"notes": "Optimized for Next.js and frontend frameworks"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"code": "FH",
|
|
76
|
+
"label": "Firebase Hosting",
|
|
77
|
+
"description": "Google Firebase static hosting",
|
|
78
|
+
"provider": "GCP",
|
|
79
|
+
"type": "static-hosting",
|
|
80
|
+
"notes": "For static sites and SPAs"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"code": "EC2",
|
|
84
|
+
"label": "AWS EC2",
|
|
85
|
+
"description": "Amazon Elastic Compute Cloud VMs",
|
|
86
|
+
"provider": "AWS",
|
|
87
|
+
"type": "vm",
|
|
88
|
+
"notes": "Traditional VM-based deployments"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"code": "CR",
|
|
92
|
+
"label": "Google Cloud Run",
|
|
93
|
+
"description": "Google's container-as-a-service platform",
|
|
94
|
+
"provider": "GCP",
|
|
95
|
+
"type": "managed-container",
|
|
96
|
+
"notes": "Managed container runtime with auto-scaling"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"code": "ECS",
|
|
100
|
+
"label": "AWS ECS",
|
|
101
|
+
"description": "Amazon Elastic Container Service",
|
|
102
|
+
"provider": "AWS",
|
|
103
|
+
"type": "managed-container",
|
|
104
|
+
"notes": "AWS-native container orchestration"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"code": "AP",
|
|
108
|
+
"label": "AWS App Runner",
|
|
109
|
+
"description": "AWS fully managed container application service",
|
|
110
|
+
"provider": "AWS",
|
|
111
|
+
"type": "managed-container",
|
|
112
|
+
"notes": "Simplified container deployments"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"code": "EXP",
|
|
116
|
+
"label": "Expo (mobile)",
|
|
117
|
+
"description": "Expo platform for React Native",
|
|
118
|
+
"provider": "Expo",
|
|
119
|
+
"type": "mobile-platform",
|
|
120
|
+
"notes": "React Native mobile app builds and OTA updates"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"code": "APP",
|
|
124
|
+
"label": "App Stores (iOS/Android)",
|
|
125
|
+
"description": "Apple App Store and Google Play Store",
|
|
126
|
+
"provider": "Apple/Google",
|
|
127
|
+
"type": "mobile-distribution",
|
|
128
|
+
"notes": "Native app distribution"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"code": "OP",
|
|
132
|
+
"label": "On-Prem (customer environment)",
|
|
133
|
+
"description": "Customer-managed infrastructure",
|
|
134
|
+
"provider": "Customer",
|
|
135
|
+
"type": "on-premise",
|
|
136
|
+
"notes": "Deployed to customer's own infrastructure"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"code": "LH",
|
|
140
|
+
"label": "Local Host (dev/test deliverable)",
|
|
141
|
+
"description": "Local development or testing environment",
|
|
142
|
+
"provider": "Dev",
|
|
143
|
+
"type": "local",
|
|
144
|
+
"notes": "Not for production use"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"code": "PYPI",
|
|
148
|
+
"label": "Python Package Index",
|
|
149
|
+
"description": "Python package distribution",
|
|
150
|
+
"provider": "PyPI",
|
|
151
|
+
"type": "package-registry",
|
|
152
|
+
"notes": "For Python SDK/library distribution"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"code": "NPM",
|
|
156
|
+
"label": "npm Registry",
|
|
157
|
+
"description": "Node.js package distribution",
|
|
158
|
+
"provider": "npm",
|
|
159
|
+
"type": "package-registry",
|
|
160
|
+
"notes": "For JavaScript/TypeScript package distribution"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"code": "GH",
|
|
164
|
+
"label": "GitHub Releases",
|
|
165
|
+
"description": "GitHub release artifacts",
|
|
166
|
+
"provider": "GitHub",
|
|
167
|
+
"type": "artifact-distribution",
|
|
168
|
+
"notes": "For CLI tools, binaries, and releases"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"code": "GHA",
|
|
172
|
+
"label": "GitHub Actions",
|
|
173
|
+
"description": "CI/CD on GitHub Actions",
|
|
174
|
+
"provider": "GitHub",
|
|
175
|
+
"type": "ci-cd",
|
|
176
|
+
"notes": "For test suites and automation that runs in CI"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"code": "AWS",
|
|
180
|
+
"label": "Amazon Web Services (generic)",
|
|
181
|
+
"description": "Generic AWS deployment",
|
|
182
|
+
"provider": "AWS",
|
|
183
|
+
"type": "cloud-platform",
|
|
184
|
+
"notes": "Use when specific AWS service is unknown or mixed"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"code": "GCP",
|
|
188
|
+
"label": "Google Cloud Platform (generic)",
|
|
189
|
+
"description": "Generic GCP deployment",
|
|
190
|
+
"provider": "GCP",
|
|
191
|
+
"type": "cloud-platform",
|
|
192
|
+
"notes": "Use when specific GCP service is unknown or mixed"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"code": "AZR",
|
|
196
|
+
"label": "Microsoft Azure (generic)",
|
|
197
|
+
"description": "Generic Azure deployment",
|
|
198
|
+
"provider": "Azure",
|
|
199
|
+
"type": "cloud-platform",
|
|
200
|
+
"notes": "Use when specific Azure service is unknown or mixed"
|
|
201
|
+
}
|
|
202
|
+
]
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"code": "API",
|
|
4
|
+
"label": "API service / microservice",
|
|
5
|
+
"description": "RESTful or GraphQL API endpoints, backend services",
|
|
6
|
+
"examples": ["FastAPI backend", "Express API", "Django REST", "gRPC service"],
|
|
7
|
+
"typical_platforms": ["EKS", "GKE", "DK", "LM"],
|
|
8
|
+
"notes": "Use for any backend service exposing HTTP/RPC endpoints"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"code": "BOT",
|
|
12
|
+
"label": "Chatbot / Automation agent",
|
|
13
|
+
"description": "Event-driven automation, background workers, message processors, bots",
|
|
14
|
+
"examples": ["Discord bot", "Slack bot", "Queue worker", "Cron jobs", "Telegram bot"],
|
|
15
|
+
"typical_platforms": ["EKS", "GKE", "DK", "LM"],
|
|
16
|
+
"notes": "Includes both conversational bots and background automation workers"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"code": "MA",
|
|
20
|
+
"label": "Mobile App (native or hybrid)",
|
|
21
|
+
"description": "Native or hybrid mobile applications for iOS/Android",
|
|
22
|
+
"examples": ["React Native app", "Flutter app", "Swift/Kotlin native", "Expo app"],
|
|
23
|
+
"typical_platforms": ["EXP", "CF", "APP"],
|
|
24
|
+
"notes": "SKU tracks the app binary/release, not backend services"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"code": "WA",
|
|
28
|
+
"label": "Web Application (interactive web apps, portals)",
|
|
29
|
+
"description": "Frontend web applications (SPAs, SSR, SSG)",
|
|
30
|
+
"examples": ["React SPA", "Next.js app", "Vue app", "Angular", "Svelte"],
|
|
31
|
+
"typical_platforms": ["VE", "CF", "EKS", "FH"],
|
|
32
|
+
"notes": "Use for user-facing web applications"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"code": "RAG",
|
|
36
|
+
"label": "Retrieval-Augmented Generation (AI systems with retrieval)",
|
|
37
|
+
"description": "RAG systems with vector search, embeddings, and LLM integration",
|
|
38
|
+
"examples": ["LangChain RAG", "LlamaIndex app", "Pinecone integration", "Semantic search"],
|
|
39
|
+
"typical_platforms": ["EKS", "LM", "EC2"],
|
|
40
|
+
"notes": "Specifically for RAG architectures, not general AI"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"code": "DA",
|
|
44
|
+
"label": "Data Analytics / Dashboard",
|
|
45
|
+
"description": "Data processing, analytics pipelines, BI dashboards",
|
|
46
|
+
"examples": ["Metabase", "Superset", "dbt models", "Tableau integration"],
|
|
47
|
+
"typical_platforms": ["EKS", "EC2", "GKE"],
|
|
48
|
+
"notes": "Use for analytics/BI tools and data visualization"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"code": "AI",
|
|
52
|
+
"label": "AI pipeline/agent (non-RAG)",
|
|
53
|
+
"description": "Machine learning models, inference APIs, training pipelines",
|
|
54
|
+
"examples": ["TensorFlow Serving", "PyTorch models", "Hugging Face", "OpenAI integration"],
|
|
55
|
+
"typical_platforms": ["EKS", "LM", "EC2"],
|
|
56
|
+
"notes": "Use for AI/ML services that aren't RAG-specific"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"code": "CMS",
|
|
60
|
+
"label": "Content Management System",
|
|
61
|
+
"description": "Content management systems, headless CMS, documentation sites",
|
|
62
|
+
"examples": ["Strapi", "Docusaurus", "Sanity", "WordPress"],
|
|
63
|
+
"typical_platforms": ["EKS", "VE", "FH"],
|
|
64
|
+
"notes": "Includes both traditional and headless CMS"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"code": "DB",
|
|
68
|
+
"label": "Database system / data layer deliverable",
|
|
69
|
+
"description": "Managed databases, data stores, database migrations",
|
|
70
|
+
"examples": ["PostgreSQL", "MongoDB", "Redis", "Elasticsearch"],
|
|
71
|
+
"typical_platforms": ["AWS", "GCP", "AZR", "DK"],
|
|
72
|
+
"notes": "Use for database instances or migration projects"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"code": "OP",
|
|
76
|
+
"label": "Operations Tool",
|
|
77
|
+
"description": "Internal tooling, admin panels, DevOps utilities, on-prem systems",
|
|
78
|
+
"examples": ["Admin dashboard", "Deployment tool", "Monitoring agent", "CLI tool"],
|
|
79
|
+
"typical_platforms": ["EKS", "DK", "EC2", "OP"],
|
|
80
|
+
"notes": "Internal tools not exposed to end users"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"code": "ETL",
|
|
84
|
+
"label": "Data pipelines (extract/transform/load)",
|
|
85
|
+
"description": "ETL/ELT pipelines, data ingestion, data transformation",
|
|
86
|
+
"examples": ["Airflow DAGs", "dbt", "Fivetran sync", "Custom ETL"],
|
|
87
|
+
"typical_platforms": ["LM", "EKS", "GKE", "EC2"],
|
|
88
|
+
"notes": "Any data pipeline or transformation workflow"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"code": "INT",
|
|
92
|
+
"label": "Integration/Orchestration layer",
|
|
93
|
+
"description": "Third-party API integrations, middleware, webhooks",
|
|
94
|
+
"examples": ["Stripe integration", "Salesforce sync", "Zapier connector", "Webhook processor"],
|
|
95
|
+
"typical_platforms": ["LM", "EKS", "CF"],
|
|
96
|
+
"notes": "Services that connect or orchestrate other systems"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"code": "FX",
|
|
100
|
+
"label": "Feature module/add-on shipped as a distinct unit",
|
|
101
|
+
"description": "Serverless functions, edge functions, feature modules",
|
|
102
|
+
"examples": ["AWS Lambda function", "Cloudflare Workers", "Vercel Functions"],
|
|
103
|
+
"typical_platforms": ["LM", "CF", "VE"],
|
|
104
|
+
"notes": "Small, single-purpose functions or modules"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"code": "SDK",
|
|
108
|
+
"label": "Client/developer SDK deliverable",
|
|
109
|
+
"description": "Client libraries, SDKs, packages for developers",
|
|
110
|
+
"examples": ["Python package", "npm library", "Go module", "REST client"],
|
|
111
|
+
"typical_platforms": ["PYPI", "NPM", "GH"],
|
|
112
|
+
"notes": "Distributed as packages, not deployed services"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"code": "CLI",
|
|
116
|
+
"label": "Command-line tool",
|
|
117
|
+
"description": "Command-line interface tools and utilities",
|
|
118
|
+
"examples": ["Deployment CLI", "Admin CLI", "Data migration tool"],
|
|
119
|
+
"typical_platforms": ["GH", "PYPI", "NPM"],
|
|
120
|
+
"notes": "Downloadable/installable CLI tools"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"code": "QA",
|
|
124
|
+
"label": "QA/Test harness deliverable",
|
|
125
|
+
"description": "Test suites, E2E testing, load testing, QA automation",
|
|
126
|
+
"examples": ["Playwright tests", "k6 load tests", "Postman collections", "Selenium"],
|
|
127
|
+
"typical_platforms": ["GHA", "EKS", "GKE"],
|
|
128
|
+
"notes": "Testing infrastructure and automation"
|
|
129
|
+
}
|
|
130
|
+
]
|