camunda-cloud-idac 1.0.0 → 1.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -90,6 +90,83 @@ some principals are granted access by username/client ID rather than by an
90
90
  IdP group claim. These aren't declared entities elsewhere in the spec, so
91
91
  there's no referential-integrity check on them (any string is accepted).
92
92
 
93
+ ## Environment files
94
+
95
+ The single `<spec>` argument to `validate`/`plan`/`apply` can be either a
96
+ flat spec (above) or an **environment file** that composes several spec
97
+ fragments into one, for organizations that run the same identity model
98
+ across multiple environments/clusters with per-cluster differences:
99
+
100
+ ```yaml
101
+ # specs/e0.yaml
102
+ include:
103
+ - base.yaml
104
+ - procapps/leistung.yaml
105
+ - procapps/workflow.yaml
106
+ # a procapp fragment can be left out here if this cluster doesn't need it
107
+
108
+ values:
109
+ ENV_SUFFIX: E0
110
+ KUMUL_CLIENT_ID: kumul
111
+ ```
112
+
113
+ A file is treated as an environment file purely because it has a top-level
114
+ `include` key (flat specs never do, since `RawSpec` is `.strict()` and has
115
+ no such field, so detection can't collide with the existing format).
116
+ `include` paths resolve relative to the environment file's own directory.
117
+ See `test/fixtures/compose/` for a complete worked example (base + 3
118
+ procapp fragments + a full and a partial environment file).
119
+
120
+ **Fragments** are plain YAML files shaped like a spec, but looser: only
121
+ each entity's ID field (`tenantId`/`roleId`/`groupId`/`mappingRuleId`) is
122
+ required - every other field is optional, since one fragment may only be
123
+ contributing a *piece* of an entity that another fragment (or a shared
124
+ `base.yaml`) already declares in full. This is what lets a role like
125
+ `process-owner` span every procapp without every procapp fragment knowing
126
+ about the others: `base.yaml` declares `process-owner`'s `name`, and each
127
+ `procapps/<name>.yaml` fragment separately contributes its own group to
128
+ `process-owner.groups`.
129
+
130
+ When multiple fragments mention the same entity ID:
131
+
132
+ - **Array fields** (`groups`, `mappingRules`, `users`, `clients`, `roles`)
133
+ are **unioned** (deduplicated, first-seen order).
134
+ - **Scalar fields** (`name`, `description`, `claimName`, `claimValue`) must
135
+ not disagree - two fragments giving the same ID a different value for the
136
+ same scalar field is a composition error naming both files and values.
137
+ - `authorizations` have no ID field and are simply concatenated; the
138
+ existing schema already tolerates exact duplicate authorization tuples
139
+ and flags genuine permission conflicts, so no special merging is needed.
140
+
141
+ Only the fully merged, ID-deduplicated result is validated against the same
142
+ unmodified `Spec` schema used for flat specs - so every existing guarantee
143
+ (referential integrity, no duplicate IDs, authorization-tuple conflicts)
144
+ still applies to the assembled spec exactly as it does today. If no
145
+ fragment ever provides a required field (e.g. `name`), that surfaces as the
146
+ same "Required" error you'd get from an incomplete flat spec.
147
+
148
+ `include` is exactly one level deep - a fragment listed under `include` may
149
+ not itself have a top-level `include` key (nested composition isn't
150
+ supported, and produces a clear error if attempted).
151
+
152
+ **`${VAR}` substitution**: each fragment's raw text is scanned for
153
+ `${NAME}` placeholders before it's parsed as YAML, substituted from the
154
+ environment file's `values` map. Any placeholder left unresolved after
155
+ substitution (a typo, or a `values` map missing an entry) is a hard error
156
+ naming the fragment file and the unresolved name(s) - it never silently
157
+ passes through as a literal string. A flat spec (no `include` key) is never
158
+ scanned for placeholders, so a literal `${...}`-looking value in a flat
159
+ spec is passed through untouched.
160
+
161
+ **Prune caveat**: omitting a procapp fragment from an environment's
162
+ `include` is, to the reconciler, indistinguishable from "this was
163
+ intentionally removed." If that procapp's tenant/group/mapping rules still
164
+ exist on the target cluster (e.g. it was deployed there before, or under a
165
+ different environment file), a later `apply --prune` run against that
166
+ cluster will delete them - same as removing any other entity from a flat
167
+ spec. Make sure `include` lists match what should actually exist on each
168
+ cluster before pruning.
169
+
93
170
  ## Known limitations
94
171
 
95
172
  - Direct **tenant**-to-user/client assignments aren't modeled by the spec
@@ -0,0 +1,219 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
3
+ import { parse as parseYaml, YAMLParseError } from "yaml";
4
+ import { z } from "zod";
5
+ import { OwnerType, PermissionType, ResourceType } from "./schema.js";
6
+ import { formatZodError, SpecValidationError } from "./errors.js";
7
+ export const EnvironmentFile = z
8
+ .object({
9
+ include: z.array(z.string().min(1)).min(1),
10
+ values: z.record(z.string(), z.string()).default({}),
11
+ })
12
+ .strict();
13
+ export function isEnvironmentFile(parsed) {
14
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && Object.hasOwn(parsed, "include");
15
+ }
16
+ const PLACEHOLDER_PATTERN = /\$\{([^}]+)\}/g;
17
+ export function substituteVariables(text, values) {
18
+ return text.replace(PLACEHOLDER_PATTERN, (match, name) => (Object.hasOwn(values, name) ? values[name] : match));
19
+ }
20
+ export function findUnresolvedPlaceholders(text) {
21
+ const found = new Set();
22
+ for (const match of text.matchAll(PLACEHOLDER_PATTERN)) {
23
+ found.add(match[1]);
24
+ }
25
+ return [...found];
26
+ }
27
+ // Fragment schemas mirror schema.ts's entities, but only the ID field is required -
28
+ // every other field (including `name`, required on the final Spec) is optional,
29
+ // since a fragment may only be contributing a piece of an entity that another
30
+ // fragment (or base.yaml) already fully declares. mergeEntitiesById() below unions
31
+ // array fields and rejects conflicting scalar fields across fragments; only the
32
+ // fully merged result is validated against the real, unmodified `Spec` schema in
33
+ // load.ts, which is what actually enforces that every entity ends up complete.
34
+ const MappingRuleFragment = z
35
+ .object({
36
+ mappingRuleId: z.string().min(1),
37
+ claimName: z.string().min(1).optional(),
38
+ claimValue: z.string().min(1).optional(),
39
+ name: z.string().optional(),
40
+ })
41
+ .strict();
42
+ const GroupFragment = z
43
+ .object({
44
+ groupId: z.string().min(1),
45
+ name: z.string().min(1).optional(),
46
+ description: z.string().optional(),
47
+ mappingRules: z.array(z.string()).default([]),
48
+ users: z.array(z.string()).default([]),
49
+ clients: z.array(z.string()).default([]),
50
+ })
51
+ .strict();
52
+ const RoleFragment = z
53
+ .object({
54
+ roleId: z.string().min(1),
55
+ name: z.string().min(1).optional(),
56
+ description: z.string().optional(),
57
+ mappingRules: z.array(z.string()).default([]),
58
+ groups: z.array(z.string()).default([]),
59
+ users: z.array(z.string()).default([]),
60
+ clients: z.array(z.string()).default([]),
61
+ })
62
+ .strict();
63
+ const TenantFragment = z
64
+ .object({
65
+ tenantId: z.string().min(1),
66
+ name: z.string().min(1).optional(),
67
+ description: z.string().optional(),
68
+ roles: z.array(z.string()).default([]),
69
+ groups: z.array(z.string()).default([]),
70
+ mappingRules: z.array(z.string()).default([]),
71
+ })
72
+ .strict();
73
+ const AuthorizationFragment = z
74
+ .object({
75
+ ownerType: OwnerType,
76
+ ownerId: z.string().min(1),
77
+ resourceType: ResourceType,
78
+ resourceId: z.string().min(1),
79
+ permissions: z.array(PermissionType).min(1),
80
+ })
81
+ .strict();
82
+ const FragmentSpec = z
83
+ .object({
84
+ tenants: z.array(TenantFragment).default([]),
85
+ roles: z.array(RoleFragment).default([]),
86
+ groups: z.array(GroupFragment).default([]),
87
+ mappingRules: z.array(MappingRuleFragment).default([]),
88
+ authorizations: z.array(AuthorizationFragment).default([]),
89
+ })
90
+ .strict();
91
+ // Merges entities sharing the same ID across fragments: array fields are unioned
92
+ // (deduped, first-seen order), scalar fields must agree everywhere they're set -
93
+ // two fragments giving the same ID a different value for the same scalar field is
94
+ // a real authoring conflict, not something a union could resolve.
95
+ function mergeEntitiesById(entries, config, entityLabel) {
96
+ const order = [];
97
+ const merged = new Map();
98
+ const scalarSources = new Map();
99
+ for (const { filePath, entity } of entries) {
100
+ const id = entity[config.idField];
101
+ if (!merged.has(id)) {
102
+ merged.set(id, { [config.idField]: id });
103
+ scalarSources.set(id, new Map());
104
+ order.push(id);
105
+ }
106
+ const target = merged.get(id);
107
+ const sources = scalarSources.get(id);
108
+ for (const field of config.scalarFields) {
109
+ const value = entity[field];
110
+ if (value === undefined)
111
+ continue;
112
+ const existing = sources.get(field);
113
+ if (existing && existing.value !== value) {
114
+ throw new Error(`${entityLabel} "${id}" has conflicting "${field}": "${existing.value}" (${existing.filePath}) vs. "${value}" (${filePath})`);
115
+ }
116
+ if (!existing) {
117
+ sources.set(field, { value: value, filePath });
118
+ target[field] = value;
119
+ }
120
+ }
121
+ for (const field of config.arrayFields) {
122
+ const values = entity[field] ?? [];
123
+ const union = [...(target[field] ?? [])];
124
+ for (const v of values) {
125
+ if (!union.includes(v))
126
+ union.push(v);
127
+ }
128
+ target[field] = union;
129
+ }
130
+ }
131
+ return order.map((id) => merged.get(id));
132
+ }
133
+ async function loadFragment(fragmentPath, values) {
134
+ let raw;
135
+ try {
136
+ raw = await readFile(fragmentPath, "utf-8");
137
+ }
138
+ catch (err) {
139
+ throw new Error(`Could not read fragment "${fragmentPath}": ${err instanceof Error ? err.message : String(err)}`);
140
+ }
141
+ const substituted = substituteVariables(raw, values);
142
+ const unresolved = findUnresolvedPlaceholders(substituted);
143
+ if (unresolved.length > 0) {
144
+ throw new Error(`Fragment "${fragmentPath}" has unresolved placeholder(s): ${unresolved.map((n) => `\${${n}}`).join(", ")}`);
145
+ }
146
+ let parsed;
147
+ try {
148
+ parsed = parseYaml(substituted);
149
+ }
150
+ catch (err) {
151
+ if (err instanceof YAMLParseError) {
152
+ throw new Error(`Fragment "${fragmentPath}" is not valid YAML: ${err.message}`);
153
+ }
154
+ throw err;
155
+ }
156
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && Object.hasOwn(parsed, "include")) {
157
+ throw new Error(`Fragment "${fragmentPath}" has a top-level "include" key - nested composition isn't supported, "include" may only appear in the top-level environment file`);
158
+ }
159
+ const result = FragmentSpec.safeParse(parsed);
160
+ if (!result.success) {
161
+ throw new Error(`Fragment "${fragmentPath}" is invalid:\n${formatZodError(result.error).map((i) => ` - ${i}`).join("\n")}`);
162
+ }
163
+ return result.data;
164
+ }
165
+ export function mergeFragments(fragments) {
166
+ const asEntries = (key) => fragments.flatMap((f) => f.data[key].map((entity) => ({ filePath: f.filePath, entity })));
167
+ return {
168
+ tenants: mergeEntitiesById(asEntries("tenants"), {
169
+ idField: "tenantId",
170
+ scalarFields: ["name", "description"],
171
+ arrayFields: ["roles", "groups", "mappingRules"],
172
+ }, "Tenant"),
173
+ roles: mergeEntitiesById(asEntries("roles"), {
174
+ idField: "roleId",
175
+ scalarFields: ["name", "description"],
176
+ arrayFields: ["mappingRules", "groups", "users", "clients"],
177
+ }, "Role"),
178
+ groups: mergeEntitiesById(asEntries("groups"), {
179
+ idField: "groupId",
180
+ scalarFields: ["name", "description"],
181
+ arrayFields: ["mappingRules", "users", "clients"],
182
+ }, "Group"),
183
+ mappingRules: mergeEntitiesById(asEntries("mappingRules"), {
184
+ idField: "mappingRuleId",
185
+ scalarFields: ["claimName", "claimValue", "name"],
186
+ arrayFields: [],
187
+ }, "Mapping rule"),
188
+ authorizations: fragments.flatMap((f) => f.data.authorizations),
189
+ };
190
+ }
191
+ export async function composeSpec(envFilePath, envFile) {
192
+ const baseDir = dirname(envFilePath);
193
+ const seen = new Set();
194
+ const resolvedPaths = [];
195
+ for (const includePath of envFile.include) {
196
+ const resolved = resolve(baseDir, includePath);
197
+ if (seen.has(resolved)) {
198
+ throw new SpecValidationError(envFilePath, [`Duplicate include entry resolves to the same file: "${resolved}"`]);
199
+ }
200
+ seen.add(resolved);
201
+ resolvedPaths.push(resolved);
202
+ }
203
+ const fragments = [];
204
+ for (const fragmentPath of resolvedPaths) {
205
+ try {
206
+ fragments.push({ filePath: fragmentPath, data: await loadFragment(fragmentPath, envFile.values) });
207
+ }
208
+ catch (err) {
209
+ throw new SpecValidationError(envFilePath, [err instanceof Error ? err.message : String(err)]);
210
+ }
211
+ }
212
+ try {
213
+ return mergeFragments(fragments);
214
+ }
215
+ catch (err) {
216
+ throw new SpecValidationError(envFilePath, [err instanceof Error ? err.message : String(err)]);
217
+ }
218
+ }
219
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/spec/compose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export class SpecValidationError extends Error {
2
+ filePath;
3
+ issues;
4
+ constructor(filePath, issues) {
5
+ super(`Invalid spec at ${filePath}:\n${issues.map((i) => ` - ${i}`).join("\n")}`);
6
+ this.filePath = filePath;
7
+ this.issues = issues;
8
+ this.name = "SpecValidationError";
9
+ }
10
+ }
11
+ export function formatZodError(error) {
12
+ return error.issues.map((issue) => {
13
+ const path = issue.path.join(".");
14
+ return path ? `${path}: ${issue.message}` : issue.message;
15
+ });
16
+ }
17
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/spec/errors.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAE1B;IACA;IAFlB,YACkB,QAAgB,EAChB,MAAgB;QAEhC,KAAK,CAAC,mBAAmB,QAAQ,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAHnE,aAAQ,GAAR,QAAQ,CAAQ;QAChB,WAAM,GAAN,MAAM,CAAU;QAGhC,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAAC,KAAe;IAC5C,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC"}
package/dist/spec/load.js CHANGED
@@ -1,22 +1,9 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { parse as parseYaml, YAMLParseError } from "yaml";
3
3
  import { Spec } from "./schema.js";
