c8y-nitro 0.9.2 → 0.10.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/dist/apiClient-kxv5KHYJ.mjs +206 -0
- package/dist/{autoBootstrap-qCcCgset.mjs → autoBootstrap-DUjHuHe_.mjs} +10 -6
- package/dist/{bootstrap-BcTCUiqG.mjs → bootstrap-DrMjty-k.mjs} +6 -2
- package/dist/{c8y-api-DpCWhaJU.mjs → c8y-api-DSs1xIIh.mjs} +1 -125
- package/dist/cli/index.d.mts +1 -1
- package/dist/cli/index.mjs +8 -6
- package/dist/{client-DCov9jV6.mjs → client-BjA5bBxj.mjs} +3 -1
- package/dist/{index-DHd7xpBX.d.mts → index-BEFoDy-y.d.mts} +6 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +30 -230
- package/dist/manifest-DV3UW9Dl.mjs +135 -0
- package/dist/{options-BBnR-y_W.mjs → options-54uJmuUV.mjs} +3 -2
- package/dist/{package-bl4QZvAk.mjs → package-Bt8__Vz0.mjs} +1 -1
- package/dist/{roles-CkUvL64s.mjs → roles-CNCObfa3.mjs} +2 -1
- package/dist/runtime/handlers/invalidateTenantOptions.mjs +1 -1
- package/dist/runtime/handlers/openapi.mjs +1 -1
- package/dist/runtime/handlers/rewriteRequestUrl.mjs +1 -1
- package/dist/typegen-BO_AkyNc.mjs +30 -0
- package/dist/types.d.mts +1 -1
- package/dist/utils.mjs +5 -2
- package/package.json +13 -13
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { n as createC8yManifestFromNitro } from "./manifest-DV3UW9Dl.mjs";
|
|
2
|
+
import { dirname, join, relative } from "node:path";
|
|
3
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
|
+
//#region src/module/apiClient.ts
|
|
5
|
+
/**
|
|
6
|
+
* Converts route path to PascalCase function name.
|
|
7
|
+
* Examples:
|
|
8
|
+
* - /health + get -> GetHealth
|
|
9
|
+
* - /health + post -> PostHealth
|
|
10
|
+
* - /someRoute -> SomeRoute (default)
|
|
11
|
+
* - /[id] or /:id + get -> GetById
|
|
12
|
+
* - /api/[multiple]/[params] -> GetApiByMultipleByParams
|
|
13
|
+
* @param path - The route path (e.g., "/api/[id]" or "/api/:id")
|
|
14
|
+
* @param method - HTTP method (get, post, put, delete, etc.)
|
|
15
|
+
*/
|
|
16
|
+
function generateFunctionName(path, method) {
|
|
17
|
+
return `${method}${path.replace(/^\//, "").replaceAll(".", "_").replaceAll("*", "").split("/").map((seg) => {
|
|
18
|
+
if (seg.startsWith("[") && seg.endsWith("]")) return `By${capitalize(seg.slice(1, -1))}`;
|
|
19
|
+
if (seg.startsWith(":")) return `By${capitalize(seg.slice(1))}`;
|
|
20
|
+
return capitalize(seg);
|
|
21
|
+
}).join("") || "Index"}`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Extracts route parameters from path.
|
|
25
|
+
* Example: "/api/[id]/items/[itemId]" or "/api/:id/items/:itemId" -> [{name: "id", type: "string"}, {name: "itemId", type: "string"}]
|
|
26
|
+
* @param path - The route path to extract parameters from
|
|
27
|
+
*/
|
|
28
|
+
function extractParams(path) {
|
|
29
|
+
const params = [];
|
|
30
|
+
const segments = path.split("/");
|
|
31
|
+
for (const seg of segments) if (seg.startsWith("[") && seg.endsWith("]")) {
|
|
32
|
+
const name = seg.slice(1, -1);
|
|
33
|
+
params.push(name);
|
|
34
|
+
} else if (seg.startsWith(":")) {
|
|
35
|
+
const name = seg.slice(1);
|
|
36
|
+
params.push(name);
|
|
37
|
+
}
|
|
38
|
+
return params;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Capitalizes first letter of a string.
|
|
42
|
+
* @param str - The string to capitalize
|
|
43
|
+
*/
|
|
44
|
+
function capitalize(str) {
|
|
45
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Converts a file name to a valid JavaScript class name.
|
|
49
|
+
* Only allows letters, numbers (not at start), $ and _.
|
|
50
|
+
* Removes or replaces invalid characters and ensures PascalCase.
|
|
51
|
+
* Always prefixed with "Generated" for clarity.
|
|
52
|
+
* @param fileName - The file name to convert
|
|
53
|
+
* @example "my-service-api" -> "GeneratedMyServiceApi"
|
|
54
|
+
* @example "playgroundAPIClient" -> "GeneratedPlaygroundAPIClient"
|
|
55
|
+
* @example "special@chars!" -> "GeneratedSpecialchars"
|
|
56
|
+
*/
|
|
57
|
+
function toValidClassName(fileName) {
|
|
58
|
+
let cleaned = fileName.replace(/\.(ts|js|mjs|cjs|tsx|jsx)$/, "").replace(/[-_.]/g, " ");
|
|
59
|
+
cleaned = cleaned.replace(/[^a-zA-Z0-9\s$]/g, "");
|
|
60
|
+
return `Generated${cleaned.split(/\s+/).filter((word) => word.length > 0).map((word) => capitalize(word)).join("")}`;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Generates TypeScript method code for a route in an Angular service.
|
|
64
|
+
* @param route - Parsed route information including path, method, params, and return type
|
|
65
|
+
*/
|
|
66
|
+
function generateMethod(route) {
|
|
67
|
+
const hasParams = route.params.length > 0;
|
|
68
|
+
let inlineParamsType = "";
|
|
69
|
+
if (hasParams) inlineParamsType = `{ ${route.params.map((name) => `${name}: string | number`).join("; ")} }`;
|
|
70
|
+
const methodParam = hasParams ? `params: ${inlineParamsType}` : "";
|
|
71
|
+
const returnTypeAnnotation = `Promise<${route.returnType}>`;
|
|
72
|
+
let pathExpression = `\`\${this.BASE_PATH}${route.path}\``;
|
|
73
|
+
if (hasParams) pathExpression = `\`\${this.BASE_PATH}${route.path.replace(/\[([^\]]+)\]/g, (_, paramName) => `\${params.${paramName}}`)}\``;
|
|
74
|
+
const fetchOptions = `{ method: '${route.method}', headers: { 'Content-Type': 'application/json' } }`;
|
|
75
|
+
return ` async ${route.functionName}(${methodParam}): ${returnTypeAnnotation} {
|
|
76
|
+
const response = await this.fetchClient.fetch(${pathExpression}, ${fetchOptions});
|
|
77
|
+
return this.serialize(response);
|
|
78
|
+
}`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Helper types for proper type serialization from Nitro.
|
|
82
|
+
* https://github.com/nitrojs/nitro/blob/67b43f2692a41728a2759462b6982c6872ed3a81/src/types/fetch/_serialize.ts
|
|
83
|
+
*/
|
|
84
|
+
const serializationTypes = `type JsonPrimitive = string | number | boolean | string | number | boolean | null;
|
|
85
|
+
type NonJsonPrimitive = undefined | Function | symbol;
|
|
86
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
87
|
+
type FilterKeys<TObj extends object, TFilter> = { [TKey in keyof TObj]: TObj[TKey] extends TFilter ? TKey : never }[keyof TObj];
|
|
88
|
+
type Serialize<T> = IsAny<T> extends true ? any : T extends JsonPrimitive | undefined ? T : T extends Map<any, any> | Set<any> ? Record<string, never> : T extends NonJsonPrimitive ? never : T extends {
|
|
89
|
+
toJSON: () => infer U;
|
|
90
|
+
} ? U : T extends [] ? [] : T extends [unknown, ...unknown[]] ? SerializeTuple<T> : T extends ReadonlyArray<infer U> ? (U extends NonJsonPrimitive ? null : Serialize<U>)[] : T extends object ? SerializeObject<T> : never;
|
|
91
|
+
type SerializeTuple<T extends [unknown, ...unknown[]]> = { [k in keyof T]: T[k] extends NonJsonPrimitive ? null : Serialize<T[k]> };
|
|
92
|
+
type SerializeObject<T extends object> = { [k in keyof Omit<T, FilterKeys<T, NonJsonPrimitive>>]: Serialize<T[k]> };
|
|
93
|
+
type Simplify<TType> = TType extends any[] | Date ? TType : { [K in keyof TType]: Simplify<TType[K]> };
|
|
94
|
+
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
|
|
95
|
+
type C8ySerialize<T> = T extends void | undefined | null ? undefined : T extends string ? string : Simplify<Serialize<T>>;
|
|
96
|
+
`;
|
|
97
|
+
/**
|
|
98
|
+
* Generates complete Angular API client service.
|
|
99
|
+
* @param routes - Array of parsed routes to generate methods for
|
|
100
|
+
* @param contextPath - The microservice context path (e.g., "my-service")
|
|
101
|
+
* @param className - The class name for the generated service (e.g., "PlaygroundAPIClient")
|
|
102
|
+
*/
|
|
103
|
+
function generateAPIClient(routes, contextPath, className) {
|
|
104
|
+
const methods = routes.map((route) => generateMethod(route)).join("\n\n");
|
|
105
|
+
return `/* eslint-disable eslint-comments/no-unlimited-disable */
|
|
106
|
+
/* eslint-disable */
|
|
107
|
+
/**
|
|
108
|
+
* Auto-generated Cumulocity API Client
|
|
109
|
+
* Generated by c8y-nitro
|
|
110
|
+
*
|
|
111
|
+
* This Angular service provides typed methods for all Nitro routes.
|
|
112
|
+
* Each method corresponds to a route handler and returns properly typed responses.
|
|
113
|
+
*/
|
|
114
|
+
import { Injectable, inject } from '@angular/core'
|
|
115
|
+
import { FetchClient } from '@c8y/client'
|
|
116
|
+
|
|
117
|
+
// Type helpers for proper serialization
|
|
118
|
+
${serializationTypes}
|
|
119
|
+
|
|
120
|
+
@Injectable({ providedIn: 'root' })
|
|
121
|
+
export class ${className} {
|
|
122
|
+
private readonly BASE_PATH = '/service/${contextPath}';
|
|
123
|
+
private readonly fetchClient: FetchClient = inject(FetchClient)
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Serializes the response based on content type and body.
|
|
127
|
+
* - Empty body (null, undefined, void) -> undefined
|
|
128
|
+
* - text/plain (string) -> raw text
|
|
129
|
+
* - application/json (number, boolean, object, array) -> JSON parsed
|
|
130
|
+
*/
|
|
131
|
+
private async serialize(response: Response): Promise<any> {
|
|
132
|
+
const contentType = response.headers.get('content-type');
|
|
133
|
+
const text = await response.text();
|
|
134
|
+
|
|
135
|
+
// Handle empty responses (null, undefined, empty return)
|
|
136
|
+
if (!text || text.length === 0) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Handle plain text responses (strings)
|
|
141
|
+
if (contentType?.includes('text/plain')) {
|
|
142
|
+
return text;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Handle JSON responses (numbers, booleans, objects, arrays)
|
|
146
|
+
return JSON.parse(text);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
${methods}
|
|
150
|
+
}`;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Generates the TypeScript return type for a route handler.
|
|
154
|
+
* @param handlerPath - Absolute path to the route handler file
|
|
155
|
+
* @param outputFile - Absolute path to the output API client file
|
|
156
|
+
* @returns TypeScript return type string with proper serialization wrappers
|
|
157
|
+
*/
|
|
158
|
+
function getReturnType(handlerPath, outputFile) {
|
|
159
|
+
const relativeHandlerPath = relative(dirname(outputFile), handlerPath);
|
|
160
|
+
return `C8ySerialize<Awaited<ReturnType<typeof import('${(relativeHandlerPath.startsWith(".") ? relativeHandlerPath : `./${relativeHandlerPath}`).replace(/\.ts$/, "")}').default>>>`;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Writes the generated API client to disk.
|
|
164
|
+
* @param nitro - Nitro instance
|
|
165
|
+
* @param options - Complete module options including apiClient and manifest
|
|
166
|
+
*/
|
|
167
|
+
async function writeAPIClient(nitro, options) {
|
|
168
|
+
const { apiClient: apiClientOptions } = options;
|
|
169
|
+
if (!apiClientOptions) {
|
|
170
|
+
nitro.logger.debug("API client generation skipped: no apiClient options provided");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const manifest = await createC8yManifestFromNitro(nitro);
|
|
174
|
+
const serviceName = manifest.name;
|
|
175
|
+
const serviceContextPath = apiClientOptions.contextPath ?? manifest.contextPath ?? serviceName;
|
|
176
|
+
const name = `${serviceName}APIClient`;
|
|
177
|
+
const rootDir = nitro.options.rootDir;
|
|
178
|
+
const outputDir = join(rootDir, apiClientOptions.dir);
|
|
179
|
+
const outputFile = join(outputDir, `${name}.ts`);
|
|
180
|
+
const routes = (nitro.routing.routes._routes ?? []).filter((route) => {
|
|
181
|
+
return !route.handler.includes("nitro/dist/runtime/internal");
|
|
182
|
+
}).map((route) => {
|
|
183
|
+
const path = route.route;
|
|
184
|
+
const method = (!route.method ? "GET" : route.method).toUpperCase();
|
|
185
|
+
const params = extractParams(path);
|
|
186
|
+
const functionName = generateFunctionName(path, method);
|
|
187
|
+
const returnType = getReturnType(route.handler, outputFile);
|
|
188
|
+
return {
|
|
189
|
+
path: path.replace(/:([^/]+)/g, "[$1]"),
|
|
190
|
+
method,
|
|
191
|
+
functionName,
|
|
192
|
+
params,
|
|
193
|
+
returnType
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
if (routes.length === 0) {
|
|
197
|
+
nitro.logger.warn("No routes found to generate API client");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const code = generateAPIClient(routes, serviceContextPath, toValidClassName(name));
|
|
201
|
+
await mkdir(outputDir, { recursive: true });
|
|
202
|
+
await writeFile(outputFile, code, "utf-8");
|
|
203
|
+
nitro.logger.success(`Generated API client with ${routes.length} routes at: ${relative(rootDir, outputFile)}`);
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
206
|
+
export { writeAPIClient as t };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as createC8yManifest } from "./manifest-DV3UW9Dl.mjs";
|
|
2
|
+
import { a as findMicroserviceByName, d as updateMicroservice, l as subscribeToApplication, n as createBasicAuthHeader, o as getBootstrapCredentials, r as createMicroservice } from "./c8y-api-DSs1xIIh.mjs";
|
|
2
3
|
import { t as writeBootstrapCredentials } from "./env-file-BrOTmVjA.mjs";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
@@ -55,7 +56,8 @@ async function readManifestCache(rootDir) {
|
|
|
55
56
|
* @param state - The state to persist (schema version is filled in automatically)
|
|
56
57
|
*/
|
|
57
58
|
async function writeManifestCache(rootDir, state) {
|
|
58
|
-
|
|
59
|
+
const dir = getCacheDir(rootDir);
|
|
60
|
+
await mkdir(dir, { recursive: true });
|
|
59
61
|
const payload = {
|
|
60
62
|
schemaVersion: CACHE_SCHEMA_VERSION,
|
|
61
63
|
...state
|
|
@@ -138,10 +140,12 @@ async function syncBootstrappedMicroservice(options) {
|
|
|
138
140
|
const appId = existingApp.id;
|
|
139
141
|
const isRealService = Boolean(existingApp.activeVersionId);
|
|
140
142
|
const cache = await readManifestCache(rootDir);
|
|
141
|
-
if (!cache || cache.manifestHash !== currentHash || cache.appId !== appId)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
if (!cache || cache.manifestHash !== currentHash || cache.appId !== appId) {
|
|
144
|
+
if (isRealService) logger.warn(`Local manifest for "${manifest.name}" differs from the deployed microservice on the development tenant. The running service was left untouched. Redeploy or run \`npx c8y-nitro bootstrap\` to update it explicitly.`);
|
|
145
|
+
else {
|
|
146
|
+
await updateMicroservice(baseUrl, appId, manifest, authHeader);
|
|
147
|
+
logger.success(`Updated microservice "${manifest.name}" with changed manifest.`);
|
|
148
|
+
}
|
|
145
149
|
}
|
|
146
150
|
const hasBootstrapCreds = BOOTSTRAP_ENV_VARS.every((v) => env[v]);
|
|
147
151
|
const appChanged = cache?.appId !== appId;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as createC8yManifest } from "./manifest-DV3UW9Dl.mjs";
|
|
2
|
+
import { a as findMicroserviceByName, d as updateMicroservice, l as subscribeToApplication, n as createBasicAuthHeader, o as getBootstrapCredentials, r as createMicroservice } from "./c8y-api-DSs1xIIh.mjs";
|
|
2
3
|
import { t as writeBootstrapCredentials } from "./env-file-BrOTmVjA.mjs";
|
|
3
4
|
import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-BVQHqlgV.mjs";
|
|
4
5
|
import { defineCommand, runCommand } from "citty";
|
|
@@ -52,7 +53,10 @@ var bootstrap_default = defineCommand({
|
|
|
52
53
|
});
|
|
53
54
|
consola.success(`Bootstrap credentials written to ${envFileName}`);
|
|
54
55
|
if (manifest.roles && manifest.roles.length > 0) {
|
|
55
|
-
if (await consola.prompt("Do you want to manage microservice roles for your development user?", { type: "confirm" }))
|
|
56
|
+
if (await consola.prompt("Do you want to manage microservice roles for your development user?", { type: "confirm" })) {
|
|
57
|
+
const rolesCommand = await import("./roles-CNCObfa3.mjs").then((r) => r.default);
|
|
58
|
+
await runCommand(rolesCommand, { rawArgs: [] });
|
|
59
|
+
}
|
|
56
60
|
}
|
|
57
61
|
consola.success("Bootstrap complete!");
|
|
58
62
|
}
|
|
@@ -1,128 +1,4 @@
|
|
|
1
|
-
import { readPackage } from "pkg-types";
|
|
2
1
|
import { Buffer } from "node:buffer";
|
|
3
|
-
//#region src/module/constants.ts
|
|
4
|
-
const GENERATED_LIVENESS_ROUTE = "/_c8y_nitro/liveness";
|
|
5
|
-
const GENERATED_READINESS_ROUTE = "/_c8y_nitro/readiness";
|
|
6
|
-
const GENERATED_INVALIDATE_TENANT_OPTIONS_ROUTE = "/_c8y_nitro/invalidate-tenant-options";
|
|
7
|
-
//#endregion
|
|
8
|
-
//#region src/module/manifest.ts
|
|
9
|
-
const DEFAULT_OPENAPI_ROUTE = "/_openapi.json";
|
|
10
|
-
const ROLE_OPTION_MANAGEMENT_READ = "ROLE_OPTION_MANAGEMENT_READ";
|
|
11
|
-
/**
|
|
12
|
-
* Cumulocity microservice name constraints: lowercase letters, digits and
|
|
13
|
-
* hyphens only, 1-23 characters. Enforced by the platform on upload, so we
|
|
14
|
-
* fail early with a clear message instead of a cryptic API rejection.
|
|
15
|
-
* @see https://github.com/schplitt/c8y-nitro/issues/75
|
|
16
|
-
*/
|
|
17
|
-
const MICROSERVICE_NAME_REGEX = /^[a-z0-9-]{1,23}$/;
|
|
18
|
-
/**
|
|
19
|
-
* Decide whether Nitro will expose an OpenAPI spec in the deployed build,
|
|
20
|
-
* and if so return the configured route (with leading slash).
|
|
21
|
-
* Returns null when OpenAPI is not enabled for production.
|
|
22
|
-
* @param config - Nitro config (or options) to inspect
|
|
23
|
-
*/
|
|
24
|
-
function resolveNitroOpenApiRoute(config) {
|
|
25
|
-
if (!config.experimental?.openAPI) return null;
|
|
26
|
-
if (!config.openAPI?.production) return null;
|
|
27
|
-
return config.openAPI?.route || DEFAULT_OPENAPI_ROUTE;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Pull the parts of a Nitro config that feed manifest generation.
|
|
31
|
-
* @param config - Nitro config (or options) loaded from disk or from a Nitro instance
|
|
32
|
-
*/
|
|
33
|
-
function extractManifestInputs(config) {
|
|
34
|
-
return {
|
|
35
|
-
options: config.c8y?.manifest ?? {},
|
|
36
|
-
openApiSpec: resolveNitroOpenApiRoute(config) ?? void 0
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function validateManifestSettings(options) {
|
|
40
|
-
const invalidSettings = options.settings?.filter((setting) => typeof setting.defaultValue !== "string" || setting.defaultValue.length === 0) ?? [];
|
|
41
|
-
if (invalidSettings.length === 0) return;
|
|
42
|
-
const invalidKeys = invalidSettings.map((setting) => `"${setting.key}"`).join(", ");
|
|
43
|
-
throw new Error(`manifest.settings entries must define a non-empty defaultValue. Invalid keys: ${invalidKeys}`);
|
|
44
|
-
}
|
|
45
|
-
async function readPackageJsonFieldsForManifest(rootDir, logger) {
|
|
46
|
-
logger?.debug(`Reading package file from ${rootDir}`);
|
|
47
|
-
const pkg = await readPackage(rootDir);
|
|
48
|
-
const name = pkg.name?.replace(/^@[^/]+\//, "");
|
|
49
|
-
const version = pkg.version;
|
|
50
|
-
const author = pkg.author;
|
|
51
|
-
const authorName = typeof author === "string" ? author : author?.name;
|
|
52
|
-
const authorEmail = typeof author === "string" ? void 0 : author?.email;
|
|
53
|
-
const authorUrl = typeof author === "string" ? void 0 : author?.url;
|
|
54
|
-
if (!name || !version || !authorName) throw new Error("package.json must contain name, version, and author name fields");
|
|
55
|
-
if (!MICROSERVICE_NAME_REGEX.test(name)) throw new Error(`Invalid microservice name "${name}". Cumulocity requires the name to match ${MICROSERVICE_NAME_REGEX.source} (lowercase letters, digits and hyphens only, 1-23 characters). Adjust the "name" field in package.json accordingly (scope like "@org/" is stripped and does not count).`);
|
|
56
|
-
const support = typeof pkg.bugs === "string" ? pkg.bugs : pkg.bugs?.url ?? pkg.bugs?.email;
|
|
57
|
-
const provider = {
|
|
58
|
-
name: authorName ?? name,
|
|
59
|
-
domain: authorUrl ?? pkg.homepage,
|
|
60
|
-
support: support ?? authorEmail
|
|
61
|
-
};
|
|
62
|
-
logger?.debug(`Found package.json fields for manifest: name=${name}, version=${version}, provider=${JSON.stringify(provider)}`);
|
|
63
|
-
return {
|
|
64
|
-
name,
|
|
65
|
-
version,
|
|
66
|
-
provider,
|
|
67
|
-
contextPath: name
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Create a Cumulocity manifest from a project root and its Nitro config.
|
|
72
|
-
* Reads `package.json` for identity fields, takes user manifest options from
|
|
73
|
-
* `nitroConfig.c8y.manifest`, and auto-injects `openApiSpec` when Nitro
|
|
74
|
-
* OpenAPI is enabled for production.
|
|
75
|
-
*
|
|
76
|
-
* Used by both the Nitro module and the CLI commands.
|
|
77
|
-
* @param rootDir - Directory containing the project's `package.json`
|
|
78
|
-
* @param nitroConfig - Nitro config (or options) for this project
|
|
79
|
-
* @param logger - Optional logger for debug output
|
|
80
|
-
*/
|
|
81
|
-
async function createC8yManifest(rootDir, nitroConfig = {}, logger) {
|
|
82
|
-
const { options, openApiSpec } = extractManifestInputs(nitroConfig);
|
|
83
|
-
validateManifestSettings(options);
|
|
84
|
-
const { name, version, provider, ...restManifestFields } = await readPackageJsonFieldsForManifest(rootDir, logger);
|
|
85
|
-
const probeFields = {};
|
|
86
|
-
if (!options.livenessProbe?.httpGet) probeFields.livenessProbe = {
|
|
87
|
-
...options.livenessProbe,
|
|
88
|
-
httpGet: { path: GENERATED_LIVENESS_ROUTE }
|
|
89
|
-
};
|
|
90
|
-
if (!options.readinessProbe?.httpGet) probeFields.readinessProbe = {
|
|
91
|
-
...options.readinessProbe,
|
|
92
|
-
httpGet: { path: GENERATED_READINESS_ROUTE }
|
|
93
|
-
};
|
|
94
|
-
const key = `${name}-key`;
|
|
95
|
-
let requiredRoles = options.requiredRoles ? [...options.requiredRoles] : void 0;
|
|
96
|
-
if (options.settings && options.settings.length > 0 && !requiredRoles?.includes(ROLE_OPTION_MANAGEMENT_READ)) {
|
|
97
|
-
requiredRoles = [...requiredRoles ?? [], ROLE_OPTION_MANAGEMENT_READ];
|
|
98
|
-
logger?.debug(`Auto-added ${ROLE_OPTION_MANAGEMENT_READ} to requiredRoles because manifest.settings are defined`);
|
|
99
|
-
}
|
|
100
|
-
const manifest = {
|
|
101
|
-
...restManifestFields,
|
|
102
|
-
...probeFields,
|
|
103
|
-
...options,
|
|
104
|
-
...requiredRoles !== void 0 ? { requiredRoles } : {},
|
|
105
|
-
...openApiSpec ? { openApiSpec } : {},
|
|
106
|
-
provider,
|
|
107
|
-
name,
|
|
108
|
-
version,
|
|
109
|
-
apiVersion: "v2",
|
|
110
|
-
key,
|
|
111
|
-
type: "MICROSERVICE"
|
|
112
|
-
};
|
|
113
|
-
if (openApiSpec) logger?.debug(`Auto-added openApiSpec="${openApiSpec}" to manifest (Nitro OpenAPI is enabled)`);
|
|
114
|
-
logger?.debug(`Created Cumulocity manifest: ${JSON.stringify(manifest, null, 2)}`);
|
|
115
|
-
return manifest;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Creates a Cumulocity manifest from a Nitro instance.
|
|
119
|
-
* Convenience wrapper for use in the Nitro module.
|
|
120
|
-
* @param nitro - The Nitro instance
|
|
121
|
-
*/
|
|
122
|
-
async function createC8yManifestFromNitro(nitro) {
|
|
123
|
-
return createC8yManifest(nitro.options.rootDir, nitro.options, nitro.logger);
|
|
124
|
-
}
|
|
125
|
-
//#endregion
|
|
126
2
|
//#region src/cli/utils/c8y-api.ts
|
|
127
3
|
/**
|
|
128
4
|
* Creates a Basic Auth header for Cumulocity API requests.
|
|
@@ -432,4 +308,4 @@ async function deleteTenantOption(baseUrl, category, key, authHeader) {
|
|
|
432
308
|
}
|
|
433
309
|
}
|
|
434
310
|
//#endregion
|
|
435
|
-
export {
|
|
311
|
+
export { findMicroserviceByName as a, getTenantOptionsByCategory as c, updateMicroservice as d, upsertTenantOption as f, deleteTenantOption as i, subscribeToApplication as l, createBasicAuthHeader as n, getBootstrapCredentials as o, createMicroservice as r, getCurrentApplicationSettings as s, assignUserRole as t, unassignUserRole as u };
|
package/dist/cli/index.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import { n as name, r as version, t as description } from "../package-
|
|
1
|
+
import { n as name, r as version, t as description } from "../package-Bt8__Vz0.mjs";
|
|
2
2
|
import { defineCommand, runMain } from "citty";
|
|
3
3
|
//#region src/cli/index.ts
|
|
4
|
-
|
|
4
|
+
const main = defineCommand({
|
|
5
5
|
meta: {
|
|
6
6
|
name,
|
|
7
7
|
version,
|
|
8
8
|
description
|
|
9
9
|
},
|
|
10
10
|
subCommands: {
|
|
11
|
-
bootstrap: () => import("../bootstrap-
|
|
12
|
-
roles: () => import("../roles-
|
|
13
|
-
options: () => import("../options-
|
|
11
|
+
bootstrap: () => import("../bootstrap-DrMjty-k.mjs").then((r) => r.default),
|
|
12
|
+
roles: () => import("../roles-CNCObfa3.mjs").then((r) => r.default),
|
|
13
|
+
options: () => import("../options-54uJmuUV.mjs").then((r) => r.default),
|
|
14
|
+
typegen: () => import("../typegen-BO_AkyNc.mjs").then((r) => r.default)
|
|
14
15
|
}
|
|
15
|
-
})
|
|
16
|
+
});
|
|
17
|
+
runMain(main);
|
|
16
18
|
//#endregion
|
|
17
19
|
export {};
|
|
@@ -262,7 +262,9 @@ const useDeployedTenantCredentials = Object.assign(async () => {
|
|
|
262
262
|
function useUserClient(requestOrEvent) {
|
|
263
263
|
const request = "req" in requestOrEvent ? requestOrEvent.req : requestOrEvent;
|
|
264
264
|
if (request.context?.["c8y_user_client"]) return request.context["c8y_user_client"];
|
|
265
|
-
const
|
|
265
|
+
const headers = convertRequestHeadersToC8yFormat(request);
|
|
266
|
+
const auth = new MicroserviceClientRequestAuth(headers);
|
|
267
|
+
const client = new Client(auth, process.env.C8Y_BASEURL);
|
|
266
268
|
request.context ??= {};
|
|
267
269
|
request.context["c8y_user_client"] = client;
|
|
268
270
|
return client;
|
|
@@ -381,6 +381,12 @@ interface C8YZipOptions {
|
|
|
381
381
|
* @default './'
|
|
382
382
|
*/
|
|
383
383
|
outputDir?: string;
|
|
384
|
+
/**
|
|
385
|
+
* ZIP DEFLATE compression level used for `image.tar` and `cumulocity.json`.
|
|
386
|
+
* Valid values are 1 (faster, larger) to 9 (slower, smaller).
|
|
387
|
+
* @default 6
|
|
388
|
+
*/
|
|
389
|
+
compressionLevel?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
|
384
390
|
/**
|
|
385
391
|
* Configuration of the "cumulocity.json" manifest file used for the zip.
|
|
386
392
|
*/
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as C8yNitroModuleOptions } from "./index-
|
|
1
|
+
import { n as C8yNitroModuleOptions } from "./index-BEFoDy-y.mjs";
|
|
2
2
|
import { t as TenantCredentials } from "./credentials-eKnJ7m9Y.mjs";
|
|
3
3
|
import { NitroModule } from "nitro/types";
|
|
4
4
|
//#region src/index.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { a as GENERATED_INVALIDATE_TENANT_OPTIONS_ROUTE, i as C8Y_NITRO_TYPES_FILE, n as createC8yManifestFromNitro, o as GENERATED_LIVENESS_ROUTE, r as C8Y_NITRO_TYPES_DIR, s as GENERATED_READINESS_ROUTE, t as createC8yManifest } from "./manifest-DV3UW9Dl.mjs";
|
|
2
|
+
import { t as writeAPIClient } from "./apiClient-kxv5KHYJ.mjs";
|
|
3
|
+
import { t as autoBootstrap } from "./autoBootstrap-DUjHuHe_.mjs";
|
|
4
|
+
import { n as name } from "./package-Bt8__Vz0.mjs";
|
|
5
|
+
import { basename, join, resolve } from "node:path";
|
|
5
6
|
import { copyFile, mkdir, writeFile } from "node:fs/promises";
|
|
6
7
|
import { x } from "tinyexec";
|
|
7
8
|
import { join as join$1 } from "pathe";
|
|
@@ -14,208 +15,6 @@ import { isValidRealtimeName, toRealtimeName } from "c8y-realtime";
|
|
|
14
15
|
import { createHash } from "node:crypto";
|
|
15
16
|
import { fileURLToPath } from "node:url";
|
|
16
17
|
import evlog from "evlog/nitro/v3";
|
|
17
|
-
//#region src/module/apiClient.ts
|
|
18
|
-
/**
|
|
19
|
-
* Converts route path to PascalCase function name.
|
|
20
|
-
* Examples:
|
|
21
|
-
* - /health + get -> GetHealth
|
|
22
|
-
* - /health + post -> PostHealth
|
|
23
|
-
* - /someRoute -> SomeRoute (default)
|
|
24
|
-
* - /[id] or /:id + get -> GetById
|
|
25
|
-
* - /api/[multiple]/[params] -> GetApiByMultipleByParams
|
|
26
|
-
* @param path - The route path (e.g., "/api/[id]" or "/api/:id")
|
|
27
|
-
* @param method - HTTP method (get, post, put, delete, etc.)
|
|
28
|
-
*/
|
|
29
|
-
function generateFunctionName(path, method) {
|
|
30
|
-
return `${method}${path.replace(/^\//, "").replaceAll(".", "_").replaceAll("*", "").split("/").map((seg) => {
|
|
31
|
-
if (seg.startsWith("[") && seg.endsWith("]")) return `By${capitalize(seg.slice(1, -1))}`;
|
|
32
|
-
if (seg.startsWith(":")) return `By${capitalize(seg.slice(1))}`;
|
|
33
|
-
return capitalize(seg);
|
|
34
|
-
}).join("") || "Index"}`;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Extracts route parameters from path.
|
|
38
|
-
* Example: "/api/[id]/items/[itemId]" or "/api/:id/items/:itemId" -> [{name: "id", type: "string"}, {name: "itemId", type: "string"}]
|
|
39
|
-
* @param path - The route path to extract parameters from
|
|
40
|
-
*/
|
|
41
|
-
function extractParams(path) {
|
|
42
|
-
const params = [];
|
|
43
|
-
const segments = path.split("/");
|
|
44
|
-
for (const seg of segments) if (seg.startsWith("[") && seg.endsWith("]")) {
|
|
45
|
-
const name = seg.slice(1, -1);
|
|
46
|
-
params.push(name);
|
|
47
|
-
} else if (seg.startsWith(":")) {
|
|
48
|
-
const name = seg.slice(1);
|
|
49
|
-
params.push(name);
|
|
50
|
-
}
|
|
51
|
-
return params;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Capitalizes first letter of a string.
|
|
55
|
-
* @param str - The string to capitalize
|
|
56
|
-
*/
|
|
57
|
-
function capitalize(str) {
|
|
58
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Converts a file name to a valid JavaScript class name.
|
|
62
|
-
* Only allows letters, numbers (not at start), $ and _.
|
|
63
|
-
* Removes or replaces invalid characters and ensures PascalCase.
|
|
64
|
-
* Always prefixed with "Generated" for clarity.
|
|
65
|
-
* @param fileName - The file name to convert
|
|
66
|
-
* @example "my-service-api" -> "GeneratedMyServiceApi"
|
|
67
|
-
* @example "playgroundAPIClient" -> "GeneratedPlaygroundAPIClient"
|
|
68
|
-
* @example "special@chars!" -> "GeneratedSpecialchars"
|
|
69
|
-
*/
|
|
70
|
-
function toValidClassName(fileName) {
|
|
71
|
-
let cleaned = fileName.replace(/\.(ts|js|mjs|cjs|tsx|jsx)$/, "").replace(/[-_.]/g, " ");
|
|
72
|
-
cleaned = cleaned.replace(/[^a-zA-Z0-9\s$]/g, "");
|
|
73
|
-
return `Generated${cleaned.split(/\s+/).filter((word) => word.length > 0).map((word) => capitalize(word)).join("")}`;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Generates TypeScript method code for a route in an Angular service.
|
|
77
|
-
* @param route - Parsed route information including path, method, params, and return type
|
|
78
|
-
*/
|
|
79
|
-
function generateMethod(route) {
|
|
80
|
-
const hasParams = route.params.length > 0;
|
|
81
|
-
let inlineParamsType = "";
|
|
82
|
-
if (hasParams) inlineParamsType = `{ ${route.params.map((name) => `${name}: string | number`).join("; ")} }`;
|
|
83
|
-
const methodParam = hasParams ? `params: ${inlineParamsType}` : "";
|
|
84
|
-
const returnTypeAnnotation = `Promise<${route.returnType}>`;
|
|
85
|
-
let pathExpression = `\`\${this.BASE_PATH}${route.path}\``;
|
|
86
|
-
if (hasParams) pathExpression = `\`\${this.BASE_PATH}${route.path.replace(/\[([^\]]+)\]/g, (_, paramName) => `\${params.${paramName}}`)}\``;
|
|
87
|
-
const fetchOptions = `{ method: '${route.method}', headers: { 'Content-Type': 'application/json' } }`;
|
|
88
|
-
return ` async ${route.functionName}(${methodParam}): ${returnTypeAnnotation} {
|
|
89
|
-
const response = await this.fetchClient.fetch(${pathExpression}, ${fetchOptions});
|
|
90
|
-
return this.serialize(response);
|
|
91
|
-
}`;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Helper types for proper type serialization from Nitro.
|
|
95
|
-
* https://github.com/nitrojs/nitro/blob/67b43f2692a41728a2759462b6982c6872ed3a81/src/types/fetch/_serialize.ts
|
|
96
|
-
*/
|
|
97
|
-
const serializationTypes = `type JsonPrimitive = string | number | boolean | string | number | boolean | null;
|
|
98
|
-
type NonJsonPrimitive = undefined | Function | symbol;
|
|
99
|
-
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
100
|
-
type FilterKeys<TObj extends object, TFilter> = { [TKey in keyof TObj]: TObj[TKey] extends TFilter ? TKey : never }[keyof TObj];
|
|
101
|
-
type Serialize<T> = IsAny<T> extends true ? any : T extends JsonPrimitive | undefined ? T : T extends Map<any, any> | Set<any> ? Record<string, never> : T extends NonJsonPrimitive ? never : T extends {
|
|
102
|
-
toJSON: () => infer U;
|
|
103
|
-
} ? U : T extends [] ? [] : T extends [unknown, ...unknown[]] ? SerializeTuple<T> : T extends ReadonlyArray<infer U> ? (U extends NonJsonPrimitive ? null : Serialize<U>)[] : T extends object ? SerializeObject<T> : never;
|
|
104
|
-
type SerializeTuple<T extends [unknown, ...unknown[]]> = { [k in keyof T]: T[k] extends NonJsonPrimitive ? null : Serialize<T[k]> };
|
|
105
|
-
type SerializeObject<T extends object> = { [k in keyof Omit<T, FilterKeys<T, NonJsonPrimitive>>]: Serialize<T[k]> };
|
|
106
|
-
type Simplify<TType> = TType extends any[] | Date ? TType : { [K in keyof TType]: Simplify<TType[K]> };
|
|
107
|
-
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
|
|
108
|
-
type C8ySerialize<T> = T extends void | undefined | null ? undefined : T extends string ? string : Simplify<Serialize<T>>;
|
|
109
|
-
`;
|
|
110
|
-
/**
|
|
111
|
-
* Generates complete Angular API client service.
|
|
112
|
-
* @param routes - Array of parsed routes to generate methods for
|
|
113
|
-
* @param contextPath - The microservice context path (e.g., "my-service")
|
|
114
|
-
* @param className - The class name for the generated service (e.g., "PlaygroundAPIClient")
|
|
115
|
-
*/
|
|
116
|
-
function generateAPIClient(routes, contextPath, className) {
|
|
117
|
-
const methods = routes.map((route) => generateMethod(route)).join("\n\n");
|
|
118
|
-
return `/* eslint-disable eslint-comments/no-unlimited-disable */
|
|
119
|
-
/* eslint-disable */
|
|
120
|
-
/**
|
|
121
|
-
* Auto-generated Cumulocity API Client
|
|
122
|
-
* Generated by c8y-nitro
|
|
123
|
-
*
|
|
124
|
-
* This Angular service provides typed methods for all Nitro routes.
|
|
125
|
-
* Each method corresponds to a route handler and returns properly typed responses.
|
|
126
|
-
*/
|
|
127
|
-
import { Injectable, inject } from '@angular/core'
|
|
128
|
-
import { FetchClient } from '@c8y/client'
|
|
129
|
-
|
|
130
|
-
// Type helpers for proper serialization
|
|
131
|
-
${serializationTypes}
|
|
132
|
-
|
|
133
|
-
@Injectable({ providedIn: 'root' })
|
|
134
|
-
export class ${className} {
|
|
135
|
-
private readonly BASE_PATH = '/service/${contextPath}';
|
|
136
|
-
private readonly fetchClient: FetchClient = inject(FetchClient)
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Serializes the response based on content type and body.
|
|
140
|
-
* - Empty body (null, undefined, void) -> undefined
|
|
141
|
-
* - text/plain (string) -> raw text
|
|
142
|
-
* - application/json (number, boolean, object, array) -> JSON parsed
|
|
143
|
-
*/
|
|
144
|
-
private async serialize(response: Response): Promise<any> {
|
|
145
|
-
const contentType = response.headers.get('content-type');
|
|
146
|
-
const text = await response.text();
|
|
147
|
-
|
|
148
|
-
// Handle empty responses (null, undefined, empty return)
|
|
149
|
-
if (!text || text.length === 0) {
|
|
150
|
-
return undefined;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Handle plain text responses (strings)
|
|
154
|
-
if (contentType?.includes('text/plain')) {
|
|
155
|
-
return text;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Handle JSON responses (numbers, booleans, objects, arrays)
|
|
159
|
-
return JSON.parse(text);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
${methods}
|
|
163
|
-
}`;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Generates the TypeScript return type for a route handler.
|
|
167
|
-
* @param handlerPath - Absolute path to the route handler file
|
|
168
|
-
* @param outputFile - Absolute path to the output API client file
|
|
169
|
-
* @returns TypeScript return type string with proper serialization wrappers
|
|
170
|
-
*/
|
|
171
|
-
function getReturnType(handlerPath, outputFile) {
|
|
172
|
-
const relativeHandlerPath = relative(dirname(outputFile), handlerPath);
|
|
173
|
-
return `C8ySerialize<Awaited<ReturnType<typeof import('${(relativeHandlerPath.startsWith(".") ? relativeHandlerPath : `./${relativeHandlerPath}`).replace(/\.ts$/, "")}').default>>>`;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Writes the generated API client to disk.
|
|
177
|
-
* @param nitro - Nitro instance
|
|
178
|
-
* @param options - Complete module options including apiClient and manifest
|
|
179
|
-
*/
|
|
180
|
-
async function writeAPIClient(nitro, options) {
|
|
181
|
-
const { apiClient: apiClientOptions } = options;
|
|
182
|
-
if (!apiClientOptions) {
|
|
183
|
-
nitro.logger.debug("API client generation skipped: no apiClient options provided");
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
const manifest = await createC8yManifestFromNitro(nitro);
|
|
187
|
-
const serviceName = manifest.name;
|
|
188
|
-
const serviceContextPath = apiClientOptions.contextPath ?? manifest.contextPath ?? serviceName;
|
|
189
|
-
const name = `${serviceName}APIClient`;
|
|
190
|
-
const rootDir = nitro.options.rootDir;
|
|
191
|
-
const outputDir = join(rootDir, apiClientOptions.dir);
|
|
192
|
-
const outputFile = join(outputDir, `${name}.ts`);
|
|
193
|
-
const routes = (nitro.routing.routes._routes ?? []).filter((route) => {
|
|
194
|
-
return !route.handler.includes("nitro/dist/runtime/internal");
|
|
195
|
-
}).map((route) => {
|
|
196
|
-
const path = route.route;
|
|
197
|
-
const method = (!route.method ? "GET" : route.method).toUpperCase();
|
|
198
|
-
const params = extractParams(path);
|
|
199
|
-
const functionName = generateFunctionName(path, method);
|
|
200
|
-
const returnType = getReturnType(route.handler, outputFile);
|
|
201
|
-
return {
|
|
202
|
-
path: path.replace(/:([^/]+)/g, "[$1]"),
|
|
203
|
-
method,
|
|
204
|
-
functionName,
|
|
205
|
-
params,
|
|
206
|
-
returnType
|
|
207
|
-
};
|
|
208
|
-
});
|
|
209
|
-
if (routes.length === 0) {
|
|
210
|
-
nitro.logger.warn("No routes found to generate API client");
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
const code = generateAPIClient(routes, serviceContextPath, toValidClassName(name));
|
|
214
|
-
await mkdir(outputDir, { recursive: true });
|
|
215
|
-
await writeFile(outputFile, code, "utf-8");
|
|
216
|
-
nitro.logger.success(`Generated API client with ${routes.length} routes at: ${relative(rootDir, outputFile)}`);
|
|
217
|
-
}
|
|
218
|
-
//#endregion
|
|
219
18
|
//#region src/module/docker.ts
|
|
220
19
|
/**
|
|
221
20
|
* Generate the Dockerfile content for a Nitro build
|
|
@@ -253,8 +52,13 @@ async function writeDockerfile(outputDir, rootDir, options) {
|
|
|
253
52
|
const c8yDir = join(outputDir, "../.c8y");
|
|
254
53
|
const dockerfilePath = join(c8yDir, "Dockerfile");
|
|
255
54
|
await mkdir(c8yDir, { recursive: true });
|
|
256
|
-
if (options.dockerfile)
|
|
257
|
-
|
|
55
|
+
if (options.dockerfile) {
|
|
56
|
+
const customDockerfilePath = resolve(rootDir, options.dockerfile);
|
|
57
|
+
await copyFile(customDockerfilePath, dockerfilePath);
|
|
58
|
+
} else {
|
|
59
|
+
const dockerfileContent = getDockerfileContent(basename(outputDir), options);
|
|
60
|
+
await writeFile(dockerfilePath, dockerfileContent, "utf-8");
|
|
61
|
+
}
|
|
258
62
|
return c8yDir;
|
|
259
63
|
}
|
|
260
64
|
async function buildDockerImage(nitro, c8yDir) {
|
|
@@ -337,7 +141,9 @@ function formatBytes(bytes) {
|
|
|
337
141
|
* @returns Absolute path to the output zip file
|
|
338
142
|
*/
|
|
339
143
|
function resolveZipOutputPath(rootDir, options, manifest) {
|
|
340
|
-
|
|
144
|
+
const outputDir = join$1(rootDir, options.outputDir ?? "./");
|
|
145
|
+
const fileName = typeof options.name === "function" ? options.name(manifest.name, manifest.version) : options.name ?? `${manifest.name}-${manifest.version}.zip`;
|
|
146
|
+
return join$1(outputDir, fileName);
|
|
341
147
|
}
|
|
342
148
|
async function createC8yZip(nitro, options = {}, dockerOptions = {}) {
|
|
343
149
|
const startTime = Date.now();
|
|
@@ -353,9 +159,11 @@ async function createC8yZip(nitro, options = {}, dockerOptions = {}) {
|
|
|
353
159
|
const zip = new JSZip();
|
|
354
160
|
zip.file("image.tar", imageTarBuffer);
|
|
355
161
|
zip.file("cumulocity.json", JSON.stringify(manifest, null, 2));
|
|
162
|
+
const compressionLevel = options.compressionLevel ?? 6;
|
|
356
163
|
const zipBuffer = await zip.generateAsync({
|
|
357
164
|
type: "nodebuffer",
|
|
358
|
-
compression: "
|
|
165
|
+
compression: "DEFLATE",
|
|
166
|
+
compressionOptions: { level: compressionLevel }
|
|
359
167
|
});
|
|
360
168
|
const outputFile = resolveZipOutputPath(nitro.options.rootDir, options, manifest);
|
|
361
169
|
const outputDir = join$1(outputFile, "..");
|
|
@@ -377,8 +185,8 @@ function setupRuntime(nitro, manifest) {
|
|
|
377
185
|
const roles = manifest.roles ?? [];
|
|
378
186
|
const settingKeys = (manifest.settings ?? []).map((s) => s.key);
|
|
379
187
|
const settingsCategory = manifest.settingsCategory ?? manifest.contextPath ?? manifest.name;
|
|
380
|
-
const completeTypesDir = join$1(nitro.options.rootDir,
|
|
381
|
-
const typesFile = join$1(completeTypesDir,
|
|
188
|
+
const completeTypesDir = join$1(nitro.options.rootDir, C8Y_NITRO_TYPES_DIR);
|
|
189
|
+
const typesFile = join$1(completeTypesDir, C8Y_NITRO_TYPES_FILE);
|
|
382
190
|
const typesContent = `// generated by c8y-nitro
|
|
383
191
|
declare module 'c8y-nitro/types' {
|
|
384
192
|
interface C8YRoles {
|
|
@@ -512,14 +320,16 @@ function registerRuntime(nitro, options = {}) {
|
|
|
512
320
|
});
|
|
513
321
|
nitro.logger.debug("Registered public request URL rewrite middleware");
|
|
514
322
|
}
|
|
515
|
-
if (Boolean(nitro.options.experimental?.openAPI && (isNitroDev || nitro.options.openAPI?.production)))
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
323
|
+
if (Boolean(nitro.options.experimental?.openAPI && (isNitroDev || nitro.options.openAPI?.production))) {
|
|
324
|
+
if (!isNitroDev && nitro.options.openAPI?.production === "prerender") nitro.logger.warn("c8y-nitro cannot transform a prerendered OpenAPI document (internal routes stay listed and the server URL is baked in at build time). Use `openAPI: { production: true }` to serve the document at runtime instead.");
|
|
325
|
+
else {
|
|
326
|
+
nitro.options.handlers.push({
|
|
327
|
+
route: "/**",
|
|
328
|
+
handler: join$1(thisFilePath, "./runtime/handlers/openapi"),
|
|
329
|
+
middleware: true
|
|
330
|
+
});
|
|
331
|
+
nitro.logger.debug(`Registered OpenAPI transform middleware for ${nitro.options.openAPI?.route || "/_openapi.json"}`);
|
|
332
|
+
}
|
|
523
333
|
}
|
|
524
334
|
/**
|
|
525
335
|
* Handlers (can't be auto scanned as they need methods etc)
|
|
@@ -588,10 +398,6 @@ function c8y() {
|
|
|
588
398
|
name: "c8y-nitro",
|
|
589
399
|
setup: async (nitro) => {
|
|
590
400
|
const options = nitro.options.c8y ?? {};
|
|
591
|
-
nitro.options.typescript.generateTsConfig = true;
|
|
592
|
-
nitro.options.typescript.tsConfig = {};
|
|
593
|
-
nitro.options.typescript.tsConfig.include = ["./**/*.d.ts"];
|
|
594
|
-
nitro.options.typescript.tsConfig.exclude = [];
|
|
595
401
|
nitro.options.noExternals = nitro.options.noExternals === true ? true : [
|
|
596
402
|
...Array.isArray(nitro.options.noExternals) ? nitro.options.noExternals : [],
|
|
597
403
|
name,
|
|
@@ -634,12 +440,6 @@ function c8y() {
|
|
|
634
440
|
registerRuntime(nitro, options);
|
|
635
441
|
if (options.manifest) checkProbes(nitro, options.manifest);
|
|
636
442
|
});
|
|
637
|
-
nitro.hooks.hook("types:extend", async () => {
|
|
638
|
-
if (options.apiClient) {
|
|
639
|
-
nitro.logger.debug("Generating C8Y API client");
|
|
640
|
-
await writeAPIClient(nitro, options);
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
443
|
nitro.hooks.hook("close", async () => {
|
|
644
444
|
if (nitro.options.preset !== "nitro-dev") await createC8yZip(nitro, options.zip, options.docker);
|
|
645
445
|
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { readPackage } from "pkg-types";
|
|
2
|
+
//#region src/module/constants.ts
|
|
3
|
+
const GENERATED_LIVENESS_ROUTE = "/_c8y_nitro/liveness";
|
|
4
|
+
const GENERATED_READINESS_ROUTE = "/_c8y_nitro/readiness";
|
|
5
|
+
const GENERATED_INVALIDATE_TENANT_OPTIONS_ROUTE = "/_c8y_nitro/invalidate-tenant-options";
|
|
6
|
+
/**
|
|
7
|
+
* Directory the module writes its generated type declarations into, relative to
|
|
8
|
+
* the project root. Consumers add `node_modules/.c8y-nitro/*.d.ts` to their
|
|
9
|
+
* tsconfig `include` — nitro no longer generates a tsconfig to inherit from.
|
|
10
|
+
*/
|
|
11
|
+
const C8Y_NITRO_TYPES_DIR = "node_modules/.c8y-nitro";
|
|
12
|
+
/**
|
|
13
|
+
* File name of the generated declarations inside {@link C8Y_NITRO_TYPES_DIR}.
|
|
14
|
+
*/
|
|
15
|
+
const C8Y_NITRO_TYPES_FILE = "types.d.ts";
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/module/manifest.ts
|
|
18
|
+
const DEFAULT_OPENAPI_ROUTE = "/_openapi.json";
|
|
19
|
+
const ROLE_OPTION_MANAGEMENT_READ = "ROLE_OPTION_MANAGEMENT_READ";
|
|
20
|
+
/**
|
|
21
|
+
* Cumulocity microservice name constraints: lowercase letters, digits and
|
|
22
|
+
* hyphens only, 1-23 characters. Enforced by the platform on upload, so we
|
|
23
|
+
* fail early with a clear message instead of a cryptic API rejection.
|
|
24
|
+
* @see https://github.com/schplitt/c8y-nitro/issues/75
|
|
25
|
+
*/
|
|
26
|
+
const MICROSERVICE_NAME_REGEX = /^[a-z0-9-]{1,23}$/;
|
|
27
|
+
/**
|
|
28
|
+
* Decide whether Nitro will expose an OpenAPI spec in the deployed build,
|
|
29
|
+
* and if so return the configured route (with leading slash).
|
|
30
|
+
* Returns null when OpenAPI is not enabled for production.
|
|
31
|
+
* @param config - Nitro config (or options) to inspect
|
|
32
|
+
*/
|
|
33
|
+
function resolveNitroOpenApiRoute(config) {
|
|
34
|
+
if (!config.experimental?.openAPI) return null;
|
|
35
|
+
if (!config.openAPI?.production) return null;
|
|
36
|
+
return config.openAPI?.route || DEFAULT_OPENAPI_ROUTE;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Pull the parts of a Nitro config that feed manifest generation.
|
|
40
|
+
* @param config - Nitro config (or options) loaded from disk or from a Nitro instance
|
|
41
|
+
*/
|
|
42
|
+
function extractManifestInputs(config) {
|
|
43
|
+
return {
|
|
44
|
+
options: config.c8y?.manifest ?? {},
|
|
45
|
+
openApiSpec: resolveNitroOpenApiRoute(config) ?? void 0
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function validateManifestSettings(options) {
|
|
49
|
+
const invalidSettings = options.settings?.filter((setting) => typeof setting.defaultValue !== "string" || setting.defaultValue.length === 0) ?? [];
|
|
50
|
+
if (invalidSettings.length === 0) return;
|
|
51
|
+
const invalidKeys = invalidSettings.map((setting) => `"${setting.key}"`).join(", ");
|
|
52
|
+
throw new Error(`manifest.settings entries must define a non-empty defaultValue. Invalid keys: ${invalidKeys}`);
|
|
53
|
+
}
|
|
54
|
+
async function readPackageJsonFieldsForManifest(rootDir, logger) {
|
|
55
|
+
logger?.debug(`Reading package file from ${rootDir}`);
|
|
56
|
+
const pkg = await readPackage(rootDir);
|
|
57
|
+
const name = pkg.name?.replace(/^@[^/]+\//, "");
|
|
58
|
+
const version = pkg.version;
|
|
59
|
+
const author = pkg.author;
|
|
60
|
+
const authorName = typeof author === "string" ? author : author?.name;
|
|
61
|
+
const authorEmail = typeof author === "string" ? void 0 : author?.email;
|
|
62
|
+
const authorUrl = typeof author === "string" ? void 0 : author?.url;
|
|
63
|
+
if (!name || !version || !authorName) throw new Error("package.json must contain name, version, and author name fields");
|
|
64
|
+
if (!MICROSERVICE_NAME_REGEX.test(name)) throw new Error(`Invalid microservice name "${name}". Cumulocity requires the name to match ${MICROSERVICE_NAME_REGEX.source} (lowercase letters, digits and hyphens only, 1-23 characters). Adjust the "name" field in package.json accordingly (scope like "@org/" is stripped and does not count).`);
|
|
65
|
+
const support = typeof pkg.bugs === "string" ? pkg.bugs : pkg.bugs?.url ?? pkg.bugs?.email;
|
|
66
|
+
const provider = {
|
|
67
|
+
name: authorName ?? name,
|
|
68
|
+
domain: authorUrl ?? pkg.homepage,
|
|
69
|
+
support: support ?? authorEmail
|
|
70
|
+
};
|
|
71
|
+
logger?.debug(`Found package.json fields for manifest: name=${name}, version=${version}, provider=${JSON.stringify(provider)}`);
|
|
72
|
+
return {
|
|
73
|
+
name,
|
|
74
|
+
version,
|
|
75
|
+
provider,
|
|
76
|
+
contextPath: name
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create a Cumulocity manifest from a project root and its Nitro config.
|
|
81
|
+
* Reads `package.json` for identity fields, takes user manifest options from
|
|
82
|
+
* `nitroConfig.c8y.manifest`, and auto-injects `openApiSpec` when Nitro
|
|
83
|
+
* OpenAPI is enabled for production.
|
|
84
|
+
*
|
|
85
|
+
* Used by both the Nitro module and the CLI commands.
|
|
86
|
+
* @param rootDir - Directory containing the project's `package.json`
|
|
87
|
+
* @param nitroConfig - Nitro config (or options) for this project
|
|
88
|
+
* @param logger - Optional logger for debug output
|
|
89
|
+
*/
|
|
90
|
+
async function createC8yManifest(rootDir, nitroConfig = {}, logger) {
|
|
91
|
+
const { options, openApiSpec } = extractManifestInputs(nitroConfig);
|
|
92
|
+
validateManifestSettings(options);
|
|
93
|
+
const { name, version, provider, ...restManifestFields } = await readPackageJsonFieldsForManifest(rootDir, logger);
|
|
94
|
+
const probeFields = {};
|
|
95
|
+
if (!options.livenessProbe?.httpGet) probeFields.livenessProbe = {
|
|
96
|
+
...options.livenessProbe,
|
|
97
|
+
httpGet: { path: GENERATED_LIVENESS_ROUTE }
|
|
98
|
+
};
|
|
99
|
+
if (!options.readinessProbe?.httpGet) probeFields.readinessProbe = {
|
|
100
|
+
...options.readinessProbe,
|
|
101
|
+
httpGet: { path: GENERATED_READINESS_ROUTE }
|
|
102
|
+
};
|
|
103
|
+
const key = `${name}-key`;
|
|
104
|
+
let requiredRoles = options.requiredRoles ? [...options.requiredRoles] : void 0;
|
|
105
|
+
if (options.settings && options.settings.length > 0 && !requiredRoles?.includes(ROLE_OPTION_MANAGEMENT_READ)) {
|
|
106
|
+
requiredRoles = [...requiredRoles ?? [], ROLE_OPTION_MANAGEMENT_READ];
|
|
107
|
+
logger?.debug(`Auto-added ${ROLE_OPTION_MANAGEMENT_READ} to requiredRoles because manifest.settings are defined`);
|
|
108
|
+
}
|
|
109
|
+
const manifest = {
|
|
110
|
+
...restManifestFields,
|
|
111
|
+
...probeFields,
|
|
112
|
+
...options,
|
|
113
|
+
...requiredRoles !== void 0 ? { requiredRoles } : {},
|
|
114
|
+
...openApiSpec ? { openApiSpec } : {},
|
|
115
|
+
provider,
|
|
116
|
+
name,
|
|
117
|
+
version,
|
|
118
|
+
apiVersion: "v2",
|
|
119
|
+
key,
|
|
120
|
+
type: "MICROSERVICE"
|
|
121
|
+
};
|
|
122
|
+
if (openApiSpec) logger?.debug(`Auto-added openApiSpec="${openApiSpec}" to manifest (Nitro OpenAPI is enabled)`);
|
|
123
|
+
logger?.debug(`Created Cumulocity manifest: ${JSON.stringify(manifest, null, 2)}`);
|
|
124
|
+
return manifest;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Creates a Cumulocity manifest from a Nitro instance.
|
|
128
|
+
* Convenience wrapper for use in the Nitro module.
|
|
129
|
+
* @param nitro - The Nitro instance
|
|
130
|
+
*/
|
|
131
|
+
async function createC8yManifestFromNitro(nitro) {
|
|
132
|
+
return createC8yManifest(nitro.options.rootDir, nitro.options, nitro.logger);
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
export { GENERATED_INVALIDATE_TENANT_OPTIONS_ROUTE as a, C8Y_NITRO_TYPES_FILE as i, createC8yManifestFromNitro as n, GENERATED_LIVENESS_ROUTE as o, C8Y_NITRO_TYPES_DIR as r, GENERATED_READINESS_ROUTE as s, createC8yManifest as t };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
1
|
+
import { t as createC8yManifest } from "./manifest-DV3UW9Dl.mjs";
|
|
2
|
+
import { c as getTenantOptionsByCategory, f as upsertTenantOption, i as deleteTenantOption, n as createBasicAuthHeader, s as getCurrentApplicationSettings } from "./c8y-api-DSs1xIIh.mjs";
|
|
3
|
+
import { n as readBootstrapCredentialsFromEnv, r as syncBootstrappedMicroservice } from "./autoBootstrap-DUjHuHe_.mjs";
|
|
3
4
|
import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-BVQHqlgV.mjs";
|
|
4
5
|
import process from "node:process";
|
|
5
6
|
import { defineCommand } from "citty";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as createC8yManifest } from "./manifest-DV3UW9Dl.mjs";
|
|
2
|
+
import { n as createBasicAuthHeader, t as assignUserRole, u as unassignUserRole } from "./c8y-api-DSs1xIIh.mjs";
|
|
2
3
|
import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-BVQHqlgV.mjs";
|
|
3
4
|
import { defineCommand } from "citty";
|
|
4
5
|
import { consola } from "consola";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as createError } from "../../logging-BUYT8Z2V.mjs";
|
|
2
|
-
import { t as useDeployedTenantClient } from "../../client-
|
|
2
|
+
import { t as useDeployedTenantClient } from "../../client-BjA5bBxj.mjs";
|
|
3
3
|
import { c8yTenantOptionKeys } from "c8y-nitro/runtime";
|
|
4
4
|
import { defineCachedFunction } from "nitro/cache";
|
|
5
5
|
import { useRuntimeConfig } from "nitro/runtime-config";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as tryGetUserTenantDomainFast } from "../../client-
|
|
1
|
+
import { r as tryGetUserTenantDomainFast } from "../../client-BjA5bBxj.mjs";
|
|
2
2
|
import { c8yManifest } from "c8y-nitro/runtime";
|
|
3
3
|
import { useRuntimeConfig } from "nitro/runtime-config";
|
|
4
4
|
import { getRequestURL } from "nitro/h3";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as buildPublicRequestUrl, r as tryGetUserTenantDomainFast } from "../../client-
|
|
1
|
+
import { n as buildPublicRequestUrl, r as tryGetUserTenantDomainFast } from "../../client-BjA5bBxj.mjs";
|
|
2
2
|
import { c8yManifest } from "c8y-nitro/runtime";
|
|
3
3
|
import { defineMiddleware } from "nitro";
|
|
4
4
|
//#region src/module/runtime/handlers/rewriteRequestUrl.ts
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { i as C8Y_NITRO_TYPES_FILE, r as C8Y_NITRO_TYPES_DIR } from "./manifest-DV3UW9Dl.mjs";
|
|
2
|
+
import { t as writeAPIClient } from "./apiClient-kxv5KHYJ.mjs";
|
|
3
|
+
import { resolve } from "pathe";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import { defineCommand } from "citty";
|
|
6
|
+
import { consola } from "consola";
|
|
7
|
+
import { createNitro } from "nitro/builder";
|
|
8
|
+
//#region src/cli/commands/typegen.ts
|
|
9
|
+
var typegen_default = defineCommand({
|
|
10
|
+
meta: {
|
|
11
|
+
name: "typegen",
|
|
12
|
+
description: "Generate the c8y-nitro type declarations (replaces the removed `nitro prepare`)"
|
|
13
|
+
},
|
|
14
|
+
args: { dir: {
|
|
15
|
+
type: "positional",
|
|
16
|
+
description: "Project root directory",
|
|
17
|
+
required: false,
|
|
18
|
+
default: "."
|
|
19
|
+
} },
|
|
20
|
+
async run({ args }) {
|
|
21
|
+
const rootDir = resolve(process.cwd(), args.dir);
|
|
22
|
+
const nitro = await createNitro({ rootDir });
|
|
23
|
+
consola.success(`Generated types at ${C8Y_NITRO_TYPES_DIR}/${C8Y_NITRO_TYPES_FILE}`);
|
|
24
|
+
const c8yOptions = nitro.options.c8y ?? {};
|
|
25
|
+
if (c8yOptions.apiClient) await writeAPIClient(nitro, c8yOptions);
|
|
26
|
+
consola.info(`Add "${C8Y_NITRO_TYPES_DIR}/*.d.ts" to your tsconfig \`include\` if you have not already.`);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
//#endregion
|
|
30
|
+
export { typegen_default as default };
|
package/dist/types.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as C8YTenantOptionKey, c as C8yDockerOptions, d as C8YManifest, f as C8YManifestOptions, g as C8YAPIClientOptions, h as C8YKnownRole, i as C8YSettingsCategory, l as C8yCacheOptions, m as C8YRoles, n as C8yNitroModuleOptions, o as C8YTenantOptionKeysCacheConfig, p as C8YRole, r as C8yOpenAPIOptions, s as C8yRealtimeOptions, t as C8yDevOptions, u as C8YZipOptions } from "./index-
|
|
1
|
+
import { a as C8YTenantOptionKey, c as C8yDockerOptions, d as C8YManifest, f as C8YManifestOptions, g as C8YAPIClientOptions, h as C8YKnownRole, i as C8YSettingsCategory, l as C8yCacheOptions, m as C8YRoles, n as C8yNitroModuleOptions, o as C8YTenantOptionKeysCacheConfig, p as C8YRole, r as C8yOpenAPIOptions, s as C8yRealtimeOptions, t as C8yDevOptions, u as C8YZipOptions } from "./index-BEFoDy-y.mjs";
|
|
2
2
|
import { t as TenantCredentials } from "./credentials-eKnJ7m9Y.mjs";
|
|
3
3
|
export { C8YAPIClientOptions, type C8YKnownRole, type C8YManifest, type C8YManifestOptions, C8YRole, C8YRoles, C8YSettingsCategory, C8YTenantOptionKey, C8YTenantOptionKeysCacheConfig, type C8YZipOptions, type C8yCacheOptions, C8yDevOptions, type C8yDockerOptions, C8yNitroModuleOptions, C8yOpenAPIOptions, type C8yRealtimeOptions, type TenantCredentials };
|
package/dist/utils.mjs
CHANGED
|
@@ -132,7 +132,8 @@ defineCachedFunction(async (requestOrEvent, _tenantId) => {
|
|
|
132
132
|
* const log = useLogger(useRequest())
|
|
133
133
|
*/
|
|
134
134
|
function useLogger(eventOrRequest, service) {
|
|
135
|
-
|
|
135
|
+
const event = "req" in eventOrRequest ? eventOrRequest : { req: eventOrRequest };
|
|
136
|
+
return useLogger$1(event, service);
|
|
136
137
|
}
|
|
137
138
|
/**
|
|
138
139
|
* Create a structured error for a Cumulocity microservice. Always prefer this
|
|
@@ -324,7 +325,9 @@ async function useUserTenantCredentials(requestOrEvent) {
|
|
|
324
325
|
function useUserClient(requestOrEvent) {
|
|
325
326
|
const request = "req" in requestOrEvent ? requestOrEvent.req : requestOrEvent;
|
|
326
327
|
if (request.context?.["c8y_user_client"]) return request.context["c8y_user_client"];
|
|
327
|
-
const
|
|
328
|
+
const headers = convertRequestHeadersToC8yFormat(request);
|
|
329
|
+
const auth = new MicroserviceClientRequestAuth(headers);
|
|
330
|
+
const client = new Client(auth, process.env.C8Y_BASEURL);
|
|
328
331
|
request.context ??= {};
|
|
329
332
|
request.context["c8y_user_client"] = client;
|
|
330
333
|
return client;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "c8y-nitro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Lightning fast Cumulocity IoT microservice development powered by Nitro",
|
|
6
6
|
"keywords": [
|
|
@@ -38,18 +38,18 @@
|
|
|
38
38
|
"dist"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"c12": "^4.0.0-
|
|
41
|
+
"c12": "^4.0.0-rc.1",
|
|
42
42
|
"c8y-realtime": "^0.2.2",
|
|
43
43
|
"citty": "^0.2.2",
|
|
44
44
|
"consola": "^3.4.2",
|
|
45
45
|
"croner": "^10.0.1",
|
|
46
|
-
"evlog": "^2.
|
|
46
|
+
"evlog": "^2.28.1",
|
|
47
47
|
"itty-time": "^2.0.2",
|
|
48
48
|
"jszip": "^3.10.1",
|
|
49
49
|
"pathe": "^2.0.3",
|
|
50
|
-
"pkg-types": "^2.3.
|
|
50
|
+
"pkg-types": "^2.3.2",
|
|
51
51
|
"spinnies": "^0.5.1",
|
|
52
|
-
"tinyexec": "^1.
|
|
52
|
+
"tinyexec": "^1.3.1",
|
|
53
53
|
"ws": "^8.21.3"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
@@ -58,20 +58,20 @@
|
|
|
58
58
|
"@types/node": "^24.12.3",
|
|
59
59
|
"@types/spinnies": "^0.5.3",
|
|
60
60
|
"@types/ws": "^8.18.1",
|
|
61
|
-
"bumpp": "^
|
|
62
|
-
"changelogithub": "^
|
|
63
|
-
"eslint": "^10.
|
|
64
|
-
"memfs": "^4.
|
|
65
|
-
"tsdown": "^0.22.
|
|
66
|
-
"tsnapi": "^1.
|
|
61
|
+
"bumpp": "^12.3.0",
|
|
62
|
+
"changelogithub": "^15.0.5",
|
|
63
|
+
"eslint": "^10.9.1",
|
|
64
|
+
"memfs": "^4.69.1",
|
|
65
|
+
"tsdown": "^0.22.14",
|
|
66
|
+
"tsnapi": "^1.4.2",
|
|
67
67
|
"typescript": "^6.0.3",
|
|
68
68
|
"vitepress": "2.0.0-alpha.17",
|
|
69
69
|
"vitepress-plugin-llms": "1.13.3",
|
|
70
|
-
"vitest": "^4.1.
|
|
70
|
+
"vitest": "^4.1.11"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"@c8y/client": ">=1021",
|
|
74
|
-
"nitro": "3.0.
|
|
74
|
+
"nitro": "3.0.260903-beta"
|
|
75
75
|
},
|
|
76
76
|
"engines": {
|
|
77
77
|
"node": ">=24.0.0"
|