first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
# `partitions/partition-manager/partition-manager.ts`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`PartitionManager` orchestrates the lifecycle of partition records. It accepts a `PartitionStore` for persistence and a `KeyRing` for per-user key material, validates every mutation against the active policy via `validateAction`, and emits `partition:*` and `member:*` events on a caller-supplied `EventBusLike`. The manager is the single throat through which all partition state changes pass; downstream modules (grants, tokens, transfer) treat the partition as immutable input.
## Features
- Five mutating operations (`create`, `delete`, `addMember`, `removeMember`, `update`) plus three read-only operations (`get`, `list`, `getKeyRing`).
- Zod-validated inputs at every public entrypoint via `CreatePartitionOptionsSchema` / `AddMemberOptionsSchema` / `UpdatePartitionOptionsSchema`.
- Injectable `Clock` for deterministic timestamping and an injectable id generator for deterministic IDs in tests.
- `EventBusLike<PartitionManagerEvents>` carries `partition:created`, `partition:deleted`, `partition:updated`, `member:added`, `member:removed`. No key material in event payloads.
- OTel spans on every mutating operation; counter `share.partition.member_total` per partition; counter `share.partition.create_total`.
- Graceful-degradation read-only toggle (`setReadOnly`) — mutations throw `SHARE_POLICY_VIOLATION` while the flag is true.
## Usage: Basic Example
```ts
import { PartitionManager } from "@suaveplan/drive-sharing/partitions/partition-manager";
import { InMemoryPartitionStore } from "@suaveplan/drive-sharing/partitions/partition-store";
import { KeyRing } from "@suaveplan/drive-sharing/encryption/key-ring";
import { userId } from "@suaveplan/drive-sharing/types/common";
const manager = new PartitionManager({
store: new InMemoryPartitionStore(),
keyRing: new KeyRing(),
});
const partition = await manager.create({
name: "research",
type: "shared",
ownerId: userId("alice"),
});
```
## Usage: Advanced Example
```ts
import { PartitionManager } from "@suaveplan/drive-sharing/partitions/partition-manager";
import { InMemoryPartitionStore } from "@suaveplan/drive-sharing/partitions/partition-store";
import { KeyRing } from "@suaveplan/drive-sharing/encryption/key-ring";
import { partitionId, userId } from "@suaveplan/drive-sharing/types/common";
let now = 0;
let counter = 0;
const events = new Map<string, unknown[]>();
const bus = {
on: () => () => {},
emit: (k: string, p: unknown) => {
const arr = events.get(k) ?? [];
arr.push(p);
events.set(k, arr);
},
};
const manager = new PartitionManager({
store: new InMemoryPartitionStore(),
keyRing: new KeyRing(),
clock: () => (now += 1000),
idGenerator: () => `id-${(counter += 1)}`,
events: bus,
});
const p = await manager.create({
name: "vault",
type: "encrypted",
ownerId: userId("alice"),
policy: { maxMembers: 3 },
});
await manager.addMember(p.id, userId("bob"), { permissions: ["read"] });
manager.setReadOnly(true);
// further mutations throw SHARE_POLICY_VIOLATION
```
## API Reference
### `class PartitionManager`
- `constructor(config: PartitionManagerConfig)`
- `create(options): Promise<Partition>`
- `get(id): Promise<Partition>` — throws `SHARE_PARTITION_NOT_FOUND`.
- `list(): Promise<Partition[]>`
- `delete(id): Promise<void>`
- `addMember(id, userId, options?): Promise<Partition>` — throws `SHARE_POLICY_VIOLATION` when policy denies.
- `removeMember(id, userId): Promise<Partition>` — idempotent.
- `update(id, options): Promise<Partition>`
- `setReadOnly(value): void` / `isReadOnly(): boolean`
- `getKeyRing(): KeyRing`
### `interface CreatePartitionOptions`
`{ name, type, ownerId, policy? }`.
### `interface AddMemberOptions`
`{ permissions?, wrappedKey? }`. Default permissions are `["read"]`.
### `interface UpdatePartitionOptions`
`{ name?, policy? }`.
### `interface PartitionManagerEvents`
`partition:created` / `partition:deleted` / `partition:updated` / `member:added` / `member:removed`. No key material.
### `interface PartitionManagerConfig`
`{ store, keyRing, policy?, clock?, events?, idGenerator? }`.
## Implementation Notes
- The default policy is computed once in the constructor (`createPolicy(config.policy ?? {})`). Per-partition policies are layered on top via `mergePolicies` in `create` and `update`.
- `addMember` validates the partition's policy against the action `share` (the canonical "I am modifying membership" verb); when the policy denies, the call throws `SHARE_POLICY_VIOLATION`. Importantly, the check is performed against the partition's CURRENT membership — adding the Nth member when `maxMembers` is N triggers the denial branch.
- `removeMember` is intentionally idempotent. Calling it twice with the same userId returns the same partition the second time and does NOT emit `member:removed`. This matches the spec scenario "RemoveMember restores count".
- `setReadOnly(true)` is the spec's documented graceful-degradation hook; the partition store's health check is the trigger but the toggle is exposed publicly so consumers can wire any policy they like.
- Event emission is fire-and-forget — the `EventBusLike.emit` contract is synchronous and does not surface listener errors. Asynchronous side effects belong to the listener, not the manager.