@weaveintel/realm 0.1.0

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,89 @@
1
+ // SPDX-License-Identifier: MIT
2
+ /**
3
+ * The resolution engine — given all the config rows a tenant is *allowed to see* for some family
4
+ * (e.g. every copy of the prompts), pick the ONE that applies to them, and explain why.
5
+ *
6
+ * The rule is "nearest owner wins": your own copy beats a parent's shared copy, which beats the
7
+ * global default. Every result carries **provenance** — is this the global default, your own edit,
8
+ * or something inherited from a parent — plus, for your own edits, whether they've drifted from the
9
+ * default they were forked from. Pure functions: no database, no clock. The store just hands them the
10
+ * visible rows.
11
+ */
12
+ import { driftState, } from './realm-record.js';
13
+ import { ownerDepth, parentTenantId, strictGrandAncestorIds, } from './context.js';
14
+ /**
15
+ * Can tenant `ctx` see this record?
16
+ * • every global default, and your own records, always;
17
+ * • a parent's record only if it's shared to 'children' or 'subtree';
18
+ * • a higher ancestor's record only if it's shared to the whole 'subtree'.
19
+ * A parent's *private* record stays invisible — you resolve past it to the next-nearest or global.
20
+ */
21
+ export function isVisible(record, ctx) {
22
+ if (record.realm === 'global')
23
+ return true;
24
+ if (ctx.tenantId == null)
25
+ return false; // global caller sees only global
26
+ if (record.ownerTenantId === ctx.tenantId)
27
+ return true;
28
+ if (record.ownerTenantId === parentTenantId(ctx))
29
+ return record.shareMode === 'children' || record.shareMode === 'subtree';
30
+ if (record.ownerTenantId != null && strictGrandAncestorIds(ctx).includes(record.ownerTenantId))
31
+ return record.shareMode === 'subtree';
32
+ return false;
33
+ }
34
+ /** Explain a winning record for a given context. `remoteHashOf` looks up the origin's CURRENT hash (for drift). */
35
+ export function provenanceOf(record, ctx, remoteHashOf) {
36
+ if (record.realm === 'global')
37
+ return { kind: 'global' };
38
+ const owner = record.ownerTenantId;
39
+ if (owner === ctx.tenantId) {
40
+ if (record.originId == null)
41
+ return { kind: 'native', ownerTenantId: owner };
42
+ const remote = remoteHashOf?.(record.originId) ?? null;
43
+ return { kind: 'own_override', ownerTenantId: owner, drift: driftState(record.originHash, record.contentHash, remote) };
44
+ }
45
+ const d = ownerDepth(ctx, owner);
46
+ return { kind: 'inherited', fromTenantId: owner, distance: d == null ? Infinity : ctx.depth - d };
47
+ }
48
+ /** The "closeness" score of a candidate: your own = deepest, ancestors shallower, global last. */
49
+ function nearness(record, ctx) {
50
+ if (record.realm === 'global')
51
+ return -Infinity;
52
+ return ownerDepth(ctx, record.ownerTenantId) ?? -Infinity;
53
+ }
54
+ /**
55
+ * Resolve every logical key in a candidate set to its single effective record. Input can be the whole
56
+ * family (all copies of all keys); the function filters to what `ctx` may see, then keeps the nearest
57
+ * owner per logical key, attaching provenance. `remoteHashOf` (optional) enables drift on own edits;
58
+ * by default it's derived from the candidate set (the global original usually sits right there).
59
+ */
60
+ export function resolveEffective(records, ctx, remoteHashOf) {
61
+ const hashById = new Map(records.map((r) => [r.id, r.contentHash]));
62
+ const lookupRemote = remoteHashOf ?? ((originId) => hashById.get(originId));
63
+ const bestByKey = new Map();
64
+ for (const r of records) {
65
+ if (!isVisible(r, ctx))
66
+ continue;
67
+ const cur = bestByKey.get(r.logicalKey);
68
+ if (cur == null) {
69
+ bestByKey.set(r.logicalKey, r);
70
+ continue;
71
+ }
72
+ const better = nearness(r, ctx) - nearness(cur, ctx);
73
+ // Tie-break deterministically by id so results are stable (ties shouldn't occur under the unique key).
74
+ if (better > 0 || (better === 0 && r.id < cur.id))
75
+ bestByKey.set(r.logicalKey, r);
76
+ }
77
+ const out = [];
78
+ for (const r of bestByKey.values()) {
79
+ out.push({ ...r, realmProvenance: provenanceOf(r, ctx, lookupRemote) });
80
+ }
81
+ out.sort((a, b) => (a.logicalKey < b.logicalKey ? -1 : a.logicalKey > b.logicalKey ? 1 : 0));
82
+ return out;
83
+ }
84
+ /** Resolve a single logical key, or null if the tenant can see no copy of it. */
85
+ export function resolveOne(records, logicalKey, ctx, remoteHashOf) {
86
+ const forKey = records.filter((r) => r.logicalKey === logicalKey);
87
+ return resolveEffective(forKey, ctx, remoteHashOf)[0] ?? null;
88
+ }
89
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@weaveintel/realm",
3
+ "version": "0.1.0",
4
+ "description": "Realm configuration — resolve the effective config record for a tenant across a global→tenant hierarchy, with provenance and git-style drift.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" } },
9
+ "files": ["dist", "!dist/**/*.test.*", "!dist/**/*.spec.*"],
10
+ "publishConfig": { "access": "public" },
11
+ "scripts": {
12
+ "build": "tsc -b",
13
+ "test": "vitest run",
14
+ "typecheck": "tsc --noEmit",
15
+ "clean": "rm -rf dist *.tsbuildinfo"
16
+ },
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "@weaveintel/core": "^0.1.1",
20
+ "@weaveintel/identity": "^0.1.1"
21
+ },
22
+ "devDependencies": {
23
+ "typescript": "^5.7.0",
24
+ "vitest": "^3.2.6",
25
+ "better-sqlite3": "^11.8.0",
26
+ "@types/better-sqlite3": "^7.6.11",
27
+ "pg": "^8.13.0",
28
+ "@types/pg": "^8.11.0",
29
+ "@testcontainers/postgresql": "^11.0.0"
30
+ }
31
+ }