@rolexjs/local-platform 0.9.1 → 0.11.0-dev.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.
package/dist/index.d.ts CHANGED
@@ -1,76 +1,83 @@
1
- import { Platform, Feature, OrganizationInfo, Duty, Assignment, PositionInfo, Goal, Plan, Task } from '@rolexjs/core';
1
+ import { Platform } from '@rolexjs/core';
2
+ import { State } from '@rolexjs/system';
2
3
 
3
4
  /**
4
- * LocalPlatformLocal filesystem implementation of Platform.
5
+ * localPlatformcreate a Platform backed by local filesystem.
5
6
  *
6
- * Everything lives under a single .rolex/ directory.
7
- * rolex.json is the single source of truth (CAS):
8
- * - roles: all born role names
9
- * - organizations: org configs with positions
10
- * - assignments: role → org/position mappings
7
+ * Storage layout:
8
+ * {dataDir}/
9
+ * role/<id>/
10
+ * individual.json — manifest (tree structure + links)
11
+ * <id>.<type>.feature — node information (Gherkin)
12
+ * organization/<id>/
13
+ * organization.json — manifest (tree structure + links)
14
+ * <id>.<type>.feature — node information (Gherkin)
11
15
  *
12
- * Directory convention:
13
- * <rootDir>/rolex.json
14
- * <rootDir>/roles/<role>/identity/*.identity.feature
15
- * <rootDir>/roles/<role>/goals/<name>/<name>.goal.feature
16
- * <rootDir>/roles/<role>/goals/<name>/<name>.plan.feature
17
- * <rootDir>/roles/<role>/goals/<name>/tasks/<name>.task.feature
18
- * <rootDir>/orgs/<org>/org.feature (optional)
19
- * <rootDir>/orgs/<org>/positions/<name>/<name>.position.feature
20
- * <rootDir>/orgs/<org>/positions/<name>/*.duty.feature
16
+ * In-memory: Map-based tree (same model as @rolexjs/system createRuntime).
17
+ * Persistence: loaded before every operation, saved after every mutation.
18
+ * Refs are stored in manifests to ensure stability across reload cycles.
19
+ * When dataDir is null, runs purely in-memory (useful for tests).
21
20
  */
22
21
 
