@auth-gate/rbac 0.8.0 → 0.9.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.
@@ -14,6 +14,12 @@ var __spreadValues = (a, b) => {
14
14
  }
15
15
  return a;
16
16
  };
17
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
18
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
19
+ }) : x)(function(x) {
20
+ if (typeof require !== "undefined") return require.apply(this, arguments);
21
+ throw Error('Dynamic require of "' + x + '" is not supported');
22
+ });
17
23
 
18
24
  // src/validator.ts
19
25
  var KEY_PATTERN = /^[a-z][a-z0-9_]*$/;
@@ -63,15 +69,28 @@ function validateRbacConfig(config) {
63
69
  );
64
70
  }
65
71
  }
66
- resourceActions.set(key, new Set(resource.actions));
72
+ const actionSet = /* @__PURE__ */ new Set();
73
+ for (const action of resource.actions) {
74
+ if (actionSet.has(action)) {
75
+ throw new Error(`Resource "${key}" has duplicate action: "${action}".`);
76
+ }
77
+ actionSet.add(action);
78
+ }
79
+ resourceActions.set(key, actionSet);
67
80
  if (resource.scopes !== void 0) {
68
81
  if (!Array.isArray(resource.scopes)) {
69
82
  throw new Error(`Resource "${key}".scopes must be an array of strings.`);
70
83
  }
84
+ const scopeSet = /* @__PURE__ */ new Set();
71
85
  for (let i = 0; i < resource.scopes.length; i++) {
72
86
  if (typeof resource.scopes[i] !== "string") {
73
87
  throw new Error(`Resource "${key}".scopes[${i}] must be a string.`);
74
88
  }
89
+ const scope = resource.scopes[i];
90
+ if (scopeSet.has(scope)) {
91
+ throw new Error(`Resource "${key}" has duplicate scope: "${scope}".`);
92
+ }
93
+ scopeSet.add(scope);
75
94
  }
76
95
  }
77
96
  }
@@ -112,14 +131,40 @@ function validateRbacConfig(config) {
112
131
  );
113
132
  }
114
133
  const value = actionsGrant[actionKey];
115
- if (value !== true && typeof value !== "string" && !(value && typeof value === "object" && "when" in value && typeof value.when === "string")) {
134
+ if (value !== true && (typeof value !== "string" || value === "") && !(value && typeof value === "object" && "when" in value && typeof value.when === "string" && value.when !== "")) {
116
135
  throw new Error(
117
- `Role "${key}".grants.${resourceKey}.${actionKey} has invalid value. Expected true, a scope string, or { when: string }.`
136
+ `Role "${key}".grants.${resourceKey}.${actionKey} has invalid value. Expected true, a non-empty scope string, or { when: non-empty string }.`
118
137
  );
119
138
  }
139
+ if (typeof value === "string" && value !== "") {
140
+ const resource = resources[resourceKey];
141
+ if (resource.scopes && Array.isArray(resource.scopes)) {
142
+ const declaredScopes = new Set(resource.scopes);
143
+ if (!declaredScopes.has(value)) {
144
+ throw new Error(
145
+ `Role "${key}".grants.${resourceKey}.${actionKey} scope "${value}" is not in declared scopes for "${resourceKey}": ${[...declaredScopes].join(", ")}.`
146
+ );
147
+ }
148
+ }
149
+ }
120
150
  }
121
151
  }
122
152
  }
