@lorepack/cli 0.0.0-stage → 0.1.0-alpha.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 (229) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/commands/activate.d.ts +20 -0
  3. package/dist/commands/activate.d.ts.map +1 -0
  4. package/dist/commands/activate.js +171 -0
  5. package/dist/commands/activate.js.map +1 -0
  6. package/dist/commands/build.d.ts +3 -0
  7. package/dist/commands/build.d.ts.map +1 -0
  8. package/dist/commands/build.js +53 -0
  9. package/dist/commands/build.js.map +1 -0
  10. package/dist/commands/config.d.ts +14 -0
  11. package/dist/commands/config.d.ts.map +1 -0
  12. package/dist/commands/config.js +109 -0
  13. package/dist/commands/config.js.map +1 -0
  14. package/dist/commands/connect.d.ts +5 -0
  15. package/dist/commands/connect.d.ts.map +1 -0
  16. package/dist/commands/connect.js +196 -0
  17. package/dist/commands/connect.js.map +1 -0
  18. package/dist/commands/deploy.d.ts +14 -0
  19. package/dist/commands/deploy.d.ts.map +1 -0
  20. package/dist/commands/deploy.js +248 -0
  21. package/dist/commands/deploy.js.map +1 -0
  22. package/dist/commands/dev.d.ts +25 -0
  23. package/dist/commands/dev.d.ts.map +1 -0
  24. package/dist/commands/dev.js +227 -0
  25. package/dist/commands/dev.js.map +1 -0
  26. package/dist/commands/diff.d.ts +3 -0
  27. package/dist/commands/diff.d.ts.map +1 -0
  28. package/dist/commands/diff.js +75 -0
  29. package/dist/commands/diff.js.map +1 -0
  30. package/dist/commands/doctor.d.ts +14 -0
  31. package/dist/commands/doctor.d.ts.map +1 -0
  32. package/dist/commands/doctor.js +96 -0
  33. package/dist/commands/doctor.js.map +1 -0
  34. package/dist/commands/export.d.ts +3 -0
  35. package/dist/commands/export.d.ts.map +1 -0
  36. package/dist/commands/export.js +126 -0
  37. package/dist/commands/export.js.map +1 -0
  38. package/dist/commands/index.d.ts +8 -0
  39. package/dist/commands/index.d.ts.map +1 -0
  40. package/dist/commands/index.js +51 -0
  41. package/dist/commands/index.js.map +1 -0
  42. package/dist/commands/init.d.ts +3 -0
  43. package/dist/commands/init.d.ts.map +1 -0
  44. package/dist/commands/init.js +95 -0
  45. package/dist/commands/init.js.map +1 -0
  46. package/dist/commands/inspect.d.ts +15 -0
  47. package/dist/commands/inspect.d.ts.map +1 -0
  48. package/dist/commands/inspect.js +527 -0
  49. package/dist/commands/inspect.js.map +1 -0
  50. package/dist/commands/mcp.d.ts +12 -0
  51. package/dist/commands/mcp.d.ts.map +1 -0
  52. package/dist/commands/mcp.js +219 -0
  53. package/dist/commands/mcp.js.map +1 -0
  54. package/dist/commands/pack.d.ts +9 -0
  55. package/dist/commands/pack.d.ts.map +1 -0
  56. package/dist/commands/pack.js +57 -0
  57. package/dist/commands/pack.js.map +1 -0
  58. package/dist/commands/plan.d.ts +3 -0
  59. package/dist/commands/plan.d.ts.map +1 -0
  60. package/dist/commands/plan.js +44 -0
  61. package/dist/commands/plan.js.map +1 -0
  62. package/dist/commands/prune.d.ts +7 -0
  63. package/dist/commands/prune.d.ts.map +1 -0
  64. package/dist/commands/prune.js +341 -0
  65. package/dist/commands/prune.js.map +1 -0
  66. package/dist/commands/search.d.ts +3 -0
  67. package/dist/commands/search.d.ts.map +1 -0
  68. package/dist/commands/search.js +84 -0
  69. package/dist/commands/search.js.map +1 -0
  70. package/dist/commands/serve.d.ts +20 -0
  71. package/dist/commands/serve.d.ts.map +1 -0
  72. package/dist/commands/serve.js +66 -0
  73. package/dist/commands/serve.js.map +1 -0
  74. package/dist/commands/status.d.ts +3 -0
  75. package/dist/commands/status.d.ts.map +1 -0
  76. package/dist/commands/status.js +27 -0
  77. package/dist/commands/status.js.map +1 -0
  78. package/dist/commands/target.d.ts +71 -0
  79. package/dist/commands/target.d.ts.map +1 -0
  80. package/dist/commands/target.js +945 -0
  81. package/dist/commands/target.js.map +1 -0
  82. package/dist/entry.d.ts +3 -0
  83. package/dist/entry.d.ts.map +1 -0
  84. package/dist/entry.js +19 -0
  85. package/dist/entry.js.map +1 -0
  86. package/dist/framework/context.d.ts +49 -0
  87. package/dist/framework/context.d.ts.map +1 -0
  88. package/dist/framework/context.js +47 -0
  89. package/dist/framework/context.js.map +1 -0
  90. package/dist/framework/exit.d.ts +36 -0
  91. package/dist/framework/exit.d.ts.map +1 -0
  92. package/dist/framework/exit.js +57 -0
  93. package/dist/framework/exit.js.map +1 -0
  94. package/dist/framework/metadata.d.ts +38 -0
  95. package/dist/framework/metadata.d.ts.map +1 -0
  96. package/dist/framework/metadata.js +47 -0
  97. package/dist/framework/metadata.js.map +1 -0
  98. package/dist/framework/program.d.ts +47 -0
  99. package/dist/framework/program.d.ts.map +1 -0
  100. package/dist/framework/program.js +189 -0
  101. package/dist/framework/program.js.map +1 -0
  102. package/dist/index.d.ts +7 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +7 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/migrations/README.md +7 -0
  107. package/dist/migrations/build/0001_catalog.sql +85 -0
  108. package/dist/migrations/build/0002_tables.sql +43 -0
  109. package/dist/migrations/build/0003_table-cell-range.sql +13 -0
  110. package/dist/migrations/build/0004_chunk-page.sql +11 -0
  111. package/dist/migrations/state/0001_state.sql +40 -0
  112. package/dist/public-entry.js +38563 -0
  113. package/dist/public-entry.js.map +7 -0
  114. package/dist/public-index.js +36428 -0
  115. package/dist/public-index.js.map +7 -0
  116. package/dist/services/build.d.ts +48 -0
  117. package/dist/services/build.d.ts.map +1 -0
  118. package/dist/services/build.js +499 -0
  119. package/dist/services/build.js.map +1 -0
  120. package/dist/services/builds.d.ts +40 -0
  121. package/dist/services/builds.d.ts.map +1 -0
  122. package/dist/services/builds.js +184 -0
  123. package/dist/services/builds.js.map +1 -0
  124. package/dist/services/cancellation.d.ts +27 -0
  125. package/dist/services/cancellation.d.ts.map +1 -0
  126. package/dist/services/cancellation.js +41 -0
  127. package/dist/services/cancellation.js.map +1 -0
  128. package/dist/services/cloudflare-target.d.ts +38 -0
  129. package/dist/services/cloudflare-target.d.ts.map +1 -0
  130. package/dist/services/cloudflare-target.js +471 -0
  131. package/dist/services/cloudflare-target.js.map +1 -0
  132. package/dist/services/comparer.d.ts +15 -0
  133. package/dist/services/comparer.d.ts.map +1 -0
  134. package/dist/services/comparer.js +43 -0
  135. package/dist/services/comparer.js.map +1 -0
  136. package/dist/services/config-resolve.d.ts +60 -0
  137. package/dist/services/config-resolve.d.ts.map +1 -0
  138. package/dist/services/config-resolve.js +178 -0
  139. package/dist/services/config-resolve.js.map +1 -0
  140. package/dist/services/deploy.d.ts +70 -0
  141. package/dist/services/deploy.d.ts.map +1 -0
  142. package/dist/services/deploy.js +260 -0
  143. package/dist/services/deploy.js.map +1 -0
  144. package/dist/services/dev-session.d.ts +52 -0
  145. package/dist/services/dev-session.d.ts.map +1 -0
  146. package/dist/services/dev-session.js +102 -0
  147. package/dist/services/dev-session.js.map +1 -0
  148. package/dist/services/doctor.d.ts +35 -0
  149. package/dist/services/doctor.d.ts.map +1 -0
  150. package/dist/services/doctor.js +389 -0
  151. package/dist/services/doctor.js.map +1 -0
  152. package/dist/services/export.d.ts +20 -0
  153. package/dist/services/export.d.ts.map +1 -0
  154. package/dist/services/export.js +129 -0
  155. package/dist/services/export.js.map +1 -0
  156. package/dist/services/init.d.ts +46 -0
  157. package/dist/services/init.d.ts.map +1 -0
  158. package/dist/services/init.js +231 -0
  159. package/dist/services/init.js.map +1 -0
  160. package/dist/services/packing.d.ts +25 -0
  161. package/dist/services/packing.d.ts.map +1 -0
  162. package/dist/services/packing.js +69 -0
  163. package/dist/services/packing.js.map +1 -0
  164. package/dist/services/parse-cache.d.ts +47 -0
  165. package/dist/services/parse-cache.d.ts.map +1 -0
  166. package/dist/services/parse-cache.js +65 -0
  167. package/dist/services/parse-cache.js.map +1 -0
  168. package/dist/services/project.d.ts +49 -0
  169. package/dist/services/project.d.ts.map +1 -0
  170. package/dist/services/project.js +119 -0
  171. package/dist/services/project.js.map +1 -0
  172. package/dist/services/remote-prune.d.ts +7 -0
  173. package/dist/services/remote-prune.d.ts.map +1 -0
  174. package/dist/services/remote-prune.js +34 -0
  175. package/dist/services/remote-prune.js.map +1 -0
  176. package/dist/services/retention.d.ts +20 -0
  177. package/dist/services/retention.d.ts.map +1 -0
  178. package/dist/services/retention.js +106 -0
  179. package/dist/services/retention.js.map +1 -0
  180. package/dist/services/revalidate.d.ts +36 -0
  181. package/dist/services/revalidate.d.ts.map +1 -0
  182. package/dist/services/revalidate.js +73 -0
  183. package/dist/services/revalidate.js.map +1 -0
  184. package/dist/services/search.d.ts +25 -0
  185. package/dist/services/search.d.ts.map +1 -0
  186. package/dist/services/search.js +88 -0
  187. package/dist/services/search.js.map +1 -0
  188. package/dist/services/serving.d.ts +70 -0
  189. package/dist/services/serving.d.ts.map +1 -0
  190. package/dist/services/serving.js +240 -0
  191. package/dist/services/serving.js.map +1 -0
  192. package/dist/services/status.d.ts +54 -0
  193. package/dist/services/status.d.ts.map +1 -0
  194. package/dist/services/status.js +203 -0
  195. package/dist/services/status.js.map +1 -0
  196. package/dist/services/studio-assets.d.ts +13 -0
  197. package/dist/services/studio-assets.d.ts.map +1 -0
  198. package/dist/services/studio-assets.js +94 -0
  199. package/dist/services/studio-assets.js.map +1 -0
  200. package/dist/services/studio-diagnostics.d.ts +28 -0
  201. package/dist/services/studio-diagnostics.d.ts.map +1 -0
  202. package/dist/services/studio-diagnostics.js +88 -0
  203. package/dist/services/studio-diagnostics.js.map +1 -0
  204. package/dist/services/studio-endpoints.d.ts +61 -0
  205. package/dist/services/studio-endpoints.d.ts.map +1 -0
  206. package/dist/services/studio-endpoints.js +88 -0
  207. package/dist/services/studio-endpoints.js.map +1 -0
  208. package/dist/services/studio-mutations.d.ts +32 -0
  209. package/dist/services/studio-mutations.d.ts.map +1 -0
  210. package/dist/services/studio-mutations.js +149 -0
  211. package/dist/services/studio-mutations.js.map +1 -0
  212. package/dist/services/versions.d.ts +38 -0
  213. package/dist/services/versions.d.ts.map +1 -0
  214. package/dist/services/versions.js +46 -0
  215. package/dist/services/versions.js.map +1 -0
  216. package/dist/services/watch.d.ts +103 -0
  217. package/dist/services/watch.d.ts.map +1 -0
  218. package/dist/services/watch.js +420 -0
  219. package/dist/services/watch.js.map +1 -0
  220. package/package.json +73 -4
  221. package/studio-dist/assets/ibm-plex-mono-latin-400-normal-Dm_PoFIZ.woff2 +0 -0
  222. package/studio-dist/assets/ibm-plex-mono-latin-500-normal-C_OblDzq.woff2 +0 -0
  223. package/studio-dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  224. package/studio-dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  225. package/studio-dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  226. package/studio-dist/assets/index-BuAYbQVX.js +66 -0
  227. package/studio-dist/assets/index-C_3XEUt4.css +1 -0
  228. package/studio-dist/index.html +30 -0
  229. package/README.md +0 -3
