@camunda/ccma-shared-types 1.1.0 → 2.0.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.
Files changed (70) hide show
  1. package/dist/_tests/approvals/policy.spec.d.ts +1 -0
  2. package/dist/approvals/policy.d.ts +109 -0
  3. package/dist/approvals/policy.js +123 -0
  4. package/dist/approvals/policy.js.map +7 -0
  5. package/dist/config/camundi-permission-rules.js +17 -5
  6. package/dist/config/camundi-permission-rules.js.map +2 -2
  7. package/dist/constants/misc.const.d.ts +21 -0
  8. package/dist/constants/misc.const.js +5 -1
  9. package/dist/constants/misc.const.js.map +2 -2
  10. package/dist/constants/v2-list.const.d.ts +12 -0
  11. package/dist/constants/v2-list.const.js +9 -0
  12. package/dist/constants/v2-list.const.js.map +7 -0
  13. package/dist/constants/version-gates.const.d.ts +9 -2
  14. package/dist/constants/version-gates.const.js +1 -1
  15. package/dist/constants/version-gates.const.js.map +2 -2
  16. package/dist/dtos/ApprovalJob.dto.d.ts +83 -0
  17. package/dist/dtos/ApprovalJob.dto.js +14 -0
  18. package/dist/dtos/ApprovalJob.dto.js.map +7 -0
  19. package/dist/dtos/ClusterCreationDefaults.dto.d.ts +96 -0
  20. package/dist/dtos/ClusterCreationDefaults.dto.js +93 -0
  21. package/dist/dtos/ClusterCreationDefaults.dto.js.map +7 -0
  22. package/dist/dtos/CommonCluster.dto.d.ts +1 -0
  23. package/dist/dtos/ControlV2.dto.d.ts +103 -1
  24. package/dist/dtos/FeatureFlags.dto.d.ts +4 -2
  25. package/dist/dtos/Generations.dto.d.ts +10 -0
  26. package/dist/dtos/Generations.dto.js +36 -2
  27. package/dist/dtos/Generations.dto.js.map +2 -2
  28. package/dist/dtos/Hdp.dto.d.ts +29 -0
  29. package/dist/dtos/Organization.dto.d.ts +29 -0
  30. package/dist/dtos/Organization.dto.js.map +2 -2
  31. package/dist/enums/AuditEntity.enum.d.ts +2 -0
  32. package/dist/enums/AuditEntity.enum.js +2 -0
  33. package/dist/enums/AuditEntity.enum.js.map +2 -2
  34. package/dist/enums/AuditService.enum.d.ts +2 -1
  35. package/dist/enums/AuditService.enum.js +1 -0
  36. package/dist/enums/AuditService.enum.js.map +2 -2
  37. package/dist/enums/SystemAuditEntity.enum.d.ts +6 -2
  38. package/dist/enums/SystemAuditEntity.enum.js +4 -0
  39. package/dist/enums/SystemAuditEntity.enum.js.map +2 -2
  40. package/dist/index.d.ts +14 -7
  41. package/dist/index.js +53 -1
  42. package/dist/index.js.map +2 -2
  43. package/dist/permissions/combo.js +3 -0
  44. package/dist/permissions/combo.js.map +2 -2
  45. package/dist/permissions/combo.optionalTelemetry.spec.d.ts +1 -0
  46. package/dist/permissions/console.permissions.d.ts +2 -0
  47. package/dist/permissions/console.permissions.js +13 -1
  48. package/dist/permissions/console.permissions.js.map +2 -2
  49. package/dist/search/cluster-search-fields.js +9 -1
  50. package/dist/search/cluster-search-fields.js.map +2 -2
  51. package/dist/search/compiler.d.ts +25 -0
  52. package/dist/search/compiler.js +12 -3
  53. package/dist/search/compiler.js.map +2 -2
  54. package/dist/search/index.d.ts +1 -1
  55. package/dist/search/index.js +4 -0
  56. package/dist/search/index.js.map +2 -2
  57. package/dist/types/ClusterBulkOperation.type.d.ts +28 -0
  58. package/dist/types/ClusterPlanTypeAvailabilityClass.type.d.ts +11 -0
  59. package/dist/types/ClusterPlanTypeAvailabilityClass.type.js +4 -0
  60. package/dist/types/ClusterPlanTypeAvailabilityClass.type.js.map +2 -2
  61. package/dist/types/Roles.d.ts +1 -1
  62. package/dist/types/Roles.js.map +2 -2
  63. package/dist/utils/camundiPermissionAugmenter.utils.d.ts +14 -0
  64. package/dist/utils/camundiPermissionAugmenter.utils.js +4 -0
  65. package/dist/utils/camundiPermissionAugmenter.utils.js.map +2 -2
  66. package/dist/utils/camundiPermissionAugmenter.utils.spec.d.ts +1 -0
  67. package/dist/utils/versionCheck.utils.d.ts +39 -0
  68. package/dist/utils/versionCheck.utils.js +45 -4
  69. package/dist/utils/versionCheck.utils.js.map +2 -2
  70. package/package.json +1 -1
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The wire types of the generic approval mechanism.
3
+ *
4
+ * This file holds no logic and no dependency, because three consumers read it:
5
+ * control-backend, control-frontend and cloudbot. The rules that read these
6
+ * types are in `../approvals/policy.js`, and they are pure for the same reason.
7
+ */
8
+ /**
9
+ * Every state that an approval request can hold.
10
+ *
11
+ * A `const` tuple, not an enum, so it is both the union type and the list that
12
+ * validates a status filter at run time.
13
+ *
14
+ * The path of a request: `pending` until it reaches the threshold. Then
15
+ * `executing` (automatic mode) or `approved` (explicit mode, which waits for a
16
+ * person to press the button). `executed` or `failed` after the action ran. A
17
+ * request that never reaches the threshold ends `rejected`, `withdrawn` or
18
+ * `expired`.
19
+ */
20
+ export declare const APPROVAL_JOB_STATUSES: readonly ["pending", "approved", "executing", "executed", "failed", "rejected", "withdrawn", "expired"];
21
+ export type ApprovalJobStatus = (typeof APPROVAL_JOB_STATUSES)[number];
22
+ /**
23
+ * What happens when a request reaches its threshold.
24
+ *
25
+ * `auto` runs the action immediately. `explicit` holds the request at
26
+ * `approved` and waits for a person to run it.
27
+ */
28
+ export type ApprovalExecutionMode = 'auto' | 'explicit';
29
+ /**
30
+ * A person, as an approval request records them.
31
+ *
32
+ * Both keys are kept. The machine then never has to choose one identity key,
33
+ * which is what split the earlier acknowledgement flows: some held an email and
34
+ * some held a user id, and the two could not be compared.
35
+ */
36
+ export interface ApprovalActor {
37
+ userId: string;
38
+ email: string;
39
+ }
40
+ export interface ApprovalAck extends ApprovalActor {
41
+ /** An ISO timestamp. It is in a json column, so it is a string, not a Date. */
42
+ date: string;
43
+ }
44
+ /** One approval request, as the API returns it. */
45
+ export interface ApprovalJobDto {
46
+ id: string;
47
+ type: string;
48
+ dedupeKey?: string;
49
+ payload: Record<string, unknown>;
50
+ createdBy: ApprovalActor;
51
+ acks: ApprovalAck[];
52
+ /** Copied from the use case, so a reader can render "1 of 2". */
53
+ requiredAcks: number;
54
+ executionMode: ApprovalExecutionMode;
55
+ status: ApprovalJobStatus;
56
+ closedBy?: ApprovalActor;
57
+ closedReason?: string;
58
+ /** An ISO timestamp. */
59
+ expiresAt: string;
60
+ executedAt?: string;
61
+ executionError?: string;
62
+ createdAt: string;
63
+ updatedAt: string;
64
+ }
65
+ /**
66
+ * What a use case is, as the API reports it.
67
+ *
68
+ * The frontend needs `requiredPermission` to run the same rule as the backend.
69
+ * PR #9301 kept this registry a second time, by hand, in the frontend. A
70
+ * registry that the server owns means a new use case needs no frontend deploy.
71
+ */
72
+ export interface ApprovalUseCaseDto {
73
+ type: string;
74
+ /** A short name for a person to read. */
75
+ label: string;
76
+ requiredAcks: number;
77
+ ttlDays: number;
78
+ executionMode: ApprovalExecutionMode;
79
+ /** The permission that acknowledge, reject and run all need. */
80
+ requiredPermission: string;
81
+ /** The permission to open a request. Absent means any signed-in user. */
82
+ createPermission?: string;
83
+ }
@@ -0,0 +1,14 @@
1
+ const APPROVAL_JOB_STATUSES = [
2
+ "pending",
3
+ "approved",
4
+ "executing",
5
+ "executed",
6
+ "failed",
7
+ "rejected",
8
+ "withdrawn",
9
+ "expired"
10
+ ];
11
+ export {
12
+ APPROVAL_JOB_STATUSES
13
+ };
14
+ //# sourceMappingURL=ApprovalJob.dto.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/dtos/ApprovalJob.dto.ts"],
4
+ "sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\n/**\n * The wire types of the generic approval mechanism.\n *\n * This file holds no logic and no dependency, because three consumers read it:\n * control-backend, control-frontend and cloudbot. The rules that read these\n * types are in `../approvals/policy.js`, and they are pure for the same reason.\n */\n\n/**\n * Every state that an approval request can hold.\n *\n * A `const` tuple, not an enum, so it is both the union type and the list that\n * validates a status filter at run time.\n *\n * The path of a request: `pending` until it reaches the threshold. Then\n * `executing` (automatic mode) or `approved` (explicit mode, which waits for a\n * person to press the button). `executed` or `failed` after the action ran. A\n * request that never reaches the threshold ends `rejected`, `withdrawn` or\n * `expired`.\n */\nexport const APPROVAL_JOB_STATUSES = [\n 'pending',\n 'approved',\n 'executing',\n 'executed',\n 'failed',\n 'rejected',\n 'withdrawn',\n 'expired',\n] as const\n\nexport type ApprovalJobStatus = (typeof APPROVAL_JOB_STATUSES)[number]\n\n/**\n * What happens when a request reaches its threshold.\n *\n * `auto` runs the action immediately. `explicit` holds the request at\n * `approved` and waits for a person to run it.\n */\nexport type ApprovalExecutionMode = 'auto' | 'explicit'\n\n/**\n * A person, as an approval request records them.\n *\n * Both keys are kept. The machine then never has to choose one identity key,\n * which is what split the earlier acknowledgement flows: some held an email and\n * some held a user id, and the two could not be compared.\n */\nexport interface ApprovalActor {\n userId: string\n email: string\n}\n\nexport interface ApprovalAck extends ApprovalActor {\n /** An ISO timestamp. It is in a json column, so it is a string, not a Date. */\n date: string\n}\n\n/** One approval request, as the API returns it. */\nexport interface ApprovalJobDto {\n id: string\n type: string\n dedupeKey?: string\n payload: Record<string, unknown>\n createdBy: ApprovalActor\n acks: ApprovalAck[]\n /** Copied from the use case, so a reader can render \"1 of 2\". */\n requiredAcks: number\n executionMode: ApprovalExecutionMode\n status: ApprovalJobStatus\n closedBy?: ApprovalActor\n closedReason?: string\n /** An ISO timestamp. */\n expiresAt: string\n executedAt?: string\n executionError?: string\n createdAt: string\n updatedAt: string\n}\n\n/**\n * What a use case is, as the API reports it.\n *\n * The frontend needs `requiredPermission` to run the same rule as the backend.\n * PR #9301 kept this registry a second time, by hand, in the frontend. A\n * registry that the server owns means a new use case needs no frontend deploy.\n */\nexport interface ApprovalUseCaseDto {\n type: string\n /** A short name for a person to read. */\n label: string\n requiredAcks: number\n ttlDays: number\n executionMode: ApprovalExecutionMode\n /** The permission that acknowledge, reject and run all need. */\n requiredPermission: string\n /** The permission to open a request. Absent means any signed-in user. */\n createPermission?: string\n}\n"],
5
+ "mappings": "AA0BO,MAAM,wBAAwB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Cluster settings for which an organization can hold a default value.
3
+ *
4
+ * A field that is absent keeps the value that the `Cluster` constructor
5
+ * writes. The defaults apply at creation only: they seed the new row, and
6
+ * nothing applies them again. A cluster that exists keeps its own values.
7
+ *
8
+ * A value on the cluster changes afterwards through the surface of that
9
+ * setting, with the permissions of that setting — thus the customer changes
10
+ * most of them alone, and a Camunda-managed setting such as the optional
11
+ * telemetry needs a Camundi (#9732).
12
+ */
13
+ export interface ClusterCreationDefaults {
14
+ autoUpdate?: boolean;
15
+ businessIdUniquenessEnabled?: boolean;
16
+ enforceGroups?: boolean;
17
+ mcpEnabled?: boolean;
18
+ multiTenancyEnabled?: boolean;
19
+ optionalTelemetryEnabled?: boolean;
20
+ rba?: boolean;
21
+ sessionHeartbeatEnabled?: boolean;
22
+ sessionMaxInactiveIntervalMinutes?: number;
23
+ }
24
+ export type ClusterCreationDefaultKey = keyof ClusterCreationDefaults;
25
+ /**
26
+ * Who can write the default of a field today.
27
+ *
28
+ * - `planned`: nobody. The backend accepts the field, and Control shows the
29
+ * row without a control. The mechanism is there, and the field waits for
30
+ * the team that owns the setting.
31
+ * - `control`: a Camundi in Control, after a support request.
32
+ * - `customer`: a Camundi in Control, and the organization itself in the
33
+ * Console settings.
34
+ *
35
+ * The accounts route refuses what this value does not permit, thus a widened
36
+ * value is the whole rollout of a field.
37
+ */
38
+ export type ClusterCreationDefaultAvailability = 'planned' | 'control' | 'customer';
39
+ export interface ClusterCreationDefaultField {
40
+ key: ClusterCreationDefaultKey;
41
+ /** Name of the field on the Control panel. */
42
+ label: string;
43
+ kind: 'boolean' | 'number';
44
+ availability: ClusterCreationDefaultAvailability;
45
+ /**
46
+ * Value that a new cluster gets when the organization holds no default.
47
+ * It must agree with the `Cluster` constructor, because the panel shows it
48
+ * as the value that applies.
49
+ */
50
+ fallback: boolean | number;
51
+ /** Bounds of a number field. They mirror the operator CRD validation. */
52
+ min?: number;
53
+ max?: number;
54
+ /**
55
+ * Console permissions that permit a write of this default. The user needs
56
+ * ALL of them, in addition to the base permission of the route.
57
+ *
58
+ * Each path is a path into `ConsolePermissions.Features`. The frontend adds
59
+ * the `console.` prefix that the session endpoint uses; the backend reads
60
+ * the path from `res.locals.permissions`.
61
+ *
62
+ * A field without a list is a setting that the customer can change without
63
+ * help. The default of such a field gives no more power than the customer
64
+ * has, thus the base permission is sufficient.
65
+ *
66
+ * The per-cluster route of a setting can ask for a different permission for
67
+ * each direction — `autoUpdate` is such a route. A default holds both
68
+ * directions over time, and one entry cannot say which, thus the list asks
69
+ * for all permissions of that route. The default is then never easier to
70
+ * write than the setting itself.
71
+ */
72
+ permissionAllOf?: readonly string[];
73
+ /**
74
+ * Permissions of accounts that a Camundi needs for this field, in addition
75
+ * to the permission of the route. The user needs ALL of them.
76
+ *
77
+ * Each path is a path into `AccountsPermissions.Features`. The frontend
78
+ * reads the path as it is, because the session endpoint carries the
79
+ * accounts keys without a prefix.
80
+ *
81
+ * The permission of the route is `admin.readAll`, which is a read
82
+ * permission that many roles hold. A field that a Camundi may write names
83
+ * the write permission here, thus the read of the surface and the write of
84
+ * a value stay two different questions.
85
+ */
86
+ accountsPermissionAllOf?: readonly string[];
87
+ }
88
+ /**
89
+ * The defaultable fields, in the order that the Control panel shows them.
90
+ *
91
+ * This list is the only place that knows a field is defaultable. The route
92
+ * builds its validation from it, the creation path reads it to merge, and the
93
+ * panel builds its controls from it. A new cluster setting becomes
94
+ * defaultable through one entry here.
95
+ */
96
+ export declare const CLUSTER_CREATION_DEFAULT_FIELDS: readonly ClusterCreationDefaultField[];
@@ -0,0 +1,93 @@
1
+ const CLUSTER_CREATION_DEFAULT_FIELDS = [
2
+ {
3
+ key: "optionalTelemetryEnabled",
4
+ label: "Optional telemetry",
5
+ kind: "boolean",
6
+ // Camunda-managed: the customer asked for a toggle, and the answer is
7
+ // a support request. The org default follows that rule.
8
+ availability: "control",
9
+ fallback: true,
10
+ permissionAllOf: ["clusters.canUpdateOptionalTelemetry"]
11
+ },
12
+ {
13
+ key: "sessionMaxInactiveIntervalMinutes",
14
+ label: "Session idle timeout (minutes)",
15
+ kind: "number",
16
+ // The first field that the organization sets itself. A security
17
+ // posture is the reason for an org-wide idle timeout, and the customer
18
+ // owns that posture.
19
+ availability: "customer",
20
+ // The same permission that a limit of the organization asks for. A
21
+ // default of a cluster setting is org-wide configuration, thus it
22
+ // follows the Limits tab and not the read permission of the route.
23
+ accountsPermissionAllOf: ["admin.mayChangeOrgLimits"],
24
+ fallback: 30,
25
+ min: 1,
26
+ max: 1440
27
+ },
28
+ {
29
+ key: "sessionHeartbeatEnabled",
30
+ availability: "planned",
31
+ label: "Session heartbeat",
32
+ kind: "boolean",
33
+ fallback: false
34
+ },
35
+ {
36
+ key: "multiTenancyEnabled",
37
+ availability: "planned",
38
+ label: "Multi-tenancy",
39
+ kind: "boolean",
40
+ fallback: false
41
+ },
42
+ {
43
+ key: "rba",
44
+ availability: "planned",
45
+ label: "Resource-based authorizations",
46
+ kind: "boolean",
47
+ fallback: false
48
+ },
49
+ {
50
+ key: "enforceGroups",
51
+ availability: "planned",
52
+ label: "Enforce groups",
53
+ kind: "boolean",
54
+ fallback: true
55
+ },
56
+ {
57
+ key: "mcpEnabled",
58
+ availability: "planned",
59
+ label: "MCP",
60
+ kind: "boolean",
61
+ fallback: false
62
+ },
63
+ {
64
+ key: "businessIdUniquenessEnabled",
65
+ availability: "planned",
66
+ label: "Business ID uniqueness",
67
+ kind: "boolean",
68
+ fallback: false
69
+ },
70
+ {
71
+ key: "autoUpdate",
72
+ availability: "planned",
73
+ label: "Auto-update",
74
+ kind: "boolean",
75
+ // The creation path resolves an absent value to false, and the channel
76
+ // condition can still remove it: a cluster on a channel that is not
77
+ // Stable never gets auto-update. The `true` of the `Cluster`
78
+ // constructor never applies here, because every caller of
79
+ // `createNewCluster` gives the field a value.
80
+ fallback: false,
81
+ // The per-cluster route asks for `canSendToSleep` to turn auto-update
82
+ // off and for `canUpgradeGenerationYolo` to turn it on. A default holds
83
+ // both directions, thus it asks for both permissions.
84
+ permissionAllOf: [
85
+ "clusters.canSendToSleep",
86
+ "clusters.canUpgradeGenerationYolo"
87
+ ]
88
+ }
89
+ ];
90
+ export {
91
+ CLUSTER_CREATION_DEFAULT_FIELDS
92
+ };
93
+ //# sourceMappingURL=ClusterCreationDefaults.dto.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/dtos/ClusterCreationDefaults.dto.ts"],
4
+ "sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\n/**\n * Cluster settings for which an organization can hold a default value.\n *\n * A field that is absent keeps the value that the `Cluster` constructor\n * writes. The defaults apply at creation only: they seed the new row, and\n * nothing applies them again. A cluster that exists keeps its own values.\n *\n * A value on the cluster changes afterwards through the surface of that\n * setting, with the permissions of that setting \u2014 thus the customer changes\n * most of them alone, and a Camunda-managed setting such as the optional\n * telemetry needs a Camundi (#9732).\n */\nexport interface ClusterCreationDefaults {\n autoUpdate?: boolean\n businessIdUniquenessEnabled?: boolean\n enforceGroups?: boolean\n mcpEnabled?: boolean\n multiTenancyEnabled?: boolean\n optionalTelemetryEnabled?: boolean\n rba?: boolean\n sessionHeartbeatEnabled?: boolean\n sessionMaxInactiveIntervalMinutes?: number\n}\n\nexport type ClusterCreationDefaultKey = keyof ClusterCreationDefaults\n\n/**\n * Who can write the default of a field today.\n *\n * - `planned`: nobody. The backend accepts the field, and Control shows the\n * row without a control. The mechanism is there, and the field waits for\n * the team that owns the setting.\n * - `control`: a Camundi in Control, after a support request.\n * - `customer`: a Camundi in Control, and the organization itself in the\n * Console settings.\n *\n * The accounts route refuses what this value does not permit, thus a widened\n * value is the whole rollout of a field.\n */\nexport type ClusterCreationDefaultAvailability =\n | 'planned'\n | 'control'\n | 'customer'\n\nexport interface ClusterCreationDefaultField {\n key: ClusterCreationDefaultKey\n /** Name of the field on the Control panel. */\n label: string\n kind: 'boolean' | 'number'\n availability: ClusterCreationDefaultAvailability\n /**\n * Value that a new cluster gets when the organization holds no default.\n * It must agree with the `Cluster` constructor, because the panel shows it\n * as the value that applies.\n */\n fallback: boolean | number\n /** Bounds of a number field. They mirror the operator CRD validation. */\n min?: number\n max?: number\n /**\n * Console permissions that permit a write of this default. The user needs\n * ALL of them, in addition to the base permission of the route.\n *\n * Each path is a path into `ConsolePermissions.Features`. The frontend adds\n * the `console.` prefix that the session endpoint uses; the backend reads\n * the path from `res.locals.permissions`.\n *\n * A field without a list is a setting that the customer can change without\n * help. The default of such a field gives no more power than the customer\n * has, thus the base permission is sufficient.\n *\n * The per-cluster route of a setting can ask for a different permission for\n * each direction \u2014 `autoUpdate` is such a route. A default holds both\n * directions over time, and one entry cannot say which, thus the list asks\n * for all permissions of that route. The default is then never easier to\n * write than the setting itself.\n */\n permissionAllOf?: readonly string[]\n /**\n * Permissions of accounts that a Camundi needs for this field, in addition\n * to the permission of the route. The user needs ALL of them.\n *\n * Each path is a path into `AccountsPermissions.Features`. The frontend\n * reads the path as it is, because the session endpoint carries the\n * accounts keys without a prefix.\n *\n * The permission of the route is `admin.readAll`, which is a read\n * permission that many roles hold. A field that a Camundi may write names\n * the write permission here, thus the read of the surface and the write of\n * a value stay two different questions.\n */\n accountsPermissionAllOf?: readonly string[]\n}\n\n/**\n * The defaultable fields, in the order that the Control panel shows them.\n *\n * This list is the only place that knows a field is defaultable. The route\n * builds its validation from it, the creation path reads it to merge, and the\n * panel builds its controls from it. A new cluster setting becomes\n * defaultable through one entry here.\n */\nexport const CLUSTER_CREATION_DEFAULT_FIELDS: readonly ClusterCreationDefaultField[] =\n [\n {\n key: 'optionalTelemetryEnabled',\n label: 'Optional telemetry',\n kind: 'boolean',\n // Camunda-managed: the customer asked for a toggle, and the answer is\n // a support request. The org default follows that rule.\n availability: 'control',\n fallback: true,\n permissionAllOf: ['clusters.canUpdateOptionalTelemetry'],\n },\n {\n key: 'sessionMaxInactiveIntervalMinutes',\n label: 'Session idle timeout (minutes)',\n kind: 'number',\n // The first field that the organization sets itself. A security\n // posture is the reason for an org-wide idle timeout, and the customer\n // owns that posture.\n availability: 'customer',\n // The same permission that a limit of the organization asks for. A\n // default of a cluster setting is org-wide configuration, thus it\n // follows the Limits tab and not the read permission of the route.\n accountsPermissionAllOf: ['admin.mayChangeOrgLimits'],\n fallback: 30,\n min: 1,\n max: 1440,\n },\n {\n key: 'sessionHeartbeatEnabled',\n availability: 'planned',\n label: 'Session heartbeat',\n kind: 'boolean',\n fallback: false,\n },\n {\n key: 'multiTenancyEnabled',\n availability: 'planned',\n label: 'Multi-tenancy',\n kind: 'boolean',\n fallback: false,\n },\n {\n key: 'rba',\n availability: 'planned',\n label: 'Resource-based authorizations',\n kind: 'boolean',\n fallback: false,\n },\n {\n key: 'enforceGroups',\n availability: 'planned',\n label: 'Enforce groups',\n kind: 'boolean',\n fallback: true,\n },\n {\n key: 'mcpEnabled',\n availability: 'planned',\n label: 'MCP',\n kind: 'boolean',\n fallback: false,\n },\n {\n key: 'businessIdUniquenessEnabled',\n availability: 'planned',\n label: 'Business ID uniqueness',\n kind: 'boolean',\n fallback: false,\n },\n {\n key: 'autoUpdate',\n availability: 'planned',\n label: 'Auto-update',\n kind: 'boolean',\n // The creation path resolves an absent value to false, and the channel\n // condition can still remove it: a cluster on a channel that is not\n // Stable never gets auto-update. The `true` of the `Cluster`\n // constructor never applies here, because every caller of\n // `createNewCluster` gives the field a value.\n fallback: false,\n // The per-cluster route asks for `canSendToSleep` to turn auto-update\n // off and for `canUpgradeGenerationYolo` to turn it on. A default holds\n // both directions, thus it asks for both permissions.\n permissionAllOf: [\n 'clusters.canSendToSleep',\n 'clusters.canUpgradeGenerationYolo',\n ],\n },\n ]\n"],
5
+ "mappings": "AA4GO,MAAM,kCACX;AAAA,EACE;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA;AAAA;AAAA,IAGN,cAAc;AAAA,IACd,UAAU;AAAA,IACV,iBAAiB,CAAC,qCAAqC;AAAA,EACzD;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA;AAAA;AAAA;AAAA,IAIN,cAAc;AAAA;AAAA;AAAA;AAAA,IAId,yBAAyB,CAAC,0BAA0B;AAAA,IACpD,UAAU;AAAA,IACV,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,cAAc;AAAA,IACd,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,cAAc;AAAA,IACd,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,cAAc;AAAA,IACd,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,cAAc;AAAA,IACd,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,cAAc;AAAA,IACd,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,cAAc;AAAA,IACd,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,cAAc;AAAA,IACd,OAAO;AAAA,IACP,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMN,UAAU;AAAA;AAAA;AAAA;AAAA,IAIV,iBAAiB;AAAA,MACf;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -162,6 +162,7 @@ export interface CommonClusterDto {
162
162
  optimizeEnabled?: boolean;
