=== Heurix Search for WooCommerce ===
Contributors: heurix
Tags: woocommerce, search, ecommerce
Requires at least: 5.9
Tested up to: 7.1
Requires PHP: 8.1
WC requires at least: 6.1
WC tested up to: 11.1.2
Stable tag: 0.2.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Replaces WooCommerce product search with the Heurix engine: typo tolerance, trade synonyms, ranking rules you control.

== Description ==

Heurix Search intercepts WooCommerce product search and replaces the results
with ones computed by the Heurix engine -- without touching your templates, your
faceted filters or your product pages. Only which products come out, and in
what order, changes.

**What the plugin does:**

* Indexes your published catalog into your Heurix account
* Re-syncs each product when it is saved (price, stock, new product)
* Replaces native search results with Heurix's, in the relevance order returned
* Steps aside silently -- native WooCommerce search, unchanged -- when Heurix is
  temporarily unreachable

**What the plugin does not do (v0.2.1):**

* It does not search variation attributes separately (colour, size): the parent
  product is indexed, not each individual variation

= Compatibility: what has actually been run =

The headers above carry only versions that have REALLY BEEN RUN. Recorded on
22 September 2026, completed on the 23rd.

ONE OF THOSE LINES IS LESS PRECISE THAN THE MEASUREMENT, AND THE PARSER IS WHY.
`Tested up to` says 7.1; WordPress 7.1.2 is what ran. Plugin Check 2.1.0 -- the
official wordpress.org tool -- rejects the third number: "invalid_tested_upto_minor
[...] should only include major versions 7.1", an ERROR, measured on this plugin
on 23 September 2026. The exact number is on the WordPress line below.

* PHP 8.1.34 and 8.5.10: the tests/ suites, green on both (49 assertions on the
  morning of 22 September, 59 after the two suites added that day). The version
  announced before that day was 7.4, and 7.4 had never been run once.
* WordPress 5.9 to 7.1.2: RUN. 7.1.1 and 7.1.2 on the circuit-breaker bench
  (MariaDB 12.3.3, and SQLite through the SQLite Database Integration plugin
  3.0.2), then 5.9.18, 6.0.16, 6.1.14, 6.3.12, 6.5.12, 6.7.9 and 6.9.9 on
  23 September 2026, each with a WooCommerce that accepts it, inside a real
  product loop. The records carry the version (`wordpress.version`) and the
  archive verified against its published sha1.
  The lower bound is measured under PHP 8.1, and it is WordPress that sets it,
  not this plugin: 5.8.17 and 5.6.21 do not start under PHP 8.1, because mysqli
  throws by default there and their `wp-db.php` does not set
  `mysqli_report( MYSQLI_REPORT_OFF )`. That line appears in WordPress 5.9,
  with the WordPress comment that names PHP 8.1.
  NOT MEASURED: WordPress 5.8 and earlier under a PHP they accept (they go down
  to 5.6.20). That would need a PHP 7.x, ruled out on 23 September -- the
  Homebrew formula has been dead since 2022, it would mean a third-party tap and
  one more php-fpm. That gap cannot open on a merchant's site: WordPress refuses
  to activate a plugin whose `Requires PHP` is not satisfied, and ours asks for
  8.1.
* WooCommerce 6.1 to 11.1.2: RUN, on 23 September 2026. Thirteen versions played
  inside a real product loop, on WordPress 7.1.2 (tests/banc-disjoncteur,
  `crochets` scenario). What was checked each time: that WooCommerce sets
  `posts_per_page` before the plugin reads it, that the Customizer's
  `loop_shop_per_page` takes effect, that the order returned by Heurix survives
  the loop, and that page two returns the next slice.
  The lower bound is measured, not chosen: 6.0.0 does not hold
  (`loop_shop_per_page` has no effect, page 2 comes out empty), 6.1.0 holds. The
  lines "WC requires at least: 7.0" and "WC tested up to: 9.0" announced until
  22 September rested on nothing at all.
  Not measured: the intermediate versions of each branch (the runs cover 4.9.5,
  5.9.2, 6.0.0, 6.1.0, 6.2.0, 6.3.0, 6.5.0, 6.7.0, 6.9.5, 7.0.0, 7.9.2, 8.9.5,
  9.9.7, 10.9.4, 11.1.2), and WooCommerce 3.9.5, which stops on a PHP error
  before reaching the measurement.

What this means for you: on WooCommerce 6.0 or older, Heurix search shows the
first page and returns an empty second one. The plugin does not detect this -- it
does not read the WooCommerce version, only whether it is present.

THE TWO BOUNDS GUARD EACH OTHER, AND THAT IS MEASURED. WordPress refuses to
activate a plugin whose `Requires at least` or `Requires PHP` is not satisfied --
verified on 23 September 2026, both ways:

    "Current WordPress version (6.9.9) does not meet minimum requirements
     [...] The plugin requires WordPress 7.1."
    "Current versions of WordPress (6.9.9) and PHP (8.1.34) do not meet
     minimum requirements [...] requires WordPress 7.1 and PHP 9.9."