@@ -0,0 +1,65 @@
1
+ import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { writeFileAtomic } from '@lorepack/core';
4
+ /** Bumped when the cached shape changes, so old entries are ignored instead of misread. */
5
+ const CACHE_FORMAT_VERSION = 2;
6
+ export class ParseCache {
7
+ #directory;
8
+ #hits = 0;
9
+ #misses = 0;
10
+ constructor(directory) {
11
+ this.#directory = directory;
12
+ }
13
+ get hits() {
14
+ return this.#hits;
15
+ }
16
+ get misses() {
17
+ return this.#misses;
18
+ }
19
+ get(key) {
20
+ const path = this.#pathFor(key);
21
+ if (!existsSync(path)) {
22
+ this.#misses += 1;
23
+ return null;
24
+ }
25
+ try {
26
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
27
+ if (parsed.version !== CACHE_FORMAT_VERSION || typeof parsed.objectHash !== 'string') {
28
+ this.#misses += 1;
29
+ return null;
30
+ }
31
+ this.#hits += 1;
32
+ return {
33
+ artifact: parsed.artifact,
34
+ nodes: parsed.nodes,
35
+ chunks: parsed.chunks,
36
+ objectHash: parsed.objectHash,
37
+ ...(parsed.warnings === undefined ? {} : { warnings: parsed.warnings }),
38
+ };
39
+ }
40
+ catch {
41
+ this.#misses += 1;
42
+ return null;
43
+ }
44
+ }
45
+ put(key, value) {
46
+ const envelope = { version: CACHE_FORMAT_VERSION, ...value };
47
+ try {
48
+ mkdirSync(join(this.#directory, key.slice(0, 2)), { recursive: true });
49
+ writeFileAtomic(this.#pathFor(key), JSON.stringify(envelope));
50
+ }
51
+ catch {
52
+ // A cache that cannot be written (read-only volume, full disk) must not fail a build
53
+ // that is otherwise complete.
54
+ }
55
+ }
56
+ clear() {
57
+ rmSync(this.#directory, { recursive: true, force: true, maxRetries: 3 });
58
+ }
59
+ /** Sharded by the first byte of the key: a flat directory of 50,000 entries is slow to
60
+ * enumerate on every platform we support. */
61
+ #pathFor(key) {
62
+ return join(this.#directory, key.slice(0, 2), `${key}.json`);
63
+ }
64
+ }
65
+ //# sourceMappingURL=parse-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-cache.js","sourceRoot":"","sources":["../../src/services/parse-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA2CjD,2FAA2F;AAC3F,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAE/B,MAAM,OAAO,UAAU;IACZ,UAAU,CAAS;IAC5B,KAAK,GAAG,CAAC,CAAC;IACV,OAAO,GAAG,CAAC,CAAC;IAEZ,YAAY,SAAiB;QAC3B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,GAAG,CAAC,GAAW;QACb,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAkB,CAAC;YACvE,IAAI,MAAM,CAAC,OAAO,KAAK,oBAAoB,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;gBACrF,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;gBAClB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;YAChB,OAAO;gBACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;aACxE,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAkB;QACjC,MAAM,QAAQ,GAAkB,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,KAAK,EAAE,CAAC;QAC5E,IAAI,CAAC;YACH,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACvE,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC;YACP,qFAAqF;YACrF,8BAA8B;QAChC,CAAC;IACH,CAAC;IAED,KAAK;QACH,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED;iDAC6C;IAC7C,QAAQ,CAAC,GAAW;QAClB,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;IAC/D,CAAC;CACF"}
@@ -0,0 +1,49 @@
1
+ import type { PreviousBuild } from '@lorepack/compiler';
2
+ import { type BuildId, type LoadedConfig } from '@lorepack/core';
3
+ /**
4
+ * Reads what the active build recorded, so a plan can compare against it.
5
+ *
6
+ * Every read here is read-only, including the state database. `lorepack plan` and `lorepack status`
7
+ * must not write, not even a migration, or they would stop being previews. A project that
8
+ * has never been built has no state database at all, which is the case a new user hits, so
9
+ * absence is a normal answer rather than an error.
10
+ */
11
+ export interface ProjectState {
12
+ readonly state: {
13
+ close: () => void;
14
+ } | null;
15
+ readonly previous: PreviousBuild | null;
16
+ }
17
+ export declare function readPreviousBuild(config: LoadedConfig): ProjectState;
18
+ /** The active pointer, read without opening the state store for writing. */
19
+ export declare function readActiveBuild(loreDirectory: string): {
20
+ buildId: BuildId;
21
+ generation: number;
22
+ } | null;
23
+ /**
24
+ * Reads the comparison basis out of the sealed build itself rather than a side table.
25
+ *
26
+ * The build is the source of truth (invariant 1), so dirtiness is computed against what
27
+ * the active build actually contains. A summary cached elsewhere could drift from the
28
+ * build it claims to describe, and then `lorepack status` would confidently lie.
29
+ */
30
+ /**
31
+ * What the previous build contains, or `null` if it cannot say.
32
+ *
33
+ * **Best effort, deliberately.** This exists to let a rebuild reuse unchanged artifacts, which
34
+ * is an optimisation. A build whose database is missing, truncated or overwritten used to take
35
+ * the *next* build down with it, which is precisely backwards: the command that exists to
36
+ * produce a good build failed because a bad one existed, and the project could not be recovered
37
+ * at all (#251). Returning `null` costs a full rebuild and is always correct.
38
+ */
39
+ export declare function readBuildCatalog(loreDirectory: string, buildId: BuildId): PreviousBuild | null;
40
+ /**
41
+ * Whether a sealed build can actually be read.
42
+ *
43
+ * `state.sqlite` records that a build exists; it says nothing about the directory it points at,
44
+ * which is a separate file tree that a disk, a sync tool or a `.gitignore` miss can remove or
45
+ * corrupt independently. Reuse used to trust the record alone and report "No changes" over a
46
+ * build nobody could open (#251).
47
+ */
48
+ export declare function isBuildReadable(loreDirectory: string, buildId: BuildId): boolean;
49
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/services/project.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAEL,KAAK,OAAO,EAGZ,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AAExB;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;CACzC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAKpE;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,GACpB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAcjD;AAED;;;;;;GAMG;AACH;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,aAAa,GAAG,IAAI,CAQ9F;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAOhF"}
@@ -0,0 +1,119 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { buildMigrationsDirectory, loadMigrations, openReadOnly } from '@lorepack/backend-local';
4
+ import { assertBuildId, buildManifestSchema, LORE_DIRECTORY, } from '@lorepack/core';
5
+ export function readPreviousBuild(config) {
6
+ const loreDirectory = join(config.projectRoot, LORE_DIRECTORY);
7
+ const active = readActiveBuild(loreDirectory);
8
+ if (active === null)
9
+ return { state: null, previous: null };
10
+ return { state: null, previous: readBuildCatalog(loreDirectory, active.buildId) };
11
+ }
12
+ /** The active pointer, read without opening the state store for writing. */
13
+ export function readActiveBuild(loreDirectory) {
14
+ const statePath = join(loreDirectory, 'state.sqlite');
15
+ if (!existsSync(statePath))
16
+ return null;
17
+ const db = openReadOnly(statePath);
18
+ try {
19
+ const row = db
20
+ .prepare('SELECT build_id AS buildId, generation FROM active_build WHERE id = 1')
21
+ .get();
22
+ if (row === undefined || row.buildId === null)
23
+ return null;
24
+ return { buildId: assertBuildId(row.buildId), generation: Number(row.generation) };
25
+ }
26
+ finally {
27
+ db.close();
28
+ }
29
+ }
30
+ /**
31
+ * Reads the comparison basis out of the sealed build itself rather than a side table.
32
+ *
33
+ * The build is the source of truth (invariant 1), so dirtiness is computed against what
34
+ * the active build actually contains. A summary cached elsewhere could drift from the
35
+ * build it claims to describe, and then `lorepack status` would confidently lie.
36
+ */
37
+ /**
38
+ * What the previous build contains, or `null` if it cannot say.
39
+ *
40
+ * **Best effort, deliberately.** This exists to let a rebuild reuse unchanged artifacts, which
41
+ * is an optimisation. A build whose database is missing, truncated or overwritten used to take
42
+ * the *next* build down with it, which is precisely backwards: the command that exists to
43
+ * produce a good build failed because a bad one existed, and the project could not be recovered
44
+ * at all (#251). Returning `null` costs a full rebuild and is always correct.
45
+ */
46
+ export function readBuildCatalog(loreDirectory, buildId) {
47
+ try {
48
+ return readBuildCatalogOrThrow(loreDirectory, buildId);
49
+ }
50
+ catch {
51
+ // Damaged past the point of being an optimisation. `isBuildReadable` is what turns this
52
+ // into something the user is told about, rather than a silent full rebuild.
53
+ return null;
54
+ }
55
+ }
56
+ /**
57
+ * Whether a sealed build can actually be read.
58
+ *
59
+ * `state.sqlite` records that a build exists; it says nothing about the directory it points at,
60
+ * which is a separate file tree that a disk, a sync tool or a `.gitignore` miss can remove or
61
+ * corrupt independently. Reuse used to trust the record alone and report "No changes" over a
62
+ * build nobody could open (#251).
63
+ */
64
+ export function isBuildReadable(loreDirectory, buildId) {
65
+ try {
66
+ if (readBuildCatalogOrThrow(loreDirectory, buildId) === null)
67
+ return false;
68
+ return buildSchemaMatches(loreDirectory, buildId);
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ /**
75
+ * Whether the build's catalog schema is the one this binary reads.
76
+ *
77
+ * The same comparison the runtime makes when it opens a build, made here so `lorepack build` can
78
+ * act on it. Without this a build written by an older Lorepack passes every other check, is
79
+ * reported as "No changes", and then fails at the first request with the guard's message
80
+ * telling the user to run the command that just declined to help (#251, #235).
81
+ *
82
+ * Against the migration files this binary ships, so a future migration is covered by having
83
+ * been added rather than by anyone remembering a number.
84
+ */
85
+ function buildSchemaMatches(loreDirectory, buildId) {
86
+ const path = join(loreDirectory, 'builds', buildId, 'context.sqlite');
87
+ const expected = loadMigrations(buildMigrationsDirectory()).reduce((highest, one) => (one.id > highest ? one.id : highest), '0000');
88
+ const db = openReadOnly(path);
89
+ try {
90
+ const row = db.prepare('SELECT max(id) AS latest FROM schema_migrations').get();
91
+ return (row?.latest ?? '0000') === expected;
92
+ }
93
+ finally {
94
+ db.close();
95
+ }
96
+ }
97
+ function readBuildCatalogOrThrow(loreDirectory, buildId) {
98
+ const path = join(loreDirectory, 'builds', buildId, 'context.sqlite');
99
+ if (!existsSync(path))
100
+ return null;
101
+ const manifest = buildManifestSchema.parse(JSON.parse(readFileSync(join(loreDirectory, 'builds', buildId, 'manifest.json'), 'utf8')));
102
+ const db = openReadOnly(path);
103
+ try {
104
+ const rows = db.prepare('SELECT id, content_hash FROM artifacts').all();
105
+ const chunks = db.prepare('SELECT count(*) AS n FROM chunks').get();
106
+ return {
107
+ buildId,
108
+ artifactHashes: new Map(rows.map((row) => [row.id, row.content_hash])),
109
+ chunkCount: Number(chunks.n),
110
+ configurationHash: manifest.configurationHash,
111
+ tableCount: manifest.counts.tables,
112
+ capabilities: manifest.capabilities,
113
+ };
114
+ }
115
+ finally {
116
+ db.close();
117
+ }
118
+ }
119
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/services/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { type RemoteRetentionReceipt } from '@lorepack/core';
2
+ export declare function remotePruneReceiptsDirectory(projectRoot: string): string;
3
+ export declare function remotePruneReceiptPath(projectRoot: string, receiptId: string): string;
4
+ export declare function readRemotePruneReceipt(projectRoot: string, receiptId: string): RemoteRetentionReceipt;
5
+ export declare function writeRemotePruneReceipt(projectRoot: string, receipt: RemoteRetentionReceipt): void;
6
+ export declare function remotePruneReceiptId(seed: string | null): string;
7
+ //# sourceMappingURL=remote-prune.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-prune.d.ts","sourceRoot":"","sources":["../../src/services/remote-prune.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,sBAAsB,EAG5B,MAAM,gBAAgB,CAAC;AAExB,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAErF;AAED,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,sBAAsB,CAuBxB;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,sBAAsB,GAC9B,IAAI,CAMN;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEhE"}
@@ -0,0 +1,34 @@
1
+ import { existsSync, mkdirSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { LORE_DIRECTORY, LoreError, remoteRetentionReceiptSchema, writeFileAtomic, } from '@lorepack/core';
4
+ export function remotePruneReceiptsDirectory(projectRoot) {
5
+ return join(projectRoot, LORE_DIRECTORY, 'receipts');
6
+ }
7
+ export function remotePruneReceiptPath(projectRoot, receiptId) {
8
+ return join(remotePruneReceiptsDirectory(projectRoot), `${receiptId}.json`);
9
+ }
10
+ export function readRemotePruneReceipt(projectRoot, receiptId) {
11
+ const path = remotePruneReceiptPath(projectRoot, receiptId);
12
+ if (!existsSync(path)) {
13
+ throw new LoreError('LORE_E_BUILD_NOT_FOUND', `No remote cleanup receipt ${receiptId}.`, {
14
+ remediation: 'Check the receipt id, or start the cleanup again from the beginning.',
15
+ subject: receiptId,
16
+ });
17
+ }
18
+ const parsed = remoteRetentionReceiptSchema.safeParse(JSON.parse(readFileSync(path, 'utf8')));
19
+ if (!parsed.success) {
20
+ throw new LoreError('LORE_E_BUILD_VALIDATION', `Remote cleanup receipt ${receiptId} is not readable.`, {
21
+ remediation: 'Start the cleanup again from the beginning. A receipt is a record, so losing one is safe.',
22
+ subject: receiptId,
23
+ });
24
+ }
25
+ return parsed.data;
26
+ }
27
+ export function writeRemotePruneReceipt(projectRoot, receipt) {
28
+ mkdirSync(remotePruneReceiptsDirectory(projectRoot), { recursive: true });
29
+ writeFileAtomic(remotePruneReceiptPath(projectRoot, receipt.receiptId), `${JSON.stringify(receipt, null, 2)}\n`);
30
+ }
31
+ export function remotePruneReceiptId(seed) {
32
+ return `cloudflare-prune-${seed === null ? 'none' : seed.slice(5, 17)}`;
33
+ }
34
+ //# sourceMappingURL=remote-prune.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-prune.js","sourceRoot":"","sources":["../../src/services/remote-prune.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,cAAc,EACd,SAAS,EAET,4BAA4B,EAC5B,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,MAAM,UAAU,4BAA4B,CAAC,WAAmB;IAC9D,OAAO,IAAI,CAAC,WAAW,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,WAAmB,EAAE,SAAiB;IAC3E,OAAO,IAAI,CAAC,4BAA4B,CAAC,WAAW,CAAC,EAAE,GAAG,SAAS,OAAO,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,WAAmB,EACnB,SAAiB;IAEjB,MAAM,IAAI,GAAG,sBAAsB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAC5D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,SAAS,CAAC,wBAAwB,EAAE,6BAA6B,SAAS,GAAG,EAAE;YACvF,WAAW,EAAE,sEAAsE;YACnF,OAAO,EAAE,SAAS;SACnB,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,4BAA4B,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC9F,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CACjB,yBAAyB,EACzB,0BAA0B,SAAS,mBAAmB,EACtD;YACE,WAAW,EACT,2FAA2F;YAC7F,OAAO,EAAE,SAAS;SACnB,CACF,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,WAAmB,EACnB,OAA+B;IAE/B,SAAS,CAAC,4BAA4B,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1E,eAAe,CACb,sBAAsB,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,EACtD,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACxC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAmB;IACtD,OAAO,oBAAoB,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type BuildId, type BuildSummary } from '@lorepack/core';
2
+ /**
3
+ * Local retention (architecture section 17.8): keep the active build and the previous five
4
+ * verified builds.
5
+ *
6
+ * A cleanup plan is always produced before anything is removed, and objects referenced by
7
+ * a retained build are never deleted. Retention exists to bound disk use, so deleting
8
+ * something a surviving build still needs would defeat its own purpose.
9
+ */
10
+ export declare const DEFAULT_KEEP_PREVIOUS = 5;
11
+ export interface RetentionPlan {
12
+ readonly keep: readonly BuildId[];
13
+ readonly remove: readonly BuildId[];
14
+ readonly objectsToRemove: readonly string[];
15
+ readonly bytesFreed: number;
16
+ }
17
+ export declare function planRetention(loreDirectory: string, builds: readonly BuildSummary[], active: BuildId | null, keepPrevious?: number): RetentionPlan;
18
+ export declare function applyRetention(loreDirectory: string, plan: RetentionPlan): void;
19
+ export declare function renderRetentionPlan(plan: RetentionPlan): string;
20
+ //# sourceMappingURL=retention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.d.ts","sourceRoot":"","sources":["../../src/services/retention.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,YAAY,EAAS,MAAM,gBAAgB,CAAC;AAGxE;;;;;;;GAOG;AAEH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;IACpC,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,MAAM,EAAE,OAAO,GAAG,IAAI,EACtB,YAAY,GAAE,MAA8B,GAC3C,aAAa,CAwCf;AAED,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI,CAO/E;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAY/D"}
@@ -0,0 +1,106 @@
1
+ import { existsSync, readdirSync, readFileSync, rmSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { openReadOnly } from '@lorepack/backend-local';
4
+ import { count } from '@lorepack/core';
5
+ import { buildDirectory } from './builds.js';
6
+ /**
7
+ * Local retention (architecture section 17.8): keep the active build and the previous five
8
+ * verified builds.
9
+ *
10
+ * A cleanup plan is always produced before anything is removed, and objects referenced by
11
+ * a retained build are never deleted. Retention exists to bound disk use, so deleting
12
+ * something a surviving build still needs would defeat its own purpose.
13
+ */
14
+ export const DEFAULT_KEEP_PREVIOUS = 5;
15
+ export function planRetention(loreDirectory, builds, active, keepPrevious = DEFAULT_KEEP_PREVIOUS) {
16
+ // Newest first, with the active build pinned regardless of its position in history.
17
+ const ordered = [...builds].sort((a, b) => a.createdAt === b.createdAt
18
+ ? b.buildId.localeCompare(a.buildId)
19
+ : b.createdAt.localeCompare(a.createdAt));
20
+ const keep = new Set();
21
+ if (active !== null)
22
+ keep.add(active);
23
+ for (const build of ordered) {
24
+ if (keep.size >= keepPrevious + (active === null ? 0 : 1))
25
+ break;
26
+ if (build.state === 'failed')
27
+ continue;
28
+ keep.add(build.buildId);
29
+ }
30
+ const remove = ordered.map((build) => build.buildId).filter((id) => !keep.has(id));
31
+ // Objects are content addressed and shared between builds, so a hash is only removable
32
+ // when no retained build references it.
33
+ const referenced = new Set();
34
+ for (const id of keep) {
35
+ for (const hash of objectHashesOf(loreDirectory, id))
36
+ referenced.add(hash);
37
+ }
38
+ const doomed = new Set();
39
+ for (const id of remove) {
40
+ for (const hash of objectHashesOf(loreDirectory, id)) {
41
+ if (!referenced.has(hash))
42
+ doomed.add(hash);
43
+ }
44
+ }
45
+ return {
46
+ keep: [...keep],
47
+ remove,
48
+ objectsToRemove: [...doomed].sort(),
49
+ bytesFreed: remove.reduce((sum, id) => sum + directorySize(buildDirectory(loreDirectory, id)), 0),
50
+ };
51
+ }
52
+ export function applyRetention(loreDirectory, plan) {
53
+ for (const id of plan.remove) {
54
+ rmSync(buildDirectory(loreDirectory, id), { recursive: true, force: true, maxRetries: 3 });
55
+ }
56
+ for (const hash of plan.objectsToRemove) {
57
+ rmSync(objectPath(loreDirectory, hash), { force: true, maxRetries: 3 });
58
+ }
59
+ }
60
+ export function renderRetentionPlan(plan) {
61
+ if (plan.remove.length === 0) {
62
+ return `Nothing to remove. ${count(plan.keep.length, 'build')} retained.`;
63
+ }
64
+ const lines = [
65
+ `Removing ${count(plan.remove.length, 'build')}, keeping ${plan.keep.length}:`,
66
+ '',
67
+ ];
68
+ for (const id of plan.remove)
69
+ lines.push(` - ${id}`);
70
+ lines.push('', ` ${count(plan.objectsToRemove.length, 'unreferenced object')}`);
71
+ lines.push(` about ${Math.round(plan.bytesFreed / 1024)} KB freed`);
72
+ return lines.join('\n');
73
+ }
74
+ function objectHashesOf(loreDirectory, buildId) {
75
+ const path = join(buildDirectory(loreDirectory, buildId), 'context.sqlite');
76
+ if (!existsSync(path))
77
+ return [];
78
+ const db = openReadOnly(path);
79
+ try {
80
+ return db.prepare('SELECT DISTINCT object_hash FROM artifacts').all().map((row) => row.object_hash);
81
+ }
82
+ finally {
83
+ db.close();
84
+ }
85
+ }
86
+ function objectPath(loreDirectory, hash) {
87
+ return join(loreDirectory, 'objects', 'sha256', hash.slice(0, 2), hash.slice(2, 4), hash.slice(4));
88
+ }
89
+ function directorySize(directory) {
90
+ if (!existsSync(directory))
91
+ return 0;
92
+ let total = 0;
93
+ for (const entry of readdirSync(directory, { withFileTypes: true, recursive: true })) {
94
+ if (!entry.isFile())
95
+ continue;
96
+ try {
97
+ total += readFileSync(join(entry.parentPath, entry.name)).byteLength;
98
+ }
99
+ catch {
100
+ // A file that vanished between listing and reading contributes nothing. The figure
101
+ // is an estimate shown to a human, not an accounting record.
102
+ }
103
+ }
104
+ return total;
105
+ }
106
+ //# sourceMappingURL=retention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.js","sourceRoot":"","sources":["../../src/services/retention.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { LoadedConfig, SourceState } from '@lorepack/core';
2
+ /**
3
+ * Keeping a long-lived server honest about freshness, without paying for it every request.
4
+ *
5
+ * An open MCP connection is not a session (2026-07-28), so a process can serve one
6
+ * conversation for an hour. Checking freshness once at startup would let it report `clean`
7
+ * for that entire hour while the sources moved underneath it.
8
+ *
9
+ * Checking properly on every request is the other failure. Content hashing every artifact
10
+ * is what dominates a no-op build, and at the 50,000-chunk envelope, on bursty agent
11
+ * traffic, that is seconds of work to annotate a result that took milliseconds.
12
+ *
13
+ * So: an interval, and a cheap prescreen inside it. Architecture 12.3 permits size and
14
+ * mtime as a **prescreen** while keeping content hashes as the deciding evidence, which is
15
+ * exactly this case. A scan that finds nothing moved answers from cache; a scan that finds
16
+ * something calls the real, hashing check.
17
+ */
18
+ export interface RevalidatorOptions {
19
+ readonly config: LoadedConfig;
20
+ /** Milliseconds between checks. `0` checks every request. */
21
+ readonly intervalMs: number;
22
+ /** Never checks. The answer is whatever the first one was. */
23
+ readonly frozen?: boolean;
24
+ readonly now?: () => number;
25
+ }
26
+ export interface Revalidator {
27
+ /** The freshness to stamp on a response, cheap enough to call per request. */
28
+ freshness(): Promise<SourceState>;
29
+ /** How many full, hashing checks have run. The test for "no filesystem work" reads it. */
30
+ readonly deepChecks: number;
31
+ /** How many cheap metadata scans have run. */
32
+ readonly prescreens: number;
33
+ }
34
+ export declare const DEFAULT_REVALIDATE_INTERVAL_MS = 5000;
35
+ export declare function createRevalidator(options: RevalidatorOptions): Revalidator;
36
+ //# sourceMappingURL=revalidate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revalidate.d.ts","sourceRoot":"","sources":["../../src/services/revalidate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGhE;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,8EAA8E;IAC9E,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,8BAA8B,OAAO,CAAC;AAEnD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAwC1E"}
@@ -0,0 +1,73 @@
1
+ import { statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { discover } from '@lorepack/compiler';
4
+ import { readFreshness } from './status.js';
5
+ export const DEFAULT_REVALIDATE_INTERVAL_MS = 5000;
6
+ export function createRevalidator(options) {
7
+ const now = options.now ?? Date.now;
8
+ let cached = 'unknown';
9
+ let checkedAt = Number.NEGATIVE_INFINITY;
10
+ let signature = '';
11
+ let deepChecks = 0;
12
+ let prescreens = 0;
13
+ let started = false;
14
+ const state = {
15
+ get deepChecks() {
16
+ return deepChecks;
17
+ },
18
+ get prescreens() {
19
+ return prescreens;
20
+ },
21
+ async freshness() {
22
+ if (options.frozen === true)
23
+ return 'unknown';
24
+ const elapsed = now() - checkedAt;
25
+ if (started && elapsed < options.intervalMs)
26
+ return cached;
27
+ // The prescreen: sizes and modification times only, no file contents. It cannot
28
+ // prove the sources are unchanged, which is why a difference sends us to the real
29
+ // check rather than being reported directly (architecture 12.3).
30
+ const scanned = scan(options.config);
31
+ prescreens += 1;
32
+ checkedAt = now();
33
+ if (started && scanned === signature)
34
+ return cached;
35
+ signature = scanned;
36
+ deepChecks += 1;
37
+ started = true;
38
+ cached = (await readFreshness({ config: options.config })).sourceState;
39
+ return cached;
40
+ },
41
+ };
42
+ return state;
43
+ }
44
+ /**
45
+ * A cheap signature of the source tree: path, size and modification time.
46
+ *
47
+ * Deliberately not evidence of anything on its own. A file can change without its mtime
48
+ * moving, which is the reason content hashes decide freshness, and this only decides
49
+ * whether to go and compute them.
50
+ */
51
+ function scan(config) {
52
+ try {
53
+ const discovered = discover({ config, allowLargeProject: true });
54
+ const parts = [];
55
+ for (const artifact of discovered.artifacts) {
56
+ try {
57
+ const stats = statSync(join(config.projectRoot, artifact.relativePath));
58
+ parts.push(`${artifact.relativePath}:${stats.size}:${stats.mtimeMs}`);
59
+ }
60
+ catch {
61
+ // A file that vanished between discovery and stat is a change by itself.
62
+ parts.push(`${artifact.relativePath}:gone`);
63
+ }
64
+ }
65
+ return parts.join('\n');
66
+ }
67
+ catch {
68
+ // A source tree that cannot be walked is not a reason to fail a read (#147). Returning
69
+ // a distinct value sends the caller to the real check, which degrades properly.
70
+ return `unscannable:${Math.random()}`;
71
+ }
72
+ }
73
+ //# sourceMappingURL=revalidate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revalidate.js","sourceRoot":"","sources":["../../src/services/revalidate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAqC5C,MAAM,CAAC,MAAM,8BAA8B,GAAG,IAAI,CAAC;AAEnD,MAAM,UAAU,iBAAiB,CAAC,OAA2B;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,IAAI,MAAM,GAAgB,SAAS,CAAC;IACpC,IAAI,SAAS,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACzC,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,MAAM,KAAK,GAAgB;QACzB,IAAI,UAAU;YACZ,OAAO,UAAU,CAAC;QACpB,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,UAAU,CAAC;QACpB,CAAC;QACD,KAAK,CAAC,SAAS;YACb,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI;gBAAE,OAAO,SAAS,CAAC;YAE9C,MAAM,OAAO,GAAG,GAAG,EAAE,GAAG,SAAS,CAAC;YAClC,IAAI,OAAO,IAAI,OAAO,GAAG,OAAO,CAAC,UAAU;gBAAE,OAAO,MAAM,CAAC;YAE3D,gFAAgF;YAChF,kFAAkF;YAClF,iEAAiE;YACjE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACrC,UAAU,IAAI,CAAC,CAAC;YAChB,SAAS,GAAG,GAAG,EAAE,CAAC;YAElB,IAAI,OAAO,IAAI,OAAO,KAAK,SAAS;gBAAE,OAAO,MAAM,CAAC;YAEpD,SAAS,GAAG,OAAO,CAAC;YACpB,UAAU,IAAI,CAAC,CAAC;YAChB,OAAO,GAAG,IAAI,CAAC;YACf,MAAM,GAAG,CAAC,MAAM,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC;YACvE,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC;IAEF,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,SAAS,IAAI,CAAC,MAAoB;IAChC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,QAAQ,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,QAAQ,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;YAC5C,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC;gBACxE,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,YAAY,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACxE,CAAC;YAAC,MAAM,CAAC;gBACP,yEAAyE;gBACzE,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,YAAY,OAAO,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,uFAAuF;QACvF,gFAAgF;QAChF,OAAO,eAAe,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;IACxC,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { type LoadedConfig, type SearchResult, type SourceState } from '@lorepack/core';
2
+ /**
3
+ * `lorepack search` over the active build.
4
+ *
5
+ * The query itself lives in `LoreRuntime.search`, which MCP, REST and Studio also call.
6
+ * This file is the CLI's adapter to it: build the local ports, ask, render. Keeping a
7
+ * second search path here would mean two ranking implementations and two response shapes
8
+ * to hold in step, and the shape is a published contract.
9
+ */
10
+ export interface SearchOptions {
11
+ readonly config: LoadedConfig;
12
+ readonly query: string;
13
+ readonly sourceState: SourceState;
14
+ readonly limit?: number;
15
+ readonly pathGlob?: string;
16
+ readonly type?: string;
17
+ /** One document, by artifact id or canonical path. */
18
+ readonly artifactId?: string;
19
+ readonly includeArchived?: boolean;
20
+ /** Returns the score components, so a reader can see why a page is ordered as it is. */
21
+ readonly debug?: boolean;
22
+ }
23
+ export declare function runSearch(options: SearchOptions): Promise<SearchResult>;
24
+ export declare function renderSearch(result: SearchResult, query: string, verbose?: boolean): string;
25
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/services/search.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,gBAAgB,CAAC;AAGxB;;;;;;;GAOG;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,wFAAwF;IACxF,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAsB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA2B7E;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG,MAAM,CA6CzF"}