@compforge/harness-common 0.5.3 → 0.5.5

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.
package/README.md CHANGED
@@ -27,11 +27,20 @@ view during initialize, so concurrent consumers share them and dispose before th
27
27
  Dependency graphs must be acyclic. A failed initialization permits retry only after successful cleanup;
28
28
  failed cleanup poisons that key and is reported again when the root manager is disposed.
29
29
 
30
- `FileDataSource` is an abstract, runtime-recognizable DataSource for one file path. Its `FileClient.read`
31
- returns fresh `Uint8Array` bytes with explicit `maxBytes`, `timeoutMs` and optional cancellation.
32
- An empty file succeeds; an oversized or incomplete read rejects. The source's `path` and concrete
33
- location metadata let consumers attach provenance. Parsing, configuration precedence and persistence
34
- belong to consumers. Platform implementations live in `@compforge/harness-toolbox/file`.
30
+ `FileDataSource` declares a nonempty group of distinct `paths` that can be acquired together.
31
+ `FileClient.read` obtains the whole group once and returns one `FileReadResult` per declared path,
32
+ in order: `{ path, status: "ok", bytes }` or `{ path, status: "error", error }`. Empty files succeed;
33
+ missing and oversized files keep independent errors without discarding successful siblings.
34
+
35
+ Callers specify `maxBytes` per file, `maxTotalBytes` for all retained bytes (including in-progress
36
+ files), `timeoutMs` for the complete operation including queueing, and optional cancellation.
37
+ Cancellation, total-budget exhaustion, the group deadline and transport/protocol failures reject
38
+ the whole operation. Every read observes fresh bytes; a co-acquisition group is not an atomic
39
+ filesystem snapshot. Parsing, configuration precedence and persistence belong to consumers.
40
+ Platform implementations live in `@compforge/harness-toolbox/file`.
41
+
42
+ Version 0.5.4 replaces the single `path` and byte-only return value; single-file consumers declare
43
+ `paths: [path]` and consume the sole per-file result. There is no implicit legacy adapter.
35
44
 
36
45
  Service identifies a Component's runtime presence in an Environment. A Component belongs to a Repository
37
46
  on a Forge. Product is a separate business identity; consumers own its many-to-many associations
@@ -167,3 +176,10 @@ headings or code blocks in child output.
167
176
  Move `KnowledgeMarkdownOptions.renderEntry` or `dictionaryToMarkdown` renderer callbacks
168
177
  into each Entry's `markdown()` method. These callback arguments are removed in 0.5; a
169
178
  custom Entry now renders consistently when shown alone or through its Dictionary and Knowledge.
179
+
180
+ ## Resource inventory
181
+
182
+ `Resource` declares a collection held by a running Service; `ServiceResource` records manages/uses
183
+ relationships without duplicating identity. `ResourceSnapshot` retains typed instances, unit-bearing
184
+ measurements, independently known totals and complete/partial/unavailable coverage. Discovery and
185
+ rendering remain consumer-owned. See [the shared contract](../../../docs/resource.md).
package/dist/file.d.ts CHANGED
@@ -2,28 +2,40 @@ import type { Client } from "./client.js";
2
2
  import type { ClientManager } from "./client-manager.js";
3
3
  import type { DataSource } from "./datasource.js";
4
4
  export interface FileReadOptions {
5
- /** Maximum complete file size; exceeding it rejects instead of returning a prefix. */
5
+ /** Maximum complete size of each file; an oversized file returns an error without partial bytes. */
6
6
  maxBytes: number;
7
- /** Includes time waiting for a concurrency slot. */
7
+ /** Maximum bytes retained across the whole read, including in-progress files. */
8
+ maxTotalBytes: number;
9
+ /** One deadline for queueing and acquiring the complete group. */
8
10
  timeoutMs: number;
9
11
  signal?: AbortSignal;
10
12
  }
13
+ export type FileReadResult = {
14
+ readonly path: string;
15
+ readonly status: "ok";
16
+ readonly bytes: Uint8Array;
17
+ } | {
18
+ readonly path: string;
19
+ readonly status: "error";
20
+ readonly error: Error;
21
+ };
11
22
  /**
12
- * @spec Each read obtains fresh bytes from the source's bound path. Empty files succeed;
13
- * missing files, incomplete reads and exceeded limits reject.
14
- * @rule Parsing, configuration precedence and evidence persistence belong to consumers.
23
+ * @spec Each read acquires the declared group once and returns one result per path, in declaration order.
24
+ * Individual file failures do not discard other files; cancellation, the group deadline, total budget
25
+ * and transport/protocol failures reject the operation. Every read obtains fresh bytes.
26
+ * @rule Group acquisition is not an atomic filesystem snapshot. Parsing and evidence belong to consumers.
15
27
  */
16
28
  export interface FileClient extends Client {
17
- read(options: FileReadOptions): Promise<Uint8Array>;
29
+ read(options: FileReadOptions): Promise<readonly FileReadResult[]>;
18
30
  }
