@package-broker/core 0.17.4 → 0.19.7

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 (346) hide show
  1. package/dist/cache/index.d.ts +0 -1
  2. package/dist/cache/index.d.ts.map +1 -1
  3. package/dist/cache/index.js +0 -1
  4. package/dist/cache/index.js.map +1 -1
  5. package/dist/cache/memory-driver.d.ts +1 -1
  6. package/dist/cache/memory-driver.d.ts.map +1 -1
  7. package/dist/cache/memory-driver.js +4 -2
  8. package/dist/cache/memory-driver.js.map +1 -1
  9. package/dist/db/create-database.d.ts +8 -0
  10. package/dist/db/create-database.d.ts.map +1 -0
  11. package/dist/db/create-database.js +13 -0
  12. package/dist/db/create-database.js.map +1 -0
  13. package/dist/db/index.d.ts +0 -1
  14. package/dist/db/index.d.ts.map +1 -1
  15. package/dist/db/index.js +0 -1
  16. package/dist/db/index.js.map +1 -1
  17. package/dist/db/schema.d.ts +557 -0
  18. package/dist/db/schema.d.ts.map +1 -1
  19. package/dist/db/schema.js +72 -2
  20. package/dist/db/schema.js.map +1 -1
  21. package/dist/factory.d.ts.map +1 -1
  22. package/dist/factory.js +22 -2
  23. package/dist/factory.js.map +1 -1
  24. package/dist/index.d.ts +2 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -2
  27. package/dist/index.js.map +1 -1
  28. package/dist/jobs/processor.d.ts.map +1 -1
  29. package/dist/jobs/processor.js +16 -6
  30. package/dist/jobs/processor.js.map +1 -1
  31. package/dist/kernel/container.d.ts +12 -0
  32. package/dist/kernel/container.d.ts.map +1 -0
  33. package/dist/kernel/container.js +43 -0
  34. package/dist/kernel/container.js.map +1 -0
  35. package/dist/kernel/events.d.ts +17 -0
  36. package/dist/kernel/events.d.ts.map +1 -0
  37. package/dist/kernel/events.js +54 -0
  38. package/dist/kernel/events.js.map +1 -0
  39. package/dist/kernel/hooks.d.ts +14 -0
  40. package/dist/kernel/hooks.d.ts.map +1 -0
  41. package/dist/kernel/hooks.js +31 -0
  42. package/dist/kernel/hooks.js.map +1 -0
  43. package/dist/kernel/index.d.ts +5 -0
  44. package/dist/kernel/index.d.ts.map +1 -0
  45. package/dist/kernel/index.js +5 -0
  46. package/dist/kernel/index.js.map +1 -0
  47. package/dist/kernel/plugin.d.ts +21 -0
  48. package/dist/kernel/plugin.d.ts.map +1 -0
  49. package/dist/kernel/plugin.js +30 -0
  50. package/dist/kernel/plugin.js.map +1 -0
  51. package/dist/middleware/auth.d.ts +2 -0
  52. package/dist/middleware/auth.d.ts.map +1 -1
  53. package/dist/middleware/auth.js +20 -5
  54. package/dist/middleware/auth.js.map +1 -1
  55. package/dist/modules/admin/admin.handlers.d.ts +1 -1
  56. package/dist/modules/admin/admin.handlers.d.ts.map +1 -1
  57. package/dist/modules/artifacts/artifacts.handlers.d.ts +1 -1
  58. package/dist/modules/artifacts/artifacts.handlers.d.ts.map +1 -1
  59. package/dist/modules/auth/auth.handlers.js +1 -1
  60. package/dist/modules/auth/auth.handlers.js.map +1 -1
  61. package/dist/modules/composer/index.d.ts.map +1 -1
  62. package/dist/modules/composer/index.js +5 -0
  63. package/dist/modules/composer/index.js.map +1 -1
  64. package/dist/modules/composer/tenant-composer.d.ts +32 -0
  65. package/dist/modules/composer/tenant-composer.d.ts.map +1 -0
  66. package/dist/modules/composer/tenant-composer.js +198 -0
  67. package/dist/modules/composer/tenant-composer.js.map +1 -0
  68. package/dist/modules/import/import.handlers.d.ts +4 -0
  69. package/dist/modules/import/import.handlers.d.ts.map +1 -0
  70. package/dist/modules/import/import.handlers.js +16 -0
  71. package/dist/modules/import/import.handlers.js.map +1 -0
  72. package/dist/{routes/api/openapi/settings.d.ts → modules/import/import.routes.d.ts} +20 -35
  73. package/dist/modules/import/import.routes.d.ts.map +1 -0
  74. package/dist/modules/import/import.routes.js +63 -0
  75. package/dist/modules/import/import.routes.js.map +1 -0
  76. package/dist/modules/import/index.d.ts +8 -0
  77. package/dist/modules/import/index.d.ts.map +1 -0
  78. package/dist/modules/import/index.js +8 -0
  79. package/dist/modules/import/index.js.map +1 -0
  80. package/dist/modules/organizations/index.d.ts +8 -0
  81. package/dist/modules/organizations/index.d.ts.map +1 -0
  82. package/dist/modules/organizations/index.js +22 -0
  83. package/dist/modules/organizations/index.js.map +1 -0
  84. package/dist/modules/organizations/organizations.handlers.d.ts +24 -0
  85. package/dist/modules/organizations/organizations.handlers.d.ts.map +1 -0
  86. package/dist/modules/organizations/organizations.handlers.js +278 -0
  87. package/dist/modules/organizations/organizations.handlers.js.map +1 -0
  88. package/dist/modules/organizations/organizations.routes.d.ts +596 -0
  89. package/dist/modules/organizations/organizations.routes.d.ts.map +1 -0
  90. package/dist/modules/organizations/organizations.routes.js +263 -0
  91. package/dist/modules/organizations/organizations.routes.js.map +1 -0
  92. package/dist/modules/packages/packages.handlers.d.ts +1 -1
  93. package/dist/modules/packages/packages.handlers.d.ts.map +1 -1
  94. package/dist/modules/packages/packages.handlers.js +3 -69
  95. package/dist/modules/packages/packages.handlers.js.map +1 -1
  96. package/dist/modules/repositories/repositories.handlers.d.ts +1 -1
  97. package/dist/modules/repositories/repositories.handlers.d.ts.map +1 -1
  98. package/dist/modules/repositories/repositories.handlers.js +74 -3
  99. package/dist/modules/repositories/repositories.handlers.js.map +1 -1
  100. package/dist/modules/repositories/repositories.routes.d.ts +12 -0
  101. package/dist/modules/repositories/repositories.routes.d.ts.map +1 -1
  102. package/dist/modules/system/index.d.ts.map +1 -1
  103. package/dist/modules/system/index.js +2 -1
  104. package/dist/modules/system/index.js.map +1 -1
  105. package/dist/modules/system/system.handlers.d.ts +11 -1
  106. package/dist/modules/system/system.handlers.d.ts.map +1 -1
  107. package/dist/modules/system/system.handlers.js +31 -1
  108. package/dist/modules/system/system.handlers.js.map +1 -1
  109. package/dist/modules/system/system.routes.d.ts +34 -4
  110. package/dist/modules/system/system.routes.d.ts.map +1 -1
  111. package/dist/modules/system/system.routes.js +21 -1
  112. package/dist/modules/system/system.routes.js.map +1 -1
  113. package/dist/modules/tenants/index.d.ts +8 -0
  114. package/dist/modules/tenants/index.d.ts.map +1 -0
  115. package/dist/modules/tenants/index.js +21 -0
  116. package/dist/modules/tenants/index.js.map +1 -0
  117. package/dist/modules/tenants/tenants.handlers.d.ts +24 -0
  118. package/dist/modules/tenants/tenants.handlers.d.ts.map +1 -0
  119. package/dist/modules/tenants/tenants.handlers.js +268 -0
  120. package/dist/modules/tenants/tenants.handlers.js.map +1 -0
  121. package/dist/modules/tenants/tenants.routes.d.ts +486 -0
  122. package/dist/modules/tenants/tenants.routes.d.ts.map +1 -0
  123. package/dist/modules/tenants/tenants.routes.js +227 -0
  124. package/dist/modules/tenants/tenants.routes.js.map +1 -0
  125. package/dist/modules/tokens/tokens.handlers.d.ts +1 -1
  126. package/dist/modules/tokens/tokens.handlers.d.ts.map +1 -1
  127. package/dist/modules/users/users.handlers.d.ts +1 -1
  128. package/dist/modules/users/users.handlers.d.ts.map +1 -1
  129. package/dist/modules/users/users.handlers.js +1 -1
  130. package/dist/modules/users/users.handlers.js.map +1 -1
  131. package/dist/plugins/security-advisories/advisory-db.d.ts +85 -0
  132. package/dist/plugins/security-advisories/advisory-db.d.ts.map +1 -0
  133. package/dist/plugins/security-advisories/advisory-db.js +161 -0
  134. package/dist/plugins/security-advisories/advisory-db.js.map +1 -0
  135. package/dist/plugins/security-advisories/advisory-service.d.ts +44 -0
  136. package/dist/plugins/security-advisories/advisory-service.d.ts.map +1 -0
  137. package/dist/plugins/security-advisories/advisory-service.js +122 -0
  138. package/dist/plugins/security-advisories/advisory-service.js.map +1 -0
  139. package/dist/plugins/security-advisories/advisory.handlers.d.ts +13 -0
  140. package/dist/plugins/security-advisories/advisory.handlers.d.ts.map +1 -0
  141. package/dist/plugins/security-advisories/advisory.handlers.js +87 -0
  142. package/dist/plugins/security-advisories/advisory.handlers.js.map +1 -0
  143. package/dist/plugins/security-advisories/advisory.module.d.ts +4 -0
  144. package/dist/plugins/security-advisories/advisory.module.d.ts.map +1 -0
  145. package/dist/plugins/security-advisories/advisory.module.js +13 -0
  146. package/dist/plugins/security-advisories/advisory.module.js.map +1 -0
  147. package/dist/plugins/security-advisories/advisory.routes.d.ts +73 -0
  148. package/dist/plugins/security-advisories/advisory.routes.d.ts.map +1 -0
  149. package/dist/plugins/security-advisories/advisory.routes.js +76 -0
  150. package/dist/plugins/security-advisories/advisory.routes.js.map +1 -0
  151. package/dist/plugins/security-advisories/index.d.ts +31 -0
  152. package/dist/plugins/security-advisories/index.d.ts.map +1 -0
  153. package/dist/plugins/security-advisories/index.js +100 -0
  154. package/dist/plugins/security-advisories/index.js.map +1 -0
  155. package/dist/ports.d.ts +39 -21
  156. package/dist/ports.d.ts.map +1 -1
  157. package/dist/queue/consumer.d.ts +0 -8
  158. package/dist/queue/consumer.d.ts.map +1 -1
  159. package/dist/queue/consumer.js +2 -23
  160. package/dist/queue/consumer.js.map +1 -1
  161. package/dist/queue/index.d.ts +2 -0
  162. package/dist/queue/index.d.ts.map +1 -1
  163. package/dist/queue/index.js +2 -0
  164. package/dist/queue/index.js.map +1 -1
  165. package/dist/queue/memory-driver.d.ts +3 -2
  166. package/dist/queue/memory-driver.d.ts.map +1 -1
  167. package/dist/queue/memory-driver.js.map +1 -1
  168. package/dist/routes/composer.d.ts +7 -1
  169. package/dist/routes/composer.d.ts.map +1 -1
  170. package/dist/routes/composer.js +172 -70
  171. package/dist/routes/composer.js.map +1 -1
  172. package/dist/routes/dist.d.ts.map +1 -1
  173. package/dist/routes/dist.js +26 -80
  174. package/dist/routes/dist.js.map +1 -1
  175. package/dist/routes/index.d.ts +0 -1
  176. package/dist/routes/index.d.ts.map +1 -1
  177. package/dist/routes/index.js +0 -1
  178. package/dist/routes/index.js.map +1 -1
  179. package/dist/routes/package-revalidation.d.ts +38 -0
  180. package/dist/routes/package-revalidation.d.ts.map +1 -0
  181. package/dist/routes/package-revalidation.js +66 -0
  182. package/dist/routes/package-revalidation.js.map +1 -0
  183. package/dist/services/GitHubOrgImporter.d.ts +21 -0
  184. package/dist/services/GitHubOrgImporter.d.ts.map +1 -0
  185. package/dist/services/GitHubOrgImporter.js +51 -0
  186. package/dist/services/GitHubOrgImporter.js.map +1 -0
  187. package/dist/services/TokenScopeService.d.ts +15 -0
  188. package/dist/services/TokenScopeService.d.ts.map +1 -0
  189. package/dist/services/TokenScopeService.js +42 -0
  190. package/dist/services/TokenScopeService.js.map +1 -0
  191. package/dist/storage/index.d.ts +0 -1
  192. package/dist/storage/index.d.ts.map +1 -1
  193. package/dist/storage/index.js +0 -1
  194. package/dist/storage/index.js.map +1 -1
  195. package/dist/sync/repository-sync.d.ts.map +1 -1
  196. package/dist/sync/repository-sync.js +35 -23
  197. package/dist/sync/repository-sync.js.map +1 -1
  198. package/dist/sync/strategies/git-ssh.d.ts +17 -0
  199. package/dist/sync/strategies/git-ssh.d.ts.map +1 -0
  200. package/dist/sync/strategies/git-ssh.js +325 -0
  201. package/dist/sync/strategies/git-ssh.js.map +1 -0
  202. package/dist/{routes/api/types.d.ts → types/openapi.d.ts} +3 -4
  203. package/dist/types/openapi.d.ts.map +1 -0
  204. package/dist/{routes/api/types.js → types/openapi.js} +1 -1
  205. package/dist/types/openapi.js.map +1 -0
  206. package/dist/utils/background.d.ts +7 -0
  207. package/dist/utils/background.d.ts.map +1 -0
  208. package/dist/utils/background.js +11 -0
  209. package/dist/utils/background.js.map +1 -0
  210. package/dist/utils/environment.d.ts +15 -0
  211. package/dist/utils/environment.d.ts.map +1 -0
  212. package/dist/utils/environment.js +38 -0
  213. package/dist/utils/environment.js.map +1 -0
  214. package/dist/utils/index.d.ts +1 -0
  215. package/dist/utils/index.d.ts.map +1 -1
  216. package/dist/utils/index.js +1 -0
  217. package/dist/utils/index.js.map +1 -1
  218. package/dist/utils/package-filter.d.ts +7 -0
  219. package/dist/utils/package-filter.d.ts.map +1 -0
  220. package/dist/utils/package-filter.js +23 -0
  221. package/dist/utils/package-filter.js.map +1 -0
  222. package/dist/utils/package-validator.d.ts +3 -3
  223. package/dist/utils/package-validator.d.ts.map +1 -1
  224. package/dist/utils/package-validator.js +4 -37
  225. package/dist/utils/package-validator.js.map +1 -1
  226. package/dist/utils/upstream-fetch.d.ts +18 -0
  227. package/dist/utils/upstream-fetch.d.ts.map +1 -1
  228. package/dist/utils/upstream-fetch.js +76 -5
  229. package/dist/utils/upstream-fetch.js.map +1 -1
  230. package/dist/utils/zip-utils.d.ts +13 -0
  231. package/dist/utils/zip-utils.d.ts.map +1 -0
  232. package/dist/utils/zip-utils.js +66 -0
  233. package/dist/utils/zip-utils.js.map +1 -0
  234. package/dist/vcs/bitbucket-provider.d.ts +19 -0
  235. package/dist/vcs/bitbucket-provider.d.ts.map +1 -0
  236. package/dist/vcs/bitbucket-provider.js +249 -0
  237. package/dist/vcs/bitbucket-provider.js.map +1 -0
  238. package/dist/vcs/github-provider.d.ts +16 -0
  239. package/dist/vcs/github-provider.d.ts.map +1 -0
  240. package/dist/vcs/github-provider.js +117 -0
  241. package/dist/vcs/github-provider.js.map +1 -0
  242. package/dist/vcs/gitlab-provider.d.ts +17 -0
  243. package/dist/vcs/gitlab-provider.d.ts.map +1 -0
  244. package/dist/vcs/gitlab-provider.js +216 -0
  245. package/dist/vcs/gitlab-provider.js.map +1 -0
  246. package/dist/vcs/index.d.ts +10 -0
  247. package/dist/vcs/index.d.ts.map +1 -0
  248. package/dist/vcs/index.js +24 -0
  249. package/dist/vcs/index.js.map +1 -0
  250. package/dist/vcs/registry.d.ts +32 -0
  251. package/dist/vcs/registry.d.ts.map +1 -0
  252. package/dist/vcs/registry.js +47 -0
  253. package/dist/vcs/registry.js.map +1 -0
  254. package/dist/workflows/package-storage.d.ts.map +1 -1
  255. package/dist/workflows/package-storage.js +3 -3
  256. package/dist/workflows/package-storage.js.map +1 -1
  257. package/package.json +11 -8
  258. package/dist/cache/kv-driver.d.ts +0 -16
  259. package/dist/cache/kv-driver.d.ts.map +0 -1
  260. package/dist/cache/kv-driver.js +0 -23
  261. package/dist/cache/kv-driver.js.map +0 -1
  262. package/dist/db/d1-driver.d.ts +0 -3
  263. package/dist/db/d1-driver.d.ts.map +0 -1
  264. package/dist/db/d1-driver.js +0 -7
  265. package/dist/db/d1-driver.js.map +0 -1
  266. package/dist/routes/api/artifacts.d.ts +0 -27
  267. package/dist/routes/api/artifacts.d.ts.map +0 -1
  268. package/dist/routes/api/artifacts.js +0 -57
  269. package/dist/routes/api/artifacts.js.map +0 -1
  270. package/dist/routes/api/auth.d.ts +0 -52
  271. package/dist/routes/api/auth.d.ts.map +0 -1
  272. package/dist/routes/api/auth.js +0 -277
  273. package/dist/routes/api/auth.js.map +0 -1
  274. package/dist/routes/api/index.d.ts +0 -10
  275. package/dist/routes/api/index.d.ts.map +0 -1
  276. package/dist/routes/api/index.js +0 -11
  277. package/dist/routes/api/index.js.map +0 -1
  278. package/dist/routes/api/openapi/artifacts.d.ts +0 -80
  279. package/dist/routes/api/openapi/artifacts.d.ts.map +0 -1
  280. package/dist/routes/api/openapi/artifacts.js +0 -73
  281. package/dist/routes/api/openapi/artifacts.js.map +0 -1
  282. package/dist/routes/api/openapi/auth.d.ts +0 -187
  283. package/dist/routes/api/openapi/auth.d.ts.map +0 -1
  284. package/dist/routes/api/openapi/auth.js +0 -135
  285. package/dist/routes/api/openapi/auth.js.map +0 -1
  286. package/dist/routes/api/openapi/health.d.ts +0 -23
  287. package/dist/routes/api/openapi/health.d.ts.map +0 -1
  288. package/dist/routes/api/openapi/health.js +0 -25
  289. package/dist/routes/api/openapi/health.js.map +0 -1
  290. package/dist/routes/api/openapi/index.d.ts +0 -10
  291. package/dist/routes/api/openapi/index.d.ts.map +0 -1
  292. package/dist/routes/api/openapi/index.js +0 -16
  293. package/dist/routes/api/openapi/index.js.map +0 -1
  294. package/dist/routes/api/openapi/packages.d.ts +0 -172
  295. package/dist/routes/api/openapi/packages.d.ts.map +0 -1
  296. package/dist/routes/api/openapi/packages.js +0 -126
  297. package/dist/routes/api/openapi/packages.js.map +0 -1
  298. package/dist/routes/api/openapi/repositories.d.ts +0 -451
  299. package/dist/routes/api/openapi/repositories.d.ts.map +0 -1
  300. package/dist/routes/api/openapi/repositories.js +0 -238
  301. package/dist/routes/api/openapi/repositories.js.map +0 -1
  302. package/dist/routes/api/openapi/settings.d.ts.map +0 -1
  303. package/dist/routes/api/openapi/settings.js +0 -72
  304. package/dist/routes/api/openapi/settings.js.map +0 -1
  305. package/dist/routes/api/openapi/stats.d.ts +0 -59
  306. package/dist/routes/api/openapi/stats.d.ts.map +0 -1
  307. package/dist/routes/api/openapi/stats.js +0 -53
  308. package/dist/routes/api/openapi/stats.js.map +0 -1
  309. package/dist/routes/api/openapi/tokens.d.ts +0 -202
  310. package/dist/routes/api/openapi/tokens.d.ts.map +0 -1
  311. package/dist/routes/api/openapi/tokens.js +0 -132
  312. package/dist/routes/api/openapi/tokens.js.map +0 -1
  313. package/dist/routes/api/openapi/users.d.ts +0 -190
  314. package/dist/routes/api/openapi/users.d.ts.map +0 -1
  315. package/dist/routes/api/openapi/users.js +0 -126
  316. package/dist/routes/api/openapi/users.js.map +0 -1
  317. package/dist/routes/api/packages.d.ts +0 -50
  318. package/dist/routes/api/packages.d.ts.map +0 -1
  319. package/dist/routes/api/packages.js +0 -708
  320. package/dist/routes/api/packages.js.map +0 -1
  321. package/dist/routes/api/repositories.d.ts +0 -58
  322. package/dist/routes/api/repositories.d.ts.map +0 -1
  323. package/dist/routes/api/repositories.js +0 -321
  324. package/dist/routes/api/repositories.js.map +0 -1
  325. package/dist/routes/api/settings.d.ts +0 -29
  326. package/dist/routes/api/settings.d.ts.map +0 -1
  327. package/dist/routes/api/settings.js +0 -81
  328. package/dist/routes/api/settings.js.map +0 -1
  329. package/dist/routes/api/stats.d.ts +0 -21
  330. package/dist/routes/api/stats.d.ts.map +0 -1
  331. package/dist/routes/api/stats.js +0 -51
  332. package/dist/routes/api/stats.js.map +0 -1
  333. package/dist/routes/api/tokens.d.ts +0 -40
  334. package/dist/routes/api/tokens.d.ts.map +0 -1
  335. package/dist/routes/api/tokens.js +0 -187
  336. package/dist/routes/api/tokens.js.map +0 -1
  337. package/dist/routes/api/types.d.ts.map +0 -1
  338. package/dist/routes/api/types.js.map +0 -1
  339. package/dist/routes/api/users.d.ts +0 -6
  340. package/dist/routes/api/users.d.ts.map +0 -1
  341. package/dist/routes/api/users.js +0 -115
  342. package/dist/routes/api/users.js.map +0 -1
  343. package/dist/storage/r2-driver.d.ts +0 -16
  344. package/dist/storage/r2-driver.d.ts.map +0 -1
  345. package/dist/storage/r2-driver.js +0 -28
  346. package/dist/storage/r2-driver.js.map +0 -1
