c8y-nitro 0.4.2 → 0.6.0

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.
@@ -2,6 +2,9 @@ import { dirname } from "pathe";
2
2
  import { loadConfig, loadDotenv } from "c12";
3
3
  import process from "process";
4
4
  //#region src/cli/utils/config.ts
5
+ function normalizeBaseUrl(baseUrl) {
6
+ return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
7
+ }
5
8
  /**
6
9
  * Loads c8y configuration from nitro.config and .env files.
7
10
  * Searches in cwd.
@@ -45,7 +48,7 @@ function validateBootstrapEnv(env) {
45
48
  const missing = REQUIRED_BOOTSTRAP_ENV_VARS.filter((key) => !env[key]);
46
49
  if (missing.length > 0) throw new Error(`Missing required environment variables:\n${missing.map((k) => ` - ${k}`).join("\n")}\n\nPlease set these in your .env or .env.local file.`);
47
50
  return {
48
- C8Y_BASEURL: env.C8Y_BASEURL.endsWith("/") ? env.C8Y_BASEURL.slice(0, -1) : env.C8Y_BASEURL,
51
+ C8Y_BASEURL: normalizeBaseUrl(env.C8Y_BASEURL),
49
52
  C8Y_DEVELOPMENT_TENANT: env.C8Y_DEVELOPMENT_TENANT,
50
53
  C8Y_DEVELOPMENT_USER: env.C8Y_DEVELOPMENT_USER,
51
54
  C8Y_DEVELOPMENT_PASSWORD: env.C8Y_DEVELOPMENT_PASSWORD
@@ -0,0 +1,6 @@
1
+ import { ICredentials } from "@c8y/client";
2
+
3
+ //#region src/types/credentials.d.ts
4
+ type TenantCredentials = Record<string, ICredentials>;
5
+ //#endregion
6
+ export { TenantCredentials as t };
@@ -193,10 +193,10 @@ interface Option {
193
193
  */
194
194
  key: string;
195
195
  /**
196
- * Initial value if not overridden.
196
+ * Initial non-empty value if not overridden.
197
197
  * @example "1234"
198
198
  */
