Files
53345f73b7 feat(plugins): offer data loaded by URL to plugin file handlers (#3321)
* feat(plugins): offer data loaded by URL to plugin file handlers

A file loaded with ?data= or the embed addData command never reached the
file handlers added in #3305: a TASKDATA.zip failed as a GeoJSON zip even
though an installed plugin could read it.

After the download, and before the built-in classification, the file is
offered to the registered handlers through the same routing as a drop
(extension filter, claim with the zip entries, open, same error
handling). A handler can also declare dataTypes; a URL with
dataType=<name> then goes straight to its open, for URLs without an
extension. lidar stays built in. The embed addData command now accepts
dataType too.

URL loads wait, bounded, for plugin activation before offering a file,
keep every download ceiling, and reach handlers only where drops are
allowed (not in the viewer, and with data:add).

Refs #3283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(plugins): offer an extensionless zip download to .zip file handlers

A URL load already recognizes a zip by extension, content type,
Content-Disposition or the zip signature. When the derived file name has
no .zip extension, offer it as name.zip so the extension pre-filter and
the zip entries work, for example for a signed link that returns
application/zip. No other format is guessed.

Refs #3283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(plugins): drop the status line claim for URL-loaded files

The data URL loader's success message is not shown in the UI, only its
errors are, so the docs should not promise an "Opened by" status.

Refs #3283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plugins): wait for the ?plugin= deep link before offering URL data to handlers

The startup wait only covered the project plugin restore. A plugin that a
?plugin= link activates registers its file handler from a second effect,
so a ?data= load combined with ?plugin= could offer the file too early.

The settle gate now has two parts, restore and deep link, and
whenPluginsSettled waits for both within the same 15 s bound. Without a
?plugin= link the deep link part settles at once.

Refs #3283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plugins): bound the URL load wait, clean up after handlers, and tighten the details

- One absolute 15 s deadline covers the settle parts and any activation
  still pending; once it passes, or startup has settled, later loads do
  not wait.
- The restore part settles only for the restore that follows a ?url=
  project, and the deep link part only after that project has loaded.
- A plugin hint combined with a style fails before downloading. A failed
  open, a style that arrives too late, or an abort removes the layers the
  handler added. Handled layers are framed by the shell like GeoJSON.
- Remove the unused success message; sanitize the download file name
  (control characters, 255 characters, escaped quotes, empty names).
- addData accepts dataType null; DataTypeHint keeps the lidar literal.
- Docs: the handler's plugin must be active (use plugin= or a ?url=
  project), open must validate hinted bytes, every download outside the
  viewer waits up to the limit, and the first addData after ready may
  need a longer requestTimeoutMs.

Refs #3283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(plugins): say which plugins ?plugin= can activate for URL loads

?plugin= accepts built-in plugin names and registry plugin names. A plugin
installed from a file is not found by it, so a link that relies on such a
plugin needs a ?url= project that lists it as active.

Refs #3283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plugins): address review findings on URL loads and file handler hints

- Latch the ?url= settled flag so the loader returning to idle cannot
  un-settle plugin activation; settle the ?plugin= part only after
  registry plugins awaiting trust or installing have activated.
- Count only the layers added while a handler's open runs as the
  handler's, so a layer the user adds during the download is never
  removed or framed on its behalf.
- Send no style request for a hint a handler declares, and abort the
  download as soon as the style request fails (and vice versa).
- Check the abort signal after the readiness wait, before offering.
- A malformed dataType is dropped with a console warning in both the
  ?dataType= parameter and the embed addData option.
- Registry: describe an invalid data type by its type so the warning
  cannot throw; re-registering an id now moves the handler to the end of
  the order, so hint ownership stays with the earlier handler as the
  warning says (the test that pinned "keeps the slot" changed).
- DataTypeHint is plain string; docs say the style rule applies only to
  a hint a handler declares.

Refs #3283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plugins): address a further review round on URL file handlers

- Wrap handlers for layer tracking with a proxy instead of a spread, so
  a class-instance handler keeps its prototype methods (claim) and every
  method runs with the original handler as this.
- Revert the "replacement moves to the end" order change: re-registering
  an id keeps its slot again, as in #3305. Hint ownership is tracked
  apart from the order: first declaration wins, a replacement keeps the
  hints its predecessor owned, never takes one another live handler owns,
  and a hint it drops goes to the next handler declaring it.
- The plugin readiness wait is raced against the load abort, for the
  offer, the hinted path and the style check.
- A trusted registry plugin whose manifest reports a load issue counts as
  failed even when another plugin holds its id, so it cannot hold the
  startup gate until the deadline.
- A dataType hint is trimmed and compared case-insensitively in both the
  ?dataType= parameter and embed addData; docs say so.
- The startup ?data= load and the embed addData command share one queue,
  so two URL loads never run a handler at once.
- Docs: bytes from a shared ?data= link or an allowed host reach active
  plugins' claim/open and must be treated as untrusted input.

Refs #3283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(plugins): state how URL loads reach file handlers

Three review findings on the URL file handler docs are answered in
docs/plugin-api.md. The layer attribution limit is now stated: a layer
the user adds while a handler's open runs is counted as the handler's,
since it cannot be told apart from the handler's own layers. Downloads
that the built-in loaders take by extension (.tif, .tiff, .cog,
.pmtiles, .parquet, .geoparquet, .las, .laz, EPT ept.json) are loaded
before any download, so an extensions filter never sees them; a dataType
hint still routes them to a handler.

Code changes are left to maintainers: the layer attribution design, the
plugin registry order on reactivation, the i18n of handler errors, the
load-issue mismatch for trusted manifests, and the queue stall on a
handler that never settles are marked unclear in the thread replies.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>

Applied by the claude-autofix workflow.

* fix(embed): treat an empty addData dataType as no hint

An empty dataType on addData logged a "malformed" warning, while an
empty ?dataType= parameter is ignored silently. Treat the empty string
as no hint in addData too, so both entry points behave the same, and
cover it with a regression test.

Applied by the claude-autofix workflow.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: giswqs <5016453+giswqs@users.noreply.github.com>
2026-11-03 11:46:15 -05:00
..