163
163
  optimizeFiltering?: OptimizeFilteringConfig;
164
164
  secretsFilterMode?: SecretsFilterMode;
165
+ nonSpotNodesEnabled?: boolean;
165
166
  };
166
167
  rba?: boolean;
167
168
  identityBackendChecksEnabled?: boolean;
@@ -1,4 +1,5 @@
1
1
  import type { SalesPlanType } from '../types/SalesPlanType.type.js';
2
+ import type { LicensingModelType } from './Organization.dto.js';
2
3
  /** Generic cursor-paginated response. `nextCursor === null` on the last page. */
3
4
  export interface CursorPage<T> {
4
5
  items: T[];
@@ -42,6 +43,19 @@ export interface OrgListItemV2Dto {
42
43
  /** Concrete plan name (`SalesPlan.name`), distinct from the tier `salesPlanType`. */
43
44
  salesPlanName?: string;
44
45
  salesForceId?: string;
46
+ /** ISO-8601 end of the trial, when the org is or was on a trial plan. */
47
+ trialExpiresAt?: string;
48
+ /**
49
+ * Persisted trial state: `true` when the trial ran out, `false` while it
50
+ * runs. Absent when the org is not on a trial plan or the state is not set.
51
+ */
52
+ trialExpired?: boolean;
53
+ /** Org metadata flag: the org can only create clusters on AWS. */
54
+ awsOnly?: boolean;
55
+ /** Org metadata flag: enterprise plan limited to development use. */
56
+ enterpriseDevOnly?: boolean;
57
+ /** Org metadata: which licensing model prices the org. */
58
+ licensingModel?: LicensingModelType;
45
59
  }
46
60
  export interface ListOrganizationsV2Params extends CursorParams {
47
61
  q?: string;
@@ -129,9 +143,64 @@ export interface ClusterListItemV2Dto {
129
143
  channel?: string;
130
144
  /** ISO-8601; part of the cursor key. */
131
145
  created: string;
146
+ /**
147
+ * ISO-8601 instant of the last suspend. Absent when the cluster is awake:
148
+ * `Cluster.wakeUp()` clears the column, thus this is the time since the last
149
+ * suspend and not the total time that the cluster was asleep.
150
+ */
151
+ suspendedAt?: string;
132
152
  }
133
153
  /** Detail extends the list shape; richer fields are added in Phase 3. */
134
154
  export type ClusterDetailV2Dto = ClusterListItemV2Dto;
155
+ /**
156
+ * How much work a cluster did, inside the 30-day activity window.
157
+ *
158
+ * `idle` and `unknown` are different facts and must stay apart. `idle` is
159
+ * evidence that the cluster did no work. `unknown` is the absence of
160
+ * evidence: on the dev stage most clusters have no row at all.
161
+ *
162
+ * The source is the Holistic Data Platform (HDP), which holds one row per
163
+ * cluster, per day, per value metric. The window is 30 days, thus a bucket
164
+ * for "did work, but more than 30 days ago" is not possible.
165
+ */
166
+ export type ClusterActivityBucket =
167
+ /** Work inside the 30-day window. */
168
+ 'active'
169
+ /** Rows exist inside the window. All values are zero. */
170
+ | 'idle'
171
+ /** No row at all inside the window. */
172
+ | 'unknown';
173
+ /** Quick-filter tiles on the clusters list. */
174
+ export type ClusterActivityTile = 'all' | 'active' | 'idle' | 'unknown'
175
+ /** More than 90 days old, and not active. */
176
+ | 'old-idle'
177
+ /** No more than 30 days old, with no work and no data. */
178
+ | 'new-idle';
179
+ export interface ClusterActivityV2Dto {
180
+ clusterId: string;
181
+ /**
182
+ * ISO-8601 instant of the last UTC day with work. HDP buckets by day, thus
183
+ * the time part is always midnight. Absent when the cluster did no work
184
+ * inside the window.
185
+ */
186
+ lastActiveAt?: string;
187
+ processInstances30d: number;
188
+ bucket: ClusterActivityBucket;
189
+ }
190
+ export interface ClusterActivityV2Response {
191
+ /**
192
+ * One entry per live cluster that has an HDP row inside the window. A
193
+ * cluster in the `unknown` bucket has no entry, because the bucket means
194
+ * that no row exists.
195
+ */
196
+ items: ClusterActivityV2Dto[];
197
+ /** Fleet-wide tile counts. The server joins `cluster.created` for the age tiles. */
198
+ counts: Record<ClusterActivityTile, number>;
199
+ /** ISO-8601 instant when the server assembled this response. */
200
+ fetchedAt: string;
201
+ /** The number of days that the window covers. */
202
+ windowDays: number;
203
+ }
135
204
  export interface ListClustersV2Params extends CursorParams {
136
205
  q?: string;
137
206
  /**
@@ -207,8 +276,41 @@ export interface ListClustersV2Params extends CursorParams {
207
276
  minorVersion?: string;
208
277
  /** Restrict to these release channel names. */
209
278
  channels?: string[];
210
- /** ISO-8601 lower bound on `created` (e.g. "created in the last 7 days"). */
279
+ /**
280
+ * ISO-8601 lower bound on `created` (e.g. "created in the last 7 days").
281
+ * Inclusive: a cluster created at this instant matches.
282
+ */
211
283
  createdAfter?: string;
284
+ /**
285
+ * ISO-8601 upper bound on `created`. Pairs with `createdAfter` for the
286
+ * age axis. Exclusive: a cluster created at this instant does not match.
287
+ */
288
+ createdBefore?: string;
289
+ /**
290
+ * ISO-8601 upper bound on `suspendedAt`, thus "asleep since at least N
291
+ * days". Exclusive, as `createdBefore` is. A cluster that is awake never
292
+ * matches: `Cluster.wakeUp()` clears the column.
293
+ *
294
+ * The bound is the time since the last suspend. A cluster that woke up for
295
+ * one minute and then slept again starts from zero, thus this filter finds
296
+ * the clusters that nobody touched, not the clusters that did no work.
297
+ */
298
+ suspendedBefore?: string;
299
+ /**
300
+ * ISO-8601 lower bound on `suspendedAt`, thus "asleep for not more than N
301
+ * days". Inclusive, as `createdAfter` is. It makes the suspension-age
302
+ * range together with `suspendedBefore`.
303
+ *
304
+ * A cluster that is awake never matches, for the same reason as
305
+ * `suspendedBefore`.
306
+ */
307
+ suspendedAfter?: string;
308
+ /**
309
+ * Restrict to these activity buckets. The controller resolves the buckets
310
+ * to a cluster id set from the cached HDP activity map, the same way
311
+ * `filter.status` resolves K8s state to a cluster id set.
312
+ */
313
+ activity?: ClusterActivityBucket[];
212
314
  };
213
315
  }
214
316
  export interface ClusterParameterOptionV2Dto {
@@ -9,7 +9,7 @@
9
9
  * from there. `@camunda-cloud/featureflags` re-exports `Features` from here.
10
10
  * Thus the type has one definition, and every existing import path still works.
11
11
  *
12
- * A c4 host reads one flag with `useC4FeatureFlag('console.optionalTelemetry')`
12
+ * A c4 host reads one flag with `useC4FeatureFlag('console.appIntegrations')`
13
13
  * — a dot path into this object.
14
14
  */
15
15
  export interface Features {
@@ -25,16 +25,18 @@ export interface Features {
25
25
  byoidp: boolean;
26
26
  optimizeUnsetOnDisable: boolean;
27
27
  appIntegrations: boolean;
28
- optionalTelemetry: boolean;
28
+ orgClusterDefaults: boolean;
29
29
  releaseTrainSanityFix: boolean;
30
30
  hubHelpCenterLinks: boolean;
31
31
  legacyModelerApiNaming: boolean;
32
32
  backupScheduleApi: boolean;
33
33
  clusterCreationBlocked: boolean;
34
34
  deferSpecChangeWhileAsleep: boolean;
35
+ clusterDuplication: boolean;
35
36
  };
36
37
  control: {
37
38
  frontendTelemetry: boolean;
39
+ sendToExpiredTrialApproval: boolean;
38
40
  };
39
41
  accounts: {
40
42
  moveClusterOrg: boolean;
@@ -30,3 +30,13 @@ export declare function sortGenerationBySemverOfZeebe(generationA: {
30
30
  zeebe: string;
31
31
  };
32
32
  }): number;
33
+ /**
34
+ * Orders generation names by product, then newest first within a product, by
35
+ * numeric collation: `8.10` comes before `8.9`, and `gen10` before `gen9`.
36
+ * Products stay in alphabetic order, so the Camunda generations come before
37
+ * the older Zeebe ones. A release comes before its prereleases, so
38
+ * `8.10.0` is above `8.10.0-alpha1`. For a list that has no zeebe image to
39
+ * compare, such as a filter facet. When the image is known, use
40
+ * `sortGenerationBySemverOfZeebe`.
41
+ */
42
+ export declare const compareGenerationNamesNewestFirst: (nameA: string, nameB: string) => number;
@@ -14,16 +14,50 @@ function sortGenerationBySemverOfZeebe(generationA, generationB) {
14
14
  if (byVersion !== 0) {
15
15
  return byVersion;
16
16
  }
17
- return secfixRank(zeebeB) - secfixRank(zeebeA);
17
+ const bySecfix = secfixRank(zeebeB) - secfixRank(zeebeA);
18
+ if (bySecfix !== 0) {
19
+ return bySecfix;
20
+ }
21
+ return compareGenerationNamesNewestFirst(generationA.name, generationB.name);
18
22
  } else if (versionAvalid) {
19
23
  return -1;
20
24
  } else if (versionBvalid) {
21
25
  return 1;
22
26
  } else {
23
- return generationB.name.localeCompare(generationA.name);
27
+ return compareGenerationNamesNewestFirst(generationA.name, generationB.name);
24
28
  }
25
29
  }
30
+ const productPrefix = (name) => {
31
+ const firstDigit = name.search(/\d/);
32
+ return firstDigit === -1 ? name : name.slice(0, firstDigit);
33
+ };
34
+ const splitPrerelease = (name) => {
35
+ const dash = name.indexOf("-", name.search(/\d/));
36
+ return dash === -1 ? [name, ""] : [name.slice(0, dash), name.slice(dash + 1)];
37
+ };
38
+ const compareGenerationNamesNewestFirst = (nameA, nameB) => {
39
+ const collation = { numeric: true, sensitivity: "base" };
40
+ const byProduct = productPrefix(nameA).localeCompare(
41
+ productPrefix(nameB),
42
+ void 0,
43
+ collation
44
+ );
45
+ if (byProduct !== 0) {
46
+ return byProduct;
47
+ }
48
+ const [baseA, suffixA] = splitPrerelease(nameA);
49
+ const [baseB, suffixB] = splitPrerelease(nameB);
50
+ const byBase = baseB.localeCompare(baseA, void 0, collation);
51
+ if (byBase !== 0) {
52
+ return byBase;
53
+ }
54
+ if (suffixA === "" || suffixB === "") {
55
+ return suffixA === suffixB ? 0 : suffixA === "" ? -1 : 1;
56
+ }
57
+ return suffixB.localeCompare(suffixA, void 0, collation);
58
+ };
26
59
  export {
60
+ compareGenerationNamesNewestFirst,
27
61
  sortGenerationBySemverOfZeebe
28
62
  };
29
63
  //# sourceMappingURL=Generations.dto.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/dtos/Generations.dto.ts"],
4
- "sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\nimport semver from 'semver'\nimport type { ZeebeClusterVersionsDto } from '../types/C8ClusterComponentVersions.type.js'\nimport { stripSecfixPostfix } from '../utils/versionCheck.utils.js'\n\nexport interface GenerationDto {\n name: string\n uuid: string\n versions: ZeebeClusterVersionsDto\n upgradeableFrom: GenerationDto[]\n createdBy: string\n createdByName?: string\n created: Date\n updatedBy: string\n updated: Date\n isUnsupportedStarting?: Date\n isBespoke?: boolean\n}\n\nexport interface GenerationWithClusterCountDto extends GenerationDto {\n clusterCount: number\n}\n\nexport interface UpgradableGenerationsDto {\n preferred: GenerationDto | null\n options: GenerationDto[]\n}\n\nconst secfixRank = (version: string): number =>\n version === stripSecfixPostfix(version) ? 0 : 1\n\nexport function sortGenerationBySemverOfZeebe(\n generationA: { name: string; versions?: { zeebe: string } },\n generationB: { name: string; versions?: { zeebe: string } },\n) {\n // magic sorting algorithm\n // uses the generation version of zeebe for comparison\n // if version invalid: use localeCompare\n\n const versionAvalid: boolean =\n generationA.versions?.zeebe !== undefined &&\n semver.valid(generationA.versions.zeebe.split(':')[1]) !== null\n const versionBvalid: boolean =\n generationB.versions?.zeebe !== undefined &&\n semver.valid(generationB.versions.zeebe.split(':')[1]) !== null\n\n if (versionAvalid && versionBvalid) {\n const zeebeA = generationA.versions!.zeebe.split(':')[1]\n const zeebeB = generationB.versions!.zeebe.split(':')[1]\n const byVersion = semver.compare(\n stripSecfixPostfix(zeebeB),\n stripSecfixPostfix(zeebeA),\n )\n if (byVersion !== 0) {\n return byVersion\n }\n // Both builds carry the same Camunda version here, so only the postfix\n // separates them. A secfix build holds that version plus a security patch,\n // thus it is the newer of the two and takes the higher rank.\n return secfixRank(zeebeB) - secfixRank(zeebeA)\n } else if (versionAvalid) {\n return -1\n } else if (versionBvalid) {\n return 1\n } else {\n return generationB.name.localeCompare(generationA.name)\n }\n}\n"],
5
- "mappings": "AAMA,OAAO,YAAY;AAEnB,SAAS,0BAA0B;AAyBnC,MAAM,aAAa,CAAC,YAClB,YAAY,mBAAmB,OAAO,IAAI,IAAI;AAEzC,SAAS,8BACd,aACA,aACA;AAKA,QAAM,gBACJ,YAAY,UAAU,UAAU,UAChC,OAAO,MAAM,YAAY,SAAS,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,MAAM;AAC7D,QAAM,gBACJ,YAAY,UAAU,UAAU,UAChC,OAAO,MAAM,YAAY,SAAS,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,MAAM;AAE7D,MAAI,iBAAiB,eAAe;AAClC,UAAM,SAAS,YAAY,SAAU,MAAM,MAAM,GAAG,EAAE,CAAC;AACvD,UAAM,SAAS,YAAY,SAAU,MAAM,MAAM,GAAG,EAAE,CAAC;AACvD,UAAM,YAAY,OAAO;AAAA,MACvB,mBAAmB,MAAM;AAAA,MACzB,mBAAmB,MAAM;AAAA,IAC3B;AACA,QAAI,cAAc,GAAG;AACnB,aAAO;AAAA,IACT;AAIA,WAAO,WAAW,MAAM,IAAI,WAAW,MAAM;AAAA,EAC/C,WAAW,eAAe;AACxB,WAAO;AAAA,EACT,WAAW,eAAe;AACxB,WAAO;AAAA,EACT,OAAO;AACL,WAAO,YAAY,KAAK,cAAc,YAAY,IAAI;AAAA,EACxD;AACF;",
4
+ "sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\nimport semver from 'semver'\nimport type { ZeebeClusterVersionsDto } from '../types/C8ClusterComponentVersions.type.js'\nimport { stripSecfixPostfix } from '../utils/versionCheck.utils.js'\n\nexport interface GenerationDto {\n name: string\n uuid: string\n versions: ZeebeClusterVersionsDto\n upgradeableFrom: GenerationDto[]\n createdBy: string\n createdByName?: string\n created: Date\n updatedBy: string\n updated: Date\n isUnsupportedStarting?: Date\n isBespoke?: boolean\n}\n\nexport interface GenerationWithClusterCountDto extends GenerationDto {\n clusterCount: number\n}\n\nexport interface UpgradableGenerationsDto {\n preferred: GenerationDto | null\n options: GenerationDto[]\n}\n\nconst secfixRank = (version: string): number =>\n version === stripSecfixPostfix(version) ? 0 : 1\n\nexport function sortGenerationBySemverOfZeebe(\n generationA: { name: string; versions?: { zeebe: string } },\n generationB: { name: string; versions?: { zeebe: string } },\n) {\n // magic sorting algorithm\n // uses the generation version of zeebe for comparison\n // if version invalid: use localeCompare\n\n const versionAvalid: boolean =\n generationA.versions?.zeebe !== undefined &&\n semver.valid(generationA.versions.zeebe.split(':')[1]) !== null\n const versionBvalid: boolean =\n generationB.versions?.zeebe !== undefined &&\n semver.valid(generationB.versions.zeebe.split(':')[1]) !== null\n\n if (versionAvalid && versionBvalid) {\n const zeebeA = generationA.versions!.zeebe.split(':')[1]\n const zeebeB = generationB.versions!.zeebe.split(':')[1]\n const byVersion = semver.compare(\n stripSecfixPostfix(zeebeB),\n stripSecfixPostfix(zeebeA),\n )\n if (byVersion !== 0) {\n return byVersion\n }\n // Both builds carry the same Camunda version here, so only the postfix\n // separates them. A secfix build holds that version plus a security patch,\n // thus it is the newer of the two and takes the higher rank.\n const bySecfix = secfixRank(zeebeB) - secfixRank(zeebeA)\n if (bySecfix !== 0) {\n return bySecfix\n }\n // Same image on both (8.9+gen5 and 8.9+gen6 both run 8.9.5). The name is\n // the only order left, so a picker does not show them in input order.\n return compareGenerationNamesNewestFirst(generationA.name, generationB.name)\n } else if (versionAvalid) {\n return -1\n } else if (versionBvalid) {\n return 1\n } else {\n return compareGenerationNamesNewestFirst(generationA.name, generationB.name)\n }\n}\n\n/** The text before the first digit: \"Camunda \" for \"Camunda 8.9+gen20\". */\nconst productPrefix = (name: string): string => {\n const firstDigit = name.search(/\\d/)\n return firstDigit === -1 ? name : name.slice(0, firstDigit)\n}\n\n/**\n * Splits a name at the first dash after the version: \"Camunda 8.10.0-alpha1\"\n * gives [\"Camunda 8.10.0\", \"alpha1\"]. A name without a dash gives an empty\n * suffix.\n */\nconst splitPrerelease = (name: string): [base: string, suffix: string] => {\n const dash = name.indexOf('-', name.search(/\\d/))\n return dash === -1 ? [name, ''] : [name.slice(0, dash), name.slice(dash + 1)]\n}\n\n/**\n * Orders generation names by product, then newest first within a product, by\n * numeric collation: `8.10` comes before `8.9`, and `gen10` before `gen9`.\n * Products stay in alphabetic order, so the Camunda generations come before\n * the older Zeebe ones. A release comes before its prereleases, so\n * `8.10.0` is above `8.10.0-alpha1`. For a list that has no zeebe image to\n * compare, such as a filter facet. When the image is known, use\n * `sortGenerationBySemverOfZeebe`.\n */\nexport const compareGenerationNamesNewestFirst = (\n nameA: string,\n nameB: string,\n): number => {\n const collation = { numeric: true, sensitivity: 'base' } as const\n const byProduct = productPrefix(nameA).localeCompare(\n productPrefix(nameB),\n undefined,\n collation,\n )\n if (byProduct !== 0) {\n return byProduct\n }\n const [baseA, suffixA] = splitPrerelease(nameA)\n const [baseB, suffixB] = splitPrerelease(nameB)\n const byBase = baseB.localeCompare(baseA, undefined, collation)\n if (byBase !== 0) {\n return byBase\n }\n if (suffixA === '' || suffixB === '') {\n return suffixA === suffixB ? 0 : suffixA === '' ? -1 : 1\n }\n return suffixB.localeCompare(suffixA, undefined, collation)\n}\n"],
5
+ "mappings": "AAMA,OAAO,YAAY;AAEnB,SAAS,0BAA0B;AAyBnC,MAAM,aAAa,CAAC,YAClB,YAAY,mBAAmB,OAAO,IAAI,IAAI;AAEzC,SAAS,8BACd,aACA,aACA;AAKA,QAAM,gBACJ,YAAY,UAAU,UAAU,UAChC,OAAO,MAAM,YAAY,SAAS,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,MAAM;AAC7D,QAAM,gBACJ,YAAY,UAAU,UAAU,UAChC,OAAO,MAAM,YAAY,SAAS,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,MAAM;AAE7D,MAAI,iBAAiB,eAAe;AAClC,UAAM,SAAS,YAAY,SAAU,MAAM,MAAM,GAAG,EAAE,CAAC;AACvD,UAAM,SAAS,YAAY,SAAU,MAAM,MAAM,GAAG,EAAE,CAAC;AACvD,UAAM,YAAY,OAAO;AAAA,MACvB,mBAAmB,MAAM;AAAA,MACzB,mBAAmB,MAAM;AAAA,IAC3B;AACA,QAAI,cAAc,GAAG;AACnB,aAAO;AAAA,IACT;AAIA,UAAM,WAAW,WAAW,MAAM,IAAI,WAAW,MAAM;AACvD,QAAI,aAAa,GAAG;AAClB,aAAO;AAAA,IACT;AAGA,WAAO,kCAAkC,YAAY,MAAM,YAAY,IAAI;AAAA,EAC7E,WAAW,eAAe;AACxB,WAAO;AAAA,EACT,WAAW,eAAe;AACxB,WAAO;AAAA,EACT,OAAO;AACL,WAAO,kCAAkC,YAAY,MAAM,YAAY,IAAI;AAAA,EAC7E;AACF;AAGA,MAAM,gBAAgB,CAAC,SAAyB;AAC9C,QAAM,aAAa,KAAK,OAAO,IAAI;AACnC,SAAO,eAAe,KAAK,OAAO,KAAK,MAAM,GAAG,UAAU;AAC5D;AAOA,MAAM,kBAAkB,CAAC,SAAiD;AACxE,QAAM,OAAO,KAAK,QAAQ,KAAK,KAAK,OAAO,IAAI,CAAC;AAChD,SAAO,SAAS,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,MAAM,GAAG,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC,CAAC;AAC9E;AAWO,MAAM,oCAAoC,CAC/C,OACA,UACW;AACX,QAAM,YAAY,EAAE,SAAS,MAAM,aAAa,OAAO;AACvD,QAAM,YAAY,cAAc,KAAK,EAAE;AAAA,IACrC,cAAc,KAAK;AAAA,IACnB;AAAA,IACA;AAAA,EACF;AACA,MAAI,cAAc,GAAG;AACnB,WAAO;AAAA,EACT;AACA,QAAM,CAAC,OAAO,OAAO,IAAI,gBAAgB,KAAK;AAC9C,QAAM,CAAC,OAAO,OAAO,IAAI,gBAAgB,KAAK;AAC9C,QAAM,SAAS,MAAM,cAAc,OAAO,QAAW,SAAS;AAC9D,MAAI,WAAW,GAAG;AAChB,WAAO;AAAA,EACT;AACA,MAAI,YAAY,MAAM,YAAY,IAAI;AACpC,WAAO,YAAY,UAAU,IAAI,YAAY,KAAK,KAAK;AAAA,EACzD;AACA,SAAO,QAAQ,cAAc,SAAS,QAAW,SAAS;AAC5D;",
6
6
  "names": []
