@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,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Duration and rate parsing utilities for functional tags
|
|
3
|
+
*
|
|
4
|
+
* Supports duration strings: 30s, 5m, 1h, 1d, 500ms
|
|
5
|
+
* Supports rate expressions: 10/min, 100/h, 5/s
|
|
6
|
+
*/
|
|
7
|
+
const DURATION_MULTIPLIERS = {
|
|
8
|
+
ms: 1,
|
|
9
|
+
s: 1_000,
|
|
10
|
+
sec: 1_000,
|
|
11
|
+
m: 60_000,
|
|
12
|
+
min: 60_000,
|
|
13
|
+
h: 3_600_000,
|
|
14
|
+
hr: 3_600_000,
|
|
15
|
+
d: 86_400_000,
|
|
16
|
+
day: 86_400_000,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Parse a duration string into milliseconds
|
|
20
|
+
* @example parseDuration('30s') → 30000
|
|
21
|
+
* @example parseDuration('5m') → 300000
|
|
22
|
+
* @example parseDuration('500ms') → 500
|
|
23
|
+
* @example parseDuration('1234') → 1234 (raw ms fallback)
|
|
24
|
+
*/
|
|
25
|
+
export function parseDuration(input) {
|
|
26
|
+
const trimmed = input.trim();
|
|
27
|
+
const match = trimmed.match(/^(\d+(?:\.\d+)?)(ms|s|sec|m|min|h|hr|d|day)$/i);
|
|
28
|
+
if (match) {
|
|
29
|
+
const value = parseFloat(match[1]);
|
|
30
|
+
const unit = match[2].toLowerCase();
|
|
31
|
+
return Math.round(value * DURATION_MULTIPLIERS[unit]);
|
|
32
|
+
}
|
|
33
|
+
// Fallback: raw milliseconds
|
|
34
|
+
const raw = parseInt(trimmed, 10);
|
|
35
|
+
return isNaN(raw) ? 0 : raw;
|
|
36
|
+
}
|
|
37
|
+
const RATE_WINDOW_MULTIPLIERS = {
|
|
38
|
+
s: 1_000,
|
|
39
|
+
sec: 1_000,
|
|
40
|
+
m: 60_000,
|
|
41
|
+
min: 60_000,
|
|
42
|
+
h: 3_600_000,
|
|
43
|
+
hr: 3_600_000,
|
|
44
|
+
d: 86_400_000,
|
|
45
|
+
day: 86_400_000,
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Parse a rate expression into count and window
|
|
49
|
+
* @example parseRate('10/min') → { count: 10, windowMs: 60000 }
|
|
50
|
+
* @example parseRate('100/h') → { count: 100, windowMs: 3600000 }
|
|
51
|
+
*/
|
|
52
|
+
export function parseRate(input) {
|
|
53
|
+
const trimmed = input.trim();
|
|
54
|
+
const match = trimmed.match(/^(\d+)\/(s|sec|m|min|h|hr|d|day)$/i);
|
|
55
|
+
if (match) {
|
|
56
|
+
const count = parseInt(match[1], 10);
|
|
57
|
+
const unit = match[2].toLowerCase();
|
|
58
|
+
return { count, windowMs: RATE_WINDOW_MULTIPLIERS[unit] };
|
|
59
|
+
}
|
|
60
|
+
// Fallback: treat as count per minute
|
|
61
|
+
const count = parseInt(trimmed, 10);
|
|
62
|
+
return { count: isNaN(count) ? 10 : count, windowMs: 60_000 };
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.js","sourceRoot":"","sources":["../../src/utils/duration.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,oBAAoB,GAA2B;IACnD,EAAE,EAAE,CAAC;IACL,CAAC,EAAE,KAAK;IACR,GAAG,EAAE,KAAK;IACV,CAAC,EAAE,MAAM;IACT,GAAG,EAAE,MAAM;IACX,CAAC,EAAE,SAAS;IACZ,EAAE,EAAE,SAAS;IACb,CAAC,EAAE,UAAU;IACb,GAAG,EAAE,UAAU;CAChB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;IAC7E,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,6BAA6B;IAC7B,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAClC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC9B,CAAC;AAED,MAAM,uBAAuB,GAA2B;IACtD,CAAC,EAAE,KAAK;IACR,GAAG,EAAE,KAAK;IACV,CAAC,EAAE,MAAM;IACT,GAAG,EAAE,MAAM;IACX,CAAC,EAAE,SAAS;IACZ,EAAE,EAAE,SAAS;IACb,CAAC,EAAE,UAAU;IACb,GAAG,EAAE,UAAU;CAChB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAClE,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACpC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC;IAC5D,CAAC;IACD,sCAAsC;IACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACpC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhotonWatcher
|
|
3
|
+
*
|
|
4
|
+
* Reusable file watcher for .photon.ts/.photon.js files.
|
|
5
|
+
* Extracted from the daemon's battle-tested implementation with:
|
|
6
|
+
* - Symlink resolution (macOS fs.watch fix)
|
|
7
|
+
* - Debouncing (configurable, default 100ms)
|
|
8
|
+
* - Temp file filtering (.swp, .bak, ~, .DS_Store, vim 4913)
|
|
9
|
+
* - Rename handling (macOS sed -i new inode → re-establish watcher)
|
|
10
|
+
* - Directory watching for added/removed photons
|
|
11
|
+
*
|
|
12
|
+
* Zero new dependencies — uses Node.js fs.watch().
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'events';
|
|
15
|
+
export interface PhotonWatcherOptions {
|
|
16
|
+
/** Directories to scan for photon files */
|
|
17
|
+
directories: string[];
|
|
18
|
+
/** File extensions to watch (default: ['.photon.ts', '.photon.js']) */
|
|
19
|
+
extensions?: string[];
|
|
20
|
+
/** Debounce interval in ms (default: 100) */
|
|
21
|
+
debounceMs?: number;
|
|
22
|
+
/** Watch directories for new/removed files (default: true) */
|
|
23
|
+
watchDirectories?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare class PhotonWatcher extends EventEmitter {
|
|
26
|
+
private options;
|
|
27
|
+
private fileWatchers;
|
|
28
|
+
private dirWatchers;
|
|
29
|
+
private debounceTimers;
|
|
30
|
+
/** Maps watchPath (real) → { photonName, originalPath } */
|
|
31
|
+
private watchedFiles;
|
|
32
|
+
/** Tracks known photon files per directory for diff-based add/remove detection */
|
|
33
|
+
private knownFiles;
|
|
34
|
+
private running;
|
|
35
|
+
constructor(options: PhotonWatcherOptions);
|
|
36
|
+
/**
|
|
37
|
+
* Start watching. Scans directories for existing photon files and sets up watchers.
|
|
38
|
+
*/
|
|
39
|
+
start(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Stop all watchers and clean up.
|
|
42
|
+
*/
|
|
43
|
+
stop(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Watch a specific photon file. Called automatically during scan,
|
|
46
|
+
* but can also be called manually for dynamically discovered files.
|
|
47
|
+
*/
|
|
48
|
+
watchFile(photonName: string, filePath: string): void;
|
|
49
|
+
/**
|
|
50
|
+
* Stop watching a specific file by its original path.
|
|
51
|
+
*/
|
|
52
|
+
unwatchFile(filePath: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* Get a map of currently watched files: photonName → originalPath
|
|
55
|
+
*/
|
|
56
|
+
getWatchedFiles(): Map<string, string>;
|
|
57
|
+
private handleFileEvent;
|
|
58
|
+
private unwatchByRealPath;
|
|
59
|
+
private scanDirectory;
|
|
60
|
+
private watchDirectory;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAKtC,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AA8BD,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,YAAY,CAAmC;IACvD,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,cAAc,CAAoD;IAC1E,2DAA2D;IAC3D,OAAO,CAAC,YAAY,CAAmE;IACvF,kFAAkF;IAClF,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,oBAAoB;IAUzC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3B;;;OAGG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IA6BrD;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAUnC;;OAEG;IACH,eAAe,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAYtC,OAAO,CAAC,eAAe;IAqCvB,OAAO,CAAC,iBAAiB;YAeX,aAAa;IAkD3B,OAAO,CAAC,cAAc;CAkCvB"}
|
package/dist/watcher.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhotonWatcher
|
|
3
|
+
*
|
|
4
|
+
* Reusable file watcher for .photon.ts/.photon.js files.
|
|
5
|
+
* Extracted from the daemon's battle-tested implementation with:
|
|
6
|
+
* - Symlink resolution (macOS fs.watch fix)
|
|
7
|
+
* - Debouncing (configurable, default 100ms)
|
|
8
|
+
* - Temp file filtering (.swp, .bak, ~, .DS_Store, vim 4913)
|
|
9
|
+
* - Rename handling (macOS sed -i new inode → re-establish watcher)
|
|
10
|
+
* - Directory watching for added/removed photons
|
|
11
|
+
*
|
|
12
|
+
* Zero new dependencies — uses Node.js fs.watch().
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'events';
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as fsPromises from 'fs/promises';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
/** Temp/junk files to ignore */
|
|
19
|
+
const IGNORED_PATTERNS = [
|
|
20
|
+
/\.swp$/,
|
|
21
|
+
/\.bak$/,
|
|
22
|
+
/~$/,
|
|
23
|
+
/\.DS_Store$/,
|
|
24
|
+
/^4913$/, // vim temp file check
|
|
25
|
+
/\.tmp$/,
|
|
26
|
+
/^\.#/, // emacs lock files
|
|
27
|
+
];
|
|
28
|
+
function isIgnored(filename) {
|
|
29
|
+
return IGNORED_PATTERNS.some((p) => p.test(filename));
|
|
30
|
+
}
|
|
31
|
+
function isPhotonFile(filename, extensions) {
|
|
32
|
+
return extensions.some((ext) => filename.endsWith(ext));
|
|
33
|
+
}
|
|
34
|
+
function photonNameFromFile(filename, extensions) {
|
|
35
|
+
for (const ext of extensions) {
|
|
36
|
+
if (filename.endsWith(ext)) {
|
|
37
|
+
return filename.slice(0, -ext.length);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
export class PhotonWatcher extends EventEmitter {
|
|
43
|
+
options;
|
|
44
|
+
fileWatchers = new Map();
|
|
45
|
+
dirWatchers = new Map();
|
|
46
|
+
debounceTimers = new Map();
|
|
47
|
+
/** Maps watchPath (real) → { photonName, originalPath } */
|
|
48
|
+
watchedFiles = new Map();
|
|
49
|
+
/** Tracks known photon files per directory for diff-based add/remove detection */
|
|
50
|
+
knownFiles = new Map();
|
|
51
|
+
running = false;
|
|
52
|
+
constructor(options) {
|
|
53
|
+
super();
|
|
54
|
+
this.options = {
|
|
55
|
+
directories: options.directories,
|
|
56
|
+
extensions: options.extensions ?? ['.photon.ts', '.photon.js'],
|
|
57
|
+
debounceMs: options.debounceMs ?? 100,
|
|
58
|
+
watchDirectories: options.watchDirectories ?? true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Start watching. Scans directories for existing photon files and sets up watchers.
|
|
63
|
+
*/
|
|
64
|
+
async start() {
|
|
65
|
+
if (this.running)
|
|
66
|
+
return;
|
|
67
|
+
this.running = true;
|
|
68
|
+
for (const dir of this.options.directories) {
|
|
69
|
+
await this.scanDirectory(dir);
|
|
70
|
+
if (this.options.watchDirectories) {
|
|
71
|
+
this.watchDirectory(dir);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Stop all watchers and clean up.
|
|
77
|
+
*/
|
|
78
|
+
async stop() {
|
|
79
|
+
this.running = false;
|
|
80
|
+
for (const [, watcher] of this.fileWatchers) {
|
|
81
|
+
watcher.close();
|
|
82
|
+
}
|
|
83
|
+
this.fileWatchers.clear();
|
|
84
|
+
for (const [, watcher] of this.dirWatchers) {
|
|
85
|
+
watcher.close();
|
|
86
|
+
}
|
|
87
|
+
this.dirWatchers.clear();
|
|
88
|
+
for (const [, timer] of this.debounceTimers) {
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
}
|
|
91
|
+
this.debounceTimers.clear();
|
|
92
|
+
this.watchedFiles.clear();
|
|
93
|
+
this.knownFiles.clear();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Watch a specific photon file. Called automatically during scan,
|
|
97
|
+
* but can also be called manually for dynamically discovered files.
|
|
98
|
+
*/
|
|
99
|
+
watchFile(photonName, filePath) {
|
|
100
|
+
// Resolve symlink so fs.watch() fires when the real file changes.
|
|
101
|
+
// On macOS, fs.watch on a symlink only detects changes to the symlink inode itself.
|
|
102
|
+
let watchPath = filePath;
|
|
103
|
+
try {
|
|
104
|
+
watchPath = fs.realpathSync(filePath);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Symlink target doesn't exist yet — fall back to original path
|
|
108
|
+
}
|
|
109
|
+
if (this.fileWatchers.has(watchPath))
|
|
110
|
+
return;
|
|
111
|
+
try {
|
|
112
|
+
const watcher = fs.watch(watchPath, (eventType) => {
|
|
113
|
+
this.handleFileEvent(eventType, watchPath, photonName, filePath);
|
|
114
|
+
});
|
|
115
|
+
watcher.on('error', (err) => {
|
|
116
|
+
this.emit('error', err, { photonName, path: filePath });
|
|
117
|
+
this.unwatchByRealPath(watchPath);
|
|
118
|
+
});
|
|
119
|
+
this.fileWatchers.set(watchPath, watcher);
|
|
120
|
+
this.watchedFiles.set(watchPath, { photonName, originalPath: filePath });
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
this.emit('error', err, { photonName, path: filePath });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Stop watching a specific file by its original path.
|
|
128
|
+
*/
|
|
129
|
+
unwatchFile(filePath) {
|
|
130
|
+
// Find the real path entry
|
|
131
|
+
for (const [watchPath, info] of this.watchedFiles) {
|
|
132
|
+
if (info.originalPath === filePath) {
|
|
133
|
+
this.unwatchByRealPath(watchPath);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get a map of currently watched files: photonName → originalPath
|
|
140
|
+
*/
|
|
141
|
+
getWatchedFiles() {
|
|
142
|
+
const result = new Map();
|
|
143
|
+
for (const [, info] of this.watchedFiles) {
|
|
144
|
+
result.set(info.photonName, info.originalPath);
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
149
|
+
// Internal
|
|
150
|
+
// ──────────────────────────────────────────────────────────────────────────────
|
|
151
|
+
handleFileEvent(eventType, watchPath, photonName, originalPath) {
|
|
152
|
+
// Debounce
|
|
153
|
+
const existing = this.debounceTimers.get(watchPath);
|
|
154
|
+
if (existing)
|
|
155
|
+
clearTimeout(existing);
|
|
156
|
+
this.debounceTimers.set(watchPath, setTimeout(() => {
|
|
157
|
+
this.debounceTimers.delete(watchPath);
|
|
158
|
+
// On macOS, editors like sed -i replace the file (new inode),
|
|
159
|
+
// killing the watcher. Re-watch via original path to re-resolve symlinks.
|
|
160
|
+
if (eventType === 'rename') {
|
|
161
|
+
this.unwatchByRealPath(watchPath);
|
|
162
|
+
if (fs.existsSync(originalPath)) {
|
|
163
|
+
this.watchFile(photonName, originalPath);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
this.emit('removed', photonName);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!fs.existsSync(originalPath)) {
|
|
171
|
+
this.emit('removed', photonName);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
this.emit('changed', photonName, originalPath);
|
|
175
|
+
}, this.options.debounceMs));
|
|
176
|
+
}
|
|
177
|
+
unwatchByRealPath(watchPath) {
|
|
178
|
+
const watcher = this.fileWatchers.get(watchPath);
|
|
179
|
+
if (watcher) {
|
|
180
|
+
watcher.close();
|
|
181
|
+
this.fileWatchers.delete(watchPath);
|
|
182
|
+
}
|
|
183
|
+
this.watchedFiles.delete(watchPath);
|
|
184
|
+
const timer = this.debounceTimers.get(watchPath);
|
|
185
|
+
if (timer) {
|
|
186
|
+
clearTimeout(timer);
|
|
187
|
+
this.debounceTimers.delete(watchPath);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async scanDirectory(dir) {
|
|
191
|
+
let entries;
|
|
192
|
+
try {
|
|
193
|
+
entries = await fsPromises.readdir(dir, { withFileTypes: true });
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (error.code !== 'ENOENT') {
|
|
197
|
+
this.emit('error', error, { directory: dir });
|
|
198
|
+
}
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const currentFiles = new Set();
|
|
202
|
+
for (const entry of entries) {
|
|
203
|
+
if (!entry.isFile() && !entry.isSymbolicLink())
|
|
204
|
+
continue;
|
|
205
|
+
if (isIgnored(entry.name))
|
|
206
|
+
continue;
|
|
207
|
+
if (!isPhotonFile(entry.name, this.options.extensions))
|
|
208
|
+
continue;
|
|
209
|
+
const photonName = photonNameFromFile(entry.name, this.options.extensions);
|
|
210
|
+
if (!photonName)
|
|
211
|
+
continue;
|
|
212
|
+
const filePath = path.join(dir, entry.name);
|
|
213
|
+
currentFiles.add(entry.name);
|
|
214
|
+
// Only emit 'added' and watch if this is a new file
|
|
215
|
+
const known = this.knownFiles.get(dir);
|
|
216
|
+
if (!known || !known.has(entry.name)) {
|
|
217
|
+
this.emit('added', photonName, filePath);
|
|
218
|
+
this.watchFile(photonName, filePath);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Detect removals (files that were known but no longer present)
|
|
222
|
+
const previousFiles = this.knownFiles.get(dir);
|
|
223
|
+
if (previousFiles) {
|
|
224
|
+
for (const filename of previousFiles) {
|
|
225
|
+
if (!currentFiles.has(filename)) {
|
|
226
|
+
const photonName = photonNameFromFile(filename, this.options.extensions);
|
|
227
|
+
if (photonName) {
|
|
228
|
+
const filePath = path.join(dir, filename);
|
|
229
|
+
this.unwatchFile(filePath);
|
|
230
|
+
this.emit('removed', photonName);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
this.knownFiles.set(dir, currentFiles);
|
|
236
|
+
}
|
|
237
|
+
watchDirectory(dir) {
|
|
238
|
+
if (this.dirWatchers.has(dir))
|
|
239
|
+
return;
|
|
240
|
+
try {
|
|
241
|
+
const watcher = fs.watch(dir, (eventType, filename) => {
|
|
242
|
+
if (!filename)
|
|
243
|
+
return;
|
|
244
|
+
if (isIgnored(filename))
|
|
245
|
+
return;
|
|
246
|
+
if (!isPhotonFile(filename, this.options.extensions))
|
|
247
|
+
return;
|
|
248
|
+
// Debounce directory events
|
|
249
|
+
const key = `dir:${dir}`;
|
|
250
|
+
const existing = this.debounceTimers.get(key);
|
|
251
|
+
if (existing)
|
|
252
|
+
clearTimeout(existing);
|
|
253
|
+
this.debounceTimers.set(key, setTimeout(() => {
|
|
254
|
+
this.debounceTimers.delete(key);
|
|
255
|
+
if (this.running) {
|
|
256
|
+
this.scanDirectory(dir);
|
|
257
|
+
}
|
|
258
|
+
}, this.options.debounceMs));
|
|
259
|
+
});
|
|
260
|
+
watcher.on('error', (err) => {
|
|
261
|
+
this.emit('error', err, { directory: dir });
|
|
262
|
+
});
|
|
263
|
+
this.dirWatchers.set(dir, watcher);
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
this.emit('error', err, { directory: dir });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portel/photon-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "Core library for parsing, loading, and managing .photon.ts files - runtime-agnostic foundation for building custom Photon runtimes",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"build": "tsc",
|
|
34
34
|
"clean": "rm -rf dist",
|
|
35
35
|
"prepublishOnly": "npm run clean && npm run build",
|
|
36
|
-
"test": "npm run build && npx tsx tests/channels.test.ts && npx tsx tests/shared-utils.test.ts && npx tsx tests/collections.test.ts && npx tsx tests/audit.test.ts && npx tsx tests/memory.test.ts",
|
|
36
|
+
"test": "npm run build && npx tsx tests/channels.test.ts && npx tsx tests/shared-utils.test.ts && npx tsx tests/collections.test.ts && npx tsx tests/audit.test.ts && npx tsx tests/memory.test.ts && npx tsx tests/instance-store.test.ts && npx tsx tests/watcher.test.ts",
|
|
37
37
|
"test:channels": "npx tsx tests/channels.test.ts"
|
|
38
38
|
},
|
|
39
39
|
"keywords": [
|
package/src/base.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Photon Base Class
|
|
3
3
|
*
|
|
4
|
-
* Optional base class for creating
|
|
4
|
+
* Optional base class for creating Photons.
|
|
5
5
|
* You don't need to extend this - any class with async methods works!
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
8
|
* ```typescript
|
|
9
|
-
* export default class Calculator extends
|
|
9
|
+
* export default class Calculator extends Photon {
|
|
10
10
|
* /**
|
|
11
11
|
* * Add two numbers together
|
|
12
12
|
* * @param a First number
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
*
|
|
30
30
|
* With MCP access (requires runtime support):
|
|
31
31
|
* ```typescript
|
|
32
|
-
* export default class SlackReporter extends
|
|
32
|
+
* export default class SlackReporter extends Photon {
|
|
33
33
|
* async report() {
|
|
34
34
|
* const github = this.mcp('github');
|
|
35
35
|
* const issues = await github.call('list_issues', { repo: 'foo/bar' });
|
|
@@ -46,13 +46,13 @@ import { withLock as withLockHelper } from './decorators.js';
|
|
|
46
46
|
import { MemoryProvider } from './memory.js';
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
* Simple base class for creating
|
|
49
|
+
* Simple base class for creating Photons
|
|
50
50
|
*
|
|
51
|
-
* - Class name =
|
|
51
|
+
* - Class name = Photon name
|
|
52
52
|
* - Public async methods = Tools
|
|
53
53
|
* - Return value = Tool result
|
|
54
54
|
*/
|
|
55
|
-
export class
|
|
55
|
+
export class Photon {
|
|
56
56
|
/**
|
|
57
57
|
* Photon name (MCP name) - set by runtime loader
|
|
58
58
|
* Used to identify the source of emitted events for injected photon routing
|
|
@@ -247,7 +247,7 @@ export class PhotonMCP {
|
|
|
247
247
|
|
|
248
248
|
// Get all property names from prototype chain
|
|
249
249
|
let current = prototype;
|
|
250
|
-
while (current && current !==
|
|
250
|
+
while (current && current !== Photon.prototype) {
|
|
251
251
|
Object.getOwnPropertyNames(current).forEach((name) => {
|
|
252
252
|
// Skip private methods (starting with _) and convention methods
|
|
253
253
|
if (
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```typescript
|
|
10
|
-
* import {
|
|
10
|
+
* import { Photon, Collection } from '@portel/photon-core';
|
|
11
11
|
*
|
|
12
|
-
* export default class ProductCatalog extends
|
|
12
|
+
* export default class ProductCatalog extends Photon {
|
|
13
13
|
* products = new Collection<Product>();
|
|
14
14
|
*
|
|
15
15
|
* async catalog() {
|
package/src/compiler.ts
CHANGED
|
@@ -21,9 +21,9 @@ import * as crypto from 'crypto';
|
|
|
21
21
|
* @returns Absolute path to the compiled .mjs file
|
|
22
22
|
*/
|
|
23
23
|
/**
|
|
24
|
-
* Transform arrays to reactive collections for
|
|
24
|
+
* Transform arrays to reactive collections for Photon classes
|
|
25
25
|
*
|
|
26
|
-
* ZERO-EFFORT REACTIVITY: If a class extends PhotonMCP and has array properties,
|
|
26
|
+
* ZERO-EFFORT REACTIVITY: If a class extends Photon (or PhotonMCP) and has array properties,
|
|
27
27
|
* this transform automatically:
|
|
28
28
|
* 1. Injects `import { Array as ReactiveArray } from '@portel/photon-core'`
|
|
29
29
|
* 2. Transforms `= []` to `= new ReactiveArray()` for class properties
|
|
@@ -32,23 +32,23 @@ import * as crypto from 'crypto';
|
|
|
32
32
|
*
|
|
33
33
|
* ```typescript
|
|
34
34
|
* // Developer writes this (normal TypeScript):
|
|
35
|
-
* export default class TodoList extends
|
|
35
|
+
* export default class TodoList extends Photon {
|
|
36
36
|
* items: Task[] = [];
|
|
37
37
|
* async add(text: string) { this.items.push({...}); }
|
|
38
38
|
* }
|
|
39
39
|
*
|
|
40
40
|
* // Compiler transforms to:
|
|
41
41
|
* import { Array as ReactiveArray } from '@portel/photon-core';
|
|
42
|
-
* export default class TodoList extends
|
|
42
|
+
* export default class TodoList extends Photon {
|
|
43
43
|
* items = new ReactiveArray();
|
|
44
44
|
* async add(text: string) { this.items.push({...}); } // Auto-emits!
|
|
45
45
|
* }
|
|
46
46
|
* ```
|
|
47
47
|
*/
|
|
48
48
|
function transformReactiveCollections(source: string): string {
|
|
49
|
-
// Check if this is a
|
|
50
|
-
const
|
|
51
|
-
if (!
|
|
49
|
+
// Check if this is a Photon class (extends Photon or extends PhotonMCP)
|
|
50
|
+
const isPhotonClass = /class\s+\w+\s+extends\s+(?:Photon|PhotonMCP)\b/.test(source);
|
|
51
|
+
if (!isPhotonClass) return source;
|
|
52
52
|
|
|
53
53
|
// Check if there are array properties with = [] that need transformation
|
|
54
54
|
// Look for patterns like: `items: Type[] = []` or `items = []` (class properties)
|
package/src/config.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* ```typescript
|
|
9
9
|
* import { loadPhotonConfig, savePhotonConfig, getPhotonConfigPath } from '@portel/photon-core';
|
|
10
10
|
*
|
|
11
|
-
* export default class MyPhoton extends
|
|
11
|
+
* export default class MyPhoton extends Photon {
|
|
12
12
|
* async configure(params: { apiKey: string }) {
|
|
13
13
|
* savePhotonConfig('my-photon', params);
|
|
14
14
|
* return { success: true, config: params };
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```typescript
|
|
14
|
-
* import {
|
|
14
|
+
* import { Photon, DependencyManager, SchemaExtractor } from '@portel/photon-core';
|
|
15
15
|
*
|
|
16
16
|
* // Load and parse a Photon class
|
|
17
17
|
* const photonClass = await import('./my-tool.photon.ts');
|
|
@@ -152,13 +152,13 @@ export {
|
|
|
152
152
|
// ===== PHOTON-SPECIFIC EXPORTS =====
|
|
153
153
|
|
|
154
154
|
// Core base class with lifecycle hooks
|
|
155
|
-
export { PhotonMCP } from './base.js';
|
|
155
|
+
export { Photon, Photon as PhotonMCP } from './base.js';
|
|
156
156
|
|
|
157
157
|
// Dependency management
|
|
158
158
|
export { DependencyManager } from './dependency-manager.js';
|
|
159
159
|
|
|
160
160
|
// Schema extraction
|
|
161
|
-
export { SchemaExtractor } from './schema-extractor.js';
|
|
161
|
+
export { SchemaExtractor, detectCapabilities, type PhotonCapability } from './schema-extractor.js';
|
|
162
162
|
|
|
163
163
|
// Path resolution (Photon-specific paths)
|
|
164
164
|
export {
|
|
@@ -433,7 +433,7 @@ export {
|
|
|
433
433
|
} from './audit.js';
|
|
434
434
|
|
|
435
435
|
// ===== SCOPED MEMORY =====
|
|
436
|
-
// Framework-level key-value storage (this.memory on
|
|
436
|
+
// Framework-level key-value storage (this.memory on Photon base class)
|
|
437
437
|
export {
|
|
438
438
|
MemoryProvider,
|
|
439
439
|
type MemoryScope,
|
|
@@ -446,6 +446,36 @@ export {
|
|
|
446
446
|
autoDiscoverAssets,
|
|
447
447
|
} from './asset-discovery.js';
|
|
448
448
|
|
|
449
|
+
// ===== DURATION PARSING =====
|
|
450
|
+
// Duration and rate string parsing for functional tags
|
|
451
|
+
export { parseDuration, parseRate } from './utils/duration.js';
|
|
452
|
+
|
|
453
|
+
// ===== EXTENSIBLE MIDDLEWARE =====
|
|
454
|
+
// Registry-based middleware system for functional tags and custom middleware
|
|
455
|
+
export {
|
|
456
|
+
defineMiddleware,
|
|
457
|
+
builtinRegistry,
|
|
458
|
+
MiddlewareRegistry,
|
|
459
|
+
createStateStore,
|
|
460
|
+
buildMiddlewareChain,
|
|
461
|
+
hashParams,
|
|
462
|
+
BUILT_IN_VALIDATORS,
|
|
463
|
+
type MiddlewareDefinition,
|
|
464
|
+
type MiddlewareContext,
|
|
465
|
+
type MiddlewareHandler,
|
|
466
|
+
type MiddlewareState,
|
|
467
|
+
type MiddlewareDeclaration,
|
|
468
|
+
type NextFn,
|
|
469
|
+
} from './middleware.js';
|
|
470
|
+
|
|
471
|
+
// ===== FILE WATCHING =====
|
|
472
|
+
// Reusable photon file watcher with symlink resolution, debouncing, rename handling
|
|
473
|
+
export { PhotonWatcher, type PhotonWatcherOptions } from './watcher.js';
|
|
474
|
+
|
|
475
|
+
// ===== INSTANCE STORE =====
|
|
476
|
+
// Named instance state persistence for daemon, NCP, Lumina
|
|
477
|
+
export { InstanceStore, type InstanceStoreOptions } from './instance-store.js';
|
|
478
|
+
|
|
449
479
|
// ===== MANAGED COLLECTIONS =====
|
|
450
480
|
// Auto-emit events on mutations for seamless real-time sync
|
|
451
481
|
export {
|