23
- declare class LocalPlatform implements Platform {
24
- private readonly rootDir;
25
- private config;
26
- constructor(rootDir: string);
27
- allBornRoles(): string[];
28
- born(name: string, source: string): Feature;
29
- found(name: string, source?: string, parent?: string): void;
30
- getOrganization(name: string): OrganizationInfo | null;
31
- allOrganizations(): OrganizationInfo[];
32
- hire(roleId: string, orgName: string): void;
33
- fire(roleId: string, orgName: string): void;
34
- establish(positionName: string, source: string, orgName: string): void;
35
- appoint(roleId: string, positionName: string, orgName: string): void;
36
- dismiss(roleId: string): void;
37
- positionDuties(positionName: string, orgName: string): Duty[];
38
- getAssignment(roleId: string): Assignment | null;
39
- getPosition(positionName: string, orgName: string): PositionInfo | null;
40
- identity(roleId: string): Feature[];
41
- growup(roleId: string, type: "knowledge" | "experience" | "voice", name: string, source: string): Feature;
42
- reflect(roleId: string, experienceNames: string[], knowledgeName: string, knowledgeSource: string): Feature;
43
- activeGoal(roleId: string): (Goal & {
44
- plan: Plan | null;
45
- tasks: Task[];
46
- }) | null;
47
- allActiveGoals(roleId: string): Goal[];
48
- getFocusedGoal(roleId: string): string | null;
49
- setFocusedGoal(roleId: string, name: string): void;
50
- createGoal(roleId: string, name: string, source: string, testable?: boolean): Goal;
51
- createPlan(roleId: string, source: string): Plan;
52
- createTask(roleId: string, name: string, source: string, testable?: boolean): Task;
53
- completeGoal(roleId: string, experience?: string): void;
54
- abandonGoal(roleId: string, experience?: string): void;
55
- completeTask(roleId: string, name: string): void;
56
- /**
57
- * Load config — always returns a valid RolexConfig.
58
- * Creates default config if rolex.json doesn't exist yet.
59
- */
60
- private loadConfig;
61
- private saveConfig;
62
- private resolveRoleDir;
63
- private findPositionHolder;
64
- private loadGoalIfActive;
65
- private getActiveGoalDir;
66
- private toFeature;
67
- private extractScenarios;
68
- private loadPlan;
69
- private loadTasks;
70
- private detectIdentityType;
71
- private findFeatureFile;
72
- private addTag;
73
- private addDoneTag;
22
+ interface LocalPlatformConfig {
23
+ /** Directory for persistent storage. Defaults to ~/.deepractice/rolex. Set to null for in-memory only. */
24
+ dataDir?: string | null;
25
+ /** Directory for ResourceX storage. Defaults to ~/.deepractice/resourcex. Set to null to disable. */
26
+ resourceDir?: string | null;
74
27
  }
28
+ /** Create a local Platform. Persistent by default (~/.deepractice/rolex), in-memory if dataDir is null. */
29
+ declare function localPlatform(config?: LocalPlatformConfig): Platform;
75
30
 
76
- export { LocalPlatform };
31
+ /**
32
+ * Manifest — file-based storage format for RoleX entities.
33
+ *
34
+ * Storage layout:
35
+ * role/<id>/
36
+ * individual.json — manifest (tree structure + links)
37
+ * <id>.<type>.feature — node information (Gherkin)
38
+ *
39
+ * organization/<id>/
40
+ * organization.json — manifest (tree structure + links)
41
+ * <id>.<type>.feature — node information (Gherkin)
42
+ *
43
+ * Rules:
44
+ * - Directories: only role/ and organization/ at top level
45
+ * - Files: all [id].[type].feature, flat within the entity directory
46
+ * - Manifest: tree structure in JSON, content in .feature files
47
+ * - Nodes without explicit id default to their type name
48
+ */
49
+
50
+ /** A node in the manifest tree. */
51
+ interface ManifestNode {
52
+ readonly type: string;
53
+ readonly ref?: string;
54
+ readonly children?: Record<string, ManifestNode>;
55
+ }
56
+ /** Root manifest for an entity (individual or organization). */
57
+ interface Manifest {
58
+ readonly id: string;
59
+ readonly type: string;
60
+ readonly ref?: string;
61
+ readonly alias?: readonly string[];
62
+ readonly children?: Record<string, ManifestNode>;
63
+ readonly links?: Record<string, string[]>;
64
+ }
65
+ interface FileEntry {
66
+ readonly path: string;
67
+ readonly content: string;
68
+ }
69
+ /**
70
+ * Convert a State tree to a manifest + feature files.
71
+ * Returns the manifest and a list of file entries (path → content).
72
+ */
73
+ declare function stateToFiles(state: State): {
74
+ manifest: Manifest;
75
+ files: FileEntry[];
76
+ };
77
+ /**
78
+ * Convert a manifest + feature file contents to a State tree.
79
+ * fileContents maps filename (e.g. "role-creation.principle.feature") to Gherkin text.
80
+ */
81
+ declare function filesToState(manifest: Manifest, fileContents: Record<string, string>): State;
82
+
83
+ export { type FileEntry, type LocalPlatformConfig, type Manifest, type ManifestNode, filesToState, localPlatform, stateToFiles };
package/dist/index.js CHANGED
@@ -1,476 +1,448 @@
1
1
  // src/LocalPlatform.ts
2
- import { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from "fs";
3
- import { join, basename } from "path";
4
- import { parse } from "@rolexjs/parser";
5
- import {
6
- getRoleState,
7
- getPositionState,
8
- transition,
9
- ROLE_MACHINE,
10
- POSITION_MACHINE
11
- } from "@rolexjs/core";
12
- var LocalPlatform = class {
13
- rootDir;
14
- config = null;
15
- constructor(rootDir) {
16
- this.rootDir = rootDir;
17
- }
18
- // ========== Society ==========
19
- allBornRoles() {
20
- return this.loadConfig().roles;
21
- }
22
- born(name, source) {
23
- const roleDir = join(this.rootDir, "roles", name);
24
- mkdirSync(join(roleDir, "identity"), { recursive: true });
25
- mkdirSync(join(roleDir, "goals"), { recursive: true });
26
- const filePath = join(roleDir, "identity", "persona.identity.feature");
27
- writeFileSync(filePath, source, "utf-8");
28
- const config = this.loadConfig();
29
- if (!config.roles.includes(name)) {
30
- config.roles.push(name);
31
- this.saveConfig(config);
32
- }
33
- const doc = parse(source);
34
- return this.toFeature(doc.feature, "persona");
35
- }
36
- found(name, source, parent) {
37
- const config = this.loadConfig();
38
- if (config.organizations[name]) {
39
- throw new Error(`Organization already exists: ${name}`);
40
- }
41
- if (parent && !config.organizations[parent]) {
42
- throw new Error(`Parent organization not found: ${parent}`);
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
3
+ import { homedir } from "os";
4
+ import { join } from "path";
5
+ import { NodeProvider } from "@resourcexjs/node-provider";
6
+ import { organizationType, roleType } from "@rolexjs/resourcex-types";
7
+ import { createResourceX, setProvider } from "resourcexjs";
8
+
9
+ // src/manifest.ts
10
+ function stateToFiles(state) {
11
+ const files = [];
12
+ const collectFiles = (node, nodeId) => {
13
+ if (node.information) {
14
+ files.push({
15
+ path: `${nodeId}.${node.name}.feature`,
16
+ content: node.information
17
+ });
43
18
  }
44
- const orgDir = join(this.rootDir, "orgs", name);
45
- mkdirSync(orgDir, { recursive: true });
46
- if (source) {
47
- writeFileSync(join(orgDir, "org.feature"), source, "utf-8");
19
+ if (node.children) {
20
+ for (const child of node.children) {
21
+ const childId = child.id ?? child.name;
22
+ collectFiles(child, childId);
23
+ }
48
24
  }
49
- config.organizations[name] = {
50
- parent,
51
- positions: []
25
+ };
26
+ const rootId = state.id ?? state.name;
27
+ collectFiles(state, rootId);
28
+ const buildManifestNode = (node) => {
29
+ const entry = {
30
+ type: node.name,
31
+ ...node.ref ? { ref: node.ref } : {}
52
32
  };
53
- this.saveConfig(config);
54
- }
55
- getOrganization(name) {
56
- const config = this.loadConfig();
57
- const orgConfig = config.organizations[name];
58
- if (!orgConfig) return null;
59
- const members = Object.entries(config.assignments).filter(([, a]) => a.org === name).map(([role]) => role);
60
- return {
61
- name,
62
- parent: orgConfig.parent,
63
- positions: orgConfig.positions,
64
- members
65
- };
66
- }
67
- allOrganizations() {
68
- const config = this.loadConfig();
69
- return Object.keys(config.organizations).map((name) => this.getOrganization(name));
70
- }
71
- // ========== Organization ==========
72
- hire(roleId, orgName) {
73
- const config = this.loadConfig();
74
- if (!config.roles.includes(roleId)) {
75
- throw new Error(`Role not found: ${roleId}. Call born() first.`);
76
- }
77
- if (!config.organizations[orgName]) {
78
- throw new Error(`Organization not found: ${orgName}. Call found() first.`);
33
+ if (node.children && node.children.length > 0) {
34
+ const children = {};
35
+ for (const child of node.children) {
36
+ const childId = child.id ?? child.name;
37
+ children[childId] = buildManifestNode(child);
38
+ }
39
+ return { ...entry, children };
79
40
  }
80
- const assignment = config.assignments[roleId] ?? null;
81
- const currentState = getRoleState(assignment);
82
- transition(ROLE_MACHINE, currentState, "hire");
83
- if (assignment && assignment.org !== orgName) {
84
- throw new Error(`Role "${roleId}" is already hired in organization "${assignment.org}"`);
41
+ return entry;
42
+ };
43
+ const manifestNode = buildManifestNode(state);
44
+ const manifest = {
45
+ id: rootId,
46
+ type: state.name,
47
+ ...state.ref ? { ref: state.ref } : {},
48
+ ...state.alias ? { alias: state.alias } : {},
49
+ ...manifestNode.children ? { children: manifestNode.children } : {},
50
+ ...state.links && state.links.length > 0 ? {
51
+ links: buildManifestLinks(state.links)
52
+ } : {}
53
+ };
54
+ return { manifest, files };
55
+ }
56
+ function buildManifestLinks(links) {
57
+ const result = {};
58
+ for (const link of links) {
59
+ const targetId = link.target.id ?? link.target.name;
60
+ if (!result[link.relation]) {
61
+ result[link.relation] = [];
85
62
  }
86
- config.assignments[roleId] = { org: orgName };
87
- this.saveConfig(config);
88
- }
89
- fire(roleId, orgName) {
90
- const config = this.loadConfig();
91
- const assignment = config.assignments[roleId];
92
- if (!assignment || assignment.org !== orgName) {
93
- throw new Error(`Role "${roleId}" is not hired in organization "${orgName}"`);
63
+ result[link.relation].push(targetId);
64
+ }
65
+ return result;
66
+ }
67
+ function filesToState(manifest, fileContents) {
68
+ const buildState = (id, node) => {
69
+ const filename = `${id}.${node.type}.feature`;
70
+ const information = fileContents[filename];
71
+ const children2 = [];
72
+ if (node.children) {
73
+ for (const [childId, childNode] of Object.entries(node.children)) {
74
+ children2.push(buildState(childId, childNode));
75
+ }
94
76
  }
95
- const currentState = getRoleState(assignment);
96
- transition(ROLE_MACHINE, currentState, "fire");
97
- if (assignment.position) {
77
+ return {
78
+ ...node.ref ? { ref: node.ref } : {},
79
+ id,
80
+ name: node.type,
81
+ description: "",
82
+ parent: null,
83
+ ...information ? { information } : {},
84
+ ...children2.length > 0 ? { children: children2 } : {}
85
+ };
86
+ };
87
+ const rootFilename = `${manifest.id}.${manifest.type}.feature`;
88
+ const rootInformation = fileContents[rootFilename];
89
+ const children = [];
90
+ if (manifest.children) {
91
+ for (const [childId, childNode] of Object.entries(manifest.children)) {
92
+ children.push(buildState(childId, childNode));
98
93
  }
99
- delete config.assignments[roleId];
100
- this.saveConfig(config);
101
94
  }
102
- establish(positionName, source, orgName) {
103
- const config = this.loadConfig();
104
- if (!config.organizations[orgName]) {
105
- throw new Error(`Organization not found: ${orgName}. Call found() first.`);
106
- }
107
- if (config.organizations[orgName].positions.includes(positionName)) {
108
- throw new Error(`Position "${positionName}" already exists in organization "${orgName}"`);
95
+ const links = [];
96
+ if (manifest.links) {
97
+ for (const [relation, targetIds] of Object.entries(manifest.links)) {
98
+ for (const targetId of targetIds) {
99
+ links.push({
100
+ relation,
101
+ target: {
102
+ id: targetId,
103
+ name: "",
104
+ description: "",
105
+ parent: null
106
+ }
107
+ });
108
+ }
109
109
  }
110
- const posDir = join(this.rootDir, "orgs", orgName, "positions", positionName);
111
- mkdirSync(posDir, { recursive: true });
112
- writeFileSync(join(posDir, `${positionName}.position.feature`), source, "utf-8");
113
- config.organizations[orgName].positions.push(positionName);
114
- this.saveConfig(config);
115
110
  }
116
- appoint(roleId, positionName, orgName) {
117
- const config = this.loadConfig();
118
- const assignment = config.assignments[roleId];
119
- if (!assignment || assignment.org !== orgName) {
120
- throw new Error(`Role "${roleId}" is not a member of organization "${orgName}". Hire first.`);
121
- }
122
- if (!config.organizations[orgName]?.positions.includes(positionName)) {
123
- throw new Error(`Position "${positionName}" not found in organization "${orgName}"`);
111
+ return {
112
+ ...manifest.ref ? { ref: manifest.ref } : {},
113
+ id: manifest.id,
114
+ ...manifest.alias ? { alias: manifest.alias } : {},
115
+ name: manifest.type,
116
+ description: "",
117
+ parent: null,
118
+ ...rootInformation ? { information: rootInformation } : {},
119
+ ...children.length > 0 ? { children } : {},
120
+ ...links.length > 0 ? { links } : {}
121
+ };
122
+ }
123
+
124
+ // src/LocalPlatform.ts
125
+ function localPlatform(config = {}) {
126
+ const dataDir = config.dataDir === null ? void 0 : config.dataDir ?? join(homedir(), ".deepractice", "rolex");
127
+ const nodes = /* @__PURE__ */ new Map();
128
+ const links = /* @__PURE__ */ new Map();
129
+ let counter = 0;
130
+ const nextRef = () => `n${++counter}`;
131
+ const findByStructure = (s) => {
132
+ for (const treeNode of nodes.values()) {
133
+ if (treeNode.node.name === s.name) return treeNode;
124
134
  }
125
- const roleState = getRoleState(assignment);
126
- transition(ROLE_MACHINE, roleState, "appoint");
127
- const currentHolder = this.findPositionHolder(config, orgName, positionName);
128
- const posState = getPositionState(currentHolder);
129
- transition(POSITION_MACHINE, posState, "appoint");
130
- config.assignments[roleId] = { org: orgName, position: positionName };
131
- this.saveConfig(config);
132
- }
133
- dismiss(roleId) {
134
- const config = this.loadConfig();
135
- const assignment = config.assignments[roleId];
136
- if (!assignment || !assignment.position) {
137
- throw new Error(`Role "${roleId}" is not appointed to any position`);
135
+ return void 0;
136
+ };
137
+ const removeSubtree = (ref) => {
138
+ const treeNode = nodes.get(ref);
139
+ if (!treeNode) return;
140
+ for (const childRef of [...treeNode.children]) {
141
+ removeSubtree(childRef);
138
142
  }
139
- const roleState = getRoleState(assignment);
140
- transition(ROLE_MACHINE, roleState, "dismiss");
141
- config.assignments[roleId] = { org: assignment.org };
142
- this.saveConfig(config);
143
- }
144
- positionDuties(positionName, orgName) {
145
- const posDir = join(this.rootDir, "orgs", orgName, "positions", positionName);
146
- if (!existsSync(posDir)) return [];
147
- return readdirSync(posDir).filter((f) => f.endsWith(".duty.feature")).sort().map((f) => {
148
- const source = readFileSync(join(posDir, f), "utf-8");
149
- const doc = parse(source);
150
- return this.toFeature(doc.feature, "duty");
151
- });
152
- }
153
- getAssignment(roleId) {
154
- const config = this.loadConfig();
155
- return config.assignments[roleId] ?? null;
156
- }
157
- getPosition(positionName, orgName) {
158
- const config = this.loadConfig();
159
- if (!config.organizations[orgName]?.positions.includes(positionName)) {
160
- return null;
143
+ links.delete(ref);
144
+ for (const [fromRef, fromLinks] of links.entries()) {
145
+ const filtered = fromLinks.filter((l) => l.toId !== ref);
146
+ if (filtered.length === 0) {
147
+ links.delete(fromRef);
148
+ } else {
149
+ links.set(fromRef, filtered);
150
+ }
161
151
  }
162
- const holder = this.findPositionHolder(config, orgName, positionName);
163
- const duties = this.positionDuties(positionName, orgName);
152
+ nodes.delete(ref);
153
+ };
154
+ const projectRef = (ref) => {
155
+ const treeNode = nodes.get(ref);
156
+ return { ...treeNode.node, children: [] };
157
+ };
158
+ const projectNode = (ref) => {
159
+ const treeNode = nodes.get(ref);
160
+ const nodeLinks = links.get(ref);
164
161
  return {
165
- name: positionName,
166
- org: orgName,
167
- state: getPositionState(holder),
168
- assignedRole: holder,
169
- duties
162
+ ...treeNode.node,
163
+ children: treeNode.children.map(projectNode),
164
+ ...nodeLinks && nodeLinks.length > 0 ? {
165
+ links: nodeLinks.map((l) => ({
166
+ relation: l.relation,
167
+ target: projectRef(l.toId)
168
+ }))
169
+ } : {}
170
170
  };
171
- }
172
- // ========== Role Identity ==========
173
- identity(roleId) {
174
- const roleDir = this.resolveRoleDir(roleId);
175
- const dir = join(roleDir, "identity");
176
- if (!existsSync(dir)) return [];
177
- const features = readdirSync(dir).filter((f) => f.endsWith(".identity.feature")).sort().map((f) => {
178
- const source = readFileSync(join(dir, f), "utf-8");
179
- const doc = parse(source);
180
- return this.toFeature(doc.feature, this.detectIdentityType(f));
181
- });
182
- const assignment = this.getAssignment(roleId);
183
- if (assignment?.position) {
184
- const duties = this.positionDuties(assignment.position, assignment.org);
185
- features.push(...duties);
171
+ };
172
+ const createNode = (parentRef, type, information, id, alias) => {
173
+ const ref = nextRef();
174
+ const node = {
175
+ ref,
176
+ ...id ? { id } : {},
177
+ ...alias && alias.length > 0 ? { alias } : {},
178
+ name: type.name,
179
+ description: type.description,
180
+ parent: type.parent,
181
+ information
182
+ };
183
+ const treeNode = { node, parent: parentRef, children: [] };
184
+ nodes.set(ref, treeNode);
185
+ if (parentRef) {
186
+ const parentTreeNode = nodes.get(parentRef);
187
+ if (!parentTreeNode) throw new Error(`Parent not found: ${parentRef}`);
188
+ parentTreeNode.children.push(ref);
186
189
  }
187
- return features;
188
- }
189
- // ========== Growup ==========
190
- growup(roleId, type, name, source) {
191
- const roleDir = this.resolveRoleDir(roleId);
192
- const dir = join(roleDir, "identity");
193
- mkdirSync(dir, { recursive: true });
194
- const filePath = join(dir, `${name}.${type}.identity.feature`);
195
- writeFileSync(filePath, source, "utf-8");
196
- const doc = parse(source);
197
- return this.toFeature(doc.feature, type);
198
- }
199
- // ========== Reflect ==========
200
- reflect(roleId, experienceNames, knowledgeName, knowledgeSource) {
201
- if (experienceNames.length === 0) {
202
- throw new Error("At least one experience required");
190
+ return node;
191
+ };
192
+ const useRef = (storedRef) => {
193
+ const n = parseInt(storedRef.slice(1), 10);
194
+ if (!Number.isNaN(n) && n > counter) counter = n;
195
+ return storedRef;
196
+ };
197
+ const replayState = (state, parentRef) => {
198
+ const ref = state.ref ? useRef(state.ref) : nextRef();
199
+ const node = {
200
+ ref,
201
+ ...state.id ? { id: state.id } : {},
202
+ ...state.alias ? { alias: state.alias } : {},
203
+ name: state.name,
204
+ description: state.description ?? "",
205
+ parent: null,
206
+ ...state.information ? { information: state.information } : {}
207
+ };
208
+ const treeNode = { node, parent: parentRef, children: [] };
209
+ nodes.set(ref, treeNode);
210
+ if (parentRef) {
211
+ nodes.get(parentRef).children.push(ref);
203
212
  }
204
- const roleDir = this.resolveRoleDir(roleId);
205
- const identityDir = join(roleDir, "identity");
206
- const expFiles = [];
207
- for (const expName of experienceNames) {
208
- if (expName.includes("/") || expName.includes("\\") || expName.includes("..")) {
209
- throw new Error(`Invalid experience name: ${expName}`);
213
+ if (state.children) {
214
+ for (const child of state.children) {
215
+ replayState(child, ref);
210
216
  }
211
- const expFile = join(identityDir, `${expName}.experience.identity.feature`);
212
- if (!existsSync(expFile)) {
213
- throw new Error(`Experience not found: ${expName}`);
214
- }
215
- expFiles.push(expFile);
216
- }
217
- const feature = this.growup(roleId, "knowledge", knowledgeName, knowledgeSource);
218
- for (const expFile of expFiles) {
219
- rmSync(expFile);
220
- }
221
- return feature;
222
- }
223
- // ========== Goals ==========
224
- activeGoal(roleId) {
225
- const roleDir = this.resolveRoleDir(roleId);
226
- const goalsDir = join(roleDir, "goals");
227
- if (!existsSync(goalsDir)) return null;
228
- const focusedName = this.getFocusedGoal(roleId);
229
- if (focusedName) {
230
- const goalDir = join(goalsDir, focusedName);
231
- const result = this.loadGoalIfActive(goalDir);
232
- if (result) return result;
233
- }
234
- const goalDirs = readdirSync(goalsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
235
- for (const goalName of goalDirs) {
236
- const goalDir = join(goalsDir, goalName);
237
- const result = this.loadGoalIfActive(goalDir);
238
- if (result) return result;
239
217
  }
240
- return null;
241
- }
242
- allActiveGoals(roleId) {
243
- const roleDir = this.resolveRoleDir(roleId);
244
- const goalsDir = join(roleDir, "goals");
245
- if (!existsSync(goalsDir)) return [];
246
- const goalDirs = readdirSync(goalsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
247
- const goals = [];
248
- for (const goalName of goalDirs) {
249
- const goalDir = join(goalsDir, goalName);
250
- const goalFile = this.findFeatureFile(goalDir, ".goal.feature");
251
- if (!goalFile) continue;
252
- const source = readFileSync(goalFile, "utf-8");
253
- const doc = parse(source);
254
- if (!doc.feature) continue;
255
- if (doc.feature.tags.some((t) => t.name === "@done" || t.name === "@abandoned")) continue;
256
- goals.push(this.toFeature(doc.feature, "goal"));
257
- }
258
- return goals;
259
- }
260
- getFocusedGoal(roleId) {
261
- const roleDir = this.resolveRoleDir(roleId);
262
- const focusFile = join(roleDir, "goals", ".focus");
263
- if (!existsSync(focusFile)) return null;
264
- return readFileSync(focusFile, "utf-8").trim() || null;
265
- }
266
- setFocusedGoal(roleId, name) {
267
- const roleDir = this.resolveRoleDir(roleId);
268
- const goalsDir = join(roleDir, "goals");
269
- const goalDir = join(goalsDir, name);
270
- if (!existsSync(goalDir)) {
271
- throw new Error(`Goal not found: ${name}`);
218
+ return ref;
219
+ };
220
+ const loadEntitiesFrom = (dir, manifestName, parentRef) => {
221
+ const results = [];
222
+ if (!existsSync(dir)) return results;
223
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
224
+ if (!entry.isDirectory()) continue;
225
+ const entityDir = join(dir, entry.name);
226
+ const manifestPath = join(entityDir, manifestName);
227
+ if (!existsSync(manifestPath)) continue;
228
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
229
+ const featureFiles = {};
230
+ for (const file of readdirSync(entityDir)) {
231
+ if (file.endsWith(".feature")) {
232
+ featureFiles[file] = readFileSync(join(entityDir, file), "utf-8");
233
+ }
234
+ }
235
+ const state = filesToState(manifest, featureFiles);
236
+ const entityRef = replayState(state, parentRef);
237
+ results.push({ ref: entityRef, manifest });
272
238
  }
273
- writeFileSync(join(goalsDir, ".focus"), name, "utf-8");
274
- }
275
- // ========== Write ==========
276
- createGoal(roleId, name, source, testable) {
277
- const roleDir = this.resolveRoleDir(roleId);
278
- const goalDir = join(roleDir, "goals", name);
279
- mkdirSync(goalDir, { recursive: true });
280
- if (testable) source = `@testable
281
- ${source}`;
282
- const filePath = join(goalDir, `${name}.goal.feature`);
283
- writeFileSync(filePath, source, "utf-8");
284
- const doc = parse(source);
285
- return this.toFeature(doc.feature, "goal");
286
- }
287
- createPlan(roleId, source) {
288
- const roleDir = this.resolveRoleDir(roleId);
289
- const goalDir = this.getActiveGoalDir(roleDir);
290
- if (!goalDir) throw new Error("No active goal");
291
- const goalName = basename(goalDir);
292
- const filePath = join(goalDir, `${goalName}.plan.feature`);
293
- writeFileSync(filePath, source, "utf-8");
294
- const doc = parse(source);
295
- return this.toFeature(doc.feature, "plan");
296
- }
297
- createTask(roleId, name, source, testable) {
298
- const roleDir = this.resolveRoleDir(roleId);
299
- const goalDir = this.getActiveGoalDir(roleDir);
300
- if (!goalDir) throw new Error("No active goal");
301
- const tasksDir = join(goalDir, "tasks");
302
- mkdirSync(tasksDir, { recursive: true });
303
- if (testable) source = `@testable
304
- ${source}`;
305
- const filePath = join(tasksDir, `${name}.task.feature`);
306
- writeFileSync(filePath, source, "utf-8");
307
- const doc = parse(source);
308
- return this.toFeature(doc.feature, "task");
309
- }
310
- // ========== Close ==========
311
- completeGoal(roleId, experience) {
312
- const roleDir = this.resolveRoleDir(roleId);
313
- const goalDir = this.getActiveGoalDir(roleDir);
314
- if (!goalDir) throw new Error("No active goal");
315
- const goalFile = this.findFeatureFile(goalDir, ".goal.feature");
316
- this.addDoneTag(goalFile);
317
- if (experience) {
318
- const goalName = basename(goalDir);
319
- this.growup(roleId, "experience", goalName, experience);
239
+ return results;
240
+ };
241
+ const load = () => {
242
+ if (!dataDir) return;
243
+ nodes.clear();
244
+ links.clear();
245
+ counter = 0;
246
+ const societyRef = nextRef();
247
+ nodes.set(societyRef, {
248
+ node: {
249
+ ref: societyRef,
250
+ name: "society",
251
+ description: "",
252
+ parent: null
253
+ },
254
+ parent: null,
255
+ children: []
256
+ });
257
+ const entityRefs = [
258
+ ...loadEntitiesFrom(join(dataDir, "role"), "individual.json", societyRef),
259
+ ...loadEntitiesFrom(join(dataDir, "organization"), "organization.json", societyRef)
260
+ ];
261
+ const idToRef = /* @__PURE__ */ new Map();
262
+ for (const [ref, treeNode] of nodes) {
263
+ if (treeNode.node.id) {
264
+ idToRef.set(treeNode.node.id, ref);
265
+ }
320
266
  }
321
- }
322
- abandonGoal(roleId, experience) {
323
- const roleDir = this.resolveRoleDir(roleId);
324
- const goalDir = this.getActiveGoalDir(roleDir);
325
- if (!goalDir) throw new Error("No active goal");
326
- const goalFile = this.findFeatureFile(goalDir, ".goal.feature");
327
- this.addTag(goalFile, "@abandoned");
328
- if (experience) {
329
- const goalName = basename(goalDir);
330
- this.growup(roleId, "experience", goalName, experience);
267
+ for (const { ref, manifest } of entityRefs) {
268
+ if (!manifest.links) continue;
269
+ const entityLinks = [];
270
+ for (const [relation, targetIds] of Object.entries(manifest.links)) {
271
+ for (const targetId of targetIds) {
272
+ const targetRef = idToRef.get(targetId);
273
+ if (targetRef) {
274
+ entityLinks.push({ toId: targetRef, relation });
275
+ }
276
+ }
277
+ }
278
+ if (entityLinks.length > 0) {
279
+ links.set(ref, entityLinks);
280
+ }
331
281
  }
332
- }
333
- completeTask(roleId, name) {
334
- const roleDir = this.resolveRoleDir(roleId);
335
- const goalDir = this.getActiveGoalDir(roleDir);
336
- if (!goalDir) throw new Error("No active goal");
337
- const tasksDir = join(goalDir, "tasks");
338
- const taskFile = join(tasksDir, `${name}.task.feature`);
339
- if (!existsSync(taskFile)) throw new Error(`Task not found: ${name}`);
340
- this.addDoneTag(taskFile);
341
- }
342
- // ========== Internal ==========
343
- /**
344
- * Load config — always returns a valid RolexConfig.
345
- * Creates default config if rolex.json doesn't exist yet.
346
- */
347
- loadConfig() {
348
- if (this.config) return this.config;
349
- const configPath = join(this.rootDir, "rolex.json");
350
- if (existsSync(configPath)) {
351
- const raw = JSON.parse(readFileSync(configPath, "utf-8"));
352
- this.config = {
353
- roles: raw.roles ?? [],
354
- organizations: raw.organizations ?? {},
355
- assignments: raw.assignments ?? {}
356
- };
357
- return this.config;
282
+ };
283
+ const saveEntity = (baseDir, entityId, manifestName, state) => {
284
+ const entityDir = join(baseDir, entityId);
285
+ mkdirSync(entityDir, { recursive: true });
286
+ const { manifest, files } = stateToFiles(state);
287
+ writeFileSync(join(entityDir, manifestName), JSON.stringify(manifest, null, 2), "utf-8");
288
+ for (const file of files) {
289
+ writeFileSync(join(entityDir, file.path), file.content, "utf-8");
358
290
  }
359
- mkdirSync(this.rootDir, { recursive: true });
360
- const config = { roles: [], organizations: {}, assignments: {} };
361
- this.saveConfig(config);
362
- return config;
363
- }
364
- saveConfig(config) {
365
- mkdirSync(this.rootDir, { recursive: true });
366
- writeFileSync(join(this.rootDir, "rolex.json"), JSON.stringify(config, null, 2), "utf-8");
367
- this.config = config;
368
- }
369
- resolveRoleDir(roleId) {
370
- const roleDir = join(this.rootDir, "roles", roleId);
371
- if (!existsSync(roleDir)) {
372
- throw new Error(`Role directory not found: ${roleDir}`);
291
+ };
292
+ const save = () => {
293
+ if (!dataDir) return;
294
+ mkdirSync(dataDir, { recursive: true });
295
+ let societyTreeNode;
296
+ for (const treeNode of nodes.values()) {
297
+ if (treeNode.parent === null && treeNode.node.name === "society") {
298
+ societyTreeNode = treeNode;
299
+ break;
300
+ }
373
301
  }
374
- return roleDir;
375
- }
376
- findPositionHolder(config, orgName, positionName) {
377
- for (const [role, assignment] of Object.entries(config.assignments)) {
378
- if (assignment.org === orgName && assignment.position === positionName) {
379
- return role;
302
+ if (!societyTreeNode) return;
303
+ const roleDir = join(dataDir, "role");
304
+ const orgDir = join(dataDir, "organization");
305
+ if (existsSync(roleDir)) rmSync(roleDir, { recursive: true });
306
+ if (existsSync(orgDir)) rmSync(orgDir, { recursive: true });
307
+ for (const childRef of societyTreeNode.children) {
308
+ if (!nodes.has(childRef)) continue;
309
+ const state = projectNode(childRef);
310
+ const entityId = state.id ?? state.name;
311
+ if (state.name === "individual") {
312
+ saveEntity(roleDir, entityId, "individual.json", state);
313
+ } else if (state.name === "organization") {
314
+ saveEntity(orgDir, entityId, "organization.json", state);
380
315
  }
381
316
  }
382
- return null;
383
- }
384
- loadGoalIfActive(goalDir) {
385
- const goalFile = this.findFeatureFile(goalDir, ".goal.feature");
386
- if (!goalFile) return null;
387
- const source = readFileSync(goalFile, "utf-8");
388
- const doc = parse(source);
389
- if (!doc.feature) return null;
390
- if (doc.feature.tags.some((t) => t.name === "@done" || t.name === "@abandoned")) return null;
391
- const goal = this.toFeature(doc.feature, "goal");
392
- return {
393
- ...goal,
394
- plan: this.loadPlan(goalDir),
395
- tasks: this.loadTasks(goalDir)
396
- };
397
- }
398
- getActiveGoalDir(roleDir) {
399
- const goalsDir = join(roleDir, "goals");
400
- if (!existsSync(goalsDir)) return null;
401
- const focusFile = join(goalsDir, ".focus");
402
- if (existsSync(focusFile)) {
403
- const focusedName = readFileSync(focusFile, "utf-8").trim();
404
- if (focusedName) {
405
- const focusedDir = join(goalsDir, focusedName);
406
- if (existsSync(focusedDir) && this.loadGoalIfActive(focusedDir)) {
407
- return focusedDir;
317
+ };
318
+ const runtime = {
319
+ create(parent, type, information, id, alias) {
320
+ load();
321
+ const node = createNode(parent?.ref ?? null, type, information, id, alias);
322
+ save();
323
+ return node;
324
+ },
325
+ remove(node) {
326
+ load();
327
+ if (!node.ref) return;
328
+ const treeNode = nodes.get(node.ref);
329
+ if (!treeNode) return;
330
+ if (treeNode.parent) {
331
+ const parentTreeNode = nodes.get(treeNode.parent);
332
+ if (parentTreeNode) {
333
+ parentTreeNode.children = parentTreeNode.children.filter((r) => r !== node.ref);
408
334
  }
409
335
  }
410
- }
411
- const goalDirs = readdirSync(goalsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
412
- for (const goalName of goalDirs) {
413
- const goalDir = join(goalsDir, goalName);
414
- if (this.loadGoalIfActive(goalDir)) {
415
- return goalDir;
336
+ removeSubtree(node.ref);
337
+ save();
338
+ },
339
+ transform(_source, target, information) {
340
+ load();
341
+ const targetParent = target.parent;
342
+ if (!targetParent) {
343
+ throw new Error(`Cannot transform to root structure: ${target.name}`);
416
344
  }
345
+ const parentTreeNode = findByStructure(targetParent);
346
+ if (!parentTreeNode) {
347
+ throw new Error(`No node found for structure: ${targetParent.name}`);
348
+ }
349
+ const node = createNode(parentTreeNode.node.ref, target, information);
350
+ save();
351
+ return node;
352
+ },
353
+ link(from, to, relationName, reverseName) {
354
+ load();
355
+ if (!from.ref) throw new Error("Source node has no ref");
356
+ if (!to.ref) throw new Error("Target node has no ref");
357
+ const fromLinks = links.get(from.ref) ?? [];
358
+ if (!fromLinks.some((l) => l.toId === to.ref && l.relation === relationName)) {
359
+ fromLinks.push({ toId: to.ref, relation: relationName });
360
+ links.set(from.ref, fromLinks);
361
+ }
362
+ const toLinks = links.get(to.ref) ?? [];
363
+ if (!toLinks.some((l) => l.toId === from.ref && l.relation === reverseName)) {
364
+ toLinks.push({ toId: from.ref, relation: reverseName });
365
+ links.set(to.ref, toLinks);
366
+ }
367
+ save();
368
+ },
369
+ unlink(from, to, relationName, reverseName) {
370
+ load();
371
+ if (!from.ref || !to.ref) return;
372
+ const fromLinks = links.get(from.ref);
373
+ if (fromLinks) {
374
+ const filtered = fromLinks.filter(
375
+ (l) => !(l.toId === to.ref && l.relation === relationName)
376
+ );
377
+ if (filtered.length === 0) links.delete(from.ref);
378
+ else links.set(from.ref, filtered);
379
+ }
380
+ const toLinks = links.get(to.ref);
381
+ if (toLinks) {
382
+ const filtered = toLinks.filter(
383
+ (l) => !(l.toId === from.ref && l.relation === reverseName)
384
+ );
385
+ if (filtered.length === 0) links.delete(to.ref);
386
+ else links.set(to.ref, filtered);
387
+ }
388
+ save();
389
+ },
390
+ project(node) {
391
+ load();
392
+ if (!node.ref || !nodes.has(node.ref)) {
393
+ throw new Error(`Node not found: ${node.ref}`);
394
+ }
395
+ return projectNode(node.ref);
396
+ },
397
+ roots() {
398
+ load();
399
+ const result = [];
400
+ for (const treeNode of nodes.values()) {
401
+ if (treeNode.parent === null) {
402
+ result.push(treeNode.node);
403
+ }
404
+ }
405
+ return result;
417
406
  }
418
- return null;
419
- }
420
- toFeature(gherkin, type) {
421
- return {
422
- ...gherkin,
423
- type,
424
- scenarios: this.extractScenarios(gherkin)
425
- };
426
- }
427
- extractScenarios(feature) {
428
- const featureTestable = feature.tags.some((t) => t.name === "@testable");
429
- return (feature.children || []).filter((c) => c.scenario).map((c) => ({
430
- ...c.scenario,
431
- verifiable: featureTestable || c.scenario.tags.some((t) => t.name === "@testable")
432
- }));
433
- }
434
- loadPlan(goalDir) {
435
- const planFile = this.findFeatureFile(goalDir, ".plan.feature");
436
- if (!planFile) return null;
437
- const source = readFileSync(planFile, "utf-8");
438
- const doc = parse(source);
439
- if (!doc.feature) return null;
440
- return this.toFeature(doc.feature, "plan");
441
- }
442
- loadTasks(goalDir) {
443
- const tasksDir = join(goalDir, "tasks");
444
- if (!existsSync(tasksDir)) return [];
445
- return readdirSync(tasksDir).filter((f) => f.endsWith(".task.feature")).sort().map((f) => {
446
- const source = readFileSync(join(tasksDir, f), "utf-8");
447
- const doc = parse(source);
448
- return this.toFeature(doc.feature, "task");
407
+ };
408
+ let resourcex;
409
+ if (config.resourceDir !== null) {
410
+ setProvider(new NodeProvider());
411
+ resourcex = createResourceX({
412
+ path: config.resourceDir ?? join(homedir(), ".deepractice", "resourcex"),
413
+ types: [roleType, organizationType]
449
414
  });
450
415
  }
451
- detectIdentityType(filename) {
452
- if (filename === "persona.identity.feature") return "persona";
453
- if (filename.endsWith(".knowledge.identity.feature")) return "knowledge";
454
- if (filename.endsWith(".experience.identity.feature")) return "experience";
455
- if (filename.endsWith(".voice.identity.feature")) return "voice";
456
- return "knowledge";
457
- }
458
- findFeatureFile(dir, suffix) {
459
- if (!existsSync(dir)) return null;
460
- const file = readdirSync(dir).find((f) => f.endsWith(suffix));
461
- return file ? join(dir, file) : null;
462
- }
463
- addTag(filePath, tag) {
464
- const content = readFileSync(filePath, "utf-8");
465
- const updated = content.replace(/^(Feature:)/m, `${tag}
466
- $1`);
467
- writeFileSync(filePath, updated, "utf-8");
468
- }
469
- addDoneTag(filePath) {
470
- this.addTag(filePath, "@done");
471
- }
472
- };
416
+ const registryPath = dataDir ? join(dataDir, "prototype.json") : void 0;
417
+ const readRegistry = () => {
418
+ if (!registryPath || !existsSync(registryPath)) return {};
419
+ return JSON.parse(readFileSync(registryPath, "utf-8"));
420
+ };
421
+ const registerPrototype = (id, source) => {
422
+ if (!registryPath) return;
423
+ const registry = readRegistry();
424
+ registry[id] = source;
425
+ mkdirSync(dataDir, { recursive: true });
426
+ writeFileSync(registryPath, JSON.stringify(registry, null, 2), "utf-8");
427
+ };
428
+ const prototype = {
429
+ async resolve(id) {
430
+ if (!resourcex) return void 0;
431
+ const registry = readRegistry();
432
+ const source = registry[id];
433
+ if (!source) return void 0;
434
+ try {
435
+ return await resourcex.ingest(source);
436
+ } catch {
437
+ return void 0;
438
+ }
439
+ }
440
+ };
441
+ return { runtime, prototype, resourcex, registerPrototype };
442
+ }
473
443
  export {
474
- LocalPlatform
444
+ filesToState,
445
+ localPlatform,
446
+ stateToFiles
475
447
  };
476
448
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/LocalPlatform.ts"],"sourcesContent":["/**\n * LocalPlatform — Local filesystem implementation of Platform.\n *\n * Everything lives under a single .rolex/ directory.\n * rolex.json is the single source of truth (CAS):\n * - roles: all born role names\n * - organizations: org configs with positions\n * - assignments: role → org/position mappings\n *\n * Directory convention:\n * <rootDir>/rolex.json\n * <rootDir>/roles/<role>/identity/*.identity.feature\n * <rootDir>/roles/<role>/goals/<name>/<name>.goal.feature\n * <rootDir>/roles/<role>/goals/<name>/<name>.plan.feature\n * <rootDir>/roles/<role>/goals/<name>/tasks/<name>.task.feature\n * <rootDir>/orgs/<org>/org.feature (optional)\n * <rootDir>/orgs/<org>/positions/<name>/<name>.position.feature\n * <rootDir>/orgs/<org>/positions/<name>/*.duty.feature\n */\n\nimport { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { parse } from \"@rolexjs/parser\";\nimport type { Feature as GherkinFeature } from \"@rolexjs/parser\";\nimport type {\n Platform,\n RolexConfig,\n OrganizationInfo,\n PositionInfo,\n Assignment,\n Feature,\n Scenario,\n Goal,\n Plan,\n Task,\n Duty,\n} from \"@rolexjs/core\";\nimport {\n getRoleState,\n getPositionState,\n transition,\n ROLE_MACHINE,\n POSITION_MACHINE,\n} from \"@rolexjs/core\";\n\nexport class LocalPlatform implements Platform {\n private readonly rootDir: string;\n private config: RolexConfig | null = null;\n\n constructor(rootDir: string) {\n this.rootDir = rootDir;\n }\n\n // ========== Society ==========\n\n allBornRoles(): string[] {\n return this.loadConfig().roles;\n }\n\n born(name: string, source: string): Feature {\n const roleDir = join(this.rootDir, \"roles\", name);\n mkdirSync(join(roleDir, \"identity\"), { recursive: true });\n mkdirSync(join(roleDir, \"goals\"), { recursive: true });\n\n const filePath = join(roleDir, \"identity\", \"persona.identity.feature\");\n writeFileSync(filePath, source, \"utf-8\");\n\n // Register in config (CAS)\n const config = this.loadConfig();\n if (!config.roles.includes(name)) {\n config.roles.push(name);\n this.saveConfig(config);\n }\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"persona\");\n }\n\n found(name: string, source?: string, parent?: string): void {\n const config = this.loadConfig();\n\n if (config.organizations[name]) {\n throw new Error(`Organization already exists: ${name}`);\n }\n\n if (parent && !config.organizations[parent]) {\n throw new Error(`Parent organization not found: ${parent}`);\n }\n\n // Create org directory\n const orgDir = join(this.rootDir, \"orgs\", name);\n mkdirSync(orgDir, { recursive: true });\n\n // Write optional org feature\n if (source) {\n writeFileSync(join(orgDir, \"org.feature\"), source, \"utf-8\");\n }\n\n // Register in config\n config.organizations[name] = {\n parent,\n positions: [],\n };\n this.saveConfig(config);\n }\n\n getOrganization(name: string): OrganizationInfo | null {\n const config = this.loadConfig();\n const orgConfig = config.organizations[name];\n if (!orgConfig) return null;\n\n const members = Object.entries(config.assignments)\n .filter(([, a]) => a.org === name)\n .map(([role]) => role);\n\n return {\n name,\n parent: orgConfig.parent,\n positions: orgConfig.positions,\n members,\n };\n }\n\n allOrganizations(): OrganizationInfo[] {\n const config = this.loadConfig();\n return Object.keys(config.organizations).map((name) => this.getOrganization(name)!);\n }\n\n // ========== Organization ==========\n\n hire(roleId: string, orgName: string): void {\n const config = this.loadConfig();\n\n if (!config.roles.includes(roleId)) {\n throw new Error(`Role not found: ${roleId}. Call born() first.`);\n }\n if (!config.organizations[orgName]) {\n throw new Error(`Organization not found: ${orgName}. Call found() first.`);\n }\n\n const assignment = config.assignments[roleId] ?? null;\n const currentState = getRoleState(assignment);\n\n // Validate state transition\n transition(ROLE_MACHINE, currentState, \"hire\");\n\n // One-to-one: role can only belong to one org\n if (assignment && assignment.org !== orgName) {\n throw new Error(`Role \"${roleId}\" is already hired in organization \"${assignment.org}\"`);\n }\n\n config.assignments[roleId] = { org: orgName };\n this.saveConfig(config);\n }\n\n fire(roleId: string, orgName: string): void {\n const config = this.loadConfig();\n\n const assignment = config.assignments[roleId];\n if (!assignment || assignment.org !== orgName) {\n throw new Error(`Role \"${roleId}\" is not hired in organization \"${orgName}\"`);\n }\n\n const currentState = getRoleState(assignment);\n\n // Validate state transition (fire works from member or on_duty)\n transition(ROLE_MACHINE, currentState, \"fire\");\n\n // Auto-dismiss: if on_duty, clear position state first\n if (assignment.position) {\n // No need to explicitly transition position — just removing assignment\n }\n\n delete config.assignments[roleId];\n this.saveConfig(config);\n }\n\n establish(positionName: string, source: string, orgName: string): void {\n const config = this.loadConfig();\n\n if (!config.organizations[orgName]) {\n throw new Error(`Organization not found: ${orgName}. Call found() first.`);\n }\n\n if (config.organizations[orgName].positions.includes(positionName)) {\n throw new Error(`Position \"${positionName}\" already exists in organization \"${orgName}\"`);\n }\n\n // Create position directory\n const posDir = join(this.rootDir, \"orgs\", orgName, \"positions\", positionName);\n mkdirSync(posDir, { recursive: true });\n\n // Write position feature\n writeFileSync(join(posDir, `${positionName}.position.feature`), source, \"utf-8\");\n\n // Register in config\n config.organizations[orgName].positions.push(positionName);\n this.saveConfig(config);\n }\n\n appoint(roleId: string, positionName: string, orgName: string): void {\n const config = this.loadConfig();\n\n // Validate role is member of this org\n const assignment = config.assignments[roleId];\n if (!assignment || assignment.org !== orgName) {\n throw new Error(`Role \"${roleId}\" is not a member of organization \"${orgName}\". Hire first.`);\n }\n\n // Validate position exists in org\n if (!config.organizations[orgName]?.positions.includes(positionName)) {\n throw new Error(`Position \"${positionName}\" not found in organization \"${orgName}\"`);\n }\n\n // Validate role state transition (member → on_duty)\n const roleState = getRoleState(assignment);\n transition(ROLE_MACHINE, roleState, \"appoint\");\n\n // Validate position state transition (vacant → filled)\n const currentHolder = this.findPositionHolder(config, orgName, positionName);\n const posState = getPositionState(currentHolder);\n transition(POSITION_MACHINE, posState, \"appoint\");\n\n // Update assignment\n config.assignments[roleId] = { org: orgName, position: positionName };\n this.saveConfig(config);\n }\n\n dismiss(roleId: string): void {\n const config = this.loadConfig();\n\n const assignment = config.assignments[roleId];\n if (!assignment || !assignment.position) {\n throw new Error(`Role \"${roleId}\" is not appointed to any position`);\n }\n\n // Validate role state transition (on_duty → member)\n const roleState = getRoleState(assignment);\n transition(ROLE_MACHINE, roleState, \"dismiss\");\n\n // Update assignment — keep org, remove position\n config.assignments[roleId] = { org: assignment.org };\n this.saveConfig(config);\n }\n\n positionDuties(positionName: string, orgName: string): Duty[] {\n const posDir = join(this.rootDir, \"orgs\", orgName, \"positions\", positionName);\n if (!existsSync(posDir)) return [];\n\n return readdirSync(posDir)\n .filter((f) => f.endsWith(\".duty.feature\"))\n .sort()\n .map((f) => {\n const source = readFileSync(join(posDir, f), \"utf-8\");\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"duty\") as Duty;\n });\n }\n\n getAssignment(roleId: string): Assignment | null {\n const config = this.loadConfig();\n return config.assignments[roleId] ?? null;\n }\n\n getPosition(positionName: string, orgName: string): PositionInfo | null {\n const config = this.loadConfig();\n\n if (!config.organizations[orgName]?.positions.includes(positionName)) {\n return null;\n }\n\n const holder = this.findPositionHolder(config, orgName, positionName);\n const duties = this.positionDuties(positionName, orgName);\n\n return {\n name: positionName,\n org: orgName,\n state: getPositionState(holder),\n assignedRole: holder,\n duties,\n };\n }\n\n // ========== Role Identity ==========\n\n identity(roleId: string): Feature[] {\n const roleDir = this.resolveRoleDir(roleId);\n const dir = join(roleDir, \"identity\");\n if (!existsSync(dir)) return [];\n\n // Personal identity features\n const features = readdirSync(dir)\n .filter((f) => f.endsWith(\".identity.feature\"))\n .sort()\n .map((f) => {\n const source = readFileSync(join(dir, f), \"utf-8\");\n const doc = parse(source);\n return this.toFeature(doc.feature!, this.detectIdentityType(f));\n });\n\n // Inject duty features if on_duty\n const assignment = this.getAssignment(roleId);\n if (assignment?.position) {\n const duties = this.positionDuties(assignment.position, assignment.org);\n features.push(...duties);\n }\n\n return features;\n }\n\n // ========== Growup ==========\n\n growup(\n roleId: string,\n type: \"knowledge\" | \"experience\" | \"voice\",\n name: string,\n source: string\n ): Feature {\n const roleDir = this.resolveRoleDir(roleId);\n const dir = join(roleDir, \"identity\");\n mkdirSync(dir, { recursive: true });\n\n const filePath = join(dir, `${name}.${type}.identity.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, type);\n }\n\n // ========== Reflect ==========\n\n reflect(\n roleId: string,\n experienceNames: string[],\n knowledgeName: string,\n knowledgeSource: string\n ): Feature {\n if (experienceNames.length === 0) {\n throw new Error(\"At least one experience required\");\n }\n\n const roleDir = this.resolveRoleDir(roleId);\n const identityDir = join(roleDir, \"identity\");\n\n // Phase 1: Validate ALL experiences exist before any mutation\n const expFiles: string[] = [];\n for (const expName of experienceNames) {\n if (expName.includes(\"/\") || expName.includes(\"\\\\\") || expName.includes(\"..\")) {\n throw new Error(`Invalid experience name: ${expName}`);\n }\n const expFile = join(identityDir, `${expName}.experience.identity.feature`);\n if (!existsSync(expFile)) {\n throw new Error(`Experience not found: ${expName}`);\n }\n expFiles.push(expFile);\n }\n\n // Phase 2: Create knowledge FIRST (safe — if this fails, nothing is lost)\n const feature = this.growup(roleId, \"knowledge\", knowledgeName, knowledgeSource);\n\n // Phase 3: Delete experience files (only after knowledge is safely persisted)\n for (const expFile of expFiles) {\n rmSync(expFile);\n }\n\n return feature;\n }\n\n // ========== Goals ==========\n\n activeGoal(roleId: string): (Goal & { plan: Plan | null; tasks: Task[] }) | null {\n const roleDir = this.resolveRoleDir(roleId);\n const goalsDir = join(roleDir, \"goals\");\n if (!existsSync(goalsDir)) return null;\n\n // If a focused goal is set, try to load it first\n const focusedName = this.getFocusedGoal(roleId);\n if (focusedName) {\n const goalDir = join(goalsDir, focusedName);\n const result = this.loadGoalIfActive(goalDir);\n if (result) return result;\n }\n\n // Fallback: first uncompleted goal alphabetically\n const goalDirs = readdirSync(goalsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n\n for (const goalName of goalDirs) {\n const goalDir = join(goalsDir, goalName);\n const result = this.loadGoalIfActive(goalDir);\n if (result) return result;\n }\n\n return null;\n }\n\n allActiveGoals(roleId: string): Goal[] {\n const roleDir = this.resolveRoleDir(roleId);\n const goalsDir = join(roleDir, \"goals\");\n if (!existsSync(goalsDir)) return [];\n\n const goalDirs = readdirSync(goalsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n\n const goals: Goal[] = [];\n for (const goalName of goalDirs) {\n const goalDir = join(goalsDir, goalName);\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\");\n if (!goalFile) continue;\n\n const source = readFileSync(goalFile, \"utf-8\");\n const doc = parse(source);\n if (!doc.feature) continue;\n\n if (doc.feature.tags.some((t) => t.name === \"@done\" || t.name === \"@abandoned\")) continue;\n\n goals.push(this.toFeature(doc.feature, \"goal\") as Goal);\n }\n\n return goals;\n }\n\n getFocusedGoal(roleId: string): string | null {\n const roleDir = this.resolveRoleDir(roleId);\n const focusFile = join(roleDir, \"goals\", \".focus\");\n if (!existsSync(focusFile)) return null;\n return readFileSync(focusFile, \"utf-8\").trim() || null;\n }\n\n setFocusedGoal(roleId: string, name: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalsDir = join(roleDir, \"goals\");\n const goalDir = join(goalsDir, name);\n\n if (!existsSync(goalDir)) {\n throw new Error(`Goal not found: ${name}`);\n }\n\n writeFileSync(join(goalsDir, \".focus\"), name, \"utf-8\");\n }\n\n // ========== Write ==========\n\n createGoal(roleId: string, name: string, source: string, testable?: boolean): Goal {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = join(roleDir, \"goals\", name);\n mkdirSync(goalDir, { recursive: true });\n\n if (testable) source = `@testable\\n${source}`;\n const filePath = join(goalDir, `${name}.goal.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"goal\") as Goal;\n }\n\n createPlan(roleId: string, source: string): Plan {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const goalName = basename(goalDir);\n const filePath = join(goalDir, `${goalName}.plan.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"plan\") as Plan;\n }\n\n createTask(roleId: string, name: string, source: string, testable?: boolean): Task {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const tasksDir = join(goalDir, \"tasks\");\n mkdirSync(tasksDir, { recursive: true });\n\n if (testable) source = `@testable\\n${source}`;\n const filePath = join(tasksDir, `${name}.task.feature`);\n writeFileSync(filePath, source, \"utf-8\");\n\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"task\") as Task;\n }\n\n // ========== Close ==========\n\n completeGoal(roleId: string, experience?: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\")!;\n this.addDoneTag(goalFile);\n\n if (experience) {\n const goalName = basename(goalDir);\n this.growup(roleId, \"experience\", goalName, experience);\n }\n }\n\n abandonGoal(roleId: string, experience?: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\")!;\n this.addTag(goalFile, \"@abandoned\");\n\n if (experience) {\n const goalName = basename(goalDir);\n this.growup(roleId, \"experience\", goalName, experience);\n }\n }\n\n completeTask(roleId: string, name: string): void {\n const roleDir = this.resolveRoleDir(roleId);\n const goalDir = this.getActiveGoalDir(roleDir);\n if (!goalDir) throw new Error(\"No active goal\");\n\n const tasksDir = join(goalDir, \"tasks\");\n const taskFile = join(tasksDir, `${name}.task.feature`);\n if (!existsSync(taskFile)) throw new Error(`Task not found: ${name}`);\n\n this.addDoneTag(taskFile);\n }\n\n // ========== Internal ==========\n\n /**\n * Load config — always returns a valid RolexConfig.\n * Creates default config if rolex.json doesn't exist yet.\n */\n private loadConfig(): RolexConfig {\n if (this.config) return this.config;\n\n const configPath = join(this.rootDir, \"rolex.json\");\n if (existsSync(configPath)) {\n const raw = JSON.parse(readFileSync(configPath, \"utf-8\"));\n // Migrate old format: ensure required fields exist\n this.config = {\n roles: raw.roles ?? [],\n organizations: raw.organizations ?? {},\n assignments: raw.assignments ?? {},\n };\n return this.config;\n }\n\n // Create default config\n mkdirSync(this.rootDir, { recursive: true });\n const config: RolexConfig = { roles: [], organizations: {}, assignments: {} };\n this.saveConfig(config);\n return config;\n }\n\n private saveConfig(config: RolexConfig): void {\n mkdirSync(this.rootDir, { recursive: true });\n writeFileSync(join(this.rootDir, \"rolex.json\"), JSON.stringify(config, null, 2), \"utf-8\");\n this.config = config;\n }\n\n private resolveRoleDir(roleId: string): string {\n const roleDir = join(this.rootDir, \"roles\", roleId);\n if (!existsSync(roleDir)) {\n throw new Error(`Role directory not found: ${roleDir}`);\n }\n return roleDir;\n }\n\n private findPositionHolder(\n config: RolexConfig,\n orgName: string,\n positionName: string\n ): string | null {\n for (const [role, assignment] of Object.entries(config.assignments)) {\n if (assignment.org === orgName && assignment.position === positionName) {\n return role;\n }\n }\n return null;\n }\n\n private loadGoalIfActive(goalDir: string): (Goal & { plan: Plan | null; tasks: Task[] }) | null {\n const goalFile = this.findFeatureFile(goalDir, \".goal.feature\");\n if (!goalFile) return null;\n\n const source = readFileSync(goalFile, \"utf-8\");\n const doc = parse(source);\n if (!doc.feature) return null;\n\n if (doc.feature.tags.some((t) => t.name === \"@done\" || t.name === \"@abandoned\")) return null;\n\n const goal = this.toFeature(doc.feature, \"goal\") as Goal;\n return {\n ...goal,\n plan: this.loadPlan(goalDir),\n tasks: this.loadTasks(goalDir),\n };\n }\n\n private getActiveGoalDir(roleDir: string): string | null {\n const goalsDir = join(roleDir, \"goals\");\n if (!existsSync(goalsDir)) return null;\n\n // Respect focused goal\n const focusFile = join(goalsDir, \".focus\");\n if (existsSync(focusFile)) {\n const focusedName = readFileSync(focusFile, \"utf-8\").trim();\n if (focusedName) {\n const focusedDir = join(goalsDir, focusedName);\n if (existsSync(focusedDir) && this.loadGoalIfActive(focusedDir)) {\n return focusedDir;\n }\n }\n }\n\n // Fallback: first uncompleted goal\n const goalDirs = readdirSync(goalsDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name)\n .sort();\n\n for (const goalName of goalDirs) {\n const goalDir = join(goalsDir, goalName);\n if (this.loadGoalIfActive(goalDir)) {\n return goalDir;\n }\n }\n\n return null;\n }\n\n private toFeature(gherkin: GherkinFeature, type: Feature[\"type\"]): Feature {\n return {\n ...gherkin,\n type,\n scenarios: this.extractScenarios(gherkin),\n };\n }\n\n private extractScenarios(feature: GherkinFeature): Scenario[] {\n const featureTestable = feature.tags.some((t) => t.name === \"@testable\");\n return (feature.children || [])\n .filter((c) => c.scenario)\n .map((c) => ({\n ...c.scenario!,\n verifiable: featureTestable || c.scenario!.tags.some((t) => t.name === \"@testable\"),\n }));\n }\n\n private loadPlan(goalDir: string): Plan | null {\n const planFile = this.findFeatureFile(goalDir, \".plan.feature\");\n if (!planFile) return null;\n\n const source = readFileSync(planFile, \"utf-8\");\n const doc = parse(source);\n if (!doc.feature) return null;\n\n return this.toFeature(doc.feature, \"plan\") as Plan;\n }\n\n private loadTasks(goalDir: string): Task[] {\n const tasksDir = join(goalDir, \"tasks\");\n if (!existsSync(tasksDir)) return [];\n\n return readdirSync(tasksDir)\n .filter((f) => f.endsWith(\".task.feature\"))\n .sort()\n .map((f) => {\n const source = readFileSync(join(tasksDir, f), \"utf-8\");\n const doc = parse(source);\n return this.toFeature(doc.feature!, \"task\") as Task;\n });\n }\n\n private detectIdentityType(filename: string): Feature[\"type\"] {\n if (filename === \"persona.identity.feature\") return \"persona\";\n if (filename.endsWith(\".knowledge.identity.feature\")) return \"knowledge\";\n if (filename.endsWith(\".experience.identity.feature\")) return \"experience\";\n if (filename.endsWith(\".voice.identity.feature\")) return \"voice\";\n return \"knowledge\";\n }\n\n private findFeatureFile(dir: string, suffix: string): string | null {\n if (!existsSync(dir)) return null;\n const file = readdirSync(dir).find((f) => f.endsWith(suffix));\n return file ? join(dir, file) : null;\n }\n\n private addTag(filePath: string, tag: string): void {\n const content = readFileSync(filePath, \"utf-8\");\n const updated = content.replace(/^(Feature:)/m, `${tag}\\n$1`);\n writeFileSync(filePath, updated, \"utf-8\");\n }\n\n private addDoneTag(filePath: string): void {\n this.addTag(filePath, \"@done\");\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/LocalPlatform.ts","../src/manifest.ts"],"sourcesContent":["/**\n * localPlatform — create a Platform backed by local filesystem.\n *\n * Storage layout:\n * {dataDir}/\n * role/<id>/\n * individual.json — manifest (tree structure + links)\n * <id>.<type>.feature — node information (Gherkin)\n * organization/<id>/\n * organization.json — manifest (tree structure + links)\n * <id>.<type>.feature — node information (Gherkin)\n *\n * In-memory: Map-based tree (same model as @rolexjs/system createRuntime).\n * Persistence: loaded before every operation, saved after every mutation.\n * Refs are stored in manifests to ensure stability across reload cycles.\n * When dataDir is null, runs purely in-memory (useful for tests).\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { NodeProvider } from \"@resourcexjs/node-provider\";\nimport type { Platform } from \"@rolexjs/core\";\nimport { organizationType, roleType } from \"@rolexjs/resourcex-types\";\nimport type { Prototype, Runtime, State, Structure } from \"@rolexjs/system\";\nimport { createResourceX, setProvider } from \"resourcexjs\";\nimport { filesToState, type Manifest, stateToFiles } from \"./manifest.js\";\n\n// ===== Internal types =====\n\ninterface TreeNode {\n node: Structure;\n parent: string | null;\n children: string[];\n}\n\ninterface LinkEntry {\n toId: string;\n relation: string;\n}\n\n// ===== Config =====\n\nexport interface LocalPlatformConfig {\n /** Directory for persistent storage. Defaults to ~/.deepractice/rolex. Set to null for in-memory only. */\n dataDir?: string | null;\n /** Directory for ResourceX storage. Defaults to ~/.deepractice/resourcex. Set to null to disable. */\n resourceDir?: string | null;\n}\n\n/** Create a local Platform. Persistent by default (~/.deepractice/rolex), in-memory if dataDir is null. */\nexport function localPlatform(config: LocalPlatformConfig = {}): Platform {\n const dataDir =\n config.dataDir === null\n ? undefined\n : (config.dataDir ?? join(homedir(), \".deepractice\", \"rolex\"));\n\n const nodes = new Map<string, TreeNode>();\n const links = new Map<string, LinkEntry[]>();\n let counter = 0;\n\n // ===== Internal helpers =====\n\n const nextRef = () => `n${++counter}`;\n\n const findByStructure = (s: Structure): TreeNode | undefined => {\n for (const treeNode of nodes.values()) {\n if (treeNode.node.name === s.name) return treeNode;\n }\n return undefined;\n };\n\n const removeSubtree = (ref: string): void => {\n const treeNode = nodes.get(ref);\n if (!treeNode) return;\n for (const childRef of [...treeNode.children]) {\n removeSubtree(childRef);\n }\n links.delete(ref);\n for (const [fromRef, fromLinks] of links.entries()) {\n const filtered = fromLinks.filter((l) => l.toId !== ref);\n if (filtered.length === 0) {\n links.delete(fromRef);\n } else {\n links.set(fromRef, filtered);\n }\n }\n nodes.delete(ref);\n };\n\n const projectRef = (ref: string): State => {\n const treeNode = nodes.get(ref)!;\n return { ...treeNode.node, children: [] };\n };\n\n const projectNode = (ref: string): State => {\n const treeNode = nodes.get(ref)!;\n const nodeLinks = links.get(ref);\n return {\n ...treeNode.node,\n children: treeNode.children.map(projectNode),\n ...(nodeLinks && nodeLinks.length > 0\n ? {\n links: nodeLinks.map((l) => ({\n relation: l.relation,\n target: projectRef(l.toId),\n })),\n }\n : {}),\n };\n };\n\n const createNode = (\n parentRef: string | null,\n type: Structure,\n information?: string,\n id?: string,\n alias?: readonly string[]\n ): Structure => {\n const ref = nextRef();\n const node: Structure = {\n ref,\n ...(id ? { id } : {}),\n ...(alias && alias.length > 0 ? { alias } : {}),\n name: type.name,\n description: type.description,\n parent: type.parent,\n information,\n };\n const treeNode: TreeNode = { node, parent: parentRef, children: [] };\n nodes.set(ref, treeNode);\n\n if (parentRef) {\n const parentTreeNode = nodes.get(parentRef);\n if (!parentTreeNode) throw new Error(`Parent not found: ${parentRef}`);\n parentTreeNode.children.push(ref);\n }\n\n return node;\n };\n\n // ===== Persistence =====\n\n /** Use a stored ref, updating counter to avoid future collisions. */\n const useRef = (storedRef: string): string => {\n const n = parseInt(storedRef.slice(1), 10);\n if (!Number.isNaN(n) && n > counter) counter = n;\n return storedRef;\n };\n\n /** Replay a State tree into the in-memory Maps. Returns the root ref. */\n const replayState = (state: State, parentRef: string | null): string => {\n const ref = state.ref ? useRef(state.ref) : nextRef();\n const node: Structure = {\n ref,\n ...(state.id ? { id: state.id } : {}),\n ...(state.alias ? { alias: state.alias } : {}),\n name: state.name,\n description: state.description ?? \"\",\n parent: null,\n ...(state.information ? { information: state.information } : {}),\n };\n const treeNode: TreeNode = { node, parent: parentRef, children: [] };\n nodes.set(ref, treeNode);\n\n if (parentRef) {\n nodes.get(parentRef)!.children.push(ref);\n }\n\n if (state.children) {\n for (const child of state.children) {\n replayState(child, ref);\n }\n }\n\n return ref;\n };\n\n const loadEntitiesFrom = (\n dir: string,\n manifestName: string,\n parentRef: string\n ): { ref: string; manifest: Manifest }[] => {\n const results: { ref: string; manifest: Manifest }[] = [];\n if (!existsSync(dir)) return results;\n\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const entityDir = join(dir, entry.name);\n const manifestPath = join(entityDir, manifestName);\n if (!existsSync(manifestPath)) continue;\n\n const manifest: Manifest = JSON.parse(readFileSync(manifestPath, \"utf-8\"));\n const featureFiles: Record<string, string> = {};\n for (const file of readdirSync(entityDir)) {\n if (file.endsWith(\".feature\")) {\n featureFiles[file] = readFileSync(join(entityDir, file), \"utf-8\");\n }\n }\n const state = filesToState(manifest, featureFiles);\n const entityRef = replayState(state, parentRef);\n results.push({ ref: entityRef, manifest });\n }\n\n return results;\n };\n\n const load = () => {\n if (!dataDir) return;\n\n // Clear and rebuild from disk\n nodes.clear();\n links.clear();\n counter = 0;\n\n // Create implicit society root\n const societyRef = nextRef();\n nodes.set(societyRef, {\n node: {\n ref: societyRef,\n name: \"society\",\n description: \"\",\n parent: null,\n },\n parent: null,\n children: [],\n });\n\n // Load entities\n const entityRefs = [\n ...loadEntitiesFrom(join(dataDir, \"role\"), \"individual.json\", societyRef),\n ...loadEntitiesFrom(join(dataDir, \"organization\"), \"organization.json\", societyRef),\n ];\n\n // Build id → ref index for link resolution\n const idToRef = new Map<string, string>();\n for (const [ref, treeNode] of nodes) {\n if (treeNode.node.id) {\n idToRef.set(treeNode.node.id, ref);\n }\n }\n\n // Resolve links from manifests\n for (const { ref, manifest } of entityRefs) {\n if (!manifest.links) continue;\n const entityLinks: LinkEntry[] = [];\n for (const [relation, targetIds] of Object.entries(manifest.links)) {\n for (const targetId of targetIds) {\n const targetRef = idToRef.get(targetId);\n if (targetRef) {\n entityLinks.push({ toId: targetRef, relation });\n }\n }\n }\n if (entityLinks.length > 0) {\n links.set(ref, entityLinks);\n }\n }\n };\n\n const saveEntity = (baseDir: string, entityId: string, manifestName: string, state: State) => {\n const entityDir = join(baseDir, entityId);\n mkdirSync(entityDir, { recursive: true });\n const { manifest, files } = stateToFiles(state);\n writeFileSync(join(entityDir, manifestName), JSON.stringify(manifest, null, 2), \"utf-8\");\n for (const file of files) {\n writeFileSync(join(entityDir, file.path), file.content, \"utf-8\");\n }\n };\n\n const save = () => {\n if (!dataDir) return;\n mkdirSync(dataDir, { recursive: true });\n\n // Find society root\n let societyTreeNode: TreeNode | undefined;\n for (const treeNode of nodes.values()) {\n if (treeNode.parent === null && treeNode.node.name === \"society\") {\n societyTreeNode = treeNode;\n break;\n }\n }\n if (!societyTreeNode) return;\n\n // Clean up existing entity directories\n const roleDir = join(dataDir, \"role\");\n const orgDir = join(dataDir, \"organization\");\n if (existsSync(roleDir)) rmSync(roleDir, { recursive: true });\n if (existsSync(orgDir)) rmSync(orgDir, { recursive: true });\n\n // Save each entity child of society\n for (const childRef of societyTreeNode.children) {\n if (!nodes.has(childRef)) continue;\n const state = projectNode(childRef);\n const entityId = state.id ?? state.name;\n\n if (state.name === \"individual\") {\n saveEntity(roleDir, entityId, \"individual.json\", state);\n } else if (state.name === \"organization\") {\n saveEntity(orgDir, entityId, \"organization.json\", state);\n }\n // Other types (past, etc.) are not persisted yet\n }\n };\n\n // ===== Runtime =====\n\n const runtime: Runtime = {\n create(parent, type, information, id, alias) {\n load();\n const node = createNode(parent?.ref ?? null, type, information, id, alias);\n save();\n return node;\n },\n\n remove(node) {\n load();\n if (!node.ref) return;\n const treeNode = nodes.get(node.ref);\n if (!treeNode) return;\n\n if (treeNode.parent) {\n const parentTreeNode = nodes.get(treeNode.parent);\n if (parentTreeNode) {\n parentTreeNode.children = parentTreeNode.children.filter((r) => r !== node.ref);\n }\n }\n\n removeSubtree(node.ref);\n save();\n },\n\n transform(_source, target, information) {\n load();\n const targetParent = target.parent;\n if (!targetParent) {\n throw new Error(`Cannot transform to root structure: ${target.name}`);\n }\n\n const parentTreeNode = findByStructure(targetParent);\n if (!parentTreeNode) {\n throw new Error(`No node found for structure: ${targetParent.name}`);\n }\n\n const node = createNode(parentTreeNode.node.ref!, target, information);\n save();\n return node;\n },\n\n link(from, to, relationName, reverseName) {\n load();\n if (!from.ref) throw new Error(\"Source node has no ref\");\n if (!to.ref) throw new Error(\"Target node has no ref\");\n\n const fromLinks = links.get(from.ref) ?? [];\n if (!fromLinks.some((l) => l.toId === to.ref && l.relation === relationName)) {\n fromLinks.push({ toId: to.ref, relation: relationName });\n links.set(from.ref, fromLinks);\n }\n\n const toLinks = links.get(to.ref) ?? [];\n if (!toLinks.some((l) => l.toId === from.ref && l.relation === reverseName)) {\n toLinks.push({ toId: from.ref, relation: reverseName });\n links.set(to.ref, toLinks);\n }\n\n save();\n },\n\n unlink(from, to, relationName, reverseName) {\n load();\n if (!from.ref || !to.ref) return;\n\n const fromLinks = links.get(from.ref);\n if (fromLinks) {\n const filtered = fromLinks.filter(\n (l) => !(l.toId === to.ref && l.relation === relationName)\n );\n if (filtered.length === 0) links.delete(from.ref);\n else links.set(from.ref, filtered);\n }\n\n const toLinks = links.get(to.ref);\n if (toLinks) {\n const filtered = toLinks.filter(\n (l) => !(l.toId === from.ref && l.relation === reverseName)\n );\n if (filtered.length === 0) links.delete(to.ref);\n else links.set(to.ref, filtered);\n }\n\n save();\n },\n\n project(node) {\n load();\n if (!node.ref || !nodes.has(node.ref)) {\n throw new Error(`Node not found: ${node.ref}`);\n }\n return projectNode(node.ref);\n },\n\n roots() {\n load();\n const result: Structure[] = [];\n for (const treeNode of nodes.values()) {\n if (treeNode.parent === null) {\n result.push(treeNode.node);\n }\n }\n return result;\n },\n };\n\n // ===== ResourceX =====\n\n let resourcex: ReturnType<typeof createResourceX> | undefined;\n if (config.resourceDir !== null) {\n setProvider(new NodeProvider());\n resourcex = createResourceX({\n path: config.resourceDir ?? join(homedir(), \".deepractice\", \"resourcex\"),\n types: [roleType, organizationType],\n });\n }\n\n // ===== Prototype registry =====\n\n const registryPath = dataDir ? join(dataDir, \"prototype.json\") : undefined;\n\n const readRegistry = (): Record<string, string> => {\n if (!registryPath || !existsSync(registryPath)) return {};\n return JSON.parse(readFileSync(registryPath, \"utf-8\"));\n };\n\n const registerPrototype = (id: string, source: string): void => {\n if (!registryPath) return;\n const registry = readRegistry();\n registry[id] = source;\n mkdirSync(dataDir!, { recursive: true });\n writeFileSync(registryPath, JSON.stringify(registry, null, 2), \"utf-8\");\n };\n\n const prototype: Prototype = {\n async resolve(id) {\n if (!resourcex) return undefined;\n const registry = readRegistry();\n const source = registry[id];\n if (!source) return undefined;\n try {\n return await resourcex.ingest<State>(source);\n } catch {\n return undefined;\n }\n },\n };\n\n return { runtime, prototype, resourcex, registerPrototype };\n}\n","/**\n * Manifest — file-based storage format for RoleX entities.\n *\n * Storage layout:\n * role/<id>/\n * individual.json — manifest (tree structure + links)\n * <id>.<type>.feature — node information (Gherkin)\n *\n * organization/<id>/\n * organization.json — manifest (tree structure + links)\n * <id>.<type>.feature — node information (Gherkin)\n *\n * Rules:\n * - Directories: only role/ and organization/ at top level\n * - Files: all [id].[type].feature, flat within the entity directory\n * - Manifest: tree structure in JSON, content in .feature files\n * - Nodes without explicit id default to their type name\n */\n\nimport type { State } from \"@rolexjs/system\";\n\n// ===== Manifest types =====\n\n/** A node in the manifest tree. */\nexport interface ManifestNode {\n readonly type: string;\n readonly ref?: string;\n readonly children?: Record<string, ManifestNode>;\n}\n\n/** Root manifest for an entity (individual or organization). */\nexport interface Manifest {\n readonly id: string;\n readonly type: string;\n readonly ref?: string;\n readonly alias?: readonly string[];\n readonly children?: Record<string, ManifestNode>;\n readonly links?: Record<string, string[]>;\n}\n\n// ===== State → files =====\n\nexport interface FileEntry {\n readonly path: string;\n readonly content: string;\n}\n\n/**\n * Convert a State tree to a manifest + feature files.\n * Returns the manifest and a list of file entries (path → content).\n */\nexport function stateToFiles(state: State): { manifest: Manifest; files: FileEntry[] } {\n const files: FileEntry[] = [];\n\n const collectFiles = (node: State, nodeId: string) => {\n if (node.information) {\n files.push({\n path: `${nodeId}.${node.name}.feature`,\n content: node.information,\n });\n }\n if (node.children) {\n for (const child of node.children) {\n const childId = child.id ?? child.name;\n collectFiles(child, childId);\n }\n }\n };\n\n const rootId = state.id ?? state.name;\n collectFiles(state, rootId);\n\n const buildManifestNode = (node: State): ManifestNode => {\n const entry: ManifestNode = {\n type: node.name,\n ...(node.ref ? { ref: node.ref } : {}),\n };\n if (node.children && node.children.length > 0) {\n const children: Record<string, ManifestNode> = {};\n for (const child of node.children) {\n const childId = child.id ?? child.name;\n children[childId] = buildManifestNode(child);\n }\n return { ...entry, children };\n }\n return entry;\n };\n\n const manifestNode = buildManifestNode(state);\n\n const manifest: Manifest = {\n id: rootId,\n type: state.name,\n ...(state.ref ? { ref: state.ref } : {}),\n ...(state.alias ? { alias: state.alias } : {}),\n ...(manifestNode.children ? { children: manifestNode.children } : {}),\n ...(state.links && state.links.length > 0\n ? {\n links: buildManifestLinks(state.links),\n }\n : {}),\n };\n\n return { manifest, files };\n}\n\nfunction buildManifestLinks(\n links: readonly { readonly relation: string; readonly target: State }[]\n): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n for (const link of links) {\n const targetId = link.target.id ?? link.target.name;\n if (!result[link.relation]) {\n result[link.relation] = [];\n }\n result[link.relation].push(targetId);\n }\n return result;\n}\n\n// ===== Files → State =====\n\n/**\n * Convert a manifest + feature file contents to a State tree.\n * fileContents maps filename (e.g. \"role-creation.principle.feature\") to Gherkin text.\n */\nexport function filesToState(manifest: Manifest, fileContents: Record<string, string>): State {\n const buildState = (id: string, node: ManifestNode): State => {\n const filename = `${id}.${node.type}.feature`;\n const information = fileContents[filename];\n\n const children: State[] = [];\n if (node.children) {\n for (const [childId, childNode] of Object.entries(node.children)) {\n children.push(buildState(childId, childNode));\n }\n }\n\n return {\n ...(node.ref ? { ref: node.ref } : {}),\n id,\n name: node.type,\n description: \"\",\n parent: null,\n ...(information ? { information } : {}),\n ...(children.length > 0 ? { children } : {}),\n };\n };\n\n const rootFilename = `${manifest.id}.${manifest.type}.feature`;\n const rootInformation = fileContents[rootFilename];\n\n const children: State[] = [];\n if (manifest.children) {\n for (const [childId, childNode] of Object.entries(manifest.children)) {\n children.push(buildState(childId, childNode));\n }\n }\n\n const links: { relation: string; target: State }[] = [];\n if (manifest.links) {\n for (const [relation, targetIds] of Object.entries(manifest.links)) {\n for (const targetId of targetIds) {\n links.push({\n relation,\n target: {\n id: targetId,\n name: \"\",\n description: \"\",\n parent: null,\n },\n });\n }\n }\n }\n\n return {\n ...(manifest.ref ? { ref: manifest.ref } : {}),\n id: manifest.id,\n ...(manifest.alias ? { alias: manifest.alias } : {}),\n name: manifest.type,\n description: \"\",\n parent: null,\n ...(rootInformation ? { information: rootInformation } : {}),\n ...(children.length > 0 ? { children } : {}),\n ...(links.length > 0 ? { links } : {}),\n };\n}\n"],"mappings":"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","names":["children"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rolexjs/local-platform",
3
- "version": "0.9.1",
3
+ "version": "0.11.0-dev.0",
4
4
  "description": "Local filesystem Platform for RoleX — stores roles in .rolex/ directories",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -8,6 +8,7 @@
8
8
  "exports": {
9
9
  ".": {
10
10
  "types": "./dist/index.d.ts",
11
+ "bun": "./src/index.ts",
11
12
  "default": "./dist/index.js"
12
13
  }
13
14
  },
@@ -19,12 +20,13 @@
19
20
  "clean": "rm -rf dist"
20
21
  },
21
22
  "dependencies": {
22
- "@rolexjs/core": "^0.9.1",
23
- "@rolexjs/parser": "^0.9.1"
24
- },
25
- "devDependencies": {
26
- "rolexjs": "^0.9.1"
23
+ "@rolexjs/system": "workspace:*",
24
+ "@rolexjs/core": "workspace:*",
25
+ "@rolexjs/resourcex-types": "workspace:*",
26
+ "resourcexjs": "^2.8.0",
27
+ "@resourcexjs/node-provider": "^2.8.0"
27
28
  },
29
+ "devDependencies": {},
28
30
  "publishConfig": {
29
31
  "access": "public"
30
32
  }