153
+ const defaultRoles = [];
154
+ for (const key of roleKeys) {
155
+ const role = roles[key];
156
+ if (role.isDefault !== void 0 && typeof role.isDefault !== "boolean") {
157
+ throw new Error(`Role "${key}".isDefault must be a boolean.`);
158
+ }
159
+ if (role.isDefault === true) {
160
+ defaultRoles.push(key);
161
+ }
162
+ }
163
+ if (defaultRoles.length > 1) {
164
+ throw new Error(
165
+ `Only one role may have isDefault: true. Found ${defaultRoles.length}: ${defaultRoles.join(", ")}.`
166
+ );
167
+ }
123
168
  for (const key of roleKeys) {
124
169
  const role = roles[key];
125
170
  if (role.inherits !== void 0) {
@@ -185,6 +230,7 @@ function validateRbacConfig(config) {
185
230
  renameTargets.set(role.renamedFrom, key);
186
231
  }
187
232
  }
233
+ const conditionKeys = /* @__PURE__ */ new Set();
188
234
  if (c.conditions !== void 0) {
189
235
  if (typeof c.conditions !== "object" || Array.isArray(c.conditions) || c.conditions === null) {
190
236
  throw new Error("RBAC config `conditions` must be an object mapping names to functions.");
@@ -196,6 +242,23 @@ function validateRbacConfig(config) {
196
242
  `Condition "${condKey}" must be a function. Got ${typeof condValue}.`
197
243
  );
198
244
  }
245
+ conditionKeys.add(condKey);
246
+ }
247
+ }
248
+ for (const key of roleKeys) {
249
+ const role = roles[key];
250
+ const grants = role.grants;
251
+ for (const [resourceKey, actionGrants] of Object.entries(grants)) {
252
+ for (const [actionKey, value] of Object.entries(actionGrants)) {
253
+ if (value && typeof value === "object" && "when" in value) {
254
+ const whenKey = value.when;
255
+ if (!conditionKeys.has(whenKey)) {
256
+ throw new Error(
257
+ `Role "${key}".grants.${resourceKey}.${actionKey} references condition "${whenKey}" but it is not declared in conditions.` + (conditionKeys.size > 0 ? ` Declared conditions: ${[...conditionKeys].join(", ")}.` : ` No conditions are declared.`)
258
+ );
259
+ }
260
+ }
261
+ }
199
262
  }
200
263
  }
201
264
  return config;
@@ -234,17 +297,27 @@ Run \`npx @auth-gate/rbac init\` to create one.`
234
297
  }
235
298
 
236
299
  // src/diff.ts
300
+ function deepEqual(a, b) {
301
+ if (a === b) return true;
302
+ if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
303
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
304
+ if (Array.isArray(a) && Array.isArray(b)) {
305
+ if (a.length !== b.length) return false;
306
+ return a.every((val, i) => deepEqual(val, b[i]));
307
+ }
308
+ const keysA = Object.keys(a).sort();
309
+ const keysB = Object.keys(b).sort();
310
+ if (keysA.length !== keysB.length) return false;
311
+ return keysA.every(
312
+ (key, i) => key === keysB[i] && deepEqual(a[key], b[key])
313
+ );
314
+ }
237
315
  function hashConditionSource(fn) {
238
- const src = fn.toString();
239
- let hash = 0;
240
- for (let i = 0; i < src.length; i++) {
241
- const ch = src.charCodeAt(i);
242
- hash = (hash << 5) - hash + ch | 0;
243
- }
244
- return hash.toString(36);
316
+ const { createHash } = __require("crypto");
317
+ return createHash("sha256").update(fn.toString()).digest("hex").slice(0, 16);
245
318
  }