@@ -12,6 +12,8 @@ import { nanoid } from 'nanoid';
12
12
  import { encryptCredentials } from '../utils/encryption';
13
13
  import { getLogger } from '../utils/logger';
14
14
  import { getAnalytics } from '../utils/analytics';
15
+ import { runInBackground } from '../utils/background';
16
+ import { revalidateStalePackage, REVALIDATION_TTL_SECONDS } from './package-revalidation';
15
17
  /** Default maximum versions per package to avoid CPU timeout on Cloudflare Workers */
16
18
  export const DEFAULT_MAX_VERSIONS = 50;
17
19
  /**
@@ -28,7 +30,7 @@ function schedulePackageStorage(c, packageName, packageData, repoId, baseUrl) {
28
30
  const logger = getLogger();
29
31
  if (workflow) {
30
32
  // Use Cloudflare Workflow for durable background processing
31
- c.executionCtx.waitUntil((async () => {
33
+ runInBackground(c, (async () => {
32
34
  try {
33
35
  const instance = await workflow.create({
34
36
  id: `pkg-${packageName.replace('/', '-')}-${repoId}-${Date.now()}`,
@@ -67,7 +69,7 @@ function schedulePackageStorage(c, packageName, packageData, repoId, baseUrl) {
67
69
  }
68
70
  else {
69
71
  // Inline background processing
70
- c.executionCtx.waitUntil((async () => {
72
+ runInBackground(c, (async () => {
71
73
  try {
72
74
  const db = c.get('database');
73
75
  const { storedCount, errors } = await transformPackageDistUrls(packageData, repoId, baseUrl, db);
@@ -133,10 +135,13 @@ export async function packagesJsonRoute(c) {
133
135
  }
134
136
  // No cache - build packages.json from database
135
137
  const packagesJson = await buildPackagesJson(c);
136
- // Cache the result (fire-and-forget to avoid blocking on KV rate limits)
138
+ // Cache the result (fire-and-forget to avoid blocking on KV rate limits).
139
+ // Never cache while repository syncs are pending or in flight: the list is
140
+ // built from pre-sync data and would otherwise be pinned in KV until the
141
+ // next manual purge.
137
142
  const cachingEnabled = await isPackageCachingEnabled(c.env.KV);
138
- if (cachingEnabled && c.env.KV) {
139
- c.executionCtx.waitUntil(Promise.all([
143
+ if (cachingEnabled && c.env.KV && !hasPendingRepos) {
144
+ runInBackground(c, Promise.all([
140
145
  c.env.KV.put(kvKey, JSON.stringify(packagesJson)).catch(() => { }),
141
146
  c.env.KV.put(metadataKey, JSON.stringify({ lastModified: Date.now() })).catch(() => { })
142
147
  ]));
@@ -186,6 +191,36 @@ function matchesPackageFilter(packageName, filter) {
186
191
  return pkgLower === pattern;
187
192
  });
188
193
  }
194
+ /**
195
+ * Fetch package metadata from a single repository, dispatching on vcs_type.
196
+ * Returns the raw upstream package data, or null when the repo does not
197
+ * provide the package (filter mismatch, unsupported host, not found).
198
+ */
199
+ export async function fetchPackageFromRepository(repo, packageName, encryptionKey) {
200
+ if (!matchesPackageFilter(packageName, repo.package_filter)) {
201
+ return null;
202
+ }
203
+ const repoRef = {
204
+ id: repo.id,
205
+ url: repo.url,
206
+ vcs_type: repo.vcs_type,
207
+ credential_type: repo.credential_type,
208
+ auth_credentials: repo.auth_credentials,
209
+ package_filter: repo.package_filter,
210
+ };
211
+ if (repo.vcs_type === 'composer') {
212
+ const { fetchPackageFromUpstream } = await import('../utils/upstream-fetch');
213
+ return fetchPackageFromUpstream(repoRef, packageName, encryptionKey);
214
+ }
215
+ if (repo.vcs_type === 'git') {
216
+ const { fetchPackageFromGitHub, isGitHubUrl } = await import('../utils/upstream-fetch');
217
+ if (!isGitHubUrl(repo.url)) {
218
+ return null;
219
+ }
220
+ return fetchPackageFromGitHub(repoRef, packageName, encryptionKey);
221
+ }
222
+ return null;
223
+ }
189
224
  /**
190
225
  * Lazy load package metadata from all available repositories
191
226
  * Returns package data and repo_id if found, null otherwise
@@ -198,35 +233,7 @@ export async function lazyLoadPackageFromRepositories(db, packageName, encryptio
198
233
  const sortedRepos = sortRepositoriesByPriority(allRepos);
199
234
  for (const repo of sortedRepos) {
200
235
  try {
201
- if (!matchesPackageFilter(packageName, repo.package_filter)) {
202
- continue;
203
- }
204
- let packageData = null;
205
- if (repo.vcs_type === 'composer') {
206
- const { fetchPackageFromUpstream } = await import('../utils/upstream-fetch');
207
- packageData = await fetchPackageFromUpstream({
208
- id: repo.id,
209
- url: repo.url,
210
- vcs_type: repo.vcs_type,
211
- credential_type: repo.credential_type,
212
- auth_credentials: repo.auth_credentials,
213
- package_filter: repo.package_filter,
214
- }, packageName, encryptionKey);
215
- }
216
- else if (repo.vcs_type === 'git') {
217
- const { fetchPackageFromGitHub, isGitHubUrl } = await import('../utils/upstream-fetch');
218
- if (!isGitHubUrl(repo.url)) {
219
- continue;
220
- }
221
- packageData = await fetchPackageFromGitHub({
222
- id: repo.id,
223
- url: repo.url,
224
- vcs_type: repo.vcs_type,
225
- credential_type: repo.credential_type,
226
- auth_credentials: repo.auth_credentials,
227
- package_filter: repo.package_filter,
228
- }, packageName, encryptionKey);
229
- }
236
+ const packageData = await fetchPackageFromRepository(repo, packageName, encryptionKey);
230
237
  if (packageData) {
231
238
  return { packageData, repoId: repo.id };
232
239
  }
@@ -245,6 +252,7 @@ export async function lazyLoadPackageFromRepositories(db, packageName, encryptio
245
252
  const mirroringEnabled = await isPackagistMirroringEnabled(kv);
246
253
  if (mirroringEnabled) {
247
254
  try {
255
+ await ensurePackagistRepository(db, encryptionKey, kv || undefined);
248
256
  const packagistUrl = `https://repo.packagist.org/p2/${packageName}.json`;
249
257
  const response = await fetch(packagistUrl, {
250
258
  headers: {
@@ -274,13 +282,16 @@ export async function lazyLoadPackageFromRepositories(db, packageName, encryptio
274
282
  export async function p2PackageRoute(c) {
275
283
  const vendor = c.req.param('vendor');
276
284
  const packageFile = c.req.param('package');
277
- const packageName = `${vendor}/${packageFile?.replace('.json', '')}`;
285
+ // Strip only the trailing extension - package names may contain ".json"
286
+ const packageName = `${vendor}/${packageFile?.replace(/\.json$/, '')}`;
278
287
  if (!vendor || !packageFile) {
279
288
  return c.json({ error: 'Bad Request', message: 'Invalid package name' }, 400);
280
289
  }
281
290
  const kvKey = `p2:${packageName}`;
282
291
  const metadataKey = `p2:${packageName}:metadata`;
283
292
  const db = c.get('database');
293
+ const requestUrl = new URL(c.req.url);
294
+ const requestBaseUrl = `${requestUrl.protocol}//${requestUrl.host}`;
284
295
  // Check conditional request
285
296
  const ifModifiedSince = c.req.header('If-Modified-Since');
286
297
  const metadata = c.env.KV ? await c.env.KV.get(metadataKey, 'json') : null;
@@ -303,7 +314,7 @@ export async function p2PackageRoute(c) {
303
314
  logger.warn('Invalid cache format (not an object or old format), treating as cache miss', { packageName });
304
315
  // Fire-and-forget cache deletion
305
316
  if (c.env.KV) {
306
- c.executionCtx.waitUntil(Promise.all([
317
+ runInBackground(c, Promise.all([
307
318
  c.env.KV.delete(kvKey).catch(() => { }),
308
319
  c.env.KV.delete(metadataKey).catch(() => { })
309
320
  ]));
@@ -336,7 +347,7 @@ export async function p2PackageRoute(c) {
336
347
  logger.warn('Invalid JSON in cache, treating as cache miss', { packageName, error: e instanceof Error ? e.message : String(e) });
337
348
  // Fire-and-forget cache deletion
338
349
  if (c.env.KV) {
339
- c.executionCtx.waitUntil(Promise.all([
350
+ runInBackground(c, Promise.all([
340
351
  c.env.KV.delete(kvKey).catch(() => { }),
341
352
  c.env.KV.delete(metadataKey).catch(() => { })
342
353
  ]));
@@ -351,16 +362,41 @@ export async function p2PackageRoute(c) {
351
362
  if (existingPackages.length > 0) {
352
363
  // Build response from database packages
353
364
  const maxVersions = getMaxVersions(c.env);
354
- const url = new URL(c.req.url);
355
- const baseUrl = `${url.protocol}//${url.host}`;
356
- const packageData = buildP2Response(packageName, existingPackages, maxVersions, baseUrl);
365
+ const packageData = buildP2Response(packageName, existingPackages, maxVersions, requestBaseUrl);
357
366
  // Cache the result (fire-and-forget to avoid blocking on KV rate limits)
367
+ // TTL keeps cached responses from outliving the revalidation window
358
368
  const cachingEnabled = await isPackageCachingEnabled(c.env.KV);
359
369
  if (cachingEnabled && c.env.KV) {
360
- c.executionCtx.waitUntil(Promise.all([
361
- c.env.KV.put(kvKey, JSON.stringify(packageData)).catch(() => { }),
362
- c.env.KV.put(metadataKey, JSON.stringify({ lastModified: Date.now() })).catch(() => { })
363
- ]));
370
+ const kv = c.env.KV;
371
+ // Stale-while-revalidate: this branch is only reached when the cached
372
+ // response has expired (at most once per TTL), so it doubles as the
373
+ // trigger to re-check upstream for new releases. The cache write MUST
374
+ // complete before revalidation runs - otherwise revalidation's cache
375
+ // purge could be overwritten by this stale response.
376
+ // Pinned to the repos the rows came from - see package-revalidation.ts.
377
+ const repoIds = [...new Set(existingPackages.map((pkg) => pkg.repo_id))];
378
+ runInBackground(c, (async () => {
379
+ await Promise.all([
380
+ kv.put(kvKey, JSON.stringify(packageData), { expirationTtl: REVALIDATION_TTL_SECONDS }).catch(() => { }),
381
+ kv.put(metadataKey, JSON.stringify({ lastModified: Date.now() }), { expirationTtl: REVALIDATION_TTL_SECONDS }).catch(() => { })
382
+ ]);
383
+ await revalidateStalePackage({
384
+ db,
385
+ cache: kv,
386
+ packageName,
387
+ repoIds,
388
+ encryptionKey: c.env.ENCRYPTION_KEY,
389
+ proxyBaseUrl: requestBaseUrl,
390
+ loadPackageFromRepo: fetchPackageFromRepository,
391
+ storePackages: transformPackageDistUrls,
392
+ });
393
+ })().catch((error) => {
394
+ const logger = getLogger();
395
+ logger.warn('Background package revalidation failed', {
396
+ packageName,
397
+ error: error instanceof Error ? error.message : String(error),
398
+ });
399
+ }));
364
400
  }
365
401
  // Track metadata request (cache miss, from DB)
366
402
  const analytics = getAnalytics();
@@ -379,15 +415,13 @@ export async function p2PackageRoute(c) {
379
415
  return new Response(JSON.stringify(packageData), { status: 200, headers });
380
416
  }
381
417
  // Not in database - try lazy loading from upstream repositories
382
- const url = new URL(c.req.url);
383
- const baseUrl = `${url.protocol}//${url.host}`;
384
418
  const maxVersions = getMaxVersions(c.env);
385
419
  const lazyLoadResult = await lazyLoadPackageFromRepositories(db, packageName, c.env.ENCRYPTION_KEY, c.env.KV);
386
420
  if (lazyLoadResult) {
387
421
  const { packageData, repoId } = lazyLoadResult;
388
- const transformedData = transformDistUrlsInMemory(packageData, repoId, baseUrl, maxVersions);
422
+ const transformedData = transformDistUrlsInMemory(packageData, repoId, requestBaseUrl, maxVersions);
389
423
  // Store package versions in background
390
- schedulePackageStorage(c, packageName, packageData, repoId, baseUrl);
424
+ schedulePackageStorage(c, packageName, packageData, repoId, requestBaseUrl);
391
425
  // Track metadata request (cache miss, from upstream)
392
426
  const analytics = getAnalytics();
393
427
  const requestId = c.get('requestId');
@@ -673,9 +707,61 @@ export function buildP2Response(packageName, packageVersions, maxVersions = DEFA
673
707
  };
674
708
  }
675
709
  export function deriveVersionNormalized(version) {
676
- const v = version.startsWith('v') ? version.slice(1) : version;
710
+ const v = version.trim();
711
+ if (v.startsWith('dev-')) {
712
+ return v;
713
+ }
714
+ const versionWithoutV = v.startsWith('v') ? v.slice(1) : v;
715
+ const isNumericSegment = (part) => {
716
+ if (part.length === 0) {
717
+ return false;
718
+ }
719
+ for (let i = 0; i < part.length; i++) {
720
+ const ch = part.charCodeAt(i);
721
+ if (ch < 48 || ch > 57) {
722
+ return false;
723
+ }
724
+ }
725
+ return true;
726
+ };
727
+ const normalizeBranchName = (branchName) => {
728
+ const branchWithoutV = branchName.startsWith('v') ? branchName.slice(1) : branchName;
729
+ const parts = branchWithoutV.split('.');
730
+ if (parts.length < 1 || parts.length > 4) {
731
+ return undefined;
732
+ }
733
+ const normalizedParts = [];
734
+ for (const part of parts) {
735
+ if (isNumericSegment(part)) {
736
+ normalizedParts.push(part);
737
+ }
738
+ else if (part === 'x' || part === 'X' || part === '*') {
739
+ normalizedParts.push('9999999');
740
+ }
741
+ else {
742
+ return undefined;
743
+ }
744
+ }
745
+ while (normalizedParts.length < 4) {
746
+ normalizedParts.push('9999999');
747
+ }
748
+ return `${normalizedParts.join('.')}-dev`;
749
+ };
750
+ const lowerVersion = versionWithoutV.toLowerCase();
751
+ let branchName = null;
752
+ if (lowerVersion.endsWith('-dev') || lowerVersion.endsWith('.dev')) {
753
+ branchName = versionWithoutV.slice(0, -4);
754
+ }
755
+ if (branchName) {
756
+ if (branchName.includes('x') || branchName.includes('X') || branchName.includes('*')) {
757
+ const normalizedBranch = normalizeBranchName(branchName);
758
+ if (normalizedBranch) {
759
+ return normalizedBranch;
760
+ }
761
+ }
762
+ }
677
763
  // Handle patch versions (e.g., "103.0.7-p8" → "103.0.7.0-patch8")
678
- const patchMatch = v.match(/^(\d+\.\d+\.\d+)-p(\d+)$/);
764
+ const patchMatch = versionWithoutV.match(/^(\d+\.\d+\.\d+)-p(\d+)$/);
679
765
  if (patchMatch) {
680
766
  const [, base, patch] = patchMatch;
681
767
  return `${base}.0-patch${patch}`;
@@ -684,13 +770,13 @@ export function deriveVersionNormalized(version) {
684
770
  // 1. Parse up to 4 numeric segments separated by dots from the start of the string.
685
771
  // 2. Treat everything after the parsed numeric part as the suffix (e.g., "-beta", "-alpha", "-dev").
686
772
  let index = 0;
687
- const length = v.length;
773
+ const length = versionWithoutV.length;
688
774
  const segments = [];
689
775
  while (index < length && segments.length < 4) {
690
776
  const start = index;
691
777
  // Read a numeric segment
692
778
  while (index < length) {
693
- const ch = v.charCodeAt(index);
779
+ const ch = versionWithoutV.charCodeAt(index);
694
780
  if (ch < 48 || ch > 57) { // not '0'–'9'
695
781
  break;
696
782
  }
@@ -700,13 +786,13 @@ export function deriveVersionNormalized(version) {
700
786
  // No digits found where a segment was expected
701
787
  break;
702
788
  }
703
- segments.push(v.slice(start, index));
789
+ segments.push(versionWithoutV.slice(start, index));
704
790
  // If we have 4 segments or we've reached the end, stop
705
791
  if (segments.length === 4 || index >= length) {
706
792
  break;
707
793
  }
708
794
  // Expect a dot between segments; if not present, stop parsing numeric part
709
- if (v[index] === '.') {
795
+ if (versionWithoutV[index] === '.') {
710
796
  index++;
711
797
  continue;
712
798
  }
@@ -714,7 +800,7 @@ export function deriveVersionNormalized(version) {
714
800
  }
715
801
  if (segments.length > 0) {
716
802
  const [major, minor = '0', patch = '0', build = '0'] = segments;
717
- const suffix = v.slice(index); // everything after the numeric part
803
+ const suffix = versionWithoutV.slice(index); // everything after the numeric part
718
804
  return `${major}.${minor}.${patch}.${build}${suffix}`;
719
805
  }
720
806
  // Fallback: return undefined for non-standard versions
@@ -764,21 +850,35 @@ export async function ensurePackagistRepository(db, encryptionKey, kv) {
764
850
  await kv.put(cacheKey, 'true', { expirationTtl: 3600 });
765
851
  }
766
852
  }
853
+ /** A repo stuck in 'syncing' longer than this is treated as dead (killed worker) */
854
+ const STALE_SYNC_THRESHOLD_SECONDS = 900;
767
855
  /**
768
- * Sync all pending repositories
769
- * Uses job processor which automatically chooses sync vs async execution
770
- * @returns true if any repositories were synced
856
+ * Sync all pending repositories in the background.
857
+ * Uses job processor which automatically chooses sync vs async execution.
858
+ * @returns true while any repository sync is pending or still running -
859
+ * callers must not cache packages.json built in that window
771
860
  */