7
7
  }
@@ -22,4 +22,33 @@ export declare namespace Hdp {
22
22
  minDate: string;
23
23
  maxDate: string;
24
24
  }
25
+ /**
26
+ * The work of one cluster inside the activity window. HDP aggregates, thus
27
+ * one row comes back for each cluster and not one for each cluster and day.
28
+ */
29
+ interface ClusterActivityRow {
30
+ clusterId: string;
31
+ /**
32
+ * The last UTC day with work, as `YYYY-MM-DD`. When no day inside the
33
+ * window carries work, this field is absent. accounts-backend makes this
34
+ * string from the BigQuery DATE, thus no caller reads the shape that the
35
+ * BigQuery client gives.
36
+ */
37
+ lastActiveAt?: string;
38
+ processInstances: number;
39
+ }
40
+ /**
41
+ * Every cluster that HDP holds inside the window, for the whole fleet.
42
+ *
43
+ * The reader joins these rows against its own cluster table. HDP keeps the
44
+ * rows of clusters that no longer exist, thus the set is bigger than the
45
+ * live fleet.
46
+ */
47
+ interface ClusterActivity {
48
+ rows: ClusterActivityRow[];
49
+ /** The window that `rows` covers, in days. */
50
+ windowDays: number;
51
+ /** The time of the query, as an ISO instant. */
52
+ fetchedAt: string;
53
+ }
25
54
  }