@klum-db/lobby 0.2.0-pre.26 → 0.2.0-pre.28

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.
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/bin/klum.ts
21
+ var klum_exports = {};
22
+ __export(klum_exports, {
23
+ main: () => main,
24
+ parseArgs: () => parseArgs,
25
+ runInspectGroup: () => runInspectGroup,
26
+ runMeterGroup: () => runMeterGroup
27
+ });
28
+ module.exports = __toCommonJS(klum_exports);
29
+ var import_in_devtools = require("@noy-db/in-devtools");
30
+
31
+ // src/federation/group-inspector.ts
32
+ function groupInspector(group) {
33
+ let shardIds = /* @__PURE__ */ new Set();
34
+ const refresh = async () => {
35
+ const rows = await group.allRows();
36
+ shardIds = new Set(rows.map((r) => r.vaultId));
37
+ return rows;
38
+ };
39
+ return {
40
+ async listAccessibleVaults() {
41
+ const rows = await refresh();
42
+ return rows.map((r) => ({ id: r.vaultId, role: "owner" }));
43
+ },
44
+ async openVault(name) {
45
+ const vault = await group.db.openVault(name);
46
+ group.template.configure(vault);
47
+ return vault;
48
+ },
49
+ onAfterWrite(handler) {
50
+ return group.db.onAfterWrite((event) => {
51
+ if (shardIds.has(event.vault)) return handler(event);
52
+ });
53
+ },
54
+ onWriteConflict(handler) {
55
+ return group.db.onWriteConflict((c) => {
56
+ if (shardIds.has(c.vault)) handler(c);
57
+ });
58
+ },
59
+ get writeQueue() {
60
+ return group.db.writeQueue;
61
+ }
62
+ };
63
+ }
64
+
65
+ // src/federation/meter-group.ts
66
+ async function meterGroup(group, opts = {}) {
67
+ const { eligible, skipped } = await group.resolveEligible(
68
+ opts.minVersion !== void 0 ? { minVersion: opts.minVersion } : {}
69
+ );
70
+ const perShard = [];
71
+ const names = /* @__PURE__ */ new Set();
72
+ let records = 0;
73
+ for (const row of eligible) {
74
+ const vault = await group.shard(row.partitionKey);
75
+ const collNames = await vault.collections();
76
+ let shardRecords = 0;
77
+ for (const n of collNames) {
78
+ names.add(n);
79
+ shardRecords += await vault.collection(n).count();
80
+ }
81
+ records += shardRecords;
82
+ perShard.push({
83
+ vaultId: row.vaultId,
84
+ partitionKey: row.partitionKey,
85
+ schemaVersion: row.schemaVersion,
86
+ collections: collNames.length,
87
+ records: shardRecords
88
+ });
89
+ }
90
+ return { vaults: eligible.length, collections: names.size, records, perShard, skipped };
91
+ }
92
+
93
+ // src/bin/klum.ts
94
+ var import_meta = {};
95
+ function parseArgs(argv) {
96
+ const out = { command: argv[0] ?? "", meter: false };
97
+ for (const a of argv.slice(1)) {
98
+ if (a.startsWith("--group=")) out.group = a.slice("--group=".length);
99
+ else if (a.startsWith("--vault=")) out.vault = a.slice("--vault=".length);
100
+ else if (a === "--meter") out.meter = true;
101
+ else if (!a.startsWith("--")) out.configPath = a;
102
+ }
103
+ return out;
104
+ }
105
+ async function loadGroup(args) {
106
+ const mod = await import(args.configPath);
107
+ return mod.default(args.group);
108
+ }
109
+ async function runInspectGroup(args, log) {
110
+ if (!args.configPath) {
111
+ log("usage: klum inspect-group <config> --group=<name> [--vault=<id>]");
112
+ return 2;
113
+ }
114
+ const group = await loadGroup(args);
115
+ const inspector = (0, import_in_devtools.createInspector)(groupInspector(group));
116
+ const vaults = await inspector.listVaults();
117
+ log(`group "${args.group ?? ""}" \u2014 ${vaults.length} shard(s):`);
118
+ for (const v of vaults) log(` ${v.id} [${v.role}]`);
119
+ if (args.vault) {
120
+ const vault = await group.db.openVault(args.vault);
121
+ group.template.configure(vault);
122
+ const snap = await inspector.snapshot(vault);
123
+ log(` collections in ${args.vault}: ${snap.collections.map((c) => c.name).join(", ")}`);
124
+ }
125
+ return 0;
126
+ }
127
+ async function runMeterGroup(args, log) {
128
+ if (!args.configPath) {
129
+ log("usage: klum meter-group <config> --group=<name>");
130
+ return 2;
131
+ }
132
+ const group = await loadGroup(args);
133
+ const r = await meterGroup(group);
134
+ log(`group "${args.group ?? ""}" \u2014 ${r.vaults} vault(s), ${r.collections} collection(s), ${r.records} record(s)`);
135
+ for (const s of r.perShard) {
136
+ log(` ${s.vaultId} (${s.partitionKey}) v${s.schemaVersion}: ${s.collections} coll, ${s.records} rec`);
137
+ }
138
+ if (r.skipped.length) log(` skipped: ${r.skipped.length} shard(s)`);
139
+ return 0;
140
+ }
141
+ async function main(argv, log = console.log) {
142
+ const args = parseArgs(argv);
143
+ switch (args.command) {
144
+ case "inspect-group":
145
+ return runInspectGroup(args, log);
146
+ case "meter-group":
147
+ return runMeterGroup(args, log);
148
+ default:
149
+ log("klum <inspect-group|meter-group> <config> --group=<name> [--vault=<id>]");
150
+ return args.command ? 1 : 0;
151
+ }
152
+ }
153
+ if (process.argv[1] && import_meta.url === `file://${process.argv[1]}`) {
154
+ main(process.argv.slice(2)).then((code) => {
155
+ process.exitCode = code;
156
+ }).catch((e) => {
157
+ console.error(e);
158
+ process.exitCode = 1;
159
+ });
160
+ }
161
+ // Annotate the CommonJS export names for ESM import in node:
162
+ 0 && (module.exports = {
163
+ main,
164
+ parseArgs,
165
+ runInspectGroup,
166
+ runMeterGroup
167
+ });
168
+ //# sourceMappingURL=klum.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/bin/klum.ts","../../src/federation/group-inspector.ts","../../src/federation/meter-group.ts"],"sourcesContent":["import { createInspector } from '@noy-db/in-devtools'\nimport { groupInspector } from '../federation/group-inspector.js'\nimport { meterGroup } from '../federation/meter-group.js'\nimport type { VaultGroup } from '../federation/vault-group.js'\n\n/**\n * A config module for the `klum` CLI default-exports this factory: given an\n * optional group name, it returns an opened VaultGroup (the user's module owns\n * the store, templates, and sharding config — the CLI stays agnostic of them).\n */\nexport type GroupFactory = (groupName?: string) => Promise<VaultGroup<unknown>>\n\ntype Log = (s: string) => void\n\nexport interface ParsedArgs {\n command: string\n configPath?: string\n group?: string\n vault?: string\n meter: boolean\n}\n\nexport function parseArgs(argv: readonly string[]): ParsedArgs {\n const out: ParsedArgs = { command: argv[0] ?? '', meter: false }\n for (const a of argv.slice(1)) {\n if (a.startsWith('--group=')) out.group = a.slice('--group='.length)\n else if (a.startsWith('--vault=')) out.vault = a.slice('--vault='.length)\n else if (a === '--meter') out.meter = true\n else if (!a.startsWith('--')) out.configPath = a\n }\n return out\n}\n\nasync function loadGroup(args: ParsedArgs): Promise<VaultGroup<unknown>> {\n const mod = (await import(args.configPath!)) as { default: GroupFactory }\n return mod.default(args.group)\n}\n\nexport async function runInspectGroup(args: ParsedArgs, log: Log): Promise<number> {\n if (!args.configPath) {\n log('usage: klum inspect-group <config> --group=<name> [--vault=<id>]')\n return 2\n }\n const group = await loadGroup(args)\n const inspector = createInspector(groupInspector(group))\n const vaults = await inspector.listVaults()\n log(`group \"${args.group ?? ''}\" — ${vaults.length} shard(s):`)\n for (const v of vaults) log(` ${v.id} [${v.role}]`)\n if (args.vault) {\n const vault = await group.db.openVault(args.vault)\n group.template.configure(vault)\n const snap = await inspector.snapshot(vault)\n log(` collections in ${args.vault}: ${snap.collections.map((c) => c.name).join(', ')}`)\n }\n return 0\n}\n\nexport async function runMeterGroup(args: ParsedArgs, log: Log): Promise<number> {\n if (!args.configPath) {\n log('usage: klum meter-group <config> --group=<name>')\n return 2\n }\n const group = await loadGroup(args)\n const r = await meterGroup(group)\n log(`group \"${args.group ?? ''}\" — ${r.vaults} vault(s), ${r.collections} collection(s), ${r.records} record(s)`)\n for (const s of r.perShard) {\n log(` ${s.vaultId} (${s.partitionKey}) v${s.schemaVersion}: ${s.collections} coll, ${s.records} rec`)\n }\n if (r.skipped.length) log(` skipped: ${r.skipped.length} shard(s)`)\n return 0\n}\n\nexport async function main(argv: readonly string[], log: Log = console.log): Promise<number> {\n const args = parseArgs(argv)\n switch (args.command) {\n case 'inspect-group':\n return runInspectGroup(args, log)\n case 'meter-group':\n return runMeterGroup(args, log)\n default:\n log('klum <inspect-group|meter-group> <config> --group=<name> [--vault=<id>]')\n return args.command ? 1 : 0\n }\n}\n\n// bin entrypoint — only runs when executed directly, not when imported in tests.\nif (process.argv[1] && import.meta.url === `file://${process.argv[1]}`) {\n main(process.argv.slice(2))\n .then((code) => {\n process.exitCode = code\n })\n .catch((e: unknown) => {\n console.error(e)\n process.exitCode = 1\n })\n}\n","import type { InspectableContainer } from '@noy-db/in-devtools'\nimport type { AccessibleVault, Vault, WriteHook, WriteConflict, WriteQueue, Unsubscribe } from '@noy-db/hub'\nimport type { VaultGroup } from './vault-group.js'\n\n/**\n * Adapt a federation {@link VaultGroup} to the dev-tools `InspectableContainer`\n * contract from `@noy-db/in-devtools`, so the inspector / TUI can browse a\n * whole fleet exactly like a single instance.\n *\n * Built entirely on the group's public surface (`allRows`, `db`, `template`) —\n * no `VaultGroup` changes, and (critically) no `@klum-db` import lands in any\n * `@noy-db` package: the dependency runs one way, klum → noy.\n *\n * Write-event scoping: `group.db` may host vaults outside this group, so write\n * and conflict events are filtered to the group's shard ids. The id set is\n * primed/refreshed on every `listAccessibleVaults()` call — drive `listVaults()`\n * (the inspector's normal first step) before relying on event scoping.\n */\nexport function groupInspector<T>(group: VaultGroup<T>): InspectableContainer {\n let shardIds = new Set<string>()\n const refresh = async () => {\n const rows = await group.allRows()\n shardIds = new Set(rows.map((r) => r.vaultId))\n return rows\n }\n return {\n async listAccessibleVaults(): Promise<readonly AccessibleVault[]> {\n const rows = await refresh()\n return rows.map((r): AccessibleVault => ({ id: r.vaultId, role: 'owner' }))\n },\n async openVault(name: string): Promise<Vault> {\n const vault = await group.db.openVault(name)\n group.template.configure(vault)\n return vault\n },\n onAfterWrite(handler: WriteHook): Unsubscribe {\n return group.db.onAfterWrite((event) => {\n if (shardIds.has(event.vault)) return handler(event)\n })\n },\n onWriteConflict(handler: (c: WriteConflict) => void): Unsubscribe {\n return group.db.onWriteConflict((c) => {\n if (shardIds.has(c.vault)) handler(c)\n })\n },\n get writeQueue(): WriteQueue {\n return group.db.writeQueue\n },\n }\n}\n","import type { VaultGroup } from './vault-group.js'\nimport type { SkippedVault } from './types.js'\n\nexport interface GroupShardMetrics {\n readonly vaultId: string\n readonly partitionKey: string\n readonly schemaVersion: number\n readonly collections: number\n readonly records: number\n}\n\nexport interface GroupMeterReport {\n /** Number of eligible shards measured. */\n readonly vaults: number\n /** Distinct collection names across the group. */\n readonly collections: number\n /** Total record count summed across shards. */\n readonly records: number\n readonly perShard: ReadonlyArray<GroupShardMetrics>\n /** Drifted / provisioning-failed shards — surfaced, never counted or hidden. */\n readonly skipped: ReadonlyArray<SkippedVault>\n}\n\n/**\n * Fan shape-metrics (collection count + record count) across the group's\n * ELIGIBLE shards. Skipped shards (schema-drift / provisioning failures) are\n * reported in `skipped`, never silently dropped. Reuses the per-vault pattern\n * from `multi-bundle.ts` (`vault.collections()` → `collection(n).count()`).\n *\n * Operational store metrics (calls, CAS conflicts) are a separate concern and\n * are already group-wide via `@noy-db/to-meter` on the underlying store.\n */\nexport async function meterGroup<T>(\n group: VaultGroup<T>,\n opts: { minVersion?: number } = {},\n): Promise<GroupMeterReport> {\n const { eligible, skipped } = await group.resolveEligible(\n opts.minVersion !== undefined ? { minVersion: opts.minVersion } : {},\n )\n const perShard: GroupShardMetrics[] = []\n const names = new Set<string>()\n let records = 0\n for (const row of eligible) {\n const vault = await group.shard(row.partitionKey)\n const collNames = await vault.collections()\n let shardRecords = 0\n for (const n of collNames) {\n names.add(n)\n shardRecords += await vault.collection(n).count()\n }\n records += shardRecords\n perShard.push({\n vaultId: row.vaultId,\n partitionKey: row.partitionKey,\n schemaVersion: row.schemaVersion,\n collections: collNames.length,\n records: shardRecords,\n })\n }\n return { vaults: eligible.length, collections: names.size, records, perShard, skipped }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,25 @@
1
+ import { V as VaultGroup } from '../vault-group-DeBoCFT9.cjs';
2
+ import '@noy-db/hub/kernel';
3
+ import '@noy-db/hub';
4
+ import '@noy-db/hub/bundle';
5
+
6
+ /**
7
+ * A config module for the `klum` CLI default-exports this factory: given an
8
+ * optional group name, it returns an opened VaultGroup (the user's module owns
9
+ * the store, templates, and sharding config — the CLI stays agnostic of them).
10
+ */
11
+ type GroupFactory = (groupName?: string) => Promise<VaultGroup<unknown>>;
12
+ type Log = (s: string) => void;
13
+ interface ParsedArgs {
14
+ command: string;
15
+ configPath?: string;
16
+ group?: string;
17
+ vault?: string;
18
+ meter: boolean;
19
+ }
20
+ declare function parseArgs(argv: readonly string[]): ParsedArgs;
21
+ declare function runInspectGroup(args: ParsedArgs, log: Log): Promise<number>;
22
+ declare function runMeterGroup(args: ParsedArgs, log: Log): Promise<number>;
23
+ declare function main(argv: readonly string[], log?: Log): Promise<number>;
24
+
25
+ export { type GroupFactory, type ParsedArgs, main, parseArgs, runInspectGroup, runMeterGroup };
@@ -0,0 +1,25 @@
1
+ import { V as VaultGroup } from '../vault-group-DeBoCFT9.js';
2
+ import '@noy-db/hub/kernel';
3
+ import '@noy-db/hub';
4
+ import '@noy-db/hub/bundle';
5
+
6
+ /**
7
+ * A config module for the `klum` CLI default-exports this factory: given an
8
+ * optional group name, it returns an opened VaultGroup (the user's module owns
9
+ * the store, templates, and sharding config — the CLI stays agnostic of them).
10
+ */
11
+ type GroupFactory = (groupName?: string) => Promise<VaultGroup<unknown>>;
12
+ type Log = (s: string) => void;
13
+ interface ParsedArgs {
14
+ command: string;
15
+ configPath?: string;
16
+ group?: string;
17
+ vault?: string;
18
+ meter: boolean;
19
+ }
20
+ declare function parseArgs(argv: readonly string[]): ParsedArgs;
21
+ declare function runInspectGroup(args: ParsedArgs, log: Log): Promise<number>;
22
+ declare function runMeterGroup(args: ParsedArgs, log: Log): Promise<number>;
23
+ declare function main(argv: readonly string[], log?: Log): Promise<number>;
24
+
25
+ export { type GroupFactory, type ParsedArgs, main, parseArgs, runInspectGroup, runMeterGroup };
@@ -0,0 +1,139 @@
1
+ // src/bin/klum.ts
2
+ import { createInspector } from "@noy-db/in-devtools";
3
+
4
+ // src/federation/group-inspector.ts
5
+ function groupInspector(group) {
6
+ let shardIds = /* @__PURE__ */ new Set();
7
+ const refresh = async () => {
8
+ const rows = await group.allRows();
9
+ shardIds = new Set(rows.map((r) => r.vaultId));
10
+ return rows;
11
+ };
12
+ return {
13
+ async listAccessibleVaults() {
14
+ const rows = await refresh();
15
+ return rows.map((r) => ({ id: r.vaultId, role: "owner" }));
16
+ },
17
+ async openVault(name) {
18
+ const vault = await group.db.openVault(name);
19
+ group.template.configure(vault);
20
+ return vault;
21
+ },
22
+ onAfterWrite(handler) {
23
+ return group.db.onAfterWrite((event) => {
24
+ if (shardIds.has(event.vault)) return handler(event);
25
+ });
26
+ },
27
+ onWriteConflict(handler) {
28
+ return group.db.onWriteConflict((c) => {
29
+ if (shardIds.has(c.vault)) handler(c);
30
+ });
31
+ },
32
+ get writeQueue() {
33
+ return group.db.writeQueue;
34
+ }
35
+ };
36
+ }
37
+
38
+ // src/federation/meter-group.ts
39
+ async function meterGroup(group, opts = {}) {
40
+ const { eligible, skipped } = await group.resolveEligible(
41
+ opts.minVersion !== void 0 ? { minVersion: opts.minVersion } : {}
42
+ );
43
+ const perShard = [];
44
+ const names = /* @__PURE__ */ new Set();
45
+ let records = 0;
46
+ for (const row of eligible) {
47
+ const vault = await group.shard(row.partitionKey);
48
+ const collNames = await vault.collections();
49
+ let shardRecords = 0;
50
+ for (const n of collNames) {
51
+ names.add(n);
52
+ shardRecords += await vault.collection(n).count();
53
+ }
54
+ records += shardRecords;
55
+ perShard.push({
56
+ vaultId: row.vaultId,
57
+ partitionKey: row.partitionKey,
58
+ schemaVersion: row.schemaVersion,
59
+ collections: collNames.length,
60
+ records: shardRecords
61
+ });
62
+ }
63
+ return { vaults: eligible.length, collections: names.size, records, perShard, skipped };
64
+ }
65
+
66
+ // src/bin/klum.ts
67
+ function parseArgs(argv) {
68
+ const out = { command: argv[0] ?? "", meter: false };
69
+ for (const a of argv.slice(1)) {
70
+ if (a.startsWith("--group=")) out.group = a.slice("--group=".length);
71
+ else if (a.startsWith("--vault=")) out.vault = a.slice("--vault=".length);
72
+ else if (a === "--meter") out.meter = true;
73
+ else if (!a.startsWith("--")) out.configPath = a;
74
+ }
75
+ return out;
76
+ }
77
+ async function loadGroup(args) {
78
+ const mod = await import(args.configPath);
79
+ return mod.default(args.group);
80
+ }
81
+ async function runInspectGroup(args, log) {
82
+ if (!args.configPath) {
83
+ log("usage: klum inspect-group <config> --group=<name> [--vault=<id>]");
84
+ return 2;
85
+ }
86
+ const group = await loadGroup(args);
87
+ const inspector = createInspector(groupInspector(group));
88
+ const vaults = await inspector.listVaults();
89
+ log(`group "${args.group ?? ""}" \u2014 ${vaults.length} shard(s):`);
90
+ for (const v of vaults) log(` ${v.id} [${v.role}]`);
91
+ if (args.vault) {
92
+ const vault = await group.db.openVault(args.vault);
93
+ group.template.configure(vault);
94
+ const snap = await inspector.snapshot(vault);
95
+ log(` collections in ${args.vault}: ${snap.collections.map((c) => c.name).join(", ")}`);
96
+ }
97
+ return 0;
98
+ }
99
+ async function runMeterGroup(args, log) {
100
+ if (!args.configPath) {
101
+ log("usage: klum meter-group <config> --group=<name>");
102
+ return 2;
103
+ }
104
+ const group = await loadGroup(args);
105
+ const r = await meterGroup(group);
106
+ log(`group "${args.group ?? ""}" \u2014 ${r.vaults} vault(s), ${r.collections} collection(s), ${r.records} record(s)`);
107
+ for (const s of r.perShard) {
108
+ log(` ${s.vaultId} (${s.partitionKey}) v${s.schemaVersion}: ${s.collections} coll, ${s.records} rec`);
109
+ }
110
+ if (r.skipped.length) log(` skipped: ${r.skipped.length} shard(s)`);
111
+ return 0;
112
+ }
113
+ async function main(argv, log = console.log) {
114
+ const args = parseArgs(argv);
115
+ switch (args.command) {
116
+ case "inspect-group":
117
+ return runInspectGroup(args, log);
118
+ case "meter-group":
119
+ return runMeterGroup(args, log);
120
+ default:
121
+ log("klum <inspect-group|meter-group> <config> --group=<name> [--vault=<id>]");
122
+ return args.command ? 1 : 0;
123
+ }
124
+ }
125
+ if (process.argv[1] && import.meta.url === `file://${process.argv[1]}`) {
126
+ main(process.argv.slice(2)).then((code) => {
127
+ process.exitCode = code;
128
+ }).catch((e) => {
129
+ console.error(e);
130
+ process.exitCode = 1;
131
+ });
132
+ }
133
+ export {
134
+ main,
135
+ parseArgs,
136
+ runInspectGroup,
137
+ runMeterGroup
138
+ };
139
+ //# sourceMappingURL=klum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/bin/klum.ts","../../src/federation/group-inspector.ts","../../src/federation/meter-group.ts"],"sourcesContent":["import { createInspector } from '@noy-db/in-devtools'\nimport { groupInspector } from '../federation/group-inspector.js'\nimport { meterGroup } from '../federation/meter-group.js'\nimport type { VaultGroup } from '../federation/vault-group.js'\n\n/**\n * A config module for the `klum` CLI default-exports this factory: given an\n * optional group name, it returns an opened VaultGroup (the user's module owns\n * the store, templates, and sharding config — the CLI stays agnostic of them).\n */\nexport type GroupFactory = (groupName?: string) => Promise<VaultGroup<unknown>>\n\ntype Log = (s: string) => void\n\nexport interface ParsedArgs {\n command: string\n configPath?: string\n group?: string\n vault?: string\n meter: boolean\n}\n\nexport function parseArgs(argv: readonly string[]): ParsedArgs {\n const out: ParsedArgs = { command: argv[0] ?? '', meter: false }\n for (const a of argv.slice(1)) {\n if (a.startsWith('--group=')) out.group = a.slice('--group='.length)\n else if (a.startsWith('--vault=')) out.vault = a.slice('--vault='.length)\n else if (a === '--meter') out.meter = true\n else if (!a.startsWith('--')) out.configPath = a\n }\n return out\n}\n\nasync function loadGroup(args: ParsedArgs): Promise<VaultGroup<unknown>> {\n const mod = (await import(args.configPath!)) as { default: GroupFactory }\n return mod.default(args.group)\n}\n\nexport async function runInspectGroup(args: ParsedArgs, log: Log): Promise<number> {\n if (!args.configPath) {\n log('usage: klum inspect-group <config> --group=<name> [--vault=<id>]')\n return 2\n }\n const group = await loadGroup(args)\n const inspector = createInspector(groupInspector(group))\n const vaults = await inspector.listVaults()\n log(`group \"${args.group ?? ''}\" — ${vaults.length} shard(s):`)\n for (const v of vaults) log(` ${v.id} [${v.role}]`)\n if (args.vault) {\n const vault = await group.db.openVault(args.vault)\n group.template.configure(vault)\n const snap = await inspector.snapshot(vault)\n log(` collections in ${args.vault}: ${snap.collections.map((c) => c.name).join(', ')}`)\n }\n return 0\n}\n\nexport async function runMeterGroup(args: ParsedArgs, log: Log): Promise<number> {\n if (!args.configPath) {\n log('usage: klum meter-group <config> --group=<name>')\n return 2\n }\n const group = await loadGroup(args)\n const r = await meterGroup(group)\n log(`group \"${args.group ?? ''}\" — ${r.vaults} vault(s), ${r.collections} collection(s), ${r.records} record(s)`)\n for (const s of r.perShard) {\n log(` ${s.vaultId} (${s.partitionKey}) v${s.schemaVersion}: ${s.collections} coll, ${s.records} rec`)\n }\n if (r.skipped.length) log(` skipped: ${r.skipped.length} shard(s)`)\n return 0\n}\n\nexport async function main(argv: readonly string[], log: Log = console.log): Promise<number> {\n const args = parseArgs(argv)\n switch (args.command) {\n case 'inspect-group':\n return runInspectGroup(args, log)\n case 'meter-group':\n return runMeterGroup(args, log)\n default:\n log('klum <inspect-group|meter-group> <config> --group=<name> [--vault=<id>]')\n return args.command ? 1 : 0\n }\n}\n\n// bin entrypoint — only runs when executed directly, not when imported in tests.\nif (process.argv[1] && import.meta.url === `file://${process.argv[1]}`) {\n main(process.argv.slice(2))\n .then((code) => {\n process.exitCode = code\n })\n .catch((e: unknown) => {\n console.error(e)\n process.exitCode = 1\n })\n}\n","import type { InspectableContainer } from '@noy-db/in-devtools'\nimport type { AccessibleVault, Vault, WriteHook, WriteConflict, WriteQueue, Unsubscribe } from '@noy-db/hub'\nimport type { VaultGroup } from './vault-group.js'\n\n/**\n * Adapt a federation {@link VaultGroup} to the dev-tools `InspectableContainer`\n * contract from `@noy-db/in-devtools`, so the inspector / TUI can browse a\n * whole fleet exactly like a single instance.\n *\n * Built entirely on the group's public surface (`allRows`, `db`, `template`) —\n * no `VaultGroup` changes, and (critically) no `@klum-db` import lands in any\n * `@noy-db` package: the dependency runs one way, klum → noy.\n *\n * Write-event scoping: `group.db` may host vaults outside this group, so write\n * and conflict events are filtered to the group's shard ids. The id set is\n * primed/refreshed on every `listAccessibleVaults()` call — drive `listVaults()`\n * (the inspector's normal first step) before relying on event scoping.\n */\nexport function groupInspector<T>(group: VaultGroup<T>): InspectableContainer {\n let shardIds = new Set<string>()\n const refresh = async () => {\n const rows = await group.allRows()\n shardIds = new Set(rows.map((r) => r.vaultId))\n return rows\n }\n return {\n async listAccessibleVaults(): Promise<readonly AccessibleVault[]> {\n const rows = await refresh()\n return rows.map((r): AccessibleVault => ({ id: r.vaultId, role: 'owner' }))\n },\n async openVault(name: string): Promise<Vault> {\n const vault = await group.db.openVault(name)\n group.template.configure(vault)\n return vault\n },\n onAfterWrite(handler: WriteHook): Unsubscribe {\n return group.db.onAfterWrite((event) => {\n if (shardIds.has(event.vault)) return handler(event)\n })\n },\n onWriteConflict(handler: (c: WriteConflict) => void): Unsubscribe {\n return group.db.onWriteConflict((c) => {\n if (shardIds.has(c.vault)) handler(c)\n })\n },\n get writeQueue(): WriteQueue {\n return group.db.writeQueue\n },\n }\n}\n","import type { VaultGroup } from './vault-group.js'\nimport type { SkippedVault } from './types.js'\n\nexport interface GroupShardMetrics {\n readonly vaultId: string\n readonly partitionKey: string\n readonly schemaVersion: number\n readonly collections: number\n readonly records: number\n}\n\nexport interface GroupMeterReport {\n /** Number of eligible shards measured. */\n readonly vaults: number\n /** Distinct collection names across the group. */\n readonly collections: number\n /** Total record count summed across shards. */\n readonly records: number\n readonly perShard: ReadonlyArray<GroupShardMetrics>\n /** Drifted / provisioning-failed shards — surfaced, never counted or hidden. */\n readonly skipped: ReadonlyArray<SkippedVault>\n}\n\n/**\n * Fan shape-metrics (collection count + record count) across the group's\n * ELIGIBLE shards. Skipped shards (schema-drift / provisioning failures) are\n * reported in `skipped`, never silently dropped. Reuses the per-vault pattern\n * from `multi-bundle.ts` (`vault.collections()` → `collection(n).count()`).\n *\n * Operational store metrics (calls, CAS conflicts) are a separate concern and\n * are already group-wide via `@noy-db/to-meter` on the underlying store.\n */\nexport async function meterGroup<T>(\n group: VaultGroup<T>,\n opts: { minVersion?: number } = {},\n): Promise<GroupMeterReport> {\n const { eligible, skipped } = await group.resolveEligible(\n opts.minVersion !== undefined ? { minVersion: opts.minVersion } : {},\n )\n const perShard: GroupShardMetrics[] = []\n const names = new Set<string>()\n let records = 0\n for (const row of eligible) {\n const vault = await group.shard(row.partitionKey)\n const collNames = await vault.collections()\n let shardRecords = 0\n for (const n of collNames) {\n names.add(n)\n shardRecords += await vault.collection(n).count()\n }\n records += shardRecords\n perShard.push({\n vaultId: row.vaultId,\n partitionKey: row.partitionKey,\n schemaVersion: row.schemaVersion,\n collections: collNames.length,\n records: shardRecords,\n })\n }\n return { vaults: eligible.length, collections: names.size, records, perShard, skipped }\n}\n"],"mappings":"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","names":[]}