19
31
  /**
20
- * A single file location, discoverable without creating a client or performing I/O.
21
- * @why A shared runtime type lets consumers recognize file sources without a second kind registry.
22
- * @rule clientKey identifies access, not cached file contents or a Service-owned alias.
32
+ * A nonempty group of distinct paths that the caller declares can be acquired together.
33
+ * @why The source is the acquisition unit; grouping does not require consumers to guess transport capabilities.
34
+ * @rule clientKey identifies access and the group, not cached contents or a Service-owned alias.
23
35
  * @see {@link component://../../../docs/toolbox.md}
24
36
  */
25
37
  export declare abstract class FileDataSource<C extends FileClient = FileClient> implements DataSource<C> {
26
- abstract readonly path: string;
38
+ abstract readonly paths: readonly string[];
27
39
  readonly description?: string;
28
40
  abstract readonly clientKey: string;
29
41
  abstract createClient(clients: Pick<ClientManager, "get">, signal: AbortSignal): C;
package/dist/file.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * A single file location, discoverable without creating a client or performing I/O.
3
- * @why A shared runtime type lets consumers recognize file sources without a second kind registry.
4
- * @rule clientKey identifies access, not cached file contents or a Service-owned alias.
2
+ * A nonempty group of distinct paths that the caller declares can be acquired together.
3
+ * @why The source is the acquisition unit; grouping does not require consumers to guess transport capabilities.
4
+ * @rule clientKey identifies access and the group, not cached contents or a Service-owned alias.
5
5
  * @see {@link component://../../../docs/toolbox.md}
6
6
  */
7
7
  export class FileDataSource {
package/dist/index.d.ts CHANGED
@@ -15,3 +15,4 @@ export type { Product } from "./product.js";
15
15
  export * from "./knowledge.js";
16
16
  export type { Identity } from "./identity.js";
17
17
  export * from "./file.js";
18
+ export * from "./resource.js";
package/dist/index.js CHANGED
@@ -13,3 +13,4 @@ export * from "./service.js";
13
13
  export * from "./execution.js";
14
14
  export * from "./knowledge.js";
15
15
  export * from "./file.js";
16
+ export * from "./resource.js";
@@ -0,0 +1,62 @@
1
+ import type { Service } from "./service.js";
2
+ /** A category of resources persistently managed or used by a running Service.
3
+ * @spec A declaration describes what can be inventoried, not the current count or existence.
4
+ * @rule id identifies the same resource collection across Services; names are presentation only.
5
+ */
6
+ export interface Resource {
7
+ readonly id: string;
8
+ readonly kind: string;
9
+ readonly name: string;
10
+ readonly description?: string;
11
+ }
12
+ /** Association does not transfer exclusive ownership or duplicate the underlying resource. */
13
+ export interface ServiceResource<S extends Service = Service> {
14
+ readonly service: S;
15
+ readonly resource: Resource;
16
+ readonly relationship: "manages" | "uses";
17
+ }
18
+ /** Caller-owned environment and collection IDs disambiguate identical local instance IDs. */
19
+ export interface ResourceInstanceRef {
20
+ readonly environment: string;
21
+ readonly resource: string;
22
+ readonly id: string;
23
+ }
24
+ export interface ResourceInstance<Details = Readonly<Record<string, unknown>>> {
25
+ readonly ref: ResourceInstanceRef;
26
+ readonly name?: string;
27
+ /** Domain state, never an implicit health verdict. */
28
+ readonly state?: string;
29
+ readonly details?: Details;
30
+ }
31
+ /** Quantities retain their units and aggregation scope; different measures are not additive. */
32
+ export interface ResourceMeasurement {
33
+ readonly name: string;
34
+ readonly value: number;
35
+ readonly unit: string;
36
+ readonly scope: "observed" | "total";
37
+ }
38
+ export type ResourceCoverage = {
39
+ readonly status: "complete";
40
+ } | {
41
+ readonly status: "partial" | "unavailable";
42
+ readonly reason: string;
43
+ };
44
+ /**
45
+ * Read-only inventory evidence. Collection, access, pagination policy and rendering belong to consumers.
46
+ * @spec totalCount is an independently known count for scope, never inferred from a page of instances.
47
+ * @spec Unknown values are omitted; partial or unavailable acquisition is not a resource health verdict.
48
+ * @rule source, scope and details contain only safe serializable evidence, never access credentials.
49
+ */
50
+ export interface ResourceSnapshot<Details = Readonly<Record<string, unknown>>> {
51
+ readonly resource: Resource;
52
+ readonly environment: string;
53
+ readonly observedAt: string;
54
+ readonly source: string;
55
+ readonly scope: Readonly<Record<string, string>>;
56
+ readonly coverage: ResourceCoverage;
57
+ readonly totalCount?: number;
58
+ readonly measurements?: readonly ResourceMeasurement[];
59
+ readonly instances?: readonly ResourceInstance<Details>[];
60
+ readonly nextCursor?: string;
61
+ }
62
+ export declare function sameResourceInstance(left: ResourceInstanceRef, right: ResourceInstanceRef): boolean;
@@ -0,0 +1,3 @@
1
+ export function sameResourceInstance(left, right) {
2
+ return left.environment === right.environment && left.resource === right.resource && left.id === right.id;
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compforge/harness-common",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Execution-neutral client lifecycle and datasource contracts for harnesses",
5
5
  "type": "module",
6
6
  "scripts": {