4
- export class SpecValidationError extends Error {
5
- filePath;
6
- issues;
7
- constructor(filePath, issues) {
8
- super(`Invalid spec at ${filePath}:\n${issues.map((i) => ` - ${i}`).join("\n")}`);
9
- this.filePath = filePath;
10
- this.issues = issues;
11
- this.name = "SpecValidationError";
12
- }
13
- }
14
- function formatZodError(error) {
15
- return error.issues.map((issue) => {
16
- const path = issue.path.join(".");
17
- return path ? `${path}: ${issue.message}` : issue.message;
18
- });
19
- }
4
+ import { composeSpec, EnvironmentFile, isEnvironmentFile } from "./compose.js";
5
+ import { formatZodError, SpecValidationError } from "./errors.js";
6
+ export { SpecValidationError } from "./errors.js";
20
7
  export async function loadSpec(filePath) {
21
8
  let raw;
22
9
  try {
@@ -37,7 +24,15 @@ export async function loadSpec(filePath) {
37
24
  }
38
25
  throw err;
39
26
  }
40
- const result = Spec.safeParse(parsed);
27
+ let assembled = parsed;
28
+ if (isEnvironmentFile(parsed)) {
29
+ const envResult = EnvironmentFile.safeParse(parsed);
30
+ if (!envResult.success) {
31
+ throw new SpecValidationError(filePath, formatZodError(envResult.error));
32
+ }
33
+ assembled = await composeSpec(filePath, envResult.data);
34
+ }
35
+ const result = Spec.safeParse(assembled);
41
36
  if (!result.success) {
42
37
  throw new SpecValidationError(filePath, formatZodError(result.error));
43
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/spec/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAE1D,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGnC,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAE1B;IACA;IAFlB,YACkB,QAAgB,EAChB,MAAgB;QAEhC,KAAK,CAAC,mBAAmB,QAAQ,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAHnE,aAAQ,GAAR,QAAQ,CAAQ;QAChB,WAAM,GAAN,MAAM,CAAU;QAGhC,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED,SAAS,cAAc,CAAC,KAAe;IACrC,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB;IAC7C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE;YACtC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,MAAM,CAAC,GAAG,CAAC,EAAE;SAC3E,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;YAClC,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/spec/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAgB;IAC7C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE;YACtC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,MAAM,CAAC,GAAG,CAAC,EAAE;SAC3E,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;YAClC,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACzD,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,SAAS,GAAY,MAAM,CAAC;IAChC,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,eAAe,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE,cAAc,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,SAAS,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,mBAAmB,CAAC,QAAQ,EAAE,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camunda-cloud-idac",
3
- "version": "1.0.0",
3
+ "version": "1.1.0-beta.1",
4
4
  "description": "Identity-as-code reconciler for Camunda 8 (tenants, roles, groups, mapping rules, authorizations)",
5
5
  "type": "module",
6
6
  "license": "MIT",