Groups as members

Groups can be added to other groups using the addMember method.

When a group is added as a member of another group, members of the added group will become part of the containing group.

Basic usage

Here's how to add a group as a member of another group:

const const playlistGroup: GroupplaylistGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const const trackGroup: GrouptrackGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
// Tracks are now visible to the members of playlist const trackGroup: GrouptrackGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const playlistGroup: GroupplaylistGroup);

When you add groups as members:

  • Members of the added group become members of the container group
  • Their roles are inherited (with some exceptions, see below)
  • Revoking access from the member group also removes its access to the container group

Levels of inheritance

Adding a group as a member of another is not limited in depth:

const const grandParentGroup: GroupgrandParentGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const const parentGroup: GroupparentGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const const childGroup: GroupchildGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const childGroup: GroupchildGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const parentGroup: GroupparentGroup);
const parentGroup: GroupparentGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const grandParentGroup: GroupgrandParentGroup);

Members of the grandparent group will get access to all descendant groups based on their roles.

Roles

The rules of role inheritance

If the account is already a member of the container group, it will get the more permissive role:

const const addedGroup: GroupaddedGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const addedGroup: GroupaddedGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)addMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
, "reader");
const const containingGroup: GroupcontainingGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const addedGroup: GroupaddedGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)addMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
, "writer");
const containingGroup: GroupcontainingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const addedGroup: GroupaddedGroup);
// Bob stays a writer because his role is higher // than the inherited reader role.

When adding a group to another group, only admin, writer and reader roles are inherited:

const const addedGroup: GroupaddedGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const addedGroup: GroupaddedGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)addMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
, "writeOnly");
const const containingGroup: GroupcontainingGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const containingGroup: GroupcontainingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const addedGroup: GroupaddedGroup);
// Bob does not become a member of the containing group

To add a group to another group:

  1. The current account must be an admin in the containing group
  2. The current account must be a member of the added group
const const companyGroup: GroupcompanyGroup = const company: CoMapcompany.CoValueBase._owner: Account | Group
@categoryCollaboration
_owner
.CoValueBase.castAs<typeof Group>(schema: typeof Group): Group
@categoryType Helpers
castAs
(class Group
@categoryIdentity & Permissions
Group
);
const const teamGroup: GroupteamGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
// Works only if I'm a member of `companyGroup` const teamGroup: GroupteamGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const companyGroup: GroupcompanyGroup);

Overriding the added group's roles

In some cases you might want to inherit all members from an added group but override their roles to the same specific role in the containing group. You can do so by passing an "override role" as a second argument to addMember:

const const organizationGroup: GrouporganizationGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const organizationGroup: GrouporganizationGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)addMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
, "admin");
const const billingGroup: GroupbillingGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
// This way the members of the organization // can only read the billing data const billingGroup: GroupbillingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const organizationGroup: GrouporganizationGroup, "reader");

The "override role" works in both directions:

const const addedGroup: GroupaddedGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const addedGroup: GroupaddedGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)addMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
, "reader");
const addedGroup: GroupaddedGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)addMember(
const alice: Account | ({
    [x: string]: any;
} & Account)
alice
, "admin");
const const containingGroup: GroupcontainingGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const containingGroup: GroupcontainingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const addedGroup: GroupaddedGroup, "writer");
// Bob and Alice are now writers in the containing group

Permission changes

When you remove a member from an added group, they automatically lose access to all containing groups. We handle key rotation automatically to ensure security.

// Remove member from added group
await const addedGroup: GroupaddedGroup.Group.removeMember(member: Everyone | Account): Promise<void> (+1 overload)removeMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
);
// Bob loses access to both groups. // If Bob was also a member of the containing group, // he wouldn't have lost access.

Removing groups from other groups

You can remove a group from another group by using the removeMember method:

const const addedGroup: GroupaddedGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const const containingGroup: GroupcontainingGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const containingGroup: GroupcontainingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const addedGroup: GroupaddedGroup);
// Revoke the extension await const containingGroup: GroupcontainingGroup.Group.removeMember(member: Group): Promise<void> (+1 overload)
@categoryIdentity & Permissions Revokes membership from members a parent group.@parammember The group that will lose access to this group.
removeMember
(const addedGroup: GroupaddedGroup);

Getting all added groups

You can get all of the groups added to a group by calling the getParentGroups method:

const const containingGroup: GroupcontainingGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const const addedGroup: GroupaddedGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const containingGroup: GroupcontainingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const addedGroup: GroupaddedGroup);
var console: Console
The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v20.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v20.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
@see[source](https://github.com/nodejs/node/blob/v20.11.1/lib/console.js)
console
.Console.log(message?: any, ...optionalParams: any[]): void (+2 overloads)
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log_static)
log
(const containingGroup: GroupcontainingGroup.Group.getParentGroups(): Array<Group>getParentGroups()); // [addedGroup]

Example: Team Hierarchy

Here's a practical example of using group inheritance for team permissions:

// Company-wide group
const const companyGroup: GroupcompanyGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const companyGroup: GroupcompanyGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)addMember(
const CEO: Account | ({
    [x: string]: any;
} & Account)
CEO
, "admin");
// Team group with elevated permissions const const teamGroup: GroupteamGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const teamGroup: GroupteamGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const companyGroup: GroupcompanyGroup); // Inherits company-wide access
const teamGroup: GroupteamGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)addMember(
const teamLead: Account | ({
    [x: string]: any;
} & Account)
teamLead
, "admin");
const teamGroup: GroupteamGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)addMember(
const developer: Account | ({
    [x: string]: any;
} & Account)
developer
, "writer");
// Project group with specific permissions const const projectGroup: GroupprojectGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const projectGroup: GroupprojectGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
@categoryIdentity & Permissions Gives members of a parent group membership in this group.@parammember The group that will gain access to this group.@paramrole The role all members of the parent group should have in this group.
addMember
(const teamGroup: GroupteamGroup); // Inherits team permissions
const projectGroup: GroupprojectGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)addMember(
const client: Account | ({
    [x: string]: any;
} & Account)
client
, "reader"); // Client can only read project items

This creates a hierarchy where:

  • The CEO has admin access to everything
  • Team members get writer access to team and project content
  • Team leads get admin access to team and project content
  • The client can only read project content