@rankcli/agent-runtime 0.0.30 → 0.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +98 -17
- package/dist/index.d.ts +98 -17
- package/dist/index.js +228 -289
- package/dist/index.mjs +227 -289
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -94,6 +94,8 @@ var RECIPES = {
|
|
|
94
94
|
jekyll: null,
|
|
95
95
|
eleventy: null,
|
|
96
96
|
pelican: null,
|
|
97
|
+
sphinx: null,
|
|
98
|
+
docusaurus: null,
|
|
97
99
|
// Unknown
|
|
98
100
|
unknown: null
|
|
99
101
|
};
|
|
@@ -4210,234 +4212,161 @@ var ISSUE_DEFINITIONS = {
|
|
|
4210
4212
|
}
|
|
4211
4213
|
};
|
|
4212
4214
|
|
|
4213
|
-
// src/frameworks/
|
|
4214
|
-
var
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
},
|
|
4223
|
-
|
|
4224
|
-
{
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
},
|
|
4230
|
-
|
|
4231
|
-
{
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
},
|
|
4237
|
-
//
|
|
4238
|
-
{
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
},
|
|
4244
|
-
|
|
4245
|
-
{
|
|
4246
|
-
framework: "sveltekit",
|
|
4247
|
-
language: "typescript",
|
|
4248
|
-
patterns: [/__sveltekit/, /_app\/immutable/, /svelte-kit/],
|
|
4249
|
-
confidence: "high"
|
|
4250
|
-
},
|
|
4251
|
-
// Svelte (standalone)
|
|
4252
|
-
{
|
|
4253
|
-
framework: "svelte",
|
|
4254
|
-
language: "javascript",
|
|
4255
|
-
patterns: [/svelte-\w+/, /__svelte/],
|
|
4256
|
-
confidence: "medium"
|
|
4257
|
-
},
|
|
4258
|
-
// Remix
|
|
4259
|
-
{
|
|
4260
|
-
framework: "remix",
|
|
4261
|
-
language: "typescript",
|
|
4262
|
-
patterns: [/__remix/, /data-remix/],
|
|
4263
|
-
confidence: "high"
|
|
4264
|
-
},
|
|
4265
|
-
// Solid.js
|
|
4266
|
-
{
|
|
4267
|
-
framework: "solidjs",
|
|
4268
|
-
language: "typescript",
|
|
4269
|
-
patterns: [/data-solid/, /_solid/],
|
|
4270
|
-
confidence: "high"
|
|
4271
|
-
},
|
|
4272
|
-
// Qwik
|
|
4273
|
-
{
|
|
4274
|
-
framework: "qwik",
|
|
4275
|
-
language: "typescript",
|
|
4276
|
-
patterns: [/q:container/, /q:base/, /q:version/, /q:id=/, /qwik/],
|
|
4277
|
-
confidence: "high"
|
|
4278
|
-
},
|
|
4279
|
-
// Fresh (Deno)
|
|
4280
|
-
{
|
|
4281
|
-
framework: "fresh",
|
|
4282
|
-
language: "typescript",
|
|
4283
|
-
patterns: [/_frsh\//, /data-fresh/],
|
|
4284
|
-
confidence: "high"
|
|
4285
|
-
},
|
|
4286
|
-
// React (generic - lower priority)
|
|
4215
|
+
// src/frameworks/signals.ts
|
|
4216
|
+
var WEIGHT = { DECISIVE: 100, STRONG: 45, WEAK: 15 };
|
|
4217
|
+
var SCORE_FLOOR = 45;
|
|
4218
|
+
var SIGNALS = [
|
|
4219
|
+
// ---- Meta-frameworks: JavaScript ------------------------------------------------
|
|
4220
|
+
{ framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-build-dir", html: /\/_next\/(static|image|data)\// },
|
|
4221
|
+
{ framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-data", html: /__NEXT_DATA__|self\.__next_f/ },
|
|
4222
|
+
{ framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-mount", html: /<div id=["']__next["']/ },
|
|
4223
|
+
{ framework: "nextjs", language: "typescript", weight: WEIGHT.DECISIVE, name: "next-powered-by", header: /^x-powered-by: next\.js/ },
|
|
4224
|
+
{ framework: "nuxt", language: "typescript", weight: WEIGHT.DECISIVE, name: "nuxt-build-dir", html: /\/_nuxt\// },
|
|
4225
|
+
{ framework: "nuxt", language: "typescript", weight: WEIGHT.DECISIVE, name: "nuxt-payload", html: /__NUXT__|__NUXT_DATA__|<div id="__nuxt"/ },
|
|
4226
|
+
{ framework: "nuxt", language: "typescript", weight: WEIGHT.DECISIVE, name: "nuxt-powered-by", header: /^x-powered-by: nuxt/ },
|
|
4227
|
+
{ framework: "astro", language: "typescript", weight: WEIGHT.DECISIVE, name: "astro-build-dir", html: /\/_astro\// },
|
|
4228
|
+
{ framework: "astro", language: "typescript", weight: WEIGHT.DECISIVE, name: "astro-island", html: /<astro-island|astro-slot|data-astro-(cid|source-file|transition)/ },
|
|
4229
|
+
{ framework: "astro", language: "typescript", weight: WEIGHT.STRONG, name: "astro-generator", html: /name=["']?generator["']?[^>]*content=["']?Astro/i },
|
|
4230
|
+
{ framework: "sveltekit", language: "typescript", weight: WEIGHT.DECISIVE, name: "sveltekit-immutable", html: /\/_app\/immutable\// },
|
|
4231
|
+
{ framework: "sveltekit", language: "typescript", weight: WEIGHT.DECISIVE, name: "sveltekit-runtime", html: /__sveltekit|kit\.start\(/ },
|
|
4232
|
+
{ framework: "remix", language: "typescript", weight: WEIGHT.DECISIVE, name: "remix-context", html: /__remixContext|__remixManifest|__remixRouteModules/ },
|
|
4233
|
+
{ framework: "remix", language: "typescript", weight: WEIGHT.STRONG, name: "remix-build-dir", html: /\/build\/_shared\/|\/build\/entry\.client-/ },
|
|
4234
|
+
{ framework: "gatsby", language: "javascript", weight: WEIGHT.DECISIVE, name: "gatsby-runtime", html: /___gatsby|window\.___chunkMapping/ },
|
|
4235
|
+
{ framework: "gatsby", language: "javascript", weight: WEIGHT.STRONG, name: "gatsby-page-data", html: /\/page-data\/(app-data\.json|sq\/)/ },
|
|
4236
|
+
{ framework: "qwik", language: "typescript", weight: WEIGHT.DECISIVE, name: "qwik-container", html: /q:container|q:base|q:version|qwikloader/ },
|
|
4237
|
+
{ framework: "fresh", language: "typescript", weight: WEIGHT.DECISIVE, name: "fresh-build-dir", html: /\/_frsh\// },
|
|
4238
|
+
{ framework: "solidjs", language: "typescript", weight: WEIGHT.STRONG, name: "solid-hydration", html: /_\$HY\b|data-hk=["']/ },
|
|
4239
|
+
// ---- Base UI libraries: weaker by nature, and that is the point -----------------
|
|
4240
|
+
{ framework: "angular", language: "typescript", weight: WEIGHT.DECISIVE, name: "angular-version", html: /ng-version=["']/ },
|
|
4241
|
+
{ framework: "angular", language: "typescript", weight: WEIGHT.DECISIVE, name: "angular-content-attr", html: /_ngcontent-|_nghost-/ },
|
|
4242
|
+
{ framework: "react", language: "javascript", weight: WEIGHT.STRONG, name: "react-root-marker", html: /data-reactroot|__reactContainer\$|__REACT_DEVTOOLS/ },
|
|
4243
|
+
// A Vite-built SPA: an empty mount point plus Vite's hashed entry bundle. Two weak facts
|
|
4244
|
+
// that together mean something; either one alone does not.
|
|
4245
|
+
{ framework: "react", language: "javascript", weight: WEIGHT.WEAK, name: "react-empty-root", html: /<div id="root"><\/div>/ },
|
|
4246
|
+
{ framework: "react", language: "javascript", weight: WEIGHT.WEAK, name: "react-refresh", html: /RefreshRuntime|react-refresh/ },
|
|
4287
4247
|
{
|
|
4288
4248
|
framework: "react",
|
|
4289
4249
|
language: "javascript",
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
//
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
{
|
|
4298
|
-
framework: "vue",
|
|
4299
|
-
language: "javascript",
|
|
4300
|
-
patterns: [/data-v-[a-f0-9]+/, /v-cloak/, /__vue/],
|
|
4301
|
-
confidence: "medium"
|
|
4302
|
-
},
|
|
4303
|
-
// Angular
|
|
4304
|
-
{
|
|
4305
|
-
framework: "angular",
|
|
4306
|
-
language: "typescript",
|
|
4307
|
-
patterns: [/ng-version/, /_ngcontent/, /ng-\w+=/, /\[ng/],
|
|
4308
|
-
confidence: "high"
|
|
4309
|
-
},
|
|
4310
|
-
// ============ Hypermedia Frameworks ============
|
|
4311
|
-
// HTMX
|
|
4312
|
-
{
|
|
4313
|
-
framework: "htmx",
|
|
4314
|
-
language: "unknown",
|
|
4315
|
-
patterns: [/hx-get/, /hx-post/, /hx-trigger/, /hx-swap/, /htmx\.org/],
|
|
4316
|
-
confidence: "high"
|
|
4317
|
-
},
|
|
4318
|
-
// Hotwire/Turbo
|
|
4319
|
-
{
|
|
4320
|
-
framework: "hotwire",
|
|
4321
|
-
language: "ruby",
|
|
4322
|
-
patterns: [/turbo-frame/, /turbo-stream/, /data-turbo/],
|
|
4323
|
-
confidence: "high"
|
|
4324
|
-
},
|
|
4325
|
-
// ============ PHP Frameworks ============
|
|
4326
|
-
// WordPress
|
|
4327
|
-
{
|
|
4328
|
-
framework: "wordpress",
|
|
4329
|
-
language: "php",
|
|
4330
|
-
// Technical signatures only. These patterns run over the whole page, so the old
|
|
4331
|
-
// /wordpress/i matched the word anywhere in the copy: rankcli.dev's own "detects
|
|
4332
|
-
// Rails, Django, Laravel, and WordPress" line was enough to detect its React SPA as
|
|
4333
|
-
// WordPress and then tell it to install an SEO plugin. A generator meta tag is the
|
|
4334
|
-
// one place the page says it about itself.
|
|
4335
|
-
patterns: [/wp-content/, /wp-includes/, /wp-json/, /name=["']generator["'][^>]*content=["']WordPress/i],
|
|
4336
|
-
confidence: "high"
|
|
4337
|
-
},
|
|
4338
|
-
// Laravel
|
|
4339
|
-
{
|
|
4340
|
-
framework: "laravel",
|
|
4341
|
-
language: "php",
|
|
4342
|
-
// /laravel/i had the same problem as WordPress above - it matched the word in prose.
|
|
4343
|
-
patterns: [/laravel_session/, /XSRF-TOKEN/, /@csrf/],
|
|
4344
|
-
confidence: "medium"
|
|
4345
|
-
},
|
|
4346
|
-
// Symfony
|
|
4347
|
-
{
|
|
4348
|
-
framework: "symfony",
|
|
4349
|
-
language: "php",
|
|
4350
|
-
// Likewise /symfony/, which matched any lowercase mention.
|
|
4351
|
-
patterns: [/sf2/, /_sf_/, /profiler\.css/],
|
|
4352
|
-
confidence: "medium"
|
|
4353
|
-
},
|
|
4354
|
-
// ============ Ruby Frameworks ============
|
|
4355
|
-
// Rails
|
|
4356
|
-
{
|
|
4357
|
-
framework: "rails",
|
|
4358
|
-
language: "ruby",
|
|
4359
|
-
patterns: [/csrf-token/, /turbolinks/, /data-turbo/, /rails-ujs/, /action-cable/],
|
|
4360
|
-
confidence: "medium"
|
|
4361
|
-
},
|
|
4362
|
-
// ============ Python Frameworks ============
|
|
4363
|
-
// Django
|
|
4364
|
-
{
|
|
4365
|
-
framework: "django",
|
|
4366
|
-
language: "python",
|
|
4367
|
-
patterns: [/csrfmiddlewaretoken/, /django/, /__debug__\//],
|
|
4368
|
-
confidence: "medium"
|
|
4369
|
-
},
|
|
4370
|
-
// Flask
|
|
4371
|
-
{
|
|
4372
|
-
framework: "flask",
|
|
4373
|
-
language: "python",
|
|
4374
|
-
patterns: [/flask/, /werkzeug/i],
|
|
4375
|
-
confidence: "low"
|
|
4376
|
-
},
|
|
4377
|
-
// FastAPI
|
|
4378
|
-
{
|
|
4379
|
-
framework: "fastapi",
|
|
4380
|
-
language: "python",
|
|
4381
|
-
patterns: [/fastapi/, /openapi\.json/],
|
|
4382
|
-
confidence: "low"
|
|
4383
|
-
},
|
|
4384
|
-
// ============ Java Frameworks ============
|
|
4385
|
-
// Spring Boot
|
|
4386
|
-
{
|
|
4387
|
-
framework: "spring",
|
|
4388
|
-
language: "java",
|
|
4389
|
-
// /th:/ matched "width:" and "height:" in inline CSS, which made almost every styled
|
|
4390
|
-
// page on the web a Spring Boot app - rankcli.dev included, once WordPress stopped
|
|
4391
|
-
// matching first. Thymeleaf attributes are what th: actually means.
|
|
4392
|
-
patterns: [/\sth:[a-z-]+=/, /thymeleaf/, /_csrf/],
|
|
4393
|
-
confidence: "medium"
|
|
4394
|
-
},
|
|
4395
|
-
// ============ C# Frameworks ============
|
|
4396
|
-
// ASP.NET Core
|
|
4397
|
-
{
|
|
4398
|
-
framework: "aspnet",
|
|
4399
|
-
language: "csharp",
|
|
4400
|
-
patterns: [/aspnetcore/, /__RequestVerificationToken/, /_framework\/blazor/],
|
|
4401
|
-
confidence: "medium"
|
|
4402
|
-
},
|
|
4403
|
-
// ============ Elixir Frameworks ============
|
|
4404
|
-
// Phoenix
|
|
4405
|
-
{
|
|
4406
|
-
framework: "phoenix",
|
|
4407
|
-
language: "elixir",
|
|
4408
|
-
patterns: [/phx-/, /live-socket/, /data-phx/],
|
|
4409
|
-
confidence: "high"
|
|
4410
|
-
},
|
|
4411
|
-
// ============ Static Site Generators ============
|
|
4412
|
-
// Hugo
|
|
4413
|
-
{
|
|
4414
|
-
framework: "hugo",
|
|
4415
|
-
language: "go",
|
|
4416
|
-
patterns: [/name=["']generator["'][^>]*content=["']Hugo/i],
|
|
4417
|
-
confidence: "medium"
|
|
4418
|
-
},
|
|
4419
|
-
// Jekyll
|
|
4420
|
-
{
|
|
4421
|
-
framework: "jekyll",
|
|
4422
|
-
language: "ruby",
|
|
4423
|
-
patterns: [/name=["']generator["'][^>]*content=["']Jekyll/i],
|
|
4424
|
-
confidence: "medium"
|
|
4250
|
+
weight: WEIGHT.STRONG,
|
|
4251
|
+
name: "react-vite-spa",
|
|
4252
|
+
// Vite's entry chunk plus React's conventional mount point. Medium confidence by
|
|
4253
|
+
// construction: #app instead of #root stays unknown, because Vue, Solid and others use it.
|
|
4254
|
+
// Not `<div id="root"></div>`: a prerendered SPA has content inside its mount point, which
|
|
4255
|
+
// is what RankCLI's own site does and what the first version of this signal missed.
|
|
4256
|
+
allHtml: [/<script[^>]+src=["'][^"']+\.js["']/, /<div id="root"[\s>]/]
|
|
4425
4257
|
},
|
|
4426
|
-
|
|
4427
|
-
{
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
},
|
|
4433
|
-
|
|
4434
|
-
{
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
}
|
|
4258
|
+
{ framework: "vue", language: "javascript", weight: WEIGHT.DECISIVE, name: "vitepress-runtime", html: /__VP_HASH_MAP__|__VP_SITE_DATA__/ },
|
|
4259
|
+
{ framework: "vue", language: "javascript", weight: WEIGHT.DECISIVE, name: "vue-scoped-style", html: /data-v-[0-9a-f]{6,10}\b/ },
|
|
4260
|
+
{ framework: "vue", language: "javascript", weight: WEIGHT.WEAK, name: "vue-cloak", html: /v-cloak|<div id="app"><\/div>/ },
|
|
4261
|
+
{ framework: "svelte", language: "javascript", weight: WEIGHT.WEAK, name: "svelte-class", html: /class=["'][^"']*svelte-[0-9a-z]{6}/ },
|
|
4262
|
+
// ---- Ruby -----------------------------------------------------------------------
|
|
4263
|
+
// csrf-token alone is emitted by Rails, Laravel, Phoenix and Django. csrf-param is Rails.
|
|
4264
|
+
{ framework: "rails", language: "ruby", weight: WEIGHT.DECISIVE, name: "rails-csrf-param", html: /name=["']csrf-param["']/ },
|
|
4265
|
+
{ framework: "rails", language: "ruby", weight: WEIGHT.STRONG, name: "rails-ujs", html: /rails-ujs|turbolinks|data-remote=["']true["']|action_cable|actioncable/ },
|
|
4266
|
+
{ framework: "rails", language: "ruby", weight: WEIGHT.STRONG, name: "rails-session-cookie", header: /^set-cookie: [^;]*_session=/ },
|
|
4267
|
+
{ framework: "rails", language: "ruby", weight: WEIGHT.STRONG, name: "rails-app-server", header: /^(server|x-powered-by): (puma|passenger|unicorn|phusion)/ },
|
|
4268
|
+
{ framework: "hotwire", language: "ruby", weight: WEIGHT.STRONG, name: "turbo-elements", html: /<turbo-frame|<turbo-stream|data-turbo-track/ },
|
|
4269
|
+
// ---- Python ---------------------------------------------------------------------
|
|
4270
|
+
{ framework: "django", language: "python", weight: WEIGHT.DECISIVE, name: "django-csrf-input", html: /name=["']csrfmiddlewaretoken["']/ },
|
|
4271
|
+
{ framework: "django", language: "python", weight: WEIGHT.DECISIVE, name: "django-csrf-cookie", header: /^set-cookie: csrftoken=/ },
|
|
4272
|
+
{ framework: "django", language: "python", weight: WEIGHT.STRONG, name: "django-admin-static", html: /\/static\/admin\/(css|js)\// },
|
|
4273
|
+
{ framework: "django", language: "python", weight: WEIGHT.WEAK, name: "django-wsgi-server", header: /^(server|x-powered-by): (gunicorn|uvicorn|daphne)/ },
|
|
4274
|
+
{ framework: "flask", language: "python", weight: WEIGHT.STRONG, name: "flask-server", header: /^server: werkzeug/ },
|
|
4275
|
+
{ framework: "fastapi", language: "python", weight: WEIGHT.STRONG, name: "fastapi-docs", html: /swagger-ui-bundle\.js[\s\S]{0,200}openapi\.json/ },
|
|
4276
|
+
// ---- PHP ------------------------------------------------------------------------
|
|
4277
|
+
{ framework: "wordpress", language: "php", weight: WEIGHT.DECISIVE, name: "wp-paths", html: /\/wp-content\/|\/wp-includes\/|\/wp-json\// },
|
|
4278
|
+
{ framework: "wordpress", language: "php", weight: WEIGHT.DECISIVE, name: "wp-generator", html: /name=["']?generator["']?[^>]*content=["']?WordPress/i },
|
|
4279
|
+
{ framework: "wordpress", language: "php", weight: WEIGHT.STRONG, name: "wp-powered-by", header: /^x-powered-by: wordpress/ },
|
|
4280
|
+
{ framework: "laravel", language: "php", weight: WEIGHT.DECISIVE, name: "laravel-session-cookie", header: /^set-cookie: laravel_session=/ },
|
|
4281
|
+
{ framework: "laravel", language: "php", weight: WEIGHT.STRONG, name: "laravel-xsrf-cookie", header: /^set-cookie: XSRF-TOKEN=/i },
|
|
4282
|
+
{ framework: "laravel", language: "php", weight: WEIGHT.STRONG, name: "laravel-livewire", html: /livewire|wire:(click|model|submit)/i },
|
|
4283
|
+
{ framework: "laravel", language: "php", weight: WEIGHT.WEAK, name: "laravel-vite-build", html: /\/build\/assets\/app-[A-Za-z0-9_-]{8}\.js/ },
|
|
4284
|
+
{ framework: "symfony", language: "php", weight: WEIGHT.DECISIVE, name: "symfony-profiler", html: /\/_profiler\/|sf-toolbar|sf-dump/ },
|
|
4285
|
+
{ framework: "symfony", language: "php", weight: WEIGHT.STRONG, name: "symfony-cookie", header: /^set-cookie: (sfsession|symfony)/i },
|
|
4286
|
+
// ---- Elixir ---------------------------------------------------------------------
|
|
4287
|
+
{ framework: "phoenix", language: "elixir", weight: WEIGHT.DECISIVE, name: "phoenix-liveview", html: /phx-(click|submit|change|value|hook|socket|main|track-static)/ },
|
|
4288
|
+
{ framework: "phoenix", language: "elixir", weight: WEIGHT.STRONG, name: "phoenix-cowboy", header: /^server: cowboy/ },
|
|
4289
|
+
// ---- Java / C# / Go -------------------------------------------------------------
|
|
4290
|
+
{ framework: "spring", language: "java", weight: WEIGHT.STRONG, name: "thymeleaf-attr", html: /\sth:[a-z-]+=/ },
|
|
4291
|
+
{ framework: "spring", language: "java", weight: WEIGHT.STRONG, name: "spring-server", header: /^server: (apache-coyote|tomcat|jetty)/ },
|
|
4292
|
+
{ framework: "aspnet", language: "csharp", weight: WEIGHT.DECISIVE, name: "aspnet-verification-token", html: /__RequestVerificationToken|__VIEWSTATE/ },
|
|
4293
|
+
{ framework: "aspnet", language: "csharp", weight: WEIGHT.STRONG, name: "aspnet-server", header: /^(server: kestrel|x-powered-by: asp\.net|x-aspnet-version:)/ },
|
|
4294
|
+
// "gin" used to be tested as a substring of the Server header, which matches nginx - the
|
|
4295
|
+
// single most common web server there is. Anchored to the whole value now.
|
|
4296
|
+
{ framework: "go", language: "go", weight: WEIGHT.STRONG, name: "go-server", header: /^server: (gin|echo|fiber|gorilla)(\/|$| )/ },
|
|
4297
|
+
// ---- Hypermedia -----------------------------------------------------------------
|
|
4298
|
+
{ framework: "htmx", language: "unknown", weight: WEIGHT.STRONG, name: "htmx-attrs", html: /\shx-(get|post|put|delete|trigger|swap|target)=/ },
|
|
4299
|
+
{ framework: "htmx", language: "unknown", weight: WEIGHT.DECISIVE, name: "htmx-script", html: /htmx(\.min)?\.js|unpkg\.com\/htmx/ },
|
|
4300
|
+
// ---- Static site generators: they announce themselves -----------------------------
|
|
4301
|
+
{ framework: "hugo", language: "go", weight: WEIGHT.DECISIVE, name: "hugo-generator", html: /name=["']?generator["']?[^>]*content=["']?Hugo/i },
|
|
4302
|
+
{ framework: "hugo", language: "go", weight: WEIGHT.STRONG, name: "hugo-generator-header", header: /^x-generator: hugo/ },
|
|
4303
|
+
{ framework: "jekyll", language: "ruby", weight: WEIGHT.DECISIVE, name: "jekyll-generator", html: /name=["']?generator["']?[^>]*content=["']?Jekyll/i },
|
|
4304
|
+
{ framework: "eleventy", language: "javascript", weight: WEIGHT.DECISIVE, name: "eleventy-generator", html: /name=["']?generator["']?[^>]*content=["']?Eleventy/i },
|
|
4305
|
+
{ framework: "pelican", language: "python", weight: WEIGHT.DECISIVE, name: "pelican-generator", html: /name=["']?generator["']?[^>]*content=["']?Pelican/i },
|
|
4306
|
+
// Sphinx: what nearly every Python project's documentation is built with, and the shape
|
|
4307
|
+
// flask.palletsprojects.com turned out to be. Structural markers only - not the word
|
|
4308
|
+
// "sphinx", which appears in plenty of prose.
|
|
4309
|
+
// Validated against docusaurus.io, jestjs.io and redux.js.org - all three carry the mount
|
|
4310
|
+
// point and the skip-to-content id. Deliberately not the word "docusaurus", which appears on
|
|
4311
|
+
// deno.com purely as a link in a logo list; that near-miss is why this needed a real marker.
|
|
4312
|
+
{ framework: "docusaurus", language: "typescript", weight: WEIGHT.DECISIVE, name: "docusaurus-root", html: /id=["']__docusaurus/ },
|
|
4313
|
+
{ framework: "docusaurus", language: "typescript", weight: WEIGHT.STRONG, name: "docusaurus-skip-link", html: /__docusaurus_skipToContent/ },
|
|
4314
|
+
{ framework: "sphinx", language: "python", weight: WEIGHT.DECISIVE, name: "sphinx-doc-options", html: /_static\/documentation_options\.js/ },
|
|
4315
|
+
{ framework: "sphinx", language: "python", weight: WEIGHT.STRONG, name: "sphinx-theme-markup", html: /class=["'][^"']*sphinxsidebar|sphinx-highlight|_static\/searchtools\.js/ },
|
|
4316
|
+
{ framework: "sphinx", language: "python", weight: WEIGHT.STRONG, name: "docutils-generator", html: /name=["']?generator["']?[^>]*content=["']?Docutils/i }
|
|
4440
4317
|
];
|
|
4318
|
+
function headerLines(headers) {
|
|
4319
|
+
if (!headers) return "";
|
|
4320
|
+
return Object.entries(headers).map(([k, v]) => `${k.toLowerCase()}: ${String(v).toLowerCase()}`).join("\n");
|
|
4321
|
+
}
|
|
4322
|
+
function detectFromEvidence(input) {
|
|
4323
|
+
const html = input.html ?? "";
|
|
4324
|
+
const headers = headerLines(input.headers);
|
|
4325
|
+
const byFramework = /* @__PURE__ */ new Map();
|
|
4326
|
+
for (const signal of SIGNALS) {
|
|
4327
|
+
const headerHit = signal.header && headers && new RegExp(
|
|
4328
|
+
signal.header.source,
|
|
4329
|
+
signal.header.flags.includes("m") ? signal.header.flags : `${signal.header.flags}m`
|
|
4330
|
+
).test(headers);
|
|
4331
|
+
const allHit = signal.allHtml && html && signal.allHtml.every((re) => re.test(html));
|
|
4332
|
+
const hit = signal.html && html && signal.html.test(html) || headerHit || allHit;
|
|
4333
|
+
if (!hit) continue;
|
|
4334
|
+
const existing = byFramework.get(signal.framework);
|
|
4335
|
+
if (existing) {
|
|
4336
|
+
existing.score += signal.weight;
|
|
4337
|
+
existing.matched.push(signal.name);
|
|
4338
|
+
} else {
|
|
4339
|
+
byFramework.set(signal.framework, {
|
|
4340
|
+
framework: signal.framework,
|
|
4341
|
+
language: signal.language,
|
|
4342
|
+
score: signal.weight,
|
|
4343
|
+
matched: [signal.name]
|
|
4344
|
+
});
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
const ranked = [...byFramework.values()].sort((a, b) => b.score - a.score || a.framework.localeCompare(b.framework));
|
|
4348
|
+
const best = ranked[0];
|
|
4349
|
+
if (!best || best.score < SCORE_FLOOR) {
|
|
4350
|
+
return {
|
|
4351
|
+
framework: "unknown",
|
|
4352
|
+
language: "unknown",
|
|
4353
|
+
confidence: "low",
|
|
4354
|
+
score: best?.score ?? 0,
|
|
4355
|
+
matched: best?.matched ?? [],
|
|
4356
|
+
alternatives: ranked
|
|
4357
|
+
};
|
|
4358
|
+
}
|
|
4359
|
+
return {
|
|
4360
|
+
framework: best.framework,
|
|
4361
|
+
language: best.language,
|
|
4362
|
+
confidence: best.score >= WEIGHT.DECISIVE ? "high" : "medium",
|
|
4363
|
+
score: best.score,
|
|
4364
|
+
matched: best.matched,
|
|
4365
|
+
alternatives: ranked.slice(1)
|
|
4366
|
+
};
|
|
4367
|
+
}
|
|
4368
|
+
|
|
4369
|
+
// src/frameworks/detector.ts
|
|
4441
4370
|
var PACKAGE_DEPENDENCIES = [
|
|
4442
4371
|
// Meta-frameworks (check first - more specific)
|
|
4443
4372
|
{ framework: "nextjs", language: "typescript", packages: ["next"], meta_framework: "react" },
|
|
@@ -4487,19 +4416,14 @@ var ELIXIR_DEPENDENCIES = [
|
|
|
4487
4416
|
{ framework: "phoenix", language: "elixir", packages: [":phoenix", "phoenix"] }
|
|
4488
4417
|
];
|
|
4489
4418
|
function detectFromHtml(html) {
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
};
|
|
4499
|
-
}
|
|
4500
|
-
}
|
|
4501
|
-
}
|
|
4502
|
-
return null;
|
|
4419
|
+
const result = detectFromEvidence({ html });
|
|
4420
|
+
if (result.framework === "unknown") return null;
|
|
4421
|
+
return {
|
|
4422
|
+
framework: result.framework,
|
|
4423
|
+
language: result.language,
|
|
4424
|
+
confidence: result.confidence,
|
|
4425
|
+
detected_from: "html"
|
|
4426
|
+
};
|
|
4503
4427
|
}
|
|
4504
4428
|
function detectFromPackageJson(packageJson) {
|
|
4505
4429
|
const allDeps = {
|
|
@@ -4641,52 +4565,14 @@ function detectFromMixExs(mixContent) {
|
|
|
4641
4565
|
return null;
|
|
4642
4566
|
}
|
|
4643
4567
|
function detectFromHeaders(headers) {
|
|
4644
|
-
const
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
}
|
|
4653
|
-
if (poweredBy.includes("remix")) {
|
|
4654
|
-
return { framework: "remix", language: "typescript", confidence: "high", detected_from: "headers" };
|
|
4655
|
-
}
|
|
4656
|
-
if (poweredBy.includes("phusion") || poweredBy.includes("passenger") || poweredBy.includes("puma") || poweredBy.includes("unicorn")) {
|
|
4657
|
-
return { framework: "rails", language: "ruby", confidence: "medium", detected_from: "headers" };
|
|
4658
|
-
}
|
|
4659
|
-
if (poweredBy.includes("gunicorn") || poweredBy.includes("uvicorn") || poweredBy.includes("daphne")) {
|
|
4660
|
-
return null;
|
|
4661
|
-
}
|
|
4662
|
-
if (poweredBy.includes("php")) {
|
|
4663
|
-
return null;
|
|
4664
|
-
}
|
|
4665
|
-
if (server.includes("tomcat") || server.includes("jetty") || poweredBy.includes("spring")) {
|
|
4666
|
-
return { framework: "spring", language: "java", confidence: "medium", detected_from: "headers" };
|
|
4667
|
-
}
|
|
4668
|
-
if (poweredBy.includes("asp.net") || server.includes("kestrel")) {
|
|
4669
|
-
return { framework: "aspnet", language: "csharp", confidence: "high", detected_from: "headers" };
|
|
4670
|
-
}
|
|
4671
|
-
if (poweredBy.includes("phoenix") || poweredBy.includes("cowboy")) {
|
|
4672
|
-
return { framework: "phoenix", language: "elixir", confidence: "medium", detected_from: "headers" };
|
|
4673
|
-
}
|
|
4674
|
-
if (server.includes("gin") || server.includes("echo") || server.includes("fiber")) {
|
|
4675
|
-
return { framework: "go", language: "go", confidence: "medium", detected_from: "headers" };
|
|
4676
|
-
}
|
|
4677
|
-
if (generator.includes("hugo")) {
|
|
4678
|
-
return { framework: "hugo", language: "go", confidence: "high", detected_from: "headers" };
|
|
4679
|
-
}
|
|
4680
|
-
if (generator.includes("jekyll")) {
|
|
4681
|
-
return { framework: "jekyll", language: "ruby", confidence: "high", detected_from: "headers" };
|
|
4682
|
-
}
|
|
4683
|
-
if (generator.includes("eleventy") || generator.includes("11ty")) {
|
|
4684
|
-
return { framework: "eleventy", language: "javascript", confidence: "high", detected_from: "headers" };
|
|
4685
|
-
}
|
|
4686
|
-
if (generator.includes("pelican")) {
|
|
4687
|
-
return { framework: "pelican", language: "python", confidence: "high", detected_from: "headers" };
|
|
4688
|
-
}
|
|
4689
|
-
return null;
|
|
4568
|
+
const result = detectFromEvidence({ headers });
|
|
4569
|
+
if (result.framework === "unknown") return null;
|
|
4570
|
+
return {
|
|
4571
|
+
framework: result.framework,
|
|
4572
|
+
language: result.language,
|
|
4573
|
+
confidence: result.confidence,
|
|
4574
|
+
detected_from: "headers"
|
|
4575
|
+
};
|
|
4690
4576
|
}
|
|
4691
4577
|
function detectFramework(options) {
|
|
4692
4578
|
if (options.packageJson) {
|
|
@@ -4721,13 +4607,16 @@ function detectFramework(options) {
|
|
|
4721
4607
|
const result = detectFromMixExs(options.mixExsContent);
|
|
4722
4608
|
if (result) return result;
|
|
4723
4609
|
}
|
|
4724
|
-
if (options.headers) {
|
|
4725
|
-
const result =
|
|
4726
|
-
if (result)
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4610
|
+
if (options.html || options.headers) {
|
|
4611
|
+
const result = detectFromEvidence({ html: options.html, headers: options.headers });
|
|
4612
|
+
if (result.framework !== "unknown") {
|
|
4613
|
+
return {
|
|
4614
|
+
framework: result.framework,
|
|
4615
|
+
language: result.language,
|
|
4616
|
+
confidence: result.confidence,
|
|
4617
|
+
detected_from: options.html ? "html" : "headers"
|
|
4618
|
+
};
|
|
4619
|
+
}
|
|
4731
4620
|
}
|
|
4732
4621
|
if (options.files) {
|
|
4733
4622
|
if (options.files.some((f) => f.includes("next.config"))) {
|
|
@@ -4834,6 +4723,8 @@ function getFrameworkDisplayName(framework) {
|
|
|
4834
4723
|
jekyll: "Jekyll",
|
|
4835
4724
|
eleventy: "Eleventy (11ty)",
|
|
4836
4725
|
pelican: "Pelican",
|
|
4726
|
+
sphinx: "Sphinx",
|
|
4727
|
+
docusaurus: "Docusaurus",
|
|
4837
4728
|
unknown: "Unknown"
|
|
4838
4729
|
};
|
|
4839
4730
|
return names[framework];
|
|
@@ -29481,6 +29372,48 @@ function findAppRoot(files) {
|
|
|
29481
29372
|
return `${nonRoot[0]}/`;
|
|
29482
29373
|
}
|
|
29483
29374
|
|
|
29375
|
+
// src/fixer/served-dir.ts
|
|
29376
|
+
var SERVED_DIR = {
|
|
29377
|
+
// Bundlers and meta-frameworks that copy a public/ directory verbatim.
|
|
29378
|
+
react: "public/",
|
|
29379
|
+
vue: "public/",
|
|
29380
|
+
svelte: "public/",
|
|
29381
|
+
angular: "public/",
|
|
29382
|
+
solidjs: "public/",
|
|
29383
|
+
qwik: "public/",
|
|
29384
|
+
nextjs: "public/",
|
|
29385
|
+
nuxt: "public/",
|
|
29386
|
+
astro: "public/",
|
|
29387
|
+
sveltekit: "static/",
|
|
29388
|
+
// SvelteKit's is static/, not public/
|
|
29389
|
+
remix: "public/",
|
|
29390
|
+
fresh: "static/",
|
|
29391
|
+
// Gatsby, Hugo, Eleventy and Docusaurus all name theirs static/.
|
|
29392
|
+
gatsby: "static/",
|
|
29393
|
+
hugo: "static/",
|
|
29394
|
+
eleventy: "public/",
|
|
29395
|
+
docusaurus: "static/",
|
|
29396
|
+
// Server frameworks with a public document root.
|
|
29397
|
+
rails: "public/",
|
|
29398
|
+
laravel: "public/",
|
|
29399
|
+
symfony: "public/",
|
|
29400
|
+
phoenix: "priv/static/",
|
|
29401
|
+
spring: "src/main/resources/static/",
|
|
29402
|
+
aspnet: "wwwroot/",
|
|
29403
|
+
// Jekyll and Pelican copy their source tree, so the app root is the served root.
|
|
29404
|
+
jekyll: "",
|
|
29405
|
+
pelican: ""
|
|
29406
|
+
// Python web frameworks have no single convention; the tree decides below.
|
|
29407
|
+
};
|
|
29408
|
+
function servedDir(framework, files, appRoot = "") {
|
|
29409
|
+
const known = framework ? SERVED_DIR[framework] : void 0;
|
|
29410
|
+
if (known !== void 0) return known;
|
|
29411
|
+
for (const candidate of ["public/", "static/", "assets/"]) {
|
|
29412
|
+
if (files.some((f) => f.startsWith(`${appRoot}${candidate}`))) return candidate;
|
|
29413
|
+
}
|
|
29414
|
+
return "";
|
|
29415
|
+
}
|
|
29416
|
+
|
|
29484
29417
|
// src/fixer/safety.ts
|
|
29485
29418
|
var HTML_EXT = /\.html?$/i;
|
|
29486
29419
|
var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|vue|svelte|astro)$/i;
|
|
@@ -32162,8 +32095,12 @@ async function analyzeComprehensive(html, url, options = {}) {
|
|
|
32162
32095
|
// src/frameworks/index.ts
|
|
32163
32096
|
var frameworks_exports = {};
|
|
32164
32097
|
__export(frameworks_exports, {
|
|
32098
|
+
SCORE_FLOOR: () => SCORE_FLOOR,
|
|
32099
|
+
SIGNALS: () => SIGNALS,
|
|
32165
32100
|
SUPPORTED_FRAMEWORKS: () => SUPPORTED_FRAMEWORKS,
|
|
32101
|
+
WEIGHT: () => WEIGHT,
|
|
32166
32102
|
detectFramework: () => detectFramework,
|
|
32103
|
+
detectFromEvidence: () => detectFromEvidence,
|
|
32167
32104
|
detectFromHeaders: () => detectFromHeaders,
|
|
32168
32105
|
detectFromHtml: () => detectFromHtml,
|
|
32169
32106
|
detectFromPackageJson: () => detectFromPackageJson,
|
|
@@ -32448,6 +32385,7 @@ export {
|
|
|
32448
32385
|
sendAlerts,
|
|
32449
32386
|
sendDiscordAlert,
|
|
32450
32387
|
sendSlackAlert,
|
|
32388
|
+
servedDir,
|
|
32451
32389
|
shouldRunAudit,
|
|
32452
32390
|
shouldSendAlert,
|
|
32453
32391
|
startWizardSession,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rankcli/agent-runtime",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.32",
|
|
4
4
|
"description": "RankCLI agent runtime - executes SEO audits and fixes with AI",
|
|
5
5
|
"homepage": "https://rankcli.dev",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"build:wasm": "tsup --config tsup.wasm.config.ts",
|
|
28
28
|
"test": "vitest run",
|
|
29
29
|
"test:watch": "vitest",
|
|
30
|
-
"test:coverage": "vitest run --coverage"
|
|
30
|
+
"test:coverage": "vitest run --coverage",
|
|
31
|
+
"benchmark:detection": "node scripts/benchmark-detection.mjs"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
33
34
|
"@anthropic-ai/sdk": "^0.52.0",
|