@omen.foundation/node-microservice-runtime 0.1.71 → 0.1.73

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.
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Utility functions for transforming service names and validating naming conventions.
3
+ */
4
+ /**
5
+ * Converts a string to PascalCase.
6
+ * @example "my-service" -> "MyService"
7
+ * @example "user_profile" -> "UserProfile"
8
+ */
9
+ export function toPascalCase(str) {
10
+ return str
11
+ .split(/[-_\s]+/)
12
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
13
+ .join('');
14
+ }
15
+ /**
16
+ * Converts a string to kebab-case.
17
+ * @example "MyService" -> "my-service"
18
+ * @example "user_profile" -> "user-profile"
19
+ */
20
+ export function toKebabCase(str) {
21
+ return str
22
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
23
+ .replace(/[_\s]+/g, '-')
24
+ .toLowerCase();
25
+ }
26
+ /**
27
+ * Converts a string to camelCase.
28
+ * @example "MyService" -> "myService"
29
+ * @example "user-profile" -> "userProfile"
30
+ */
31
+ export function toCamelCase(str) {
32
+ const pascal = toPascalCase(str);
33
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
34
+ }
35
+ /**
36
+ * Validates that a service name matches the required pattern.
37
+ * Only alphanumeric characters and underscores are allowed.
38
+ */
39
+ export function validateServiceName(name) {
40
+ return /^[A-Za-z0-9_]+$/.test(name);
41
+ }
42
+ //# sourceMappingURL=name-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name-utils.js","sourceRoot":"","sources":["../../../src/cli/utils/name-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAG;SACP,KAAK,CAAC,SAAS,CAAC;SAChB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SACzE,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,OAAO,GAAG;SACP,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC;SACnC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,WAAW,EAAE,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACjC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC","sourcesContent":["/**\r\n * Utility functions for transforming service names and validating naming conventions.\r\n */\r\n\r\n/**\r\n * Converts a string to PascalCase.\r\n * @example \"my-service\" -> \"MyService\"\r\n * @example \"user_profile\" -> \"UserProfile\"\r\n */\r\nexport function toPascalCase(str: string): string {\r\n return str\r\n .split(/[-_\\s]+/)\r\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\r\n .join('');\r\n}\r\n\r\n/**\r\n * Converts a string to kebab-case.\r\n * @example \"MyService\" -> \"my-service\"\r\n * @example \"user_profile\" -> \"user-profile\"\r\n */\r\nexport function toKebabCase(str: string): string {\r\n return str\r\n .replace(/([a-z])([A-Z])/g, '$1-$2')\r\n .replace(/[_\\s]+/g, '-')\r\n .toLowerCase();\r\n}\r\n\r\n/**\r\n * Converts a string to camelCase.\r\n * @example \"MyService\" -> \"myService\"\r\n * @example \"user-profile\" -> \"userProfile\"\r\n */\r\nexport function toCamelCase(str: string): string {\r\n const pascal = toPascalCase(str);\r\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\r\n}\r\n\r\n/**\r\n * Validates that a service name matches the required pattern.\r\n * Only alphanumeric characters and underscores are allowed.\r\n */\r\nexport function validateServiceName(name: string): boolean {\r\n return /^[A-Za-z0-9_]+$/.test(name);\r\n}\r\n\r\n"]}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Utility functions for interactive prompts in CLI commands.
3
+ */
4
+ /**
5
+ * Prompts the user for input with an optional default value.
6
+ */
7
+ export declare function promptInput(question: string, defaultValue?: string): Promise<string>;
8
+ /**
9
+ * Prompts the user for a yes/no answer.
10
+ * @returns true if user answers 'y' or 'yes', false otherwise
11
+ */
12
+ export declare function promptYesNo(question: string, defaultValue?: boolean): Promise<boolean>;
13
+ //# sourceMappingURL=prompt-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-utils.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/prompt-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;GAEG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS1F;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,UAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAczF"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Utility functions for interactive prompts in CLI commands.
3
+ */
4
+ import readline from 'node:readline';
5
+ import { stdin, stdout } from 'node:process';
6
+ /**
7
+ * Prompts the user for input with an optional default value.
8
+ */
9
+ export async function promptInput(question, defaultValue) {
10
+ const rl = readline.createInterface({ input: stdin, output: stdout });
11
+ return new Promise((resolve) => {
12
+ rl.question(defaultValue ? `${question} (${defaultValue}): ` : `${question}: `, (answer) => {
13
+ rl.close();
14
+ const value = answer.trim();
15
+ resolve(value === '' && defaultValue !== undefined ? defaultValue : value);
16
+ });
17
+ });
18
+ }
19
+ /**
20
+ * Prompts the user for a yes/no answer.
21
+ * @returns true if user answers 'y' or 'yes', false otherwise
22
+ */
23
+ export async function promptYesNo(question, defaultValue = true) {
24
+ const rl = readline.createInterface({ input: stdin, output: stdout });
25
+ const defaultText = defaultValue ? 'Y/n' : 'y/N';
26
+ return new Promise((resolve) => {
27
+ rl.question(`${question} (${defaultText}): `, (answer) => {
28
+ rl.close();
29
+ const normalized = answer.trim().toLowerCase();
30
+ if (normalized === '') {
31
+ resolve(defaultValue);
32
+ }
33
+ else {
34
+ resolve(normalized === 'y' || normalized === 'yes');
35
+ }
36
+ });
37
+ });
38
+ }
39
+ //# sourceMappingURL=prompt-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-utils.js","sourceRoot":"","sources":["../../../src/cli/utils/prompt-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE7C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAgB,EAAE,YAAqB;IACvE,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtE,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACrC,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,QAAQ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE;YACzF,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,KAAK,EAAE,IAAI,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAgB,EAAE,YAAY,GAAG,IAAI;IACrE,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACtE,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IACjD,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;QACtC,EAAE,CAAC,QAAQ,CAAC,GAAG,QAAQ,KAAK,WAAW,KAAK,EAAE,CAAC,MAAM,EAAE,EAAE;YACvD,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC/C,IAAI,UAAU,KAAK,EAAE,EAAE,CAAC;gBACtB,OAAO,CAAC,YAAY,CAAC,CAAC;YACxB,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,KAAK,CAAC,CAAC;YACtD,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\r\n * Utility functions for interactive prompts in CLI commands.\r\n */\r\n\r\nimport readline from 'node:readline';\r\nimport { stdin, stdout } from 'node:process';\r\n\r\n/**\r\n * Prompts the user for input with an optional default value.\r\n */\r\nexport async function promptInput(question: string, defaultValue?: string): Promise<string> {\r\n const rl = readline.createInterface({ input: stdin, output: stdout });\r\n return new Promise<string>((resolve) => {\r\n rl.question(defaultValue ? `${question} (${defaultValue}): ` : `${question}: `, (answer) => {\r\n rl.close();\r\n const value = answer.trim();\r\n resolve(value === '' && defaultValue !== undefined ? defaultValue : value);\r\n });\r\n });\r\n}\r\n\r\n/**\r\n * Prompts the user for a yes/no answer.\r\n * @returns true if user answers 'y' or 'yes', false otherwise\r\n */\r\nexport async function promptYesNo(question: string, defaultValue = true): Promise<boolean> {\r\n const rl = readline.createInterface({ input: stdin, output: stdout });\r\n const defaultText = defaultValue ? 'Y/n' : 'y/N';\r\n return new Promise<boolean>((resolve) => {\r\n rl.question(`${question} (${defaultText}): `, (answer) => {\r\n rl.close();\r\n const normalized = answer.trim().toLowerCase();\r\n if (normalized === '') {\r\n resolve(defaultValue);\r\n } else {\r\n resolve(normalized === 'y' || normalized === 'yes');\r\n }\r\n });\r\n });\r\n}\r\n\r\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Utility functions for fetching the latest version of the Node Microservice Runtime from npm registry.
3
+ */
4
+ /**
5
+ * Fetches the latest version of @omen.foundation/node-microservice-runtime from npm registry.
6
+ *
7
+ * @returns The latest version string (e.g., "0.1.68")
8
+ * @throws Error if version cannot be determined and no fallback is available
9
+ */
10
+ export declare function fetchLatestRuntimeVersion(): Promise<string>;
11
+ //# sourceMappingURL=version-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-utils.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/version-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH;;;;;GAKG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,MAAM,CAAC,CAgDjE"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Utility functions for fetching the latest version of the Node Microservice Runtime from npm registry.
3
+ */
4
+ /**
5
+ * Fetches the latest version of @omen.foundation/node-microservice-runtime from npm registry.
6
+ *
7
+ * @returns The latest version string (e.g., "0.1.68")
8
+ * @throws Error if version cannot be determined and no fallback is available
9
+ */
10
+ export async function fetchLatestRuntimeVersion() {
11
+ const PACKAGE_NAME = '@omen.foundation/node-microservice-runtime';
12
+ const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}`;
13
+ const TIMEOUT_MS = 5000;
14
+ try {
15
+ const controller = new AbortController();
16
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
17
+ const response = await fetch(REGISTRY_URL, {
18
+ signal: controller.signal,
19
+ headers: { Accept: 'application/json' },
20
+ });
21
+ clearTimeout(timeoutId);
22
+ if (!response.ok) {
23
+ throw new Error(`Registry returned ${response.status}`);
24
+ }
25
+ const data = (await response.json());
26
+ const latestVersion = data['dist-tags']?.latest;
27
+ if (latestVersion && typeof latestVersion === 'string') {
28
+ return latestVersion;
29
+ }
30
+ // Fallback: find highest version from versions object
31
+ const versions = Object.keys(data.versions || {});
32
+ if (versions.length > 0) {
33
+ // Sort versions semantically and return highest
34
+ const sorted = versions.sort((a, b) => compareVersions(b, a));
35
+ return sorted[0];
36
+ }
37
+ throw new Error('No versions found in registry response');
38
+ }
39
+ catch (error) {
40
+ // Try local fallback: check if we're in Microservice directory
41
+ const localVersion = await getLocalRuntimeVersion();
42
+ if (localVersion) {
43
+ console.warn(`Warning: Could not fetch from registry, using local version ${localVersion}`);
44
+ return localVersion;
45
+ }
46
+ // Final fallback: hardcoded minimum version
47
+ console.warn('Warning: Could not fetch latest version, using fallback version 0.1.0');
48
+ return '0.1.0';
49
+ }
50
+ }
51
+ /**
52
+ * Compares two semantic version strings.
53
+ * @returns Positive if a > b, negative if a < b, zero if equal
54
+ */
55
+ function compareVersions(a, b) {
56
+ const partsA = a.split('.').map(Number);
57
+ const partsB = b.split('.').map(Number);
58
+ const maxLength = Math.max(partsA.length, partsB.length);
59
+ for (let i = 0; i < maxLength; i++) {
60
+ const partA = partsA[i] ?? 0;
61
+ const partB = partsB[i] ?? 0;
62
+ if (partA > partB)
63
+ return 1;
64
+ if (partA < partB)
65
+ return -1;
66
+ }
67
+ return 0;
68
+ }
69
+ /**
70
+ * Attempts to get the runtime version from a local package.json file.
71
+ * This is used as a fallback when the npm registry is unavailable.
72
+ */
73
+ async function getLocalRuntimeVersion() {
74
+ try {
75
+ const fs = await import('node:fs/promises');
76
+ const path = await import('node:path');
77
+ const { fileURLToPath } = await import('node:url');
78
+ // Try to find package.json in the Microservice directory
79
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
80
+ const packageJsonPath = path.resolve(__dirname, '../../package.json');
81
+ const content = await fs.readFile(packageJsonPath, 'utf8');
82
+ const packageJson = JSON.parse(content);
83
+ if (packageJson.version && typeof packageJson.version === 'string') {
84
+ return packageJson.version;
85
+ }
86
+ }
87
+ catch {
88
+ // Ignore errors, return null to use final fallback
89
+ }
90
+ return null;
91
+ }
92
+ //# sourceMappingURL=version-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-utils.js","sourceRoot":"","sources":["../../../src/cli/utils/version-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB;IAC7C,MAAM,YAAY,GAAG,4CAA4C,CAAC;IAClE,MAAM,YAAY,GAAG,8BAA8B,YAAY,EAAE,CAAC;IAClE,MAAM,UAAU,GAAG,IAAI,CAAC;IAExB,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;QAEnE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,YAAY,EAAE;YACzC,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;SACxC,CAAC,CAAC;QAEH,YAAY,CAAC,SAAS,CAAC,CAAC;QAExB,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC;QAC5D,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAEhD,IAAI,aAAa,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,aAAa,CAAC;QACvB,CAAC;QAED,sDAAsD;QACtD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;QAClD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,gDAAgD;YAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9D,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,+DAA+D;QAC/D,MAAM,YAAY,GAAG,MAAM,sBAAsB,EAAE,CAAC;QACpD,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,+DAA+D,YAAY,EAAE,CAAC,CAAC;YAC5F,OAAO,YAAY,CAAC;QACtB,CAAC;QAED,4CAA4C;QAC5C,OAAO,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;QACtF,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,CAAS,EAAE,CAAS;IAC3C,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAEzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,KAAK,GAAG,KAAK;YAAE,OAAO,CAAC,CAAC;QAC5B,IAAI,KAAK,GAAG,KAAK;YAAE,OAAO,CAAC,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,sBAAsB;IACnC,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QAEnD,yDAAyD;QACzD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/D,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;QAEtE,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAyB,CAAC;QAEhE,IAAI,WAAW,CAAC,OAAO,IAAI,OAAO,WAAW,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACnE,OAAO,WAAW,CAAC,OAAO,CAAC;QAC7B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,mDAAmD;IACrD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["/**\r\n * Utility functions for fetching the latest version of the Node Microservice Runtime from npm registry.\r\n */\r\n\r\ninterface NpmRegistryResponse {\r\n 'dist-tags': {\r\n latest: string;\r\n [key: string]: string;\r\n };\r\n versions: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Fetches the latest version of @omen.foundation/node-microservice-runtime from npm registry.\r\n * \r\n * @returns The latest version string (e.g., \"0.1.68\")\r\n * @throws Error if version cannot be determined and no fallback is available\r\n */\r\nexport async function fetchLatestRuntimeVersion(): Promise<string> {\r\n const PACKAGE_NAME = '@omen.foundation/node-microservice-runtime';\r\n const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}`;\r\n const TIMEOUT_MS = 5000;\r\n\r\n try {\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);\r\n\r\n const response = await fetch(REGISTRY_URL, {\r\n signal: controller.signal,\r\n headers: { Accept: 'application/json' },\r\n });\r\n\r\n clearTimeout(timeoutId);\r\n\r\n if (!response.ok) {\r\n throw new Error(`Registry returned ${response.status}`);\r\n }\r\n\r\n const data = (await response.json()) as NpmRegistryResponse;\r\n const latestVersion = data['dist-tags']?.latest;\r\n\r\n if (latestVersion && typeof latestVersion === 'string') {\r\n return latestVersion;\r\n }\r\n\r\n // Fallback: find highest version from versions object\r\n const versions = Object.keys(data.versions || {});\r\n if (versions.length > 0) {\r\n // Sort versions semantically and return highest\r\n const sorted = versions.sort((a, b) => compareVersions(b, a));\r\n return sorted[0];\r\n }\r\n\r\n throw new Error('No versions found in registry response');\r\n } catch (error) {\r\n // Try local fallback: check if we're in Microservice directory\r\n const localVersion = await getLocalRuntimeVersion();\r\n if (localVersion) {\r\n console.warn(`Warning: Could not fetch from registry, using local version ${localVersion}`);\r\n return localVersion;\r\n }\r\n\r\n // Final fallback: hardcoded minimum version\r\n console.warn('Warning: Could not fetch latest version, using fallback version 0.1.0');\r\n return '0.1.0';\r\n }\r\n}\r\n\r\n/**\r\n * Compares two semantic version strings.\r\n * @returns Positive if a > b, negative if a < b, zero if equal\r\n */\r\nfunction compareVersions(a: string, b: string): number {\r\n const partsA = a.split('.').map(Number);\r\n const partsB = b.split('.').map(Number);\r\n const maxLength = Math.max(partsA.length, partsB.length);\r\n\r\n for (let i = 0; i < maxLength; i++) {\r\n const partA = partsA[i] ?? 0;\r\n const partB = partsB[i] ?? 0;\r\n if (partA > partB) return 1;\r\n if (partA < partB) return -1;\r\n }\r\n return 0;\r\n}\r\n\r\n/**\r\n * Attempts to get the runtime version from a local package.json file.\r\n * This is used as a fallback when the npm registry is unavailable.\r\n */\r\nasync function getLocalRuntimeVersion(): Promise<string | null> {\r\n try {\r\n const fs = await import('node:fs/promises');\r\n const path = await import('node:path');\r\n const { fileURLToPath } = await import('node:url');\r\n\r\n // Try to find package.json in the Microservice directory\r\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n const packageJsonPath = path.resolve(__dirname, '../../package.json');\r\n\r\n const content = await fs.readFile(packageJsonPath, 'utf8');\r\n const packageJson = JSON.parse(content) as { version?: string };\r\n\r\n if (packageJson.version && typeof packageJson.version === 'string') {\r\n return packageJson.version;\r\n }\r\n } catch {\r\n // Ignore errors, return null to use final fallback\r\n }\r\n return null;\r\n}\r\n\r\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAa,EAAe,KAAK,MAAM,EAAsB,MAAM,MAAM,CAAC;AAI1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAiCpD,UAAU,oBAAoB;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA0eD,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CA6L/F"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAa,EAAe,KAAK,MAAM,EAAsB,MAAM,MAAM,CAAC;AAI1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAiCpD,UAAU,oBAAoB;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAmmBD,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CA6L/F"}
package/dist/logger.js CHANGED
@@ -51,6 +51,93 @@ function mapPinoLevelToBeamableLevel(level) {
51
51
  return 'Info';
52
52
  }
