@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.
Files changed (57) hide show
  1. package/dist/base.d.ts +7 -7
  2. package/dist/base.d.ts.map +1 -1
  3. package/dist/base.js +8 -8
  4. package/dist/base.js.map +1 -1
  5. package/dist/collections/Collection.d.ts +2 -2
  6. package/dist/collections/Collection.js +2 -2
  7. package/dist/compiler.js +7 -7
  8. package/dist/compiler.js.map +1 -1
  9. package/dist/config.d.ts +1 -1
  10. package/dist/config.js +1 -1
  11. package/dist/index.d.ts +7 -3
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +16 -4
  14. package/dist/index.js.map +1 -1
  15. package/dist/instance-store.d.ts +64 -0
  16. package/dist/instance-store.d.ts.map +1 -0
  17. package/dist/instance-store.js +144 -0
  18. package/dist/instance-store.js.map +1 -0
  19. package/dist/memory.d.ts +2 -2
  20. package/dist/memory.js +2 -2
  21. package/dist/middleware.d.ts +69 -0
  22. package/dist/middleware.d.ts.map +1 -0
  23. package/dist/middleware.js +570 -0
  24. package/dist/middleware.js.map +1 -0
  25. package/dist/schema-extractor.d.ts +111 -1
  26. package/dist/schema-extractor.d.ts.map +1 -1
  27. package/dist/schema-extractor.js +333 -2
  28. package/dist/schema-extractor.js.map +1 -1
  29. package/dist/stateful.d.ts +2 -0
  30. package/dist/stateful.d.ts.map +1 -1
  31. package/dist/stateful.js +2 -0
  32. package/dist/stateful.js.map +1 -1
  33. package/dist/types.d.ts +111 -5
  34. package/dist/types.d.ts.map +1 -1
  35. package/dist/types.js.map +1 -1
  36. package/dist/utils/duration.d.ts +24 -0
  37. package/dist/utils/duration.d.ts.map +1 -0
  38. package/dist/utils/duration.js +64 -0
  39. package/dist/utils/duration.js.map +1 -0
  40. package/dist/watcher.d.ts +62 -0
  41. package/dist/watcher.d.ts.map +1 -0
  42. package/dist/watcher.js +270 -0
  43. package/dist/watcher.js.map +1 -0
  44. package/package.json +2 -2
  45. package/src/base.ts +8 -8
  46. package/src/collections/Collection.ts +2 -2
  47. package/src/compiler.ts +7 -7
  48. package/src/config.ts +1 -1
  49. package/src/index.ts +34 -4
  50. package/src/instance-store.ts +155 -0
  51. package/src/memory.ts +2 -2
  52. package/src/middleware.ts +714 -0
  53. package/src/schema-extractor.ts +353 -2
  54. package/src/stateful.ts +4 -0
  55. package/src/types.ts +106 -5
  56. package/src/utils/duration.ts +67 -0
  57. 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 PhotonMCP.
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 PhotonMCP {
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 });