199
- defaultValue?: string;
199
+ defaultValue: string;
200
200
  /**
201
201
  * Allow tenants to modify at runtime.
202
202
  * @default false
@@ -389,11 +389,34 @@ type C8YTenantOptionKeysCacheConfig$1 = Partial<Record<C8YTenantOptionKey$1, num
389
389
  type C8YTenantOptionKey$1 = string;
390
390
  //#endregion
391
391
  //#region src/types/index.d.ts
392
+ interface C8yDevOptions {
393
+ /**
394
+ * Automatically inject the configured development user into incoming requests
395
+ * during local Nitro dev mode.
396
+ *
397
+ * Disable this when a local proxy already forwards the desired user context.
398
+ * @default true
399
+ */
400
+ injectUser?: boolean;
401
+ }
392
402
  interface C8yNitroModuleOptions {
403
+ dev?: C8yDevOptions;
393
404
  manifest?: C8YManifestOptions;
394
405
  apiClient?: C8YAPIClientOptions;
395
406
  zip?: C8YZipOptions;
396
407
  cache?: C8yCacheOptions;
408
+ /**
409
+ * Adds a debug route for invalidating already-created tenant option caches.
410
+ * Exposes `GET /_c8y_nitro/invalidate-tenant-options`.
411
+ *
412
+ * Query params:
413
+ * - `all`: invalidate all created tenant option fetchers
414
+ * - `key`: invalidate a single manifest-defined tenant option key if it exists
415
+ *
416
+ * `all` takes priority over `key`.
417
+ * @default false
418
+ */
419
+ enableTenantOptionsInvalidationRoute?: boolean;
397
420
  /**
398
421
  * Disable auto-bootstrap during development.
399
422
  * When true, the module will not automatically register the microservice
@@ -405,4 +428,4 @@ interface C8yNitroModuleOptions {
405
428
  skipBootstrap?: boolean;
406
429
  }
407
430
  //#endregion
408
- export { C8yCacheOptions as a, C8YManifestOptions as c, C8YRoles$1 as i, C8YAPIClientOptions as l, C8YTenantOptionKey$1 as n, C8YZipOptions as o, C8YTenantOptionKeysCacheConfig$1 as r, C8YManifest as s, C8yNitroModuleOptions as t };
431
+ export { C8YRoles$1 as a, C8YManifest as c, C8YTenantOptionKeysCacheConfig$1 as i, C8YManifestOptions as l, C8yNitroModuleOptions as n, C8yCacheOptions as o, C8YTenantOptionKey$1 as r, C8YZipOptions as s, C8yDevOptions as t, C8YAPIClientOptions as u };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { t as C8yNitroModuleOptions } from "./index-CzUqbp5C.mjs";
1
+ import { n as C8yNitroModuleOptions } from "./index-uOwpI6rD.mjs";
2
+ import { t as TenantCredentials } from "./credentials-9FO7rTIR.mjs";
2
3
  import { NitroModule } from "nitro/types";
3
4
 
4
5
  //#region src/index.d.ts
@@ -7,6 +8,11 @@ declare module 'nitro/types' {
7
8
  c8y?: C8yNitroModuleOptions;
8
9
  }
9
10
  }
11
+ declare module 'nitro/types' {
12
+ interface NitroRuntimeHooks {
13
+ 'c8y:tenantCredentialsUpdated': (prev: TenantCredentials | null, next: TenantCredentials) => void;
14
+ }
15
+ }
10
16
  declare function c8y(): NitroModule;
11
17
  //#endregion
12
18
  export { c8y, c8y as default };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { a as findMicroserviceByName, g as GENERATED_READINESS_ROUTE, h as GENERATED_LIVENESS_ROUTE, l as subscribeToApplication, m as createC8yManifestFromNitro, n as createBasicAuthHeader, o as getBootstrapCredentials, p as createC8yManifest, r as createMicroservice } from "./c8y-api-BBSKRwKs.mjs";
1
+ import { _ as GENERATED_READINESS_ROUTE, a as findMicroserviceByName, g as GENERATED_LIVENESS_ROUTE, h as GENERATED_INVALIDATE_TENANT_OPTIONS_ROUTE, l as subscribeToApplication, m as createC8yManifestFromNitro, n as createBasicAuthHeader, o as getBootstrapCredentials, p as createC8yManifest, r as createMicroservice } from "./c8y-api-BbRS1-Ls.mjs";
2
2
  import { t as writeBootstrapCredentials } from "./env-file-B0BK-uZW.mjs";
3
- import { n as name } from "./package-C4HtuVu_.mjs";
3
+ import { n as name } from "./package-DsLC9Mo3.mjs";
4
4
  import { basename, dirname, join, relative } from "node:path";
5
5
  import { mkdir, writeFile } from "node:fs/promises";
6
6
  import { x } from "tinyexec";
@@ -427,11 +427,21 @@ async function setupRuntimeConfig(nitro, options) {
427
427
  */
428
428
  function registerRuntime(nitro, options = {}) {
429
429
  const thisFilePath = fileURLToPath(new URL(".", import.meta.url));
430
+ const isNitroDev = nitro.options.preset === "nitro-dev";
431
+ const shouldIncludeRuntimeFile = (relativePath) => {
432
+ if (relativePath.endsWith(".dev.ts") && !isNitroDev) return false;
433
+ if (relativePath.endsWith("/dev-user.dev.ts") && options.dev?.injectUser === false) return false;
434
+ return true;
435
+ };
436
+ const toRuntimePath = (relativePath) => join$1(thisFilePath, relativePath.replace(/\.ts$/, ""));
430
437
  const allPlugins = Object.keys({
431
- "./runtime/plugins/c8y-variables.ts": 0,
438
+ "./runtime/plugins/c8y-variables.dev.ts": 0,
432
439
  "./runtime/plugins/enrich-logs.ts": 0
433
- }).map((p) => join$1(thisFilePath, p.replace(".ts", "")));
434
- const allMiddlewares = Object.keys({ "./runtime/middlewares/dev-user.ts": 0 }).map((p) => join$1(thisFilePath, p.replace(".ts", "")));
440
+ }).filter(shouldIncludeRuntimeFile).map(toRuntimePath);
441
+ const allMiddlewares = Object.keys({
442
+ "./runtime/middlewares/c8y-client.ts": 0,
443
+ "./runtime/middlewares/dev-user.dev.ts": 0
444
+ }).filter(shouldIncludeRuntimeFile).map(toRuntimePath);
435
445
  /**
436
446
  * Plugins (auto scanned)
437
447
  */
