@ajclarkson/homerun 0.0.1-edge.ea60fe0 → 0.0.1-edge.f81eb62

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.
@@ -4,5 +4,6 @@ type Importer = (dataUri: string) => Promise<{
4
4
  }>;
5
5
  export declare function _reloadFile(filePath: string, registry: AutomationRegistry, importer?: Importer, fileToIds?: Map<string, string[]>): Promise<void>;
6
6
  export declare function _deleteFile(filePath: string, registry: AutomationRegistry, fileToIds?: Map<string, string[]>): void;
7
+ export declare function rescanAutomations(automationsDir: string, registry: AutomationRegistry, fileToIds?: Map<string, string[]>, importer?: Importer): Promise<void>;
7
8
  export declare function startHotReload(automationsDir: string, registry: AutomationRegistry): void;
8
9
  export {};
@@ -45,16 +45,42 @@ export function _deleteFile(filePath, registry, fileToIds = moduleFileToIds) {
45
45
  }
46
46
  fileToIds.delete(filePath);
47
47
  }
48
+ const isAutomationFile = (f) => f.endsWith('.ts') &&
49
+ !f.endsWith('.test.ts') &&
50
+ !f.includes('node_modules') &&
51
+ !f.includes('.d.ts') &&
52
+ !f.split(path.sep).includes('types');
53
+ export async function rescanAutomations(automationsDir, registry, fileToIds = moduleFileToIds, importer = defaultImporter) {
54
+ let files = [];
55
+ try {
56
+ const { readdir } = await import('node:fs/promises');
57
+ files = (await readdir(automationsDir, { recursive: true }));
58
+ }
59
+ catch {
60
+ console.warn(`[homerun] AUTOMATIONS_DIR not found: ${automationsDir} — starting with no automations`);
61
+ }
62
+ const currentPaths = new Set(files.filter(isAutomationFile).map((f) => path.join(automationsDir, f)));
63
+ for (const trackedPath of [...fileToIds.keys()]) {
64
+ if (!currentPaths.has(trackedPath)) {
65
+ _deleteFile(trackedPath, registry, fileToIds);
66
+ }
67
+ }
68
+ for (const filePath of currentPaths) {
69
+ await _reloadFile(filePath, registry, importer, fileToIds);
70
+ }
71
+ }
48
72
  export function startHotReload(automationsDir, registry) {
49
73
  const target = process.env.AUTOMATION
50
74
  ? path.join(automationsDir, `${process.env.AUTOMATION}.ts`)
51
75
  : `${automationsDir}/**/*.ts`;
52
76
  const watcher = watch(target, { ignoreInitial: true, ignored: [/node_modules/, /\.test\.ts$/] });
53
- watcher.on('change', (filePath) => {
77
+ const reload = (filePath) => {
54
78
  _reloadFile(filePath, registry).catch((err) => {
55
79
  console.error('[hot-reload] unexpected error:', err);
56
80
  });
57
- });
81
+ };
82
+ watcher.on('add', reload);
83
+ watcher.on('change', reload);
58
84
  watcher.on('unlink', (filePath) => {
59
85
  _deleteFile(filePath, registry);
60
86
  });
package/dist/src/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import 'dotenv/config';
2
- import { readdir } from 'node:fs/promises';
3
2
  import path from 'node:path';
4
3
  import { connect } from 'mqtt';
5
4
  import { HAClient } from './framework/ha-client.js';
@@ -9,7 +8,7 @@ import { TimerManager } from './framework/timer-manager.js';
9
8
  import { ActionRuntime } from './framework/action-runtime.js';
10
9
  import { TriggerEngine } from './framework/trigger-engine.js';
11
10
  import { Scheduler } from './framework/scheduler.js';
12
- import { _reloadFile, startHotReload } from './framework/hot-reload.js';
11
+ import { rescanAutomations, startHotReload } from './framework/hot-reload.js';
13
12
  import { runPipeline } from './framework/pipeline.js';
14
13
  import { ApiServer } from './framework/api-server.js';
15
14
  process.on('uncaughtException', (err) => {
@@ -41,24 +40,7 @@ const actionRuntime = new ActionRuntime({
41
40
  });
42
41
  // 3. Initial automation load — must complete before the engine and scheduler start.
43
42
  const automationsDir = path.resolve(process.env.AUTOMATIONS_DIR);
44
- const isAutomation = (f) => f.endsWith('.ts') &&
45
- !f.endsWith('.test.ts') &&
46
- !f.includes('node_modules') &&
47
- !f.includes('.d.ts') &&
48
- !f.split(path.sep).includes('types');
49
- async function loadAutomations() {
50
- let files = [];
51
- try {
52
- files = (await readdir(automationsDir, { recursive: true }));
53
- }
54
- catch {
55
- console.warn(`[homerun] AUTOMATIONS_DIR not found: ${automationsDir} — starting with no automations`);
56
- }
57
- for (const file of files.filter(isAutomation)) {
58
- await _reloadFile(path.join(automationsDir, file), registry);
59
- }
60
- }
61
- await loadAutomations();
43
+ await rescanAutomations(automationsDir, registry);
62
44
  console.log(`[homerun] loaded ${registry.getAll().length} automation(s)`);
63
45
  // 4. Wire up the engine and scheduler.
64
46
  engine = new TriggerEngine(registry, haClient, (automation, event) => {
@@ -72,7 +54,7 @@ scheduler.start();
72
54
  // 5. Start hot-reload watcher (dev) and SIGUSR1 rescan (git-sync sidecar in K8s).
73
55
  startHotReload(automationsDir, registry);
74
56
  async function reload() {
75
- await loadAutomations();
57
+ await rescanAutomations(automationsDir, registry);
76
58
  console.log(`[homerun] rescan complete — ${registry.getAll().length} automation(s) registered`);
77
59
  }
78
60
  process.on('SIGUSR1', () => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ajclarkson/homerun",
3
3
  "type": "module",
4
- "version": "0.0.1-edge.ea60fe0",
4
+ "version": "0.0.1-edge.f81eb62",
5
5
  "description": "TypeScript automation framework for Home Assistant",
6
6
  "files": [
7
7
  "dist"