@digitalpaws/starr 0.0.1

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 (290) hide show
  1. package/README.rst +75 -0
  2. package/data/.nfs.811bbb0d.43c34.4 +0 -0
  3. package/data/.nfs.81ba4cd0.ffff87804.4 +0 -0
  4. package/data/.nfs.82d127f7.fffff5704.4 +0 -0
  5. package/data/.nfs.82d127fe.fffff5704.4 +0 -0
  6. package/data/.nfs.82d159a5.fffff5aa4.4 +0 -0
  7. package/data/.nfs.82d159aa.fffff5aa4.4 +0 -0
  8. package/data/.nfs.82d15a9c.fffff5bb4.4 +0 -0
  9. package/data/.nfs.82d15aa1.fffff5bb4.4 +0 -0
  10. package/data/.nfs.82d15c65.fffff5c24.4 +0 -0
  11. package/data/.nfs.82d1852c.fffff5e24.4 +0 -0
  12. package/data/.nfs.82d18536.fffff5e24.4 +0 -0
  13. package/data/.nfs.82d18dbe.fffff5e44.4 +0 -0
  14. package/data/.nfs.82d18dc6.fffff5e44.4 +0 -0
  15. package/data/.nfs.8309a3f5.fffff82f4.4 +0 -0
  16. package/data/.nfs.8309a3fe.fffff82f4.4 +0 -0
  17. package/data/.nfs.8314122e.fffffb044.4 +0 -0
  18. package/data/.nfs.83141236.fffffb044.4 +0 -0
  19. package/data/.nfs.833c4fe9.fffffbc34.4 +0 -0
  20. package/data/.nfs.833c4ff3.fffffbc34.4 +0 -0
  21. package/data/logs/starr.log +66244 -0
  22. package/data/starr.db +0 -0
  23. package/data/starr.db-shm +0 -0
  24. package/data/starr.db-wal +0 -0
  25. package/data/starr.jwt +1 -0
  26. package/data/starr.key +1 -0
  27. package/dist/auth/auth-service.d.ts +42 -0
  28. package/dist/auth/auth-service.d.ts.map +1 -0
  29. package/dist/auth/auth-service.js +226 -0
  30. package/dist/auth/auth-service.js.map +1 -0
  31. package/dist/auth/http-server.d.ts +16 -0
  32. package/dist/auth/http-server.d.ts.map +1 -0
  33. package/dist/auth/http-server.js +207 -0
  34. package/dist/auth/http-server.js.map +1 -0
  35. package/dist/auth/jwt-secret.d.ts +6 -0
  36. package/dist/auth/jwt-secret.d.ts.map +1 -0
  37. package/dist/auth/jwt-secret.js +51 -0
  38. package/dist/auth/jwt-secret.js.map +1 -0
  39. package/dist/auth/oidc/oidc-flow.d.ts +60 -0
  40. package/dist/auth/oidc/oidc-flow.d.ts.map +1 -0
  41. package/dist/auth/oidc/oidc-flow.js +201 -0
  42. package/dist/auth/oidc/oidc-flow.js.map +1 -0
  43. package/dist/auth/oidc/oidc-provider-service.d.ts +59 -0
  44. package/dist/auth/oidc/oidc-provider-service.d.ts.map +1 -0
  45. package/dist/auth/oidc/oidc-provider-service.js +124 -0
  46. package/dist/auth/oidc/oidc-provider-service.js.map +1 -0
  47. package/dist/auth/oidc/user-service.d.ts +67 -0
  48. package/dist/auth/oidc/user-service.d.ts.map +1 -0
  49. package/dist/auth/oidc/user-service.js +177 -0
  50. package/dist/auth/oidc/user-service.js.map +1 -0
  51. package/dist/auth/rate-limiter.d.ts +31 -0
  52. package/dist/auth/rate-limiter.d.ts.map +1 -0
  53. package/dist/auth/rate-limiter.js +70 -0
  54. package/dist/auth/rate-limiter.js.map +1 -0
  55. package/dist/config/config-service.d.ts +83 -0
  56. package/dist/config/config-service.d.ts.map +1 -0
  57. package/dist/config/config-service.js +309 -0
  58. package/dist/config/config-service.js.map +1 -0
  59. package/dist/config/validate.d.ts +12 -0
  60. package/dist/config/validate.d.ts.map +1 -0
  61. package/dist/config/validate.js +140 -0
  62. package/dist/config/validate.js.map +1 -0
  63. package/dist/content-types/registry.d.ts +75 -0
  64. package/dist/content-types/registry.d.ts.map +1 -0
  65. package/dist/content-types/registry.js +171 -0
  66. package/dist/content-types/registry.js.map +1 -0
  67. package/dist/datastore/db.d.ts +9 -0
  68. package/dist/datastore/db.d.ts.map +1 -0
  69. package/dist/datastore/db.js +27 -0
  70. package/dist/datastore/db.js.map +1 -0
  71. package/dist/datastore/encrypt.d.ts +9 -0
  72. package/dist/datastore/encrypt.d.ts.map +1 -0
  73. package/dist/datastore/encrypt.js +84 -0
  74. package/dist/datastore/encrypt.js.map +1 -0
  75. package/dist/datastore/migrations.d.ts +10 -0
  76. package/dist/datastore/migrations.d.ts.map +1 -0
  77. package/dist/datastore/migrations.js +340 -0
  78. package/dist/datastore/migrations.js.map +1 -0
  79. package/dist/datastore/storage.d.ts +4 -0
  80. package/dist/datastore/storage.d.ts.map +1 -0
  81. package/dist/datastore/storage.js +60 -0
  82. package/dist/datastore/storage.js.map +1 -0
  83. package/dist/events/event-bus.d.ts +15 -0
  84. package/dist/events/event-bus.d.ts.map +1 -0
  85. package/dist/events/event-bus.js +140 -0
  86. package/dist/events/event-bus.js.map +1 -0
  87. package/dist/events/spine.d.ts +31 -0
  88. package/dist/events/spine.d.ts.map +1 -0
  89. package/dist/events/spine.js +68 -0
  90. package/dist/events/spine.js.map +1 -0
  91. package/dist/events/trace.d.ts +24 -0
  92. package/dist/events/trace.d.ts.map +1 -0
  93. package/dist/events/trace.js +33 -0
  94. package/dist/events/trace.js.map +1 -0
  95. package/dist/host.d.ts +65 -0
  96. package/dist/host.d.ts.map +1 -0
  97. package/dist/host.js +94 -0
  98. package/dist/host.js.map +1 -0
  99. package/dist/library/importer.d.ts +29 -0
  100. package/dist/library/importer.d.ts.map +1 -0
  101. package/dist/library/importer.js +145 -0
  102. package/dist/library/importer.js.map +1 -0
  103. package/dist/library/library-cache.d.ts +46 -0
  104. package/dist/library/library-cache.d.ts.map +1 -0
  105. package/dist/library/library-cache.js +152 -0
  106. package/dist/library/library-cache.js.map +1 -0
  107. package/dist/library/library-config.d.ts +12 -0
  108. package/dist/library/library-config.d.ts.map +1 -0
  109. package/dist/library/library-config.js +10 -0
  110. package/dist/library/library-config.js.map +1 -0
  111. package/dist/library/library-scanner.d.ts +62 -0
  112. package/dist/library/library-scanner.d.ts.map +1 -0
  113. package/dist/library/library-scanner.js +336 -0
  114. package/dist/library/library-scanner.js.map +1 -0
  115. package/dist/library/library-service.d.ts +65 -0
  116. package/dist/library/library-service.d.ts.map +1 -0
  117. package/dist/library/library-service.js +197 -0
  118. package/dist/library/library-service.js.map +1 -0
  119. package/dist/library/metadata-matcher.d.ts +56 -0
  120. package/dist/library/metadata-matcher.d.ts.map +1 -0
  121. package/dist/library/metadata-matcher.js +207 -0
  122. package/dist/library/metadata-matcher.js.map +1 -0
  123. package/dist/library/naming.d.ts +8 -0
  124. package/dist/library/naming.d.ts.map +1 -0
  125. package/dist/library/naming.js +47 -0
  126. package/dist/library/naming.js.map +1 -0
  127. package/dist/library/scan-controller.d.ts +95 -0
  128. package/dist/library/scan-controller.d.ts.map +1 -0
  129. package/dist/library/scan-controller.js +379 -0
  130. package/dist/library/scan-controller.js.map +1 -0
  131. package/dist/library/source-resolver.d.ts +17 -0
  132. package/dist/library/source-resolver.d.ts.map +1 -0
  133. package/dist/library/source-resolver.js +75 -0
  134. package/dist/library/source-resolver.js.map +1 -0
  135. package/dist/logging/logger.d.ts +3 -0
  136. package/dist/logging/logger.d.ts.map +1 -0
  137. package/dist/logging/logger.js +107 -0
  138. package/dist/logging/logger.js.map +1 -0
  139. package/dist/main.d.ts +36 -0
  140. package/dist/main.d.ts.map +1 -0
  141. package/dist/main.js +229 -0
  142. package/dist/main.js.map +1 -0
  143. package/dist/plugins/context.d.ts +10 -0
  144. package/dist/plugins/context.d.ts.map +1 -0
  145. package/dist/plugins/context.js +110 -0
  146. package/dist/plugins/context.js.map +1 -0
  147. package/dist/plugins/discovery.d.ts +2 -0
  148. package/dist/plugins/discovery.d.ts.map +1 -0
  149. package/dist/plugins/discovery.js +32 -0
  150. package/dist/plugins/discovery.js.map +1 -0
  151. package/dist/plugins/loader.d.ts +9 -0
  152. package/dist/plugins/loader.d.ts.map +1 -0
  153. package/dist/plugins/loader.js +245 -0
  154. package/dist/plugins/loader.js.map +1 -0
  155. package/dist/state/request-intake.d.ts +26 -0
  156. package/dist/state/request-intake.d.ts.map +1 -0
  157. package/dist/state/request-intake.js +74 -0
  158. package/dist/state/request-intake.js.map +1 -0
  159. package/dist/state/state-store.d.ts +146 -0
  160. package/dist/state/state-store.d.ts.map +1 -0
  161. package/dist/state/state-store.js +465 -0
  162. package/dist/state/state-store.js.map +1 -0
  163. package/dist/state/trace-store.d.ts +12 -0
  164. package/dist/state/trace-store.d.ts.map +1 -0
  165. package/dist/state/trace-store.js +24 -0
  166. package/dist/state/trace-store.js.map +1 -0
  167. package/dist/types/plugin.d.ts +93 -0
  168. package/dist/types/plugin.d.ts.map +1 -0
  169. package/dist/types/plugin.js +4 -0
  170. package/dist/types/plugin.js.map +1 -0
  171. package/dist/web/index.d.ts +10 -0
  172. package/dist/web/index.d.ts.map +1 -0
  173. package/dist/web/index.js +153 -0
  174. package/dist/web/index.js.map +1 -0
  175. package/dist/web/routes/activity.d.ts +6 -0
  176. package/dist/web/routes/activity.d.ts.map +1 -0
  177. package/dist/web/routes/activity.js +31 -0
  178. package/dist/web/routes/activity.js.map +1 -0
  179. package/dist/web/routes/auth.d.ts +6 -0
  180. package/dist/web/routes/auth.d.ts.map +1 -0
  181. package/dist/web/routes/auth.js +142 -0
  182. package/dist/web/routes/auth.js.map +1 -0
  183. package/dist/web/routes/config.d.ts +7 -0
  184. package/dist/web/routes/config.d.ts.map +1 -0
  185. package/dist/web/routes/config.js +204 -0
  186. package/dist/web/routes/config.js.map +1 -0
  187. package/dist/web/routes/content-types.d.ts +6 -0
  188. package/dist/web/routes/content-types.d.ts.map +1 -0
  189. package/dist/web/routes/content-types.js +29 -0
  190. package/dist/web/routes/content-types.js.map +1 -0
  191. package/dist/web/routes/discovery.d.ts +6 -0
  192. package/dist/web/routes/discovery.d.ts.map +1 -0
  193. package/dist/web/routes/discovery.js +111 -0
  194. package/dist/web/routes/discovery.js.map +1 -0
  195. package/dist/web/routes/library-import.d.ts +6 -0
  196. package/dist/web/routes/library-import.d.ts.map +1 -0
  197. package/dist/web/routes/library-import.js +120 -0
  198. package/dist/web/routes/library-import.js.map +1 -0
  199. package/dist/web/routes/library-management.d.ts +6 -0
  200. package/dist/web/routes/library-management.d.ts.map +1 -0
  201. package/dist/web/routes/library-management.js +120 -0
  202. package/dist/web/routes/library-management.js.map +1 -0
  203. package/dist/web/routes/library.d.ts +6 -0
  204. package/dist/web/routes/library.d.ts.map +1 -0
  205. package/dist/web/routes/library.js +28 -0
  206. package/dist/web/routes/library.js.map +1 -0
  207. package/dist/web/routes/oidc.d.ts +9 -0
  208. package/dist/web/routes/oidc.d.ts.map +1 -0
  209. package/dist/web/routes/oidc.js +203 -0
  210. package/dist/web/routes/oidc.js.map +1 -0
  211. package/dist/web/routes/requests.d.ts +6 -0
  212. package/dist/web/routes/requests.d.ts.map +1 -0
  213. package/dist/web/routes/requests.js +183 -0
  214. package/dist/web/routes/requests.js.map +1 -0
  215. package/dist/web/routes/sse.d.ts +6 -0
  216. package/dist/web/routes/sse.d.ts.map +1 -0
  217. package/dist/web/routes/sse.js +54 -0
  218. package/dist/web/routes/sse.js.map +1 -0
  219. package/package.json +33 -0
  220. package/package.json.license +2 -0
  221. package/src/auth/auth-service.ts +333 -0
  222. package/src/auth/http-server.ts +249 -0
  223. package/src/auth/jwt-secret.ts +65 -0
  224. package/src/auth/oidc/oidc-flow.ts +266 -0
  225. package/src/auth/oidc/oidc-provider-service.ts +174 -0
  226. package/src/auth/oidc/user-service.ts +227 -0
  227. package/src/auth/rate-limiter.ts +85 -0
  228. package/src/config/config-service.ts +434 -0
  229. package/src/config/validate.ts +153 -0
  230. package/src/content-types/registry.ts +194 -0
  231. package/src/datastore/db.ts +38 -0
  232. package/src/datastore/encrypt.ts +107 -0
  233. package/src/datastore/migrations.ts +367 -0
  234. package/src/datastore/storage.ts +83 -0
  235. package/src/events/event-bus.ts +180 -0
  236. package/src/events/spine.ts +87 -0
  237. package/src/events/trace.ts +38 -0
  238. package/src/host.ts +130 -0
  239. package/src/library/importer.ts +174 -0
  240. package/src/library/library-cache.ts +212 -0
  241. package/src/library/library-config.ts +22 -0
  242. package/src/library/library-scanner.ts +404 -0
  243. package/src/library/library-service.ts +282 -0
  244. package/src/library/metadata-matcher.ts +285 -0
  245. package/src/library/naming.ts +59 -0
  246. package/src/library/scan-controller.ts +490 -0
  247. package/src/library/source-resolver.ts +92 -0
  248. package/src/logging/logger.ts +115 -0
  249. package/src/main.ts +364 -0
  250. package/src/plugins/context.ts +138 -0
  251. package/src/plugins/discovery.ts +29 -0
  252. package/src/plugins/loader.ts +309 -0
  253. package/src/state/request-intake.ts +101 -0
  254. package/src/state/state-store.ts +621 -0
  255. package/src/state/trace-store.ts +27 -0
  256. package/src/types/plugin.ts +110 -0
  257. package/src/web/index.ts +182 -0
  258. package/src/web/routes/activity.ts +41 -0
  259. package/src/web/routes/auth.ts +170 -0
  260. package/src/web/routes/config.ts +282 -0
  261. package/src/web/routes/content-types.ts +42 -0
  262. package/src/web/routes/discovery.ts +156 -0
  263. package/src/web/routes/library-import.ts +196 -0
  264. package/src/web/routes/library-management.ts +140 -0
  265. package/src/web/routes/library.ts +46 -0
  266. package/src/web/routes/oidc.ts +260 -0
  267. package/src/web/routes/requests.ts +250 -0
  268. package/src/web/routes/sse.ts +92 -0
  269. package/tests/auth.test.ts +510 -0
  270. package/tests/config-route.test.ts +345 -0
  271. package/tests/config.test.ts +612 -0
  272. package/tests/content-types.test.ts +376 -0
  273. package/tests/datastore.test.ts +345 -0
  274. package/tests/e2e.test.ts +430 -0
  275. package/tests/event-bus.test.ts +925 -0
  276. package/tests/host.test.ts +449 -0
  277. package/tests/library-cache.test.ts +171 -0
  278. package/tests/library-scanner.test.ts +177 -0
  279. package/tests/library.test.ts +210 -0
  280. package/tests/loader-context.test.ts +644 -0
  281. package/tests/logger-jsonl.test.ts +154 -0
  282. package/tests/metadata-matcher.test.ts +198 -0
  283. package/tests/oidc.test.ts +218 -0
  284. package/tests/scan-controller.test.ts +328 -0
  285. package/tests/source-resolver.test.ts +100 -0
  286. package/tests/starr-video-integration.test.ts +153 -0
  287. package/tests/state-store.test.ts +700 -0
  288. package/tests/trace-history.test.ts +356 -0
  289. package/tsconfig.json +8 -0
  290. package/tsconfig.json.license +2 -0