@@ -465,6 +475,15 @@ function registerRuntime(nitro, options = {}) {
465
475
  });
466
476
  nitro.logger.debug(`Generated readiness probe at ${GENERATED_READINESS_ROUTE}`);
467
477
  } else nitro.logger.debug("Readiness probe httpGet defined by user; skipping generation");
478
+ if (options.enableTenantOptionsInvalidationRoute) {
479
+ const invalidateTenantOptionsHandlerPath = join$1(thisFilePath, "./runtime/handlers/invalidateTenantOptions");
480
+ handlers.push({
481
+ route: GENERATED_INVALIDATE_TENANT_OPTIONS_ROUTE,
482
+ handler: invalidateTenantOptionsHandlerPath,
483
+ method: "GET"
484
+ });
485
+ nitro.logger.debug(`Generated tenant option invalidation route at ${GENERATED_INVALIDATE_TENANT_OPTIONS_ROUTE}`);
486
+ }
468
487
  nitro.options.handlers.push(...handlers);
469
488
  }
470
489
  //#endregion
@@ -558,12 +577,25 @@ function c8y() {
558
577
  name,
559
578
  "@c8y/client"
560
579
  ];
580
+ nitro.options.rolldownConfig = {
581
+ ...nitro.options.rolldownConfig,
582
+ resolve: {
583
+ ...nitro.options.rolldownConfig?.resolve,
584
+ alias: {
585
+ ...nitro.options.rolldownConfig?.resolve?.alias,
586
+ tslib: "tslib/tslib.es6.mjs"
587
+ }
588
+ }
589
+ };
561
590
  if (!nitro.options.preset.startsWith("nitro") && !nitro.options.preset.startsWith("node")) {
562
591
  nitro.logger.error(`Unsupported preset "${nitro.options.preset}" for c8y-nitro module, only node presets are supported.`);
563
592
  throw new Error("Unsupported preset for c8y-nitro module");
564
593
  }
565
594
  let manifest = await createC8yManifestFromNitro(nitro);
566
- const { setup: setupEvlog } = evlog({ env: { service: manifest.name } });
595
+ const { setup: setupEvlog } = evlog({
596
+ env: { service: manifest.name },
597
+ exclude: [manifest.livenessProbe?.httpGet?.path, manifest.readinessProbe?.httpGet?.path].filter(Boolean)
598
+ });
567
599
  await setupEvlog(nitro);
568
600
  if (!options.skipBootstrap) await autoBootstrap(nitro);
569
601
  await setupRuntimeConfig(nitro, options);
@@ -0,0 +1,3 @@
1
+ import "evlog/nitro/v3";
2
+ import { createError } from "evlog";
3
+ export { createError as t };
@@ -1,5 +1,5 @@
1
- import { c as getTenantOptionsByCategory, f as updateTenantOption, i as deleteTenantOption, n as createBasicAuthHeader, p as createC8yManifest, s as getTenantOption } from "./c8y-api-BBSKRwKs.mjs";
2
- import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-Dqi-ttQi.mjs";
1
+ import { c as getTenantOptionsByCategory, f as upsertTenantOption, i as deleteTenantOption, n as createBasicAuthHeader, p as createC8yManifest, s as getTenantOption } from "./c8y-api-BbRS1-Ls.mjs";
2
+ import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-BRnvtthI.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  import { consola } from "consola";
5
5
  //#region src/cli/commands/options.ts
