Group Inheritance

Groups can inherit members from other groups using the extend method.

When a group extends another group, members of the parent group will become automatically part of the child group.

Basic Usage

Here's how to extend a 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
();
// This way track becomes visible to the members of playlist const trackGroup: GrouptrackGroup.Group.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const playlistGroup: GroupplaylistGroup);

When you extend a group:

  • Members of the parent group get access to the child group
  • Their roles are inherited (with some exceptions, see below)
  • Removing a member from the parent group also removes their access to child groups

Inheriting members but overriding their role

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

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 (+1 overload)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.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const organizationGroup: GrouporganizationGroup, "reader");

The "override role" works in both directions:

const const parentGroup: GroupparentGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const parentGroup: GroupparentGroup.Group.addMember(member: Account, role: AccountRole): void (+1 overload)addMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
, "reader");
const parentGroup: GroupparentGroup.Group.addMember(member: Account, role: AccountRole): void (+1 overload)addMember(
const alice: Account | ({
    [x: string]: any;
} & Account)
alice
, "admin");
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.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const parentGroup: GroupparentGroup, "writer"); // Bob and Alice are now writers in the child group

Multiple Levels of Inheritance

Groups can be extended multiple levels deep:

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.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const parentGroup: GroupparentGroup); const parentGroup: GroupparentGroup.Group.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const grandParentGroup: GroupgrandParentGroup);

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

Permission Changes

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

// Remove member from parent
await const parentGroup: GroupparentGroup.Group.removeMember(member: Everyone | Account): Promise<void>removeMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
);
// Bob loses access to both parent and child groups

Role Inheritance Rules

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

const const parentGroup: GroupparentGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const parentGroup: GroupparentGroup.Group.addMember(member: Account, role: AccountRole): void (+1 overload)addMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
, "reader");
const const childGroup: GroupchildGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const parentGroup: GroupparentGroup.Group.addMember(member: Account, role: AccountRole): void (+1 overload)addMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
, "writer");
const childGroup: GroupchildGroup.Group.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const parentGroup: GroupparentGroup); // Bob stays a writer because his role is higher // than the inherited reader role.

When extending groups, only admin, writer and reader roles are inherited:

const const parentGroup: GroupparentGroup = class Group
@categoryIdentity & Permissions
Group
.
Group.create<Group>(this: CoValueClass<Group>, options?: {
    owner: Account;
} | Account): Group
create
();
const parentGroup: GroupparentGroup.Group.addMember(member: Account, role: AccountRole): void (+1 overload)addMember(
const bob: Account | ({
    [x: string]: any;
} & Account)
bob
, "writeOnly");
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.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const parentGroup: GroupparentGroup); // Bob does not become a member of the child group

To extend a group:

  1. The current account must be an admin in the child group
  2. The current account must be a member of the parent group
const const companyGroup: GroupcompanyGroup = 
const company: {
    name: string;
} & CoMap
company
.CoValueBase._owner: Account | Group
@categoryCollaboration
_owner
.CoValueBase.castAs<typeof Group>(cl: 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.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const companyGroup: GroupcompanyGroup);

Revoking a group extension

You can revoke a group extension by using the revokeExtend method:

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.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const parentGroup: GroupparentGroup); // Revoke the extension await const childGroup: GroupchildGroup.Group.revokeExtend(parent: Group): Promise<Group>revokeExtend(const parentGroup: GroupparentGroup);

Getting all parent groups

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

const const childGroup: GroupchildGroup = 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 childGroup: GroupchildGroup.Group.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const parentGroup: GroupparentGroup); 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 (+1 overload)
Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v20.x/api/util.html#utilformatformat-args) for more information.
@sincev0.1.100
log
(const childGroup: GroupchildGroup.Group.getParentGroups(): Array<Group>getParentGroups()); // [parentGroup]

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 (+1 overload)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.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const companyGroup: GroupcompanyGroup); // Inherits company-wide access const teamGroup: GroupteamGroup.Group.addMember(member: Account, role: AccountRole): void (+1 overload)addMember(
const teamLead: Account | ({
    [x: string]: any;
} & Account)
teamLead
, "admin");
const teamGroup: GroupteamGroup.Group.addMember(member: Account, role: AccountRole): void (+1 overload)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.extend(parent: Group, roleMapping?: "reader" | "writer" | "admin" | "inherit"): Groupextend(const teamGroup: GroupteamGroup); // Inherits team permissions const projectGroup: GroupprojectGroup.Group.addMember(member: Account, role: AccountRole): void (+1 overload)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