53
53
  }
54
+ /**
55
+ * Initializes BetterStack OTLP log exporter if enabled.
56
+ * BetterStack is an optional logging provider that can be used alongside Beamable's ClickHouse logging
57
+ * for debugging and monitoring. This enables "double-pumping" logs to both ClickHouse and BetterStack.
58
+ *
59
+ * To enable BetterStack logging, set the following environment variables:
60
+ * - BEAM_BETTERSTACK_ENABLED=true (or 1)
61
+ * - BEAM_BETTERSTACK_TOKEN=<your-betterstack-ingestion-token>
62
+ * - BEAM_BETTERSTACK_ENDPOINT=<optional-custom-endpoint> (defaults to https://in.logs.betterstack.com)
63
+ *
64
+ * Default ingestion token: QxFSMKUoS66bgA4aZmqih7ec (from created BetterStack source)
65
+ *
66
+ * Logs will be sent to both:
67
+ * 1. Beamable ClickHouse (via OTLP collector) - for Portal integration
68
+ * 2. BetterStack (via OTLP HTTP) - for debugging and monitoring
69
+ *
70
+ * @param serviceName - Service name for resource attributes
71
+ * @param qualifiedServiceName - Qualified service name for resource attributes
72
+ * @param env - Environment configuration
73
+ * @returns BetterStack OTLP exporter if configured, null otherwise
74
+ */
75
+ async function initializeBetterStackLogging(serviceName, qualifiedServiceName, env) {
76
+ // Check if BetterStack logging is enabled
77
+ const betterStackEnabled = process.env.BEAM_BETTERSTACK_ENABLED === 'true' ||
78
+ process.env.BEAM_BETTERSTACK_ENABLED === '1';
79
+ if (!betterStackEnabled) {
80
+ return Promise.resolve(null);
81
+ }
82
+ // Get BetterStack configuration
83
+ // Token: Use BEAM_BETTERSTACK_TOKEN or BETTERSTACK_TOKEN environment variable
84
+ // Default token from created source: QxFSMKUoS66bgA4aZmqih7ec (can be overridden)
85
+ const betterStackToken = process.env.BEAM_BETTERSTACK_TOKEN || process.env.BETTERSTACK_TOKEN;
86
+ // Endpoint: BetterStack OTLP endpoint (defaults to EU region)
87
+ // Can be overridden via BEAM_BETTERSTACK_ENDPOINT or BETTERSTACK_ENDPOINT
88
+ const betterStackEndpoint = process.env.BEAM_BETTERSTACK_ENDPOINT ||
89
+ process.env.BETTERSTACK_ENDPOINT ||
90
+ 'https://in.logs.betterstack.com';
91
+ if (!betterStackToken) {
92
+ console.warn('[BetterStack] BetterStack logging is enabled but BEAM_BETTERSTACK_TOKEN is not set. Skipping BetterStack logging.');
93
+ return Promise.resolve(null);
94
+ }
95
+ try {
96
+ // Build resource attributes (same as main OTLP)
97
+ const resourceAttributes = {};
98
+ if (serviceName) {
99
+ resourceAttributes['service.namespace'] = serviceName;
100
+ resourceAttributes['service.name'] = serviceName;
101
+ }
102
+ if (qualifiedServiceName) {
103
+ resourceAttributes['service.instance.id'] = qualifiedServiceName;
104
+ }
105
+ if (env?.cid) {
106
+ resourceAttributes['beam.cid'] = String(env.cid);
107
+ }
108
+ if (env?.pid) {
109
+ resourceAttributes['beam.pid'] = String(env.pid);
110
+ }
111
+ if (env?.routingKey) {
112
+ resourceAttributes['beam.routing_key'] = String(env.routingKey);
113
+ }
114
+ // BetterStack OTLP endpoint format
115
+ // BetterStack accepts OTLP logs at: https://in.logs.betterstack.com/v1/logs
116
+ const endpointUrl = betterStackEndpoint.includes('/v1/logs')
117
+ ? betterStackEndpoint
118
+ : `${betterStackEndpoint.replace(/\/$/, '')}/v1/logs`;
119
+ // Create OTLP HTTP exporter for BetterStack
120
+ // BetterStack supports OTLP HTTP with JSON or Protobuf
121
+ // Using JSON format for better compatibility
122
+ const exporterOptions = {
123
+ url: endpointUrl,
124
+ headers: {
125
+ 'Authorization': `Bearer ${betterStackToken}`,
126
+ },
127
+ contentType: 'application/json', // BetterStack supports both JSON and Protobuf, JSON is more compatible
128
+ };
129
+ const exporter = new OTLPLogExporter(exporterOptions);
130
+ console.error(`[BetterStack] BetterStack logging initialized. Endpoint: ${endpointUrl}, Service: ${serviceName || 'unknown'}`);
131
+ return Promise.resolve(exporter);
132
+ }
133
+ catch (error) {
134
+ console.error('[BetterStack] Failed to initialize BetterStack logging:', error instanceof Error ? error.message : String(error));
135
+ if (error instanceof Error && error.stack) {
136
+ console.error('[BetterStack] Stack trace:', error.stack);
137
+ }
138
+ return Promise.resolve(null);
139
+ }
140
+ }
54
141
  /**
55
142
  * Initializes OpenTelemetry OTLP log exporter if configured.
56
143
  * Similar to C# microservices, checks for BEAM_OTEL_EXPORTER_OTLP_ENDPOINT or uses standard enabled flag.
@@ -203,14 +290,27 @@ async function initializeOtlpLogging(serviceName, qualifiedServiceName, env, log
203
290
  const baseResource = defaultResource();
204
291
  const customResource = resourceFromAttributes(resourceAttributes);
205
292
  const resource = baseResource.merge(customResource);
206
- // Create log record processor
293
+ // Create log record processor for Beamable ClickHouse
207
294
  // C# uses BatchLogRecordExportProcessor which batches logs for better performance
208
295
  // and includes retry logic. We should match this behavior.
209
- const processor = new BatchLogRecordProcessor(exporter);
210
- // Create logger provider with resource and processor
296
+ const processors = [new BatchLogRecordProcessor(exporter)];
297
+ // Optionally add BetterStack as a second exporter for double-pumping logs
298
+ // This allows developers to enable BetterStack for debugging/monitoring
299
+ try {
300
+ const betterStackExporter = await initializeBetterStackLogging(serviceName, qualifiedServiceName, env);
301
+ if (betterStackExporter) {
302
+ processors.push(new BatchLogRecordProcessor(betterStackExporter));
303
+ console.error('[BetterStack] BetterStack exporter added as second processor - logs will be sent to both ClickHouse and BetterStack');
304
+ }
305
+ }
306
+ catch (error) {
307
+ // If BetterStack initialization fails, continue without it
308
+ console.warn('[BetterStack] Failed to initialize BetterStack exporter (continuing without it):', error instanceof Error ? error.message : String(error));
309
+ }
310
+ // Create logger provider with resource and processors (both ClickHouse and optionally BetterStack)
211
311
  const loggerProvider = new LoggerProvider({
212
312
  resource: resource,
213
- processors: [processor],
313
+ processors: processors,
214
314
  });
215
315
  // Set as global logger provider
216
316
  logs.setGlobalLoggerProvider(loggerProvider);