So "Requires at least: 5.9" is not an idle line. WordPress 5.9 to 6.2 officially
accept PHP 5.6.20, and we have exercised them only under 8.1 -- but a site on
PHP 7.x cannot activate this plugin at all, since `Requires PHP: 8.1` forbids
it. What we have not measured therefore cannot happen on a merchant's site.

"Requires PHP: 8.1" IS STILL A PRECAUTIONARY BOUND. 8.1.34 and 8.5.10 are
exercised; nothing below. It is the one holding the whole thing up, and it has
never been taken down.

A THIRD BOUND, DECLARED ON PURPOSE: WooCommerce MUST BE ACTIVE TO ACTIVATE THIS
PLUGIN, on WordPress 6.5 and later. That is the plugin dependency header, added
at the request of the wordpress.org review. Two things it is worth knowing,
measured on 27 September 2026:

* WordPress resolves the dependency by the NAME OF THE INSTALLED FOLDER, not by
  WooCommerce itself. If your WooCommerce lives in a folder that is not called
  `woocommerce`, WordPress will refuse to activate this plugin even though
  WooCommerce is running and the plugin would work. Rename the folder to
  `woocommerce`, or install WooCommerce from wordpress.org.
* A plugin that is ALREADY ACTIVE is not deactivated if the dependency stops
  being satisfied. Upgrading to a version carrying this header cannot turn your
  search off.

On WordPress 5.9 to 6.4 the header does nothing at all -- it is ignored in
silence, and does not even show on the Plugins screen. There, as before, the
plugin activates without WooCommerce and simply does nothing: it tells you so on
the Plugins screen, and native WooCommerce search is untouched.

= Requirements =