772
861
  async function syncPendingRepositories(c) {
773
862
  const db = c.get('database');
774
- // Get repositories that need sync (pending status)
775
- const reposToSync = await db
863
+ const unsettledRepos = await db
776
864
  .select()
777
865
  .from(repositories)
778
- .where(eq(repositories.status, 'pending'));
866
+ .where(inArray(repositories.status, ['pending', 'syncing']));
867
+ const now = Math.floor(Date.now() / 1000);
868
+ const isStaleSync = (repo) => repo.status === 'syncing' && (repo.last_synced_at || 0) <= now - STALE_SYNC_THRESHOLD_SECONDS;
869
+ // Re-sync repos stuck in 'syncing' (worker killed mid-sync) along with
870
+ // pending ones - otherwise they would stay unsynced forever
871
+ const reposToSync = unsettledRepos.filter((repo) => repo.status === 'pending' || isStaleSync(repo));
872
+ const syncsInFlight = unsettledRepos.filter((repo) => repo.status === 'syncing' && !isStaleSync(repo));
779
873
  if (reposToSync.length === 0) {
780
- return false;
874
+ return syncsInFlight.length > 0;
781
875
  }
876
+ // Claim the repos before backgrounding the sync so concurrent
877
+ // packages.json requests do not start duplicate syncs of the same repo
878
+ await db
879
+ .update(repositories)
880
+ .set({ status: 'syncing', last_synced_at: now })
881
+ .where(and(inArray(repositories.id, reposToSync.map((repo) => repo.id)), inArray(repositories.status, ['pending', 'syncing'])));
782
882
  // Determine proxy base URL from request
783
883
  const url = new URL(c.req.url);
784
884
  const proxyBaseUrl = `${url.protocol}//${url.host}`;
@@ -799,8 +899,12 @@ async function syncPendingRepositories(c) {
799
899
  type: 'sync_repository',
800
900
  repoId: repo.id,
801
901
  }));