@@ -0,0 +1,245 @@
1
+ // SPDX-License-Identifier: 0BSD
2
+ // SPDX-FileCopyrightText: 2025 Digi <digitalpaws@proton.me>
3
+ import { readFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ import { createContext } from "./context.js";
7
+ import { discoverPluginDirs } from "./discovery.js";
8
+ function withTimeout(promise, ms, label) {
9
+ return Promise.race([
10
+ promise,
11
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms)),
12
+ ]);
13
+ }
14
+ const pluginRegistry = new Map();
15
+ export async function loadPlugins(starr) {
16
+ const dirs = await discoverPluginDirs(starr.options.pluginsDir);
17
+ starr.logger.info("discovered plugins", {
18
+ count: dirs.length,
19
+ dirs: dirs.map((d) => path.basename(d)),
20
+ });
21
+ const db = starr.getDatabase();
22
+ if (!db)
23
+ throw new Error("datastore not initialized; call openDatastore first");
24
+ // ── Pass 1: Discover plugin IDs ──────────────────────────
25
+ // Trial registration to get canonical plugin IDs before querying instances.
26
+ const pluginIdMap = new Map();
27
+ for (const dir of dirs) {
28
+ let pkg;
29
+ try {
30
+ const pkgRaw = await readFile(path.join(dir, "package.json"), "utf8");
31
+ pkg = JSON.parse(pkgRaw);
32
+ }
33
+ catch {
34
+ starr.logger.warn("skipping dir: no package.json", { dir });
35
+ continue;
36
+ }
37
+ const entryUrl = pathToFileURL(path.resolve(dir, pkg.main)).href;
38
+ // Trial context (no real config service)
39
+ const trialCtx = createContext(starr, dir, db, undefined);
40
+ try {
41
+ const mod = await import(entryUrl);
42
+ const register = mod.default;
43
+ if (typeof register !== "function") {
44
+ starr.logger.warn("skipping dir: no default export function", { dir });
45
+ continue;
46
+ }
47
+ await register(trialCtx);
48
+ const meta = trialCtx.getMeta();
49
+ if (meta) {
50
+ pluginIdMap.set(meta.id, { dir, entryUrl });
51
+ }
52
+ }
53
+ catch (err) {
54
+ starr.logger.warn("trial registration failed for dir (will retry with real instances)", { dir, error: String(err) });
55
+ // Guess pluginId from directory basename
56
+ const guessedId = path.basename(dir);
57
+ pluginIdMap.set(guessedId, { dir, entryUrl });
58
+ }
59
+ }
60
+ // ── Clean up trial registrations — roll back any content types ──
61
+ // that were registered during Pass 1, so Pass 2 can register them
62
+ // with the real context without triggering duplicate errors.
63
+ for (const pluginId of pluginIdMap.keys()) {
64
+ starr.contentTypeRegistry.rollbackPlugin(pluginId);
65
+ }
66
+ // ── Pass 2: Load instances ───────────────────────────────
67
+ const seenIds = new Set();
68
+ const lifecycles = [];
69
+ for (const [pluginId, info] of pluginIdMap) {
70
+ // Get instance IDs from DB (or "default" if none)
71
+ const instanceRows = db
72
+ .prepare("SELECT DISTINCT instance_id FROM plugin_config WHERE plugin_id = ?")
73
+ .all(pluginId);
74
+ const instanceIds = instanceRows.length > 0
75
+ ? instanceRows.map(r => r.instance_id)
76
+ : ["default"];
77
+ for (const instanceId of instanceIds) {
78
+ let ctx;
79
+ try {
80
+ ctx = createContext(starr, info.dir, db, starr.configService ?? undefined, instanceId);
81
+ const mod = await import(info.entryUrl);
82
+ const register = mod.default;
83
+ if (typeof register !== "function")
84
+ throw new Error("default export is not a function");
85
+ const lifecycle = (await register(ctx)) ?? {};
86
+ const meta = ctx.getMeta();
87
+ if (!meta)
88
+ throw new Error("register did not call ctx.define");
89
+ const fullInstanceId = `${meta.id}::${instanceId}`;
90
+ if (seenIds.has(fullInstanceId)) {
91
+ throw new Error(`duplicate instance: ${fullInstanceId}`);
92
+ }
93
+ seenIds.add(fullInstanceId);
94
+ const runtime = {
95
+ meta: { ...meta, id: fullInstanceId },
96
+ lifecycle,
97
+ startOrder: -1,
98
+ };
99
+ lifecycles.push(runtime);
100
+ pluginRegistry.set(fullInstanceId, { dir: info.dir, ctx: ctx, runtime });
101
+ }
102
+ catch (err) {
103
+ starr.logger.error("instance registration failed", {
104
+ pluginId, instanceId, error: String(err),
105
+ });
106
+ if (ctx) {
107
+ ctx.unsubscribeAll();
108
+ ctx.deleteAllStorage().catch(e => starr.logger.error("storage rollback failed", { pluginId, instanceId, error: String(e) }));
109
+ const meta = ctx.getMeta();
110
+ if (meta) {
111
+ starr.contentTypeRegistry.rollbackPlugin(meta.id);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ }
117
+ // Start pass
118
+ let order = 0;
119
+ for (const runtime of lifecycles) {
120
+ if (runtime.lifecycle.start) {
121
+ try {
122
+ await withTimeout(Promise.resolve(runtime.lifecycle.start()), 30_000, `start(${runtime.meta.id})`);
123
+ runtime.startOrder = order++;
124
+ }
125
+ catch (err) {
126
+ starr.logger.error("plugin start failed", {
127
+ id: runtime.meta.id,
128
+ error: String(err),
129
+ });
130
+ runtime.startOrder = -1;
131
+ }
132
+ }
133
+ else {
134
+ runtime.startOrder = order++;
135
+ }
136
+ }
137
+ starr.setRuntimes(lifecycles);
138
+ }
139
+ /**
140
+ * Reload a single plugin in isolation: stop → unregister → re-import → start.
141
+ * In multi-instance mode, all instances of the plugin are reloaded.
142
+ */
143
+ export async function reloadPlugin(starr, pluginId) {
144
+ // Find all instances matching this pluginId (pluginId::instanceId or plain pluginId)
145
+ const records = [];
146
+ for (const [fullId, record] of pluginRegistry) {
147
+ if (fullId === pluginId || fullId.startsWith(`${pluginId}::`)) {
148
+ records.push({ fullId, record });
149
+ }
150
+ }
151
+ if (records.length === 0) {
152
+ starr.logger.warn("reload requested for unknown plugin", { pluginId });
153
+ return;
154
+ }
155
+ starr.logger.info("reloading plugin instances", { pluginId, count: records.length });
156
+ // 1. Stop all instances
157
+ for (const { fullId, record } of records) {
158
+ if (record.runtime.lifecycle.stop) {
159
+ try {
160
+ await withTimeout(Promise.resolve(record.runtime.lifecycle.stop()), 10_000, `reload-stop(${fullId})`);
161
+ }
162
+ catch (err) {
163
+ starr.logger.error("plugin stop failed during reload", {
164
+ pluginId: fullId,
165
+ error: String(err),
166
+ });
167
+ }
168
+ }
169
+ record.ctx.unsubscribeAll();
170
+ }
171
+ // 2. Remove from runtimes list
172
+ const runtimes = [...starr.getRuntimes()].filter((r) => !records.some(({ fullId }) => r.meta.id === fullId));
173
+ starr.setRuntimes(runtimes);
174
+ // 3. For each instance, re-import and start
175
+ for (const { fullId, record } of records) {
176
+ const pkgRaw = await readFile(path.join(record.dir, "package.json"), "utf8");
177
+ const pkg = JSON.parse(pkgRaw);
178
+ const entryUrl = pathToFileURL(path.resolve(record.dir, pkg.main)).href;
179
+ const cacheBustUrl = `${entryUrl}?reload=${Date.now()}`;
180
+ try {
181
+ const mod = await import(cacheBustUrl);
182
+ const register = mod.default;
183
+ if (typeof register !== "function") {
184
+ throw new Error("default export is not a function");
185
+ }
186
+ const db = starr.getDatabase();
187
+ if (!db)
188
+ throw new Error("datastore not available");
189
+ // Extract instanceId from fullId (pluginId::instanceId)
190
+ const instanceId = fullId.includes("::") ? fullId.split("::")[1] : "default";
191
+ const newCtx = createContext(starr, record.dir, db, starr.configService ?? undefined, instanceId);
192
+ const lifecycle = (await register(newCtx)) ?? {};
193
+ const meta = newCtx.getMeta();
194
+ if (!meta)
195
+ throw new Error("register did not call ctx.define");
196
+ const startOrder = runtimes.length;
197
+ if (lifecycle.start) {
198
+ try {
199
+ await withTimeout(Promise.resolve(lifecycle.start()), 30_000, `reload-start(${fullId})`);
200
+ }
201
+ catch (err) {
202
+ starr.logger.error("plugin start failed during reload", {
203
+ pluginId: fullId,
204
+ error: String(err),
205
+ });
206
+ }
207
+ }
208
+ const newRuntime = { meta: { ...meta, id: fullId }, lifecycle, startOrder };
209
+ pluginRegistry.set(fullId, {
210
+ dir: record.dir,
211
+ ctx: newCtx,
212
+ runtime: newRuntime,
213
+ });
214
+ runtimes.push(newRuntime);
215
+ starr.logger.info("plugin instance reloaded", { pluginId: fullId });
216
+ }
217
+ catch (err) {
218
+ starr.logger.error("plugin instance reload failed", {
219
+ pluginId: fullId,
220
+ error: String(err),
221
+ });
222
+ pluginRegistry.delete(fullId);
223
+ }
224
+ }
225
+ starr.setRuntimes(runtimes);
226
+ }
227
+ export async function stopPlugins(starr) {
228
+ const runtimes = [...starr.getRuntimes()]
229
+ .filter((r) => r.startOrder >= 0)
230
+ .sort((a, b) => b.startOrder - a.startOrder);
231
+ for (const runtime of runtimes) {
232
+ if (runtime.lifecycle.stop) {
233
+ try {
234
+ await withTimeout(Promise.resolve(runtime.lifecycle.stop()), 10_000, `stop(${runtime.meta.id})`);
235
+ }
236
+ catch (err) {
237
+ starr.logger.error("plugin stop failed", {
238
+ id: runtime.meta.id,
239
+ error: String(err),
240
+ });
241
+ }
242
+ }
243
+ }
244
+ }
245
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/plugins/loader.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,4DAA4D;AAE5D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGpD,SAAS,WAAW,CAClB,OAAmB,EACnB,EAAU,EACV,KAAa;IAEb,OAAO,OAAO,CAAC,IAAI,CAAC;QAClB,OAAO;QACP,IAAI,OAAO,CAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC3B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,KAAK,oBAAoB,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAC5E;KACF,CAAC,CAAC;AACL,CAAC;AAUD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAwB,CAAC;AAEvD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAY;IAC5C,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAChE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE;QACtC,KAAK,EAAE,IAAI,CAAC,MAAM;QAClB,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;KACxC,CAAC,CAAC;IAEH,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAC/B,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAEhF,4DAA4D;IAC5D,4EAA4E;IAC5E,MAAM,WAAW,GAAG,IAAI,GAAG,EAA6C,CAAC;IAEzE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;YACtE,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAqB,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YAC5D,SAAS;QACX,CAAC;QACD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAEjE,yCAAyC;QACzC,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,CAAC,CAAC;QAC1D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;YACnC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAqB,CAAC;YAC3C,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACnC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;gBACvE,SAAS;YACX,CAAC;YACD,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACzB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,IAAI,EAAE,CAAC;gBACT,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,oEAAoE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrH,yCAAyC;YACzC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YACrC,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,kEAAkE;IAClE,6DAA6D;IAC7D,KAAK,MAAM,QAAQ,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,KAAK,CAAC,mBAAmB,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;IAED,4DAA4D;IAC5D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,UAAU,GAAoB,EAAE,CAAC;IAEvC,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC;QAC3C,kDAAkD;QAClD,MAAM,YAAY,GAAG,EAAE;aACpB,OAAO,CAAC,oEAAoE,CAAC;aAC7E,GAAG,CAAC,QAAQ,CAAmC,CAAC;QACnD,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC;YACzC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC;YACtC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAEhB,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACrC,IAAI,GAAiD,CAAC;YACtD,IAAI,CAAC;gBACH,GAAG,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,aAAa,IAAI,SAAS,EAAE,UAAU,CAAC,CAAC;gBACvF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACxC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAqB,CAAC;gBAC3C,IAAI,OAAO,QAAQ,KAAK,UAAU;oBAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;gBACxF,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC9C,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;gBAC3B,IAAI,CAAC,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;gBAE/D,MAAM,cAAc,GAAG,GAAG,IAAI,CAAC,EAAE,KAAK,UAAU,EAAE,CAAC;gBACnD,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;oBAChC,MAAM,IAAI,KAAK,CAAC,uBAAuB,cAAc,EAAE,CAAC,CAAC;gBAC3D,CAAC;gBACD,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;gBAE5B,MAAM,OAAO,GAAkB;oBAC7B,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,cAAc,EAAE;oBACrC,SAAS;oBACT,UAAU,EAAE,CAAC,CAAC;iBACf,CAAC;gBACF,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACzB,cAAc,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YAC5E,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE;oBACjD,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;iBACzC,CAAC,CAAC;gBACH,IAAI,GAAG,EAAE,CAAC;oBACR,GAAG,CAAC,cAAc,EAAE,CAAC;oBACrB,GAAG,CAAC,gBAAgB,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAC/B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAC1F,CAAC;oBACF,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;oBAC3B,IAAI,IAAI,EAAE,CAAC;wBACT,KAAK,CAAC,mBAAmB,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACpD,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,aAAa;IACb,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,OAAO,IAAI,UAAU,EAAE,CAAC;QACjC,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,WAAW,CACf,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,EAC1C,MAAM,EACN,SAAS,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,CAC5B,CAAC;gBACF,OAAO,CAAC,UAAU,GAAG,KAAK,EAAE,CAAC;YAC/B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE;oBACxC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE;oBACnB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;iBACnB,CAAC,CAAC;gBACH,OAAO,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,UAAU,GAAG,KAAK,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAY,EACZ,QAAgB;IAEhB,qFAAqF;IACrF,MAAM,OAAO,GAAoD,EAAE,CAAC;IACpE,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;QAC9C,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,QAAQ,IAAI,CAAC,EAAE,CAAC;YAC9D,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,qCAAqC,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QACvE,OAAO;IACT,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAErF,wBAAwB;IACxB,KAAK,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QACzC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,WAAW,CACf,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,EAChD,MAAM,EACN,eAAe,MAAM,GAAG,CACzB,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE;oBACrD,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;iBACnB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC;IAC9B,CAAC;IAED,+BAA+B;IAC/B,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAC9C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,CAC3D,CAAC;IACF,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAE5B,4CAA4C;IAC5C,KAAK,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAC9C,CAAC;QACF,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAqB,CAAC;QACnD,MAAM,QAAQ,GAAG,aAAa,CAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CACnC,CAAC,IAAI,CAAC;QACP,MAAM,YAAY,GAAG,GAAG,QAAQ,WAAW,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAExD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YACvC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAqB,CAAC;YAC3C,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;YACtD,CAAC;YAED,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;YAC/B,IAAI,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAEpD,wDAAwD;YACxD,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAE7E,MAAM,MAAM,GAAG,aAAa,CAC1B,KAAK,EACL,MAAM,CAAC,GAAG,EACV,EAAE,EACF,KAAK,CAAC,aAAa,IAAI,SAAS,EAChC,UAAU,CACX,CAAC;YACF,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;YAE/D,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;YACnC,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;gBACpB,IAAI,CAAC;oBACH,MAAM,WAAW,CACf,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,EAClC,MAAM,EACN,gBAAgB,MAAM,GAAG,CAC1B,CAAC;gBACJ,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE;wBACtD,QAAQ,EAAE,MAAM;wBAChB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;qBACnB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,MAAM,UAAU,GAAkB,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;YAC3F,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE;gBACzB,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,GAAG,EAAE,MAAM;gBACX,OAAO,EAAE,UAAU;aACpB,CAAC,CAAC;YACH,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAE1B,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QACtE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE;gBAClD,QAAQ,EAAE,MAAM;gBAChB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;aACnB,CAAC,CAAC;YACH,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAY;IAC5C,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;SACtC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC;SAChC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;IAC/C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,WAAW,CACf,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,EACzC,MAAM,EACN,QAAQ,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,CAC3B,CAAC;YACJ,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,EAAE;oBACvC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE;oBACnB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;iBACnB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { ContentItemBase } from "@digitalpaws/starr-types-core";
2
+ import type { EventBus, Logger } from "../types/plugin.js";
3
+ import { ContentTypeRegistryImpl } from "../content-types/registry.js";
4
+ import { StateStore } from "./state-store.js";
5
+ import type { LibraryService } from "../library/library-service.js";
6
+ export interface RequestIntakeInput {
7
+ /** The user making the request (v1: always "admin"). */
8
+ userId: string;
9
+ /** The content item selected from browse/discovery. */
10
+ item: ContentItemBase;
11
+ }
12
+ /**
13
+ * Process a user request for content.
14
+ *
15
+ * 1. Compute the item's contentIdentity via the content-type registry.
16
+ * 2. Look for an existing non-failed TrackedRequest with that identity (DU1).
17
+ * 3. If found: add the user as a watcher and return the existing requestId (DU2).
18
+ * 4. If not found (or prior is failed): create a new TrackedRequest in status
19
+ * "requested", then emit "content.requested" to start the pipeline (DU3).
20
+ *
21
+ * Resolves target library for the request via LibraryService (if available).
22
+ *
23
+ * @returns The requestId (either existing or newly created).
24
+ */
25
+ export declare function requestIntake(input: RequestIntakeInput, store: StateStore, registry: ContentTypeRegistryImpl, bus: EventBus, logger: Logger, libraryService?: LibraryService): string;
26
+ //# sourceMappingURL=request-intake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-intake.d.ts","sourceRoot":"","sources":["../../src/state/request-intake.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAA0B,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAEpE,MAAM,WAAW,kBAAkB;IACjC,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,IAAI,EAAE,eAAe,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,uBAAuB,EACjC,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,MAAM,EACd,cAAc,CAAC,EAAE,cAAc,GAC9B,MAAM,CA+DR"}
@@ -0,0 +1,74 @@
1
+ // SPDX-License-Identifier: 0BSD
2
+ // SPDX-FileCopyrightText: 2025 Digi <digitalpaws@proton.me>
3
+ import { randomUUID } from "node:crypto";
4
+ import { computeContentIdentity } from "../content-types/registry.js";
5
+ /**
6
+ * Process a user request for content.
7
+ *
8
+ * 1. Compute the item's contentIdentity via the content-type registry.
9
+ * 2. Look for an existing non-failed TrackedRequest with that identity (DU1).
10
+ * 3. If found: add the user as a watcher and return the existing requestId (DU2).
11
+ * 4. If not found (or prior is failed): create a new TrackedRequest in status
12
+ * "requested", then emit "content.requested" to start the pipeline (DU3).
13
+ *
14
+ * Resolves target library for the request via LibraryService (if available).
15
+ *
16
+ * @returns The requestId (either existing or newly created).
17
+ */
18
+ export function requestIntake(input, store, registry, bus, logger, libraryService) {
19
+ // 1. Compute content identity
20
+ const contentIdentity = computeContentIdentity(input.item, registry);
21
+ logger.debug("request intake: computed identity", {
22
+ contentTypeId: input.item.contentTypeId,
23
+ contentIdentity,
24
+ });
25
+ // 2. Look for existing non-failed request (DU1)
26
+ const existing = store.findByContentIdentity(contentIdentity);
27
+ if (existing) {
28
+ // 3. Found — attach as watcher (DU2)
29
+ logger.debug("request intake: attaching to existing request", {
30
+ existingRequestId: existing.requestId,
31
+ existingStatus: existing.status,
32
+ });
33
+ store.addWatcher(existing.requestId, input.userId);
34
+ return existing.requestId;
35
+ }
36
+ // 4. Not found (or only failed exists) — create new (DU3)
37
+ const requestId = randomUUID();
38
+ // Determine target library (FR-RIR-01)
39
+ let libraryId;
40
+ if (libraryService) {
41
+ const resolved = libraryService.resolveForContentType(input.item.contentTypeId);
42
+ if (resolved) {
43
+ libraryId = resolved.libraryId;
44
+ logger.debug("request intake: resolved library", {
45
+ contentTypeId: input.item.contentTypeId,
46
+ libraryId,
47
+ });
48
+ }
49
+ }
50
+ logger.info("request intake: creating new tracked request", {
51
+ requestId,
52
+ contentTypeId: input.item.contentTypeId,
53
+ contentIdentity,
54
+ libraryId,
55
+ });
56
+ store.insert({
57
+ requestId,
58
+ contentTypeId: input.item.contentTypeId,
59
+ contentIdentity,
60
+ item: input.item,
61
+ ownerUserId: input.userId,
62
+ libraryId,
63
+ });
64
+ // Emit content.requested to start the acquisition pipeline
65
+ bus.emit("content.requested", {
66
+ requestId,
67
+ userId: input.userId,
68
+ contentTypeId: input.item.contentTypeId,
69
+ item: input.item,
70
+ libraryId,
71
+ });
72
+ return requestId;
73
+ }
74
+ //# sourceMappingURL=request-intake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-intake.js","sourceRoot":"","sources":["../../src/state/request-intake.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,4DAA4D;AAE5D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAAE,sBAAsB,EAA2B,MAAM,8BAA8B,CAAC;AAW/F;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAyB,EACzB,KAAiB,EACjB,QAAiC,EACjC,GAAa,EACb,MAAc,EACd,cAA+B;IAE/B,8BAA8B;IAC9B,MAAM,eAAe,GAAG,sBAAsB,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrE,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE;QAChD,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,aAAa;QACvC,eAAe;KAChB,CAAC,CAAC;IAEH,gDAAgD;IAChD,MAAM,QAAQ,GAAG,KAAK,CAAC,qBAAqB,CAAC,eAAe,CAAC,CAAC;IAE9D,IAAI,QAAQ,EAAE,CAAC;QACb,qCAAqC;QACrC,MAAM,CAAC,KAAK,CAAC,+CAA+C,EAAE;YAC5D,iBAAiB,EAAE,QAAQ,CAAC,SAAS;YACrC,cAAc,EAAE,QAAQ,CAAC,MAAM;SAChC,CAAC,CAAC;QACH,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACnD,OAAO,QAAQ,CAAC,SAAS,CAAC;IAC5B,CAAC;IAED,0DAA0D;IAC1D,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;IAE/B,uCAAuC;IACvC,IAAI,SAA6B,CAAC;IAClC,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,QAAQ,GAAG,cAAc,CAAC,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAChF,IAAI,QAAQ,EAAE,CAAC;YACb,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC;YAC/B,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE;gBAC/C,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,aAAa;gBACvC,SAAS;aACV,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,8CAA8C,EAAE;QAC1D,SAAS;QACT,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,aAAa;QACvC,eAAe;QACf,SAAS;KACV,CAAC,CAAC;IAEH,KAAK,CAAC,MAAM,CAAC;QACX,SAAS;QACT,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,aAAa;QACvC,eAAe;QACf,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,WAAW,EAAE,KAAK,CAAC,MAAM;QACzB,SAAS;KACV,CAAC,CAAC;IAEH,2DAA2D;IAC3D,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE;QAC5B,SAAS;QACT,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,aAAa;QACvC,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,SAAS;KACV,CAAC,CAAC;IAEH,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,146 @@
1
+ import type Database from "better-sqlite3";
2
+ import type { ContentItemBase } from "@digitalpaws/starr-types-core";
3
+ import type { EventBus, Logger } from "../types/plugin.js";
4
+ /** Union of the v1 status values (§4). */
5
+ export type RequestStatus = "requested" | "searching" | "downloading" | "downloaded" | "importing" | "imported" | "failed";
6
+ /** The domain record as stored and returned to callers. */
7
+ export interface TrackedRequest {
8
+ requestId: string;
9
+ contentTypeId: string;
10
+ contentIdentity: string;
11
+ item: ContentItemBase;
12
+ ownerUserId: string;
13
+ watchers: string[];
14
+ status: RequestStatus;
15
+ statusDetail?: string;
16
+ chosenReleaseId?: string;
17
+ attempts: number;
18
+ libraryId?: string;
19
+ libraryName?: string;
20
+ createdAt: string;
21
+ updatedAt: string;
22
+ }
23
+ /** Minimal data needed to insert a new tracked request. */
24
+ export interface TrackedRequestData {
25
+ requestId: string;
26
+ contentTypeId: string;
27
+ contentIdentity: string;
28
+ item: ContentItemBase;
29
+ ownerUserId: string;
30
+ libraryId?: string;
31
+ }
32
+ /** A single captured spine event stored in the trace log. */
33
+ export interface TraceEntry {
34
+ id: number;
35
+ traceId: string;
36
+ requestId: string;
37
+ eventType: string;
38
+ /** JSON-serialized event envelope (envelope fields + payload). */
39
+ eventData: Record<string, unknown>;
40
+ timestamp: string;
41
+ }
42
+ export declare class StateStore {
43
+ private db;
44
+ private bus;
45
+ private logger;
46
+ private stmtGetByRequestId;
47
+ private stmtFindByIdentity;
48
+ private stmtInsert;
49
+ private stmtUpdateStatus;
50
+ private stmtIncrementAttempts;
51
+ private stmtAddWatcher;
52
+ private stmtListByUser;
53
+ private stmtInsertTrace;
54
+ private stmtGetIdsByTraceId;
55
+ private stmtGetTraceByRequestId;
56
+ private stmtResetAttempts;
57
+ private stmtResetForRetry;
58
+ private stmtDeleteRequest;
59
+ private stmtDeleteTraceByRequestId;
60
+ constructor(db: Database.Database, bus: EventBus, logger: Logger);
61
+ /** Retrieve a single tracked request by its domain id. */
62
+ getByRequestId(requestId: string): TrackedRequest | undefined;
63
+ /**
64
+ * Find a non-failed tracked request with the given content identity.
65
+ * Used by request intake for de-duplication (DU1).
66
+ * Returns the most recently created match, or undefined.
67
+ */
68
+ findByContentIdentity(contentIdentity: string): TrackedRequest | undefined;
69
+ /**
70
+ * Insert a brand-new tracked request with status ``requested``.
71
+ * Called by request intake after de-dup check (DU3).
72
+ */
73
+ insert(data: TrackedRequestData): void;
74
+ /**
75
+ * Add a userId to the watchers array of an existing request.
76
+ * Called by request intake when re-attaching to an existing request (DU2).
77
+ * No-op if the user is already the owner or a watcher.
78
+ */
79
+ addWatcher(requestId: string, userId: string): void;
80
+ /**
81
+ * Return the ordered trace history for a tracked request.
82
+ *
83
+ * Returns an array of TraceEntry, oldest first. Returns an empty array
84
+ * if the requestId is unknown or has no captured events.
85
+ *
86
+ * This is the data surface for the admin "activity" view (DBG2).
87
+ * The UI (Task 022) renders the returned data; this task only provides
88
+ * the query.
89
+ */
90
+ getTraceLog(requestId: string): TraceEntry[];
91
+ /**
92
+ * Reset a tracked request to 'requested' for a manual retry.
93
+ *
94
+ * Clears chosenReleaseId, resets attempts to 0, sets status to
95
+ * 'requested' with a detail message. This is called from the
96
+ * retry endpoint before re-emitting content.requested.
97
+ *
98
+ * Uses a transaction for atomicity.
99
+ * Emits item.updated so the UI reflects the change immediately.
100
+ */
101
+ resetAndRetry(requestId: string): void;
102
+ /**
103
+ * Permanently delete a tracked request and its associated
104
+ * trace-log entries. This is unconditional (any status).
105
+ *
106
+ * Returns true if a row was deleted, false if the requestId
107
+ * did not exist.
108
+ */
109
+ deleteRequest(requestId: string): boolean;
110
+ /**
111
+ * Return ALL tracked requests (admin only).
112
+ */
113
+ getLibraryPath(requestId: string): string | undefined;
114
+ getAllRequests(): TrackedRequest[];
115
+ /**
116
+ * List tracked requests visible to a given user (owner or watcher).
117
+ * Used by the UI to show the request list (UI-ST2).
118
+ */
119
+ listByUser(userId: string): TrackedRequest[];
120
+ /**
121
+ * Capture a spine event into the trace log. Stores the full event data
122
+ * (envelope fields + payload) as JSON, then prunes if the per-traceId
123
+ * count exceeds TRACE_HISTORY_LIMIT.
124
+ *
125
+ * This is a no-op if TRACE_HISTORY_LIMIT is 0 (trace capture disabled).
126
+ */
127
+ private captureTraceEvent;
128
+ private subscribe;
129
+ private onReleaseFound;
130
+ private onDownloadStarted;
131
+ private onDownloadCompleted;
132
+ private onDownloadFailed;
133
+ private onImportStarted;
134
+ private onImportCompleted;
135
+ private onImportFailed;
136
+ private onRequestFailed;
137
+ /**
138
+ * Update status, optional detail, and optional chosenReleaseId.
139
+ * Emits item.updated on every call (the caller guards transitions,
140
+ * so duplicate emissions are not a concern).
141
+ */
142
+ private updateStatus;
143
+ /** Increment the attempts counter by 1. Called on download.started (SM4). */
144
+ private incrementAttempts;
145
+ }
146
+ //# sourceMappingURL=state-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-store.d.ts","sourceRoot":"","sources":["../../src/state/state-store.ts"],"names":[],"mappings":"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"}