246
319
  function computeRbacDiff(config, server, memberCounts) {
247
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
320
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
248
321
  const resourceOps = [];
249
322
  const roleOps = [];
250
323
  const conditionOps = [];
@@ -303,7 +376,7 @@ function computeRbacDiff(config, server, memberCounts) {
303
376
  if (!existing && role.renamedFrom) {
304
377
  existing = (_d = serverRoleByKey.get(role.renamedFrom)) != null ? _d : serverRoleByPreviousKey.get(role.renamedFrom);
305
378
  if (existing) {
306
- const members = (_e = memberCounts[existing.id]) != null ? _e : 0;
379
+ const members = (_e = memberCounts[existing.configKey]) != null ? _e : 0;
307
380
  roleOps.push({
308
381
  type: "rename",
309
382
  key,
@@ -327,16 +400,19 @@ function computeRbacDiff(config, server, memberCounts) {
327
400
  if (((_f = existing.description) != null ? _f : void 0) !== ((_g = role.description) != null ? _g : void 0)) {
328
401
  changes.push("description changed");
329
402
  }
330
- const existingGrants = JSON.stringify((_h = existing.grants) != null ? _h : null);
331
- const configGrants = JSON.stringify(role.grants);
332
- if (existingGrants !== configGrants) {
403
+ if (!deepEqual((_h = existing.grants) != null ? _h : null, (_i = role.grants) != null ? _i : null)) {
333
404
  changes.push("grants changed");
334
405
  }
335
- const existingInherits = [...(_i = existing.inherits) != null ? _i : []].sort().join(",");
336
- const configInherits = [...(_j = role.inherits) != null ? _j : []].sort().join(",");
406
+ const existingInherits = [...(_j = existing.inherits) != null ? _j : []].sort().join(",");
407
+ const configInherits = [...(_k = role.inherits) != null ? _k : []].sort().join(",");
337
408
  if (existingInherits !== configInherits) {
338
409
  changes.push("inherits changed");
339
410
  }
411
+ const existingDefault = (_l = existing.isDefault) != null ? _l : false;
412
+ const configDefault = (_m = role.isDefault) != null ? _m : false;
413
+ if (existingDefault !== configDefault) {
414
+ changes.push(`isDefault: ${existingDefault} -> ${configDefault}`);
415
+ }
340
416
  if (changes.length > 0) {
341
417
  roleOps.push({ type: "update", key, role, existing, changes });
342
418
  }
@@ -346,7 +422,7 @@ function computeRbacDiff(config, server, memberCounts) {
346
422
  for (const [key, role] of serverRoleByKey) {
347
423
  if (renameMap.has(key)) continue;
348
424
  if (role.isActive) {
349
- const members = (_k = memberCounts[role.id]) != null ? _k : 0;
425
+ const members = (_n = memberCounts[role.configKey]) != null ? _n : 0;
350
426
  if (members > 0) hasDestructive = true;
351
427
  roleOps.push({ type: "archive", key, existing: role, assignedMembers: members });
352
428
  }
@@ -433,6 +509,9 @@ function formatRbacDiff(diff, dryRun) {
433
509
  chalk.dim(` inherits: [${[...op.role.inherits].join(", ")}]`)
434
510
  );
435
511
  }
512
+ if (op.role.isDefault) {
513
+ lines.push(chalk.dim(` isDefault: true`));
514
+ }
436
515
  } else if (op.type === "update") {
437
516
  lines.push(chalk.yellow(` ~ UPDATE role "${op.key}"`));
438
517
  for (const change of op.changes) {
@@ -508,6 +587,11 @@ var RbacSyncClient = class {
508
587
  constructor(config) {
509
588
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
510
589
  this.apiKey = config.apiKey;
590
+ if (!this.baseUrl.startsWith("https://") && !this.baseUrl.startsWith("http://localhost") && !this.baseUrl.startsWith("http://127.0.0.1")) {
591
+ console.warn(
592
+ "WARNING: AUTHGATE_BASE_URL does not use HTTPS. API key may be transmitted insecurely."
593
+ );
594
+ }
511
595
  }
512
596
  async request(method, path, body) {
513
597
  var _a, _b;
@@ -526,9 +610,12 @@ var RbacSyncClient = class {
526
610
  let message;
527
611
  try {
528
612
  const json = JSON.parse(text);
529
- message = (_b = (_a = json.error) != null ? _a : json.message) != null ? _b : text;
613
+ message = (_b = (_a = json.error) != null ? _a : json.message) != null ? _b : "Unknown error";
530
614
  } catch (e) {
531
- message = text;
615
+ message = text.length > 500 ? text.slice(0, 500) + "..." : text;
616
+ }
617
+ if (this.apiKey) {
618
+ message = message.replaceAll(this.apiKey, "[REDACTED]");
532
619
  }
533
620
  throw new Error(`API error (${res.status}): ${message}`);
534
621
  }
@@ -548,7 +635,10 @@ var RbacSyncClient = class {
548
635
  resources: config.resources,
549
636
  roles: config.roles,
550
637
  conditions: config.conditions ? Object.fromEntries(
551
- Object.entries(config.conditions).map(([k]) => [k, { key: k }])
638
+ Object.entries(config.conditions).map(([k, fn]) => [
639
+ k,
640
+ { key: k, sourceHash: hashConditionSource(fn) }
641
+ ])
552
642
  ) : void 0,
553
643
  force
554
644
  });
package/dist/cli.cjs CHANGED
@@ -89,15 +89,28 @@ function validateRbacConfig(config) {
89
89
  );
90
90
  }
91
91
  }
92
- resourceActions.set(key, new Set(resource.actions));
92
+ const actionSet = /* @__PURE__ */ new Set();
93
+ for (const action of resource.actions) {
94
+ if (actionSet.has(action)) {
95
+ throw new Error(`Resource "${key}" has duplicate action: "${action}".`);
96
+ }
97
+ actionSet.add(action);
98
+ }
99
+ resourceActions.set(key, actionSet);
93
100
  if (resource.scopes !== void 0) {
94
101
  if (!Array.isArray(resource.scopes)) {
95
102
  throw new Error(`Resource "${key}".scopes must be an array of strings.`);
96
103
  }
104
+ const scopeSet = /* @__PURE__ */ new Set();
97
105
  for (let i = 0; i < resource.scopes.length; i++) {
98
106
  if (typeof resource.scopes[i] !== "string") {
99
107
  throw new Error(`Resource "${key}".scopes[${i}] must be a string.`);
100
108
  }
109
+ const scope = resource.scopes[i];
110
+ if (scopeSet.has(scope)) {
111
+ throw new Error(`Resource "${key}" has duplicate scope: "${scope}".`);
112
+ }
113
+ scopeSet.add(scope);
101
114
  }
102
115
  }
103
116
  }
@@ -138,14 +151,40 @@ function validateRbacConfig(config) {
138
151
  );
139
152
  }
140
153
  const value = actionsGrant[actionKey];
141
- if (value !== true && typeof value !== "string" && !(value && typeof value === "object" && "when" in value && typeof value.when === "string")) {
154
+ if (value !== true && (typeof value !== "string" || value === "") && !(value && typeof value === "object" && "when" in value && typeof value.when === "string" && value.when !== "")) {
142
155
  throw new Error(
143
- `Role "${key}".grants.${resourceKey}.${actionKey} has invalid value. Expected true, a scope string, or { when: string }.`
156
+ `Role "${key}".grants.${resourceKey}.${actionKey} has invalid value. Expected true, a non-empty scope string, or { when: non-empty string }.`
144
157
  );
145
158
  }
159
+ if (typeof value === "string" && value !== "") {
160
+ const resource = resources[resourceKey];
161
+ if (resource.scopes && Array.isArray(resource.scopes)) {
162
+ const declaredScopes = new Set(resource.scopes);
163
+ if (!declaredScopes.has(value)) {
164
+ throw new Error(
165
+ `Role "${key}".grants.${resourceKey}.${actionKey} scope "${value}" is not in declared scopes for "${resourceKey}": ${[...declaredScopes].join(", ")}.`
166
+ );
167
+ }
168
+ }
169
+ }
146
170
  }
147
171
  }
148
172
  }
173
+ const defaultRoles = [];
174
+ for (const key of roleKeys) {
175
+ const role = roles[key];
176
+ if (role.isDefault !== void 0 && typeof role.isDefault !== "boolean") {
177
+ throw new Error(`Role "${key}".isDefault must be a boolean.`);
178
+ }
179
+ if (role.isDefault === true) {
180
+ defaultRoles.push(key);
181
+ }
182
+ }
183
+ if (defaultRoles.length > 1) {
184
+ throw new Error(
185
+ `Only one role may have isDefault: true. Found ${defaultRoles.length}: ${defaultRoles.join(", ")}.`
186
+ );
187
+ }
149
188
  for (const key of roleKeys) {
150
189
  const role = roles[key];
151
190
  if (role.inherits !== void 0) {
@@ -211,6 +250,7 @@ function validateRbacConfig(config) {
211
250
  renameTargets.set(role.renamedFrom, key);
212
251
  }
213
252
  }
253
+ const conditionKeys = /* @__PURE__ */ new Set();
214
254
  if (c.conditions !== void 0) {
215
255
  if (typeof c.conditions !== "object" || Array.isArray(c.conditions) || c.conditions === null) {
216
256
  throw new Error("RBAC config `conditions` must be an object mapping names to functions.");
@@ -222,6 +262,23 @@ function validateRbacConfig(config) {
222
262
  `Condition "${condKey}" must be a function. Got ${typeof condValue}.`
223
263
  );
224
264
  }
265
+ conditionKeys.add(condKey);
266
+ }
267
+ }
268
+ for (const key of roleKeys) {
269
+ const role = roles[key];
270
+ const grants = role.grants;
271
+ for (const [resourceKey, actionGrants] of Object.entries(grants)) {
272
+ for (const [actionKey, value] of Object.entries(actionGrants)) {
273
+ if (value && typeof value === "object" && "when" in value) {
274
+ const whenKey = value.when;
275
+ if (!conditionKeys.has(whenKey)) {
276
+ throw new Error(
277
+ `Role "${key}".grants.${resourceKey}.${actionKey} references condition "${whenKey}" but it is not declared in conditions.` + (conditionKeys.size > 0 ? ` Declared conditions: ${[...conditionKeys].join(", ")}.` : ` No conditions are declared.`)
278
+ );
279
+ }
280
+ }
281
+ }
225
282
  }
226
283
  }
227
284
  return config;
@@ -258,17 +315,27 @@ Run \`npx @auth-gate/rbac init\` to create one.`
258
315
  }
259
316
 
260
317
  // src/diff.ts
318
+ function deepEqual(a, b) {
319
+ if (a === b) return true;
320
+ if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
321
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
322
+ if (Array.isArray(a) && Array.isArray(b)) {
323
+ if (a.length !== b.length) return false;
324
+ return a.every((val, i) => deepEqual(val, b[i]));
325
+ }
326
+ const keysA = Object.keys(a).sort();
327
+ const keysB = Object.keys(b).sort();
328
+ if (keysA.length !== keysB.length) return false;
329
+ return keysA.every(
330
+ (key, i) => key === keysB[i] && deepEqual(a[key], b[key])
331
+ );
332
+ }
261
333
  function hashConditionSource(fn) {
262
- const src = fn.toString();
263
- let hash = 0;
264
- for (let i = 0; i < src.length; i++) {
265
- const ch = src.charCodeAt(i);
266
- hash = (hash << 5) - hash + ch | 0;
267
- }
268
- return hash.toString(36);
334
+ const { createHash } = require("crypto");
335
+ return createHash("sha256").update(fn.toString()).digest("hex").slice(0, 16);
269
336
  }
270
337
  function computeRbacDiff(config, server, memberCounts) {
271
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
338
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
272
339
  const resourceOps = [];
273
340
  const roleOps = [];
274
341
  const conditionOps = [];
@@ -327,7 +394,7 @@ function computeRbacDiff(config, server, memberCounts) {
327
394
  if (!existing && role.renamedFrom) {
328
395
  existing = (_d = serverRoleByKey.get(role.renamedFrom)) != null ? _d : serverRoleByPreviousKey.get(role.renamedFrom);
329
396
  if (existing) {
330
- const members = (_e = memberCounts[existing.id]) != null ? _e : 0;
397
+ const members = (_e = memberCounts[existing.configKey]) != null ? _e : 0;
331
398
  roleOps.push({
332
399
  type: "rename",
333
400
  key,
@@ -351,16 +418,19 @@ function computeRbacDiff(config, server, memberCounts) {
351
418
  if (((_f = existing.description) != null ? _f : void 0) !== ((_g = role.description) != null ? _g : void 0)) {
352
419
  changes.push("description changed");
353
420
  }
354
- const existingGrants = JSON.stringify((_h = existing.grants) != null ? _h : null);
355
- const configGrants = JSON.stringify(role.grants);
356
- if (existingGrants !== configGrants) {
421
+ if (!deepEqual((_h = existing.grants) != null ? _h : null, (_i = role.grants) != null ? _i : null)) {
357
422
  changes.push("grants changed");
358
423
  }
359
- const existingInherits = [...(_i = existing.inherits) != null ? _i : []].sort().join(",");
360
- const configInherits = [...(_j = role.inherits) != null ? _j : []].sort().join(",");
424
+ const existingInherits = [...(_j = existing.inherits) != null ? _j : []].sort().join(",");
425
+ const configInherits = [...(_k = role.inherits) != null ? _k : []].sort().join(",");
361
426
  if (existingInherits !== configInherits) {
362
427
  changes.push("inherits changed");
363
428
  }
429
+ const existingDefault = (_l = existing.isDefault) != null ? _l : false;
430
+ const configDefault = (_m = role.isDefault) != null ? _m : false;
431
+ if (existingDefault !== configDefault) {
432
+ changes.push(`isDefault: ${existingDefault} -> ${configDefault}`);
433
+ }
364
434
  if (changes.length > 0) {
365
435
  roleOps.push({ type: "update", key, role, existing, changes });
366
436
  }
@@ -370,7 +440,7 @@ function computeRbacDiff(config, server, memberCounts) {
370
440
  for (const [key, role] of serverRoleByKey) {
371
441
  if (renameMap.has(key)) continue;
372
442
  if (role.isActive) {
373
- const members = (_k = memberCounts[role.id]) != null ? _k : 0;
443
+ const members = (_n = memberCounts[role.configKey]) != null ? _n : 0;
374
444
  if (members > 0) hasDestructive = true;
375
445
  roleOps.push({ type: "archive", key, existing: role, assignedMembers: members });
376
446
  }
@@ -457,6 +527,9 @@ function formatRbacDiff(diff, dryRun) {
457
527
  import_chalk.default.dim(` inherits: [${[...op.role.inherits].join(", ")}]`)
458
528
  );
459
529
  }
530
+ if (op.role.isDefault) {
531
+ lines.push(import_chalk.default.dim(` isDefault: true`));
532
+ }
460
533
  } else if (op.type === "update") {
461
534
  lines.push(import_chalk.default.yellow(` ~ UPDATE role "${op.key}"`));
462
535
  for (const change of op.changes) {
@@ -532,6 +605,11 @@ var RbacSyncClient = class {
532
605
  constructor(config) {
533
606
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
534
607
  this.apiKey = config.apiKey;
608
+ if (!this.baseUrl.startsWith("https://") && !this.baseUrl.startsWith("http://localhost") && !this.baseUrl.startsWith("http://127.0.0.1")) {
609
+ console.warn(
610
+ "WARNING: AUTHGATE_BASE_URL does not use HTTPS. API key may be transmitted insecurely."
611
+ );
612
+ }
535
613
  }
536
614
  async request(method, path, body) {
537
615
  var _a, _b;
@@ -550,9 +628,12 @@ var RbacSyncClient = class {
550
628
  let message;
551
629
  try {
552
630
  const json = JSON.parse(text);
553
- message = (_b = (_a = json.error) != null ? _a : json.message) != null ? _b : text;
631
+ message = (_b = (_a = json.error) != null ? _a : json.message) != null ? _b : "Unknown error";
554
632
  } catch (e) {
555
- message = text;
633
+ message = text.length > 500 ? text.slice(0, 500) + "..." : text;
634
+ }
635
+ if (this.apiKey) {
636
+ message = message.replaceAll(this.apiKey, "[REDACTED]");
556
637
  }
557
638
  throw new Error(`API error (${res.status}): ${message}`);
558
639
  }
@@ -572,7 +653,10 @@ var RbacSyncClient = class {
572
653
  resources: config.resources,
573
654
  roles: config.roles,
574
655
  conditions: config.conditions ? Object.fromEntries(
575
- Object.entries(config.conditions).map(([k]) => [k, { key: k }])
656
+ Object.entries(config.conditions).map(([k, fn]) => [
657
+ k,
658
+ { key: k, sourceHash: hashConditionSource(fn) }
659
+ ])
576
660
  ) : void 0,
577
661
  force
578
662
  });
@@ -665,6 +749,7 @@ export const rbac = defineRbac({
665
749
  },
666
750
  viewer: {
667
751
  name: "Viewer",
752
+ isDefault: true,
668
753
  grants: {
669
754
  documents: { read: true },
670
755
  },
package/dist/cli.mjs CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  computeRbacDiff,
6
6
  formatRbacDiff,
7
7
  loadRbacConfig
8
- } from "./chunk-HE57TIQI.mjs";
8
+ } from "./chunk-ZFKXT2MP.mjs";
9
9
 
10
10
  // src/cli.ts
11
11
  import chalk from "chalk";
@@ -93,6 +93,7 @@ export const rbac = defineRbac({
93
93
  },
94
94
  viewer: {
95
95
  name: "Viewer",
96
+ isDefault: true,
96
97
  grants: {
97
98
  documents: { read: true },
98
99
  },