@portel/photon-core 2.8.4 → 2.9.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/base.d.ts +7 -7
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js +8 -8
- package/dist/base.js.map +1 -1
- package/dist/collections/Collection.d.ts +2 -2
- package/dist/collections/Collection.js +2 -2
- package/dist/compiler.js +7 -7
- package/dist/compiler.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/dist/instance-store.d.ts +64 -0
- package/dist/instance-store.d.ts.map +1 -0
- package/dist/instance-store.js +144 -0
- package/dist/instance-store.js.map +1 -0
- package/dist/memory.d.ts +2 -2
- package/dist/memory.js +2 -2
- package/dist/middleware.d.ts +69 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +570 -0
- package/dist/middleware.js.map +1 -0
- package/dist/schema-extractor.d.ts +111 -1
- package/dist/schema-extractor.d.ts.map +1 -1
- package/dist/schema-extractor.js +333 -2
- package/dist/schema-extractor.js.map +1 -1
- package/dist/stateful.d.ts +2 -0
- package/dist/stateful.d.ts.map +1 -1
- package/dist/stateful.js +2 -0
- package/dist/stateful.js.map +1 -1
- package/dist/types.d.ts +111 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/duration.d.ts +24 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/duration.js +64 -0
- package/dist/utils/duration.js.map +1 -0
- package/dist/watcher.d.ts +62 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +270 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +2 -2
- package/src/base.ts +8 -8
- package/src/collections/Collection.ts +2 -2
- package/src/compiler.ts +7 -7
- package/src/config.ts +1 -1
- package/src/index.ts +34 -4
- package/src/instance-store.ts +155 -0
- package/src/memory.ts +2 -2
- package/src/middleware.ts +714 -0
- package/src/schema-extractor.ts +353 -2
- package/src/stateful.ts +4 -0
- package/src/types.ts +106 -5
- package/src/utils/duration.ts +67 -0
- package/src/watcher.ts +317 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instance Store
|
|
3
|
+
*
|
|
4
|
+
* Shared state persistence for named photon instances.
|
|
5
|
+
* Used by daemon, NCP, and Lumina to manage per-instance state.
|
|
6
|
+
*
|
|
7
|
+
* Paths (matching daemon convention):
|
|
8
|
+
* - State: ~/.photon/state/{photonName}/{instanceName}.json
|
|
9
|
+
* - Context: ~/.photon/context/{photonName}.json → { current: "name" }
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from 'fs/promises';
|
|
13
|
+
import * as fsSync from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import * as os from 'os';
|
|
16
|
+
|
|
17
|
+
export interface InstanceStoreOptions {
|
|
18
|
+
/** Base directory (default: ~/.photon) */
|
|
19
|
+
baseDir?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getBaseDir(options?: InstanceStoreOptions): string {
|
|
23
|
+
return options?.baseDir || process.env.PHOTON_DIR || path.join(os.homedir(), '.photon');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class InstanceStore {
|
|
27
|
+
private photonName: string;
|
|
28
|
+
private baseDir: string;
|
|
29
|
+
|
|
30
|
+
constructor(photonName: string, options?: InstanceStoreOptions) {
|
|
31
|
+
this.photonName = photonName;
|
|
32
|
+
this.baseDir = getBaseDir(options);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get the state directory for this photon
|
|
37
|
+
*/
|
|
38
|
+
private stateDir(): string {
|
|
39
|
+
return path.join(this.baseDir, 'state', this.photonName);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the context file path for this photon
|
|
44
|
+
*/
|
|
45
|
+
private contextPath(): string {
|
|
46
|
+
return path.join(this.baseDir, 'context', `${this.photonName}.json`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* List all named instances by scanning the state directory
|
|
51
|
+
*/
|
|
52
|
+
async list(): Promise<string[]> {
|
|
53
|
+
try {
|
|
54
|
+
const files = await fs.readdir(this.stateDir());
|
|
55
|
+
return files
|
|
56
|
+
.filter((f) => f.endsWith('.json'))
|
|
57
|
+
.map((f) => f.slice(0, -5));
|
|
58
|
+
} catch (error: any) {
|
|
59
|
+
if (error.code === 'ENOENT') return [];
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get the current instance name. Returns 'default' if none set.
|
|
66
|
+
*/
|
|
67
|
+
async getCurrent(): Promise<string> {
|
|
68
|
+
try {
|
|
69
|
+
const content = await fs.readFile(this.contextPath(), 'utf-8');
|
|
70
|
+
const data = JSON.parse(content);
|
|
71
|
+
return data.current || 'default';
|
|
72
|
+
} catch (error: any) {
|
|
73
|
+
if (error.code === 'ENOENT') return 'default';
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Set the current instance name
|
|
80
|
+
*/
|
|
81
|
+
async setCurrent(instanceName: string): Promise<void> {
|
|
82
|
+
const filePath = this.contextPath();
|
|
83
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
84
|
+
await fs.writeFile(filePath, JSON.stringify({ current: instanceName }, null, 2));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Load state for an instance. Defaults to current instance.
|
|
89
|
+
*/
|
|
90
|
+
async load<T = Record<string, unknown>>(instanceName?: string): Promise<T | null> {
|
|
91
|
+
const name = instanceName ?? await this.getCurrent();
|
|
92
|
+
const filePath = InstanceStore.statePath(this.photonName, name, this.baseDir);
|
|
93
|
+
try {
|
|
94
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
95
|
+
return JSON.parse(content) as T;
|
|
96
|
+
} catch (error: any) {
|
|
97
|
+
if (error.code === 'ENOENT') return null;
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Save state for an instance
|
|
104
|
+
*/
|
|
105
|
+
async save(instanceName: string, state: Record<string, unknown>): Promise<void> {
|
|
106
|
+
const filePath = InstanceStore.statePath(this.photonName, instanceName, this.baseDir);
|
|
107
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
108
|
+
await fs.writeFile(filePath, JSON.stringify(state, null, 2));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Delete an instance's state
|
|
113
|
+
*/
|
|
114
|
+
async delete(instanceName: string): Promise<boolean> {
|
|
115
|
+
const filePath = InstanceStore.statePath(this.photonName, instanceName, this.baseDir);
|
|
116
|
+
try {
|
|
117
|
+
await fs.unlink(filePath);
|
|
118
|
+
return true;
|
|
119
|
+
} catch (error: any) {
|
|
120
|
+
if (error.code === 'ENOENT') return false;
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Check if an instance exists. Defaults to current instance.
|
|
127
|
+
*/
|
|
128
|
+
async exists(instanceName?: string): Promise<boolean> {
|
|
129
|
+
const name = instanceName ?? await this.getCurrent();
|
|
130
|
+
const filePath = InstanceStore.statePath(this.photonName, name, this.baseDir);
|
|
131
|
+
try {
|
|
132
|
+
await fs.access(filePath);
|
|
133
|
+
return true;
|
|
134
|
+
} catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get the file path for instance state
|
|
141
|
+
*/
|
|
142
|
+
static statePath(photonName: string, instanceName: string, baseDir?: string): string {
|
|
143
|
+
const dir = baseDir || getBaseDir();
|
|
144
|
+
const name = instanceName || 'default';
|
|
145
|
+
return path.join(dir, 'state', photonName, `${name}.json`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get the context file path for a photon
|
|
150
|
+
*/
|
|
151
|
+
static contextPath(photonName: string, baseDir?: string): string {
|
|
152
|
+
const dir = baseDir || getBaseDir();
|
|
153
|
+
return path.join(dir, 'context', `${photonName}.json`);
|
|
154
|
+
}
|
|
155
|
+
}
|
package/src/memory.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Scoped Memory System
|
|
3
3
|
*
|
|
4
4
|
* Framework-level key-value storage for photons that eliminates
|
|
5
|
-
* boilerplate file I/O. Available as `this.memory` on
|
|
5
|
+
* boilerplate file I/O. Available as `this.memory` on Photon.
|
|
6
6
|
*
|
|
7
7
|
* Three scopes:
|
|
8
8
|
* | Scope | Meaning | Storage |
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* ```typescript
|
|
16
|
-
* export default class TodoList extends
|
|
16
|
+
* export default class TodoList extends Photon {
|
|
17
17
|
* async add({ text }: { text: string }) {
|
|
18
18
|
* const items = await this.memory.get<Task[]>('items') ?? [];
|
|
19
19
|
* items.push({ id: crypto.randomUUID(), text });
|