An active Heurix account, with a server API key (prefix `hx_`) and a catalog
created. See [heurix.fr/docs](https://heurix.fr/en/docs.html) to get started.

= The external service, what it costs, and what it receives =

This plugin is an interface to Heurix, a hosted search service
(https://heurix.fr). Ranking, typo tolerance and trade synonyms are computed by
the service; the plugin handles indexing and the replacement of results.

**No feature of the plugin is locked, time-limited, or held back for a paid
version.** All the code shipped is active from activation onwards, with no
licence key and no subscription check, and the zip contains no dormant code
waiting to be unlocked. What you pay for is the service, not the plugin.

Service pricing, as of 23 September 2026: from EUR 19 excl. VAT per month on
monthly billing, with a 14-day free trial and no credit card required (see
[heurix.fr/pricing](https://heurix.fr/en/pricing.html)). **The trial is not a
sandbox**: it is the full service, and the same API key keeps working unchanged
once you subscribe. No feature appears or disappears at that point.

Terms of Service: https://heurix.fr/en/cgv.html

= What is sent to Heurix, and when =

Nothing leaves your site until you have entered your API key on the
WooCommerce > Heurix Search settings page. Entering it is what grants consent;
without it the plugin opens no outbound connection at all.

At indexing time (manually, then whenever a published product is saved or
deleted), for each product: its WooCommerce ID, its SKU, its name, its
description, its stock state, its price, its public URL, its brand and its main
category.

On each visitor search: the term typed, the number of results requested and the
pagination offset. **Nothing else.** The plugin sends no IP address, no visitor
ID, no cookie and no browser header, and it does not read `$_SERVER`. Every
request leaves from the WordPress server; the visitor's browser never contacts
Heurix and never sees the API key.

Privacy policy: https://heurix.fr/en/privacy.html

If the service stops responding, native WooCommerce search takes back over
automatically: the shop stays usable without an active subscription.

== Installation ==

1. Upload the `heurix-search` folder to `/wp-content/plugins/`
2. Activate the plugin from the WordPress Plugins menu
3. Go to WooCommerce > Heurix Search
4. Enter your API key and your catalog name
5. Test the connection, then run a full indexing
6. Run a search on your shop to check the result

== Frequently Asked Questions ==

= What happens if Heurix is unavailable? =

Native WooCommerce search takes back over automatically -- visitors see no
outage.

= Does the plugin modify my WooCommerce products? =

No. It reads your products in order to index them on the Heurix side, but it
never writes to your WooCommerce database.

= Is my API key exposed to visitors' browsers? =

No. Every request to Heurix leaves from the WordPress server, never from the
visitor's browser.

== Screenshots ==

1. WooCommerce > Heurix Search: your API key (never displayed), your catalog, and
the rule packs the engine publishes -- read when you save the key, with the date
they were read. The report of the last full indexing run is at the bottom.
2. When Heurix does not answer, native WooCommerce search serves the shop --
silently for your visitors, and no longer silently for you. One line per cause,
with how many searches, the first one and the last one, and a button to reset
the counter.
3. A results page, as a visitor sees it. The term was typed with a mistake --
"vis hexagonle" -- and the products found are spelled correctly. Native
WooCommerce search returns nothing at all for that query, on this same shop.

== Changelog ==

= 0.2.2 =
**A refused indexing run now stops, and the screen says what really happened.**

When the Heurix plan's product ceiling was reached, the plugin kept sending
every remaining batch. Measured on 9 October 2026 with a 30,000-product
catalog on a free trial: 300 HTTP calls, 22 accepted, 278 refused, and the
settings page announced "30000 / 30000 products processed, 27800 failure(s)"
to a merchant who had 2,200 products indexed and searchable.

The reason written in the code for not stopping -- the engine had already
written the batch when it refused it -- had been true until 13 September 2026,
and false ever since: the engine now checks the ceiling before writing, under
its write lock. A refusal means nothing was written, so continuing could not
index one more product.

The run now stops at the first refusal, as it already did for a rejected key,
an unknown catalog or an unknown rule pack. The screen reports the products it
actually processed, adds how many published products your catalog holds, and
shows the engine's own sentence, which now says that the ceiling is the trial's
and not the product's, that search keeps working, and what to export instead.

Network errors and server errors (5xx) still do not stop the run: they are
transient, and the next batch may well go through.

= 0.2.1 =
**Your search page says what was searched again.**

Since 0.2.0, a results page served by Heurix announced an empty search. The
term was missing from the page title, from the heading, from the breadcrumb,
from the RSS feed -- and, worst of all, from the search box itself: a visitor
who mistyped could not correct the query, they had to retype it. Measured on
28 September 2026 on two themes, one block-based and one classic, pages 1 and 2.

The cause: to stop WordPress from adding its own `LIKE %term%` to the list of
products Heurix returned -- which would have dropped exactly the results the
engine is paid to find, the ones matched by synonym or despite a typo -- the
plugin emptied the search term from the query. Everything that displays the
term reads it from there.

It now empties the SQL clause instead of the term. The database query is
byte-for-byte identical; only the page changed.

One thing it did NOT affect, and it is the unpleasant part: a search where
Heurix found NOTHING was always correct. The bug only showed when the plugin
was doing its job.

= 0.2.0 =
First release on wordpress.org. 0.1.0 was never published here; what follows is
what changed since it. If you are installing for the first time, read it as a
description of what the plugin does.

**What it does when Heurix does not answer**

* Native WooCommerce search takes over, one search at a time, as it already
  did -- and you can now SEE it happen. WooCommerce > Heurix Search lists the
  searches that were served by native search instead of Heurix: a count, the
  first one and the last one, for each of four causes (all call slots taken,
  circuit breaker open, call failed, rejected by the engine). A button resets
  the counter. In 0.1.0 this was invisible: a shop could run for weeks on
  native search without anything saying so.
* At most two searches wait for Heurix at the same time; a third one goes
  straight to native search. Without that limit, a Heurix that was slow but
  alive saturated the shop: six simultaneous searches held the five processes
  of a default PHP-FPM pool for three seconds, and a page that searched
  nothing waited 2.8 seconds for its turn.
* The circuit breaker opens after 2 failed searches within 60 seconds, and
  stays open for 60 seconds. 0.1.0 needed 3.
* The search timeout is a setting now, 3 seconds by default. Past it, that
  search is answered by native WooCommerce search. In 0.1.0 the 3 seconds
  were fixed in the code.
* WordPress on SQLite works -- WordPress Playground, or the SQLite Database
  Integration plugin. Both mechanisms above need MySQL features that SQLite
  does not have, and they fall back to a form it does. Before, the two call
  slots looked permanently taken there and NO search ever reached Heurix.
* When the engine rejects your API key or your catalog, a notice says so on
  your product screens, with the count and the date of the last one. Same for
  an indexing run that was rejected. You no longer have to open the settings
  page to find out.

**What it shows you**

* The rule packs on the settings page are the list published by the engine,
  read when you save your key, with the date it was read and a button to read
  it again. A pack name you typed that the engine does not list is shown as
  such. In 0.1.0 the field was free text and the readme sent you to your
  Heurix console.
* The indexing report says how many products were processed, how many failed,
  the cause of the first failure, and whether the run stopped there.

**What it supports**

* Result pages past the first hundred. 0.1.0 asked Heurix for the first 100
  products on every page and let WordPress paginate inside them: at 16 per
  page you got 7 pages and then a 404, on a catalog where the engine had
  found 3,334 matches. Heurix now serves the page you asked for, and the
  result count shown is the engine's total, not the size of one page.
* A shop that shows more than 100 products per page, or "show all": the
  search page falls back to 100, which is the engine's own ceiling. In 0.1.0
  every single one of those searches fell back to native search.
* Declared compatibility is measured now, not hoped: WordPress 5.9 to 7.1,
  WooCommerce 6.1 to 11.1.2, PHP 8.1 and 8.5. Each lower bound is the version
  where the plugin stops working, found by trying them one at a time -- the
  section "Compatibility: what has actually been run" says what was run and
  on what. 0.1.0 announced WordPress 6.0, WooCommerce 7.0 and PHP 7.4; none
  of the three had ever been run.
* Every screen of the plugin, and this page, are in English. 0.1.0 was in
  French.

= 0.1.0 =
* First release: search interception, initial batch indexing, automatic re-sync on save and on delete, a circuit breaker (3 consecutive failures, network and 5xx only, reset after 60 seconds) that degrades immediately to native search during an outage, and a settings page.
