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: Group
playlistGroup =class Group
Group.create(); const
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const trackGroup: Group
trackGroup =class Group
Group.create(); // Tracks are now visible to the members of playlist
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const trackGroup: Group
trackGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const playlistGroup: Group
playlistGroup);
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: Group
grandParentGroup =class Group
Group.create(); const
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const parentGroup: Group
parentGroup =class Group
Group.create(); const
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const childGroup: Group
childGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const childGroup: Group
childGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const parentGroup: Group
parentGroup);const parentGroup: Group
parentGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const grandParentGroup: Group
grandParentGroup);
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: Group
addedGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const addedGroup: Group
addedGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)
addMember(bob, "reader"); const
const bob: Account | ({ [x: string]: any; } & Account)
const containingGroup: Group
containingGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const addedGroup: Group
addedGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)
addMember(bob, "writer");
const bob: Account | ({ [x: string]: any; } & Account)
const containingGroup: Group
containingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const addedGroup: Group
addedGroup); // 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: Group
addedGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const addedGroup: Group
addedGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)
addMember(bob, "writeOnly"); const
const bob: Account | ({ [x: string]: any; } & Account)
const containingGroup: Group
containingGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const containingGroup: Group
containingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const addedGroup: Group
addedGroup); // Bob does not become a member of the containing group
To add a group to another group:
- The current account must be an admin in the containing group
- The current account must be a member of the added group
const
const companyGroup: Group
companyGroup =const company: CoMap
company.CoValueBase._owner: Account | Group
_owner.CoValueBase.castAs<typeof Group>(schema: typeof Group): Group
castAs(class Group
Group); constconst teamGroup: Group
teamGroup =class Group
Group.create(); // Works only if I'm a member of `companyGroup`
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const teamGroup: Group
teamGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const companyGroup: Group
companyGroup);
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: Group
organizationGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const organizationGroup: Group
organizationGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)
addMember(bob, "admin"); const
const bob: Account | ({ [x: string]: any; } & Account)
const billingGroup: Group
billingGroup =class Group
Group.create(); // This way the members of the organization // can only read the billing data
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const billingGroup: Group
billingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const organizationGroup: Group
organizationGroup, "reader");
The "override role" works in both directions:
const
const addedGroup: Group
addedGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const addedGroup: Group
addedGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)
addMember(bob, "reader");
const bob: Account | ({ [x: string]: any; } & Account)
const addedGroup: Group
addedGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)
addMember(alice, "admin"); const
const alice: Account | ({ [x: string]: any; } & Account)
const containingGroup: Group
containingGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const containingGroup: Group
containingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const addedGroup: Group
addedGroup, "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: Group
addedGroup.Group.removeMember(member: Everyone | Account): Promise<void> (+1 overload)
removeMember(bob); // Bob loses access to both groups. // If Bob was also a member of the containing group, // he wouldn't have lost access.
const bob: Account | ({ [x: string]: any; } & Account)
Removing groups from other groups
You can remove a group from another group by using the removeMember
method:
const
const addedGroup: Group
addedGroup =class Group
Group.create(); const
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const containingGroup: Group
containingGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const containingGroup: Group
containingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const addedGroup: Group
addedGroup); // Revoke the extension awaitconst containingGroup: Group
containingGroup.Group.removeMember(member: Group): Promise<void> (+1 overload)
removeMember(const addedGroup: Group
addedGroup);
Getting all added groups
You can get all of the groups added to a group by calling the getParentGroups
method:
const
const containingGroup: Group
containingGroup =class Group
Group.create(); const
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const addedGroup: Group
addedGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const containingGroup: Group
containingGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const addedGroup: Group
addedGroup);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 ```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: Group
containingGroup.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: Group
companyGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const companyGroup: Group
companyGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)
addMember(CEO, "admin"); // Team group with elevated permissions const
const CEO: Account | ({ [x: string]: any; } & Account)
const teamGroup: Group
teamGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const teamGroup: Group
teamGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const companyGroup: Group
companyGroup); // Inherits company-wide accessconst teamGroup: Group
teamGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)
addMember(teamLead, "admin");
const teamLead: Account | ({ [x: string]: any; } & Account)
const teamGroup: Group
teamGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)
addMember(developer, "writer"); // Project group with specific permissions const
const developer: Account | ({ [x: string]: any; } & Account)
const projectGroup: Group
projectGroup =class Group
Group.create();
Group.create<Group>(this: CoValueClass<Group>, options?: { owner: Account; } | Account): Group
const projectGroup: Group
projectGroup.Group.addMember(member: Group, role?: "reader" | "writer" | "admin" | "inherit"): void (+2 overloads)
addMember(const teamGroup: Group
teamGroup); // Inherits team permissionsconst projectGroup: Group
projectGroup.Group.addMember(member: Account, role: AccountRole): void (+2 overloads)
addMember(client, "reader"); // Client can only read project items
const client: Account | ({ [x: string]: any; } & Account)
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