802
- // Process all jobs (parallel for sync, queued for async)
803
- await jobProcessor.enqueueAll(syncJobs);
902
+ // Run syncs in the background: inline sync of a large repository would
903
+ // otherwise block the packages.json response until every repo finishes
904
+ runInBackground(c, jobProcessor.enqueueAll(syncJobs).catch((error) => {
905
+ const logger = getLogger();
906
+ logger.error('Background repository sync failed', {}, error instanceof Error ? error : new Error(String(error)));
907
+ }));
804
908
  return true;
805
909
  }
806
910
  /**
@@ -1053,7 +1157,7 @@ export async function transformPackageDistUrls(packageData, repoId, proxyBaseUrl
1053
1157
  const allVersions = await db
1054
1158
  .select()
1055
1159
  .from(packages)
1056
- .where(inArray(packages.name, nameChunk));
1160
+ .where(and(eq(packages.repo_id, repoId), inArray(packages.name, nameChunk)));
1057
1161
  // Add all results to map
1058
1162
  for (const existing of allVersions) {
1059
1163
  existingMap.set(`${existing.name}:${existing.version}`, existing);
@@ -1124,9 +1228,8 @@ export async function transformPackageDistUrls(packageData, repoId, proxyBaseUrl
1124
1228
  .insert(packages)
1125
1229
  .values(pkgData)
1126
1230
  .onConflictDoUpdate({
1127
- target: [packages.name, packages.version],
1231
+ target: [packages.repo_id, packages.name, packages.version],
1128
1232
  set: {
1129
- repo_id: pkgData.repo_id,
1130
1233
  dist_url: pkgData.dist_url,
1131
1234
  source_dist_url: pkgData.source_dist_url,
1132
1235
  dist_reference: pkgData.dist_reference,
@@ -1195,7 +1298,7 @@ export async function storePackageInDB(db, packageName, version, metadata, repoI
1195
1298
  const [existing] = await db
1196
1299
  .select()
1197
1300
  .from(packages)
1198
- .where(and(eq(packages.name, packageName), eq(packages.version, version)))
1301
+ .where(and(eq(packages.repo_id, repoId), eq(packages.name, packageName), eq(packages.version, version)))
1199
1302
  .limit(1);
1200
1303
  // Use existing reference or generate simple one (no expensive crypto)
1201
1304
  // Most Packagist packages already have dist.reference, so this is rarely needed
@@ -1229,7 +1332,6 @@ export async function storePackageInDB(db, packageName, version, metadata, repoI
1229
1332
  await db
1230
1333
  .update(packages)
1231
1334
  .set({
1232
- repo_id: repoId, // Update repo_id in case it changed
1233
1335
  dist_url: proxyDistUrl,
1234
1336
  source_dist_url: sourceDistUrl,
1235
1337
  dist_reference: distReference,
@@ -1240,7 +1342,7 @@ export async function storePackageInDB(db, packageName, version, metadata, repoI
1240
1342
  released_at: releasedAt,
1241
1343
  metadata: packageData.metadata, // Update metadata
1242
1344
  })
1243
- .where(and(eq(packages.name, packageName), eq(packages.version, version)));
1345
+ .where(and(eq(packages.repo_id, repoId), eq(packages.name, packageName), eq(packages.version, version)));
1244
1346
  }
1245
1347
  else {
1246
1348
  await db.insert(packages).values(packageData);