@@ -62,6 +62,7 @@ async function handleRead(baseUrl, category, authHeader, availableKeys, currentO
62
62
  consola.warn("No options are currently set");
63
63
  return;
64
64
  }
65
+ if (credentialsKeys.length > 0) consola.warn("Encrypted credentials.* options cannot be read in decrypted form with the development user in this CLI.");
65
66
  const key = await consola.prompt("Select option to read:", {
66
67
  type: "select",
67
68
  options: allKeys.map((k) => ({
@@ -71,7 +72,7 @@ async function handleRead(baseUrl, category, authHeader, availableKeys, currentO
71
72
  cancel: "reject"
72
73
  });
73
74
  consola.info(`Reading option: ${key}`);
74
- const value = await getTenantOption(baseUrl, category, key.startsWith("credentials.") ? key.replace(/^credentials\./, "") : key, authHeader);
75
+ const value = await getTenantOption(baseUrl, category, key, authHeader);
75
76
  if (value === void 0) consola.warn(`Option '${key}' is not set`);
76
77
  else consola.success(`Value: ${value}`);
77
78
  }
@@ -101,7 +102,7 @@ async function handleUpdate(baseUrl, category, authHeader, availableKeys, curren
101
102
  cancel: "reject"
102
103
  });
103
104
  consola.info(`Updating option: ${key}`);
104
- await updateTenantOption(baseUrl, category, key.replace(/^credentials\./, ""), newValue, authHeader);
105
+ await upsertTenantOption(baseUrl, category, key, newValue, authHeader);
105
106
  currentOptions[key] = newValue;
106
107
  consola.success(`Option '${key}' updated successfully`);
107
108
  if (!await consola.prompt("Update another option?", {
@@ -136,7 +137,8 @@ async function handleDelete(baseUrl, category, authHeader, availableKeys, curren
136
137
  consola.info(`Deleting ${keysToDelete.length} option(s)...`);
137
138
  for (const key of keysToDelete) {
138
139
  consola.info(`Deleting option: ${key}`);
139
- await deleteTenantOption(baseUrl, category, key.startsWith("credentials.") ? key.replace(/^credentials\./, "") : key, authHeader);
140
+ await deleteTenantOption(baseUrl, category, key, authHeader);
141
+ delete currentOptions[key];
140
142
  consola.success(`✓ Deleted: ${key}`);
141
143
  }
142
144
  consola.success("Delete operation completed");
@@ -1,6 +1,6 @@
1
1
  //#region package.json
2
2
  var name = "c8y-nitro";
3
- var version = "0.4.2";
3
+ var version = "0.6.0";
4
4
  var description = "Lightning fast Cumulocity IoT microservice development powered by Nitro";
5
5
  //#endregion
6
6
  export { name as n, version as r, description as t };
@@ -1,5 +1,5 @@
1
- import { n as createBasicAuthHeader, p as createC8yManifest, t as assignUserRole, u as unassignUserRole } from "./c8y-api-BBSKRwKs.mjs";
2
- import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-Dqi-ttQi.mjs";
1
+ import { n as createBasicAuthHeader, p as createC8yManifest, t as assignUserRole, u as unassignUserRole } from "./c8y-api-BbRS1-Ls.mjs";
2
+ import { n as validateBootstrapEnv, t as loadC8yConfig } from "./config-BRnvtthI.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  import { consola } from "consola";
5
5
  //#region src/cli/commands/roles.ts
@@ -0,0 +1,35 @@
1
+ import { t as createError } from "../../logging-CVlaRS4O.mjs";
2
+ import { c8yTenantOptionKeys } from "c8y-nitro/runtime";
3
+ import { defineEventHandler, getQuery } from "nitro/h3";
4
+ //#region src/utils/internal/tenantOptionFetchers.ts
5
+ /**
6
+ * Internal storage for cached functions per key
7
+ */
8
+ const tenantOptionFetchers = {};
9
+ //#endregion
10
+ //#region src/module/runtime/handlers/invalidateTenantOptions.ts
11
+ var invalidateTenantOptions_default = defineEventHandler(async (event) => {
12
+ const query = getQuery(event);
13
+ if ("all" in query) {
14
+ await Promise.all(Object.values(tenantOptionFetchers).map((fetcher) => fetcher?.invalidate()));
15
+ return { message: "success" };
16
+ }
17
+ const key = Array.isArray(query.key) ? query.key[0] : query.key;
18
+ if (!key) throw createError({
19
+ status: 400,
20
+ message: "Provide either the all or key query parameter",
21
+ why: "The tenant option invalidation route requires an explicit target",
22
+ fix: "Use ?all to invalidate all created tenant option caches or ?key=<manifest setting key> to invalidate one created cache entry"
23
+ });
24
+ if (!c8yTenantOptionKeys.includes(key)) throw createError({
25
+ status: 400,
26
+ message: "Invalid tenant option invalidation request",
27
+ why: "Only tenant option keys declared in manifest.settings can be invalidated via this route",
28
+ fix: "Use ?all to invalidate all created tenant option caches or provide a valid manifest-defined key in ?key"
29
+ });
30
+ const fetcher = tenantOptionFetchers[key];
31
+ if (fetcher) await fetcher.invalidate();
32
+ return { message: "success" };
33
+ });
34
+ //#endregion
35
+ export { invalidateTenantOptions_default as default };
@@ -0,0 +1,43 @@
1
+ import { t as createError } from "../../logging-CVlaRS4O.mjs";
2
+ import { defineMiddleware } from "nitro";
3
+ import process from "node:process";
4
+ //#region src/module/runtime/middlewares/c8y-client.ts
5
+ function isC8yClientThrownResponse(error) {
6
+ if (!error || typeof error !== "object") return false;
7
+ if (!("res" in error) || !error.res || typeof error.res !== "object") return false;
8
+ const response = error.res;
9
+ if (typeof response.status !== "number") return false;
10
+ const responseUrl = typeof response.url === "string" ? response.url : void 0;
11
+ const baseUrl = process.env.C8Y_BASEURL?.replace(/\/+$/, "");
12
+ const matchesConfiguredTenant = Boolean(baseUrl && responseUrl?.startsWith(baseUrl));
13
+ if ("data" in error && error.data && typeof error.data === "object") {
14
+ const data = error.data;
15
+ if (typeof data.info === "string" && data.info.startsWith("https://cumulocity.com/")) return true;
16
+ }
17
+ return matchesConfiguredTenant;
18
+ }
19
+ var c8y_client_default = defineMiddleware(async (_event, next) => {
20
+ try {
21
+ return await next();
22
+ } catch (error) {
23
+ if (!isC8yClientThrownResponse(error)) throw error;
24
+ const upstreamMessage = error.data?.message || error.res.statusText || "Cumulocity request failed";
25
+ const upstreamCode = error.data?.error;
26
+ throw createError({
27
+ message: "Internal Server Error",
28
+ status: 500,
29
+ internal: {
30
+ help: "An error occurred while processing a request to Cumulocity from the @c8y/client library.",
31
+ upstream: "@c8y/client",
32
+ message: upstreamMessage,
33
+ code: upstreamCode,
34
+ status: error.res.status,
35
+ statusText: error.res.statusText,
36
+ url: error.res.url,
37
+ data: error.data
38
+ }
39
+ });
40
+ }
41
+ });
42
+ //#endregion
43
+ export { c8y_client_default as default };
@@ -2,8 +2,8 @@ import { defineHandler } from "nitro/h3";
2
2
  import process from "node:process";
3
3
  import { Buffer } from "node:buffer";
4
4
  import consola from "consola";
5
- //#region src/module/runtime/middlewares/dev-user.ts
6
- var dev_user_default = defineHandler((event) => {
5
+ //#region src/module/runtime/middlewares/dev-user.dev.ts
6
+ var dev_user_dev_default = defineHandler((event) => {
7
7
  if (import.meta.dev) {
8
8
  const missingDevVars = [
9
9
  "C8Y_DEVELOPMENT_TENANT",
@@ -20,4 +20,4 @@ var dev_user_default = defineHandler((event) => {
20
20
  }
21
21
  });
22
22
  //#endregion
23
- export { dev_user_default as default };
23
+ export { dev_user_dev_default as default };
@@ -1,7 +1,7 @@
1
- import process from "node:process";
2
1
  import { definePlugin } from "nitro";
3
- //#region src/module/runtime/plugins/c8y-variables.ts
4
- var c8y_variables_default = definePlugin(() => {
2
+ import process from "node:process";
3
+ //#region src/module/runtime/plugins/c8y-variables.dev.ts
4
+ var c8y_variables_dev_default = definePlugin(() => {
5
5
  if (import.meta.dev) {
6
6
  const env = process.env;
7
7
  const missingVars = [
@@ -14,4 +14,4 @@ var c8y_variables_default = definePlugin(() => {
14
14
  }
15
15
  });
16
16
  //#endregion
17
- export { c8y_variables_default as default };
17
+ export { c8y_variables_dev_default as default };
@@ -1,5 +1,5 @@
1
- import { definePlugin } from "nitro";
2
1
  import { c8yManifest } from "c8y-nitro/runtime";
2
+ import { definePlugin } from "nitro";
3
3
  //#region src/module/runtime/plugins/enrich-logs.ts
4
4
  var enrich_logs_default = definePlugin(async (nitroApp) => {
5
5
  nitroApp.hooks.hook("evlog:enrich", (enrichContext) => {
package/dist/types.d.mts CHANGED
@@ -1,2 +1,3 @@
1
- import { a as C8yCacheOptions, c as C8YManifestOptions, i as C8YRoles, l as C8YAPIClientOptions, n as C8YTenantOptionKey, o as C8YZipOptions, r as C8YTenantOptionKeysCacheConfig, s as C8YManifest, t as C8yNitroModuleOptions } from "./index-CzUqbp5C.mjs";
2
- export { C8YAPIClientOptions, C8YManifest, C8YManifestOptions, C8YRoles, C8YTenantOptionKey, C8YTenantOptionKeysCacheConfig, C8YZipOptions, C8yCacheOptions, C8yNitroModuleOptions };
1
+ import { a as C8YRoles, c as C8YManifest, i as C8YTenantOptionKeysCacheConfig, l as C8YManifestOptions, n as C8yNitroModuleOptions, o as C8yCacheOptions, r as C8YTenantOptionKey, s as C8YZipOptions, t as C8yDevOptions, u as C8YAPIClientOptions } from "./index-uOwpI6rD.mjs";
2
+ import { t as TenantCredentials } from "./credentials-9FO7rTIR.mjs";
3
+ export { C8YAPIClientOptions, C8YManifest, C8YManifestOptions, C8YRoles, C8YTenantOptionKey, C8YTenantOptionKeysCacheConfig, C8YZipOptions, C8yCacheOptions, C8yDevOptions, C8yNitroModuleOptions, TenantCredentials };
package/dist/utils.d.mts CHANGED
@@ -1,8 +1,9 @@
1
+ import { t as TenantCredentials } from "./credentials-9FO7rTIR.mjs";
1
2
  import { useLogger } from "evlog/nitro/v3";
2
3
  import { Client, ICredentials, ICurrentUser } from "@c8y/client";
3
4
  import { EventHandler, H3Event } from "nitro/h3";
4
5
  import { createError, createLogger } from "evlog";
5
- import { ServerRequest } from "nitro/types";
6
+ import { ServerRequest, TaskContext, TaskPayload } from "nitro/types";
6
7
  import { C8YRoles, C8YTenantOptionKey } from "c8y-nitro/types";
7
8
 
8
9
  //#region src/utils/client.d.ts
@@ -163,31 +164,9 @@ declare function useUser(requestOrEvent: ServerRequest | H3Event): Promise<ICurr
163
164
  declare function useUserRoles(requestOrEvent: ServerRequest | H3Event): Promise<string[]>;
164
165
  //#endregion
165
166
  //#region src/utils/credentials.d.ts
166
- /**
167
- * Fetches credentials for all tenants subscribed to this microservice.\
168
- * Uses bootstrap credentials from runtime config to query the microservice subscriptions API.\
169
- * Results are cached based on the configured TTL (default: 10 minutes).\
170
- * @returns Object mapping tenant IDs to their respective credentials
171
- * @config Cache TTL can be configured via:
172
- * - `c8y.cache.credentialsTTL` in the Nitro config (value in seconds)
173
- * - `NITRO_C8Y_CACHE_CREDENTIALS_TTL` environment variable
174
- * @example
175
- * // Get all subscribed tenant credentials:
176
- * const credentials = await useSubscribedTenantCredentials()
177
- * console.log(Object.keys(credentials)) // ['t12345', 't67890']
178
- *
179
- * // Access specific tenant:
180
- * const tenant1Creds = credentials['t12345']
181
- *
182
- * // Invalidate cache:
183
- * await useSubscribedTenantCredentials.invalidate()
184
- *
185
- * // Force refresh:
186
- * const freshCreds = await useSubscribedTenantCredentials.refresh()
187
- */
188
- declare const useSubscribedTenantCredentials: (() => Promise<Record<string, ICredentials>>) & {
167
+ declare const useSubscribedTenantCredentials: (() => Promise<TenantCredentials>) & {
189
168
  invalidate: () => Promise<void>;
190
- refresh: () => Promise<Record<string, ICredentials>>;
169
+ refresh: () => Promise<TenantCredentials>;
191
170
  };
192
171
  /**
193
172
  * Fetches credentials for the tenant where this microservice is deployed.\
@@ -242,11 +221,6 @@ declare function useUserTenantCredentials(requestOrEvent: ServerRequest | H3Even
242
221
  * - `c8y.cache.tenantOptions` — Per-key TTL overrides
243
222
  * - `NITRO_C8Y_DEFAULT_TENANT_OPTIONS_TTL` — Environment variable for default TTL
244
223
  *
245
- * @note For encrypted options (keys starting with `credentials.`), the value is automatically
246
- * decrypted by Cumulocity if this microservice is the owner of the option (category matches
247
- * the microservice's settingsCategory/contextPath/name). The `credentials.` prefix is
248
- * automatically stripped when calling the API.
249
- *
250
224
  * @example
251
225
  * // Fetch a tenant option:
252
226
  * const value = await useTenantOption('myOption')
@@ -290,4 +264,110 @@ declare const useTenantOption: ((key: C8YTenantOptionKey) => Promise<string | un
290
264
  refreshAll: () => Promise<Record<string, string | undefined>>;
291
265
  };
292
266
  //#endregion
293
- export { createError, createLogger, hasUserRequiredRole, isUserFromAllowedTenant, isUserFromDeployedTenant, useDeployedTenantClient, useDeployedTenantCredentials, useLogger, useSubscribedTenantClients, useSubscribedTenantCredentials, useTenantOption, useUser, useUserClient, useUserRoles, useUserTenantClient, useUserTenantCredentials };
267
+ //#region src/utils/schedule.d.ts
268
+ type ScheduledTaskPayload = TaskPayload;
269
+ type ScheduledTaskContext = TaskContext;
270
+ /**
271
+ * A one-shot schedule definition for `scheduleTask()`.\
272
+ * Numbers are treated as seconds from now, strings are parsed as human-readable durations
273
+ * such as `"10 minutes"`, and dates are used as exact run times.
274
+ */
275
+ type ScheduledTaskInput = Date | number | string;
276
+ /**
277
+ * Options for scheduling a Nitro task to run once in the future.\
278
+ * `payload` and `context` are forwarded to the task handler's `run()` method.
279
+ */
280
+ interface ScheduleTaskOptions {
281
+ /**
282
+ * Payload forwarded to the task handler's `TaskEvent.payload`.
283
+ */
284
+ payload?: ScheduledTaskPayload;
285
+ /**
286
+ * Context forwarded to the task handler's `TaskEvent.context`.
287
+ */
288
+ context?: ScheduledTaskContext;
289
+ /**
290
+ * When the task should run. Numbers are seconds from now.
291
+ */
292
+ schedule: ScheduledTaskInput;
293
+ }
294
+ /**
295
+ * Public information about a pending scheduled task.\
296
+ * Returned by `scheduleTask()` and by `listScheduledTasks()`.
297
+ */
298
+ interface ScheduledTaskInfo {
299
+ /**
300
+ * Stable UUID used to list or cancel the scheduled task.
301
+ */
302
+ id: string;
303
+ /**
304
+ * Nitro task name that will be passed to `runTask()`.
305
+ */
306
+ task: string;
307
+ /**
308
+ * Exact execution time as an ISO date string.
309
+ */
310
+ runAt: string;
311
+ }
312
+ /**
313
+ * Schedules a Nitro task to run once in the future.\
314
+ * Resolves and calls the task handler directly from Nitro's virtual task registry\
315
+ * Numbers are treated as seconds, strings are parsed as human-readable durations, and dates are used as exact run times.
316
+ *
317
+ * @param taskName - The Nitro task name to run (from `tasks/*.ts`)
318
+ * @param options - Task payload, context, and the schedule time
319
+ * @returns Information about the scheduled task
320
+ *
321
+ * @example
322
+ * // Run a task in 30 seconds:
323
+ * const scheduled = await scheduleTask('emails:send', {
324
+ * payload: { messageId: 'abc123' },
325
+ * schedule: 30,
326
+ * })
327
+ *
328
+ * @example
329
+ * // Run a task using a human-readable duration:
330
+ * await scheduleTask('reports:generate', {
331
+ * payload: { reportId: 'report-1' },
332
+ * schedule: '1 hour',
333
+ * })
334
+ *
335
+ * @example
336
+ * // Run a task at an exact time:
337
+ * await scheduleTask('cleanup:tenant', {
338
+ * payload: { tenant: 't12345' },
339
+ * schedule: new Date('2026-05-01T12:00:00Z'),
340
+ * })
341
+ */
342
+ declare function scheduleTask(taskName: string, options: ScheduleTaskOptions): Promise<ScheduledTaskInfo>;
343
+ /**
344
+ * Lists all tasks that are currently scheduled and have not started yet.\
345
+ * The returned object is keyed by the scheduled task UUID for easy lookup and cancellation.
346
+ *
347
+ * @returns Object mapping scheduled task IDs to their public task information
348
+ *
349
+ * @example
350
+ * const tasks = await listScheduledTasks()
351
+ * for (const [id, task] of Object.entries(tasks)) {
352
+ * console.log(id, task.task, task.runAt)
353
+ * }
354
+ */
355
+ declare function listScheduledTasks(): Promise<Record<string, ScheduledTaskInfo>>;
356
+ /**
357
+ * Cancels a scheduled task before it starts running.\
358
+ * Once the underlying Nitro task has started, it cannot be cancelled with this utility.
359
+ *
360
+ * @param id - The scheduled task UUID returned by `scheduleTask()` or `listScheduledTasks()`
361
+ * @returns `true` when a pending task was cancelled, otherwise `false`
362
+ *
363
+ * @example
364
+ * const scheduled = await scheduleTask('emails:send', {
365
+ * payload: { messageId: 'abc123' },
366
+ * schedule: '10 minutes',
367
+ * })
368
+ *
369
+ * const cancelled = await cancelScheduledTask(scheduled.id)
370
+ */
371
+ declare function cancelScheduledTask(id: string): Promise<boolean>;
372
+ //#endregion
373
+ export { ScheduleTaskOptions, ScheduledTaskContext, ScheduledTaskInfo, ScheduledTaskInput, ScheduledTaskPayload, cancelScheduledTask, createError, createLogger, hasUserRequiredRole, isUserFromAllowedTenant, isUserFromDeployedTenant, listScheduledTasks, scheduleTask, useDeployedTenantClient, useDeployedTenantCredentials, useLogger, useSubscribedTenantClients, useSubscribedTenantCredentials, useTenantOption, useUser, useUserClient, useUserRoles, useUserTenantClient, useUserTenantCredentials };