@mosaicast/plugin-sdk 0.9.0 → 0.9.1

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/README.md CHANGED
@@ -33,7 +33,9 @@ The contract version is a **single SemVer anchor** mirrored in four places that
33
33
  **How the host matches it:** core compares `major.minor` **exactly**. A plugin declaring `0.3.x` is
34
34
  rejected the moment the host runs `0.7.0` — with the reason shown in the admin log viewer. While the SDK
35
35
  is pre-1.0 a breaking change is therefore a **minor** bump (`0.6.0` → `0.7.0`), not a major one; from
36
- `1.0.0` on, normal SemVer applies and breaking means major.
36
+ `1.0.0` on, normal SemVer applies and breaking means major. **The patch floats** — a plugin declaring
37
+ `0.9.0` loads on a `0.9.1` host, which is what makes a purely additive release (a new optional extension
38
+ point, a test double) cheap: it rejects nothing already installed.
37
39
 
38
40
  ## Build & test
39
41
  ```bash
@@ -49,8 +51,8 @@ npm ci && npm run build # TypeScript: src → dist (.js + .d.ts)
49
51
  - Released: from **GitHub Packages** (see below).
50
52
  ```kotlin
51
53
  dependencies {
52
- compileOnly("dev.mosaicast:plugin-api:0.7.0") // contract, provided by the host
53
- testImplementation("dev.mosaicast:plugin-testkit:0.7.0") // test doubles only
54
+ compileOnly("dev.mosaicast:plugin-api:0.9.1") // contract, provided by the host
55
+ testImplementation("dev.mosaicast:plugin-testkit:0.9.1") // test doubles only
54
56
  }
55
57
  ```
56
58
  Sources + Javadoc JARs give IDE hover docs automatically.
@@ -394,7 +396,8 @@ ctx.route.navigate('index', { replace: true }); // no back-button step (ta
394
396
  - **Do not reach past this handle.** `history.pushState` plus a synthetic `popstate` happens to work
395
397
  against the host's current router and is not part of the contract.
396
398
  - Pair it with `ShareMetadataProvider` (OpenGraph per subpath) and `SitemapProvider` on the backend so the
397
- URLs you navigate to also preview and index properly.
399
+ URLs you navigate to also preview and index properly — and with `PageRouteProvider` (since 0.9.1) so the
400
+ ones that do *not* exist answer 404 instead of a 200 with a not-found view in it.
398
401
 
399
402
  ### Reading the query, and matching a route (since 0.9.0)
400
403
 
@@ -568,9 +571,9 @@ nothing to catch it. Note where the two *legitimately* diverge: `path`, `icon` a
568
571
  between them, but the menu **label cannot be translated** — core has no plugin catalogs — while your
569
572
  in-page tab can.
570
573
 
571
- ## Optional extension points — `SearchProvider` / `UserDataHandler` (since 0.9.0)
574
+ ## Optional extension points — `SearchProvider`, `UserDataHandler`, `PageRouteProvider`
572
575
 
573
- Both are `ExtensionPoint`s a plugin MAY implement alongside `PluginBackend`, like `SitemapProvider`. No
576
+ All three are `ExtensionPoint`s a plugin MAY implement alongside `PluginBackend`, like `SitemapProvider`. No
574
577
  manifest declaration: not implementing one is how a plugin says it has nothing to contribute.
575
578
 
576
579
  **`SearchProvider`** puts plugin content into the site's search, instead of each plugin growing a private
@@ -611,6 +614,30 @@ which one is a person. Handlers run *before* the account row goes, and **must be
611
614
  deletion is retried. `UserDataHandlerHarness.eraseTwice(userId)` is that test, and it is the one authors
612
615
  skip: the second call is the one that throws, during a retry, when the alternative is a half-done deletion.
613
616
 
617
+ **`PageRouteProvider`** (since 0.9.1) is how a plugin's unknown subpaths become real 404s. Declare a `page`
618
+ slot and *every* subpath under `/p/<id>/` answers `200` — a page never written, a mistyped slug, the URL of
619
+ a page deleted last year — each rendering your not-found view inside a `200 OK`. §6.6 rules that soft-404
620
+ out for core's own routes, and the host cannot fix it alone: only the plugin knows whether a subpath is a
621
+ thing.
622
+
623
+ ```java
624
+ public boolean hasRoute(String subpath) {
625
+ return subpath.isEmpty() || subpath.startsWith("_search/") || pages.exists(slugOf(subpath));
626
+ }
627
+ ```
628
+
629
+ **Not `ShareMetadataProvider`** — the tempting shortcut, and wrong: `_search/<term>` and `_admin` return an
630
+ empty `metaFor` *on purpose*, so reading "no share metadata" as "no page" would 404 working routes. Absent
631
+ means today's behaviour, the **root is a route** (`subpath` is empty there), it runs on a request so keep it
632
+ a lookup, and a provider that throws is logged and skipped and the route answers `200` — a broken plugin
633
+ must not turn a working page into a 404. It decides the status line only; the shell still renders the
634
+ not-found view. `PageRouteProviderHarness` asks about a handful of subpaths at once, root included:
635
+
636
+ ```java
637
+ var routes = new PageRouteProviderHarness(new WikiRoutes(store)).check("glossary/kraken", "glossary/tpyo");
638
+ assertEquals(List.of("glossary/tpyo"), routes.notFound());
639
+ ```
640
+
614
641
  ## Logging — `ctx.logger()` / `ctx.log()` (since 0.4.0)
615
642
 
616
643
  ```java
@@ -780,8 +807,10 @@ enforces the ceilings and the allow-list. A component that has only ever met a p
780
807
  first refusal in front of a podcaster.
781
808
 
782
809
  **The extension-point harnesses** (Java): `SearchProviderHarness` calls a provider once per role including
783
- anonymous; `UserDataHandlerHarness.eraseTwice(userId)` proves erasure survives the host's retry. Both are
784
- shown under [Optional extension points](#optional-extension-points--searchprovider--userdatahandler-since-090).
810
+ anonymous; `UserDataHandlerHarness.eraseTwice(userId)` proves erasure survives the host's retry;
811
+ `PageRouteProviderHarness.check(...)` answers which subpaths 404, always probing the plugin root. All three
812
+ are shown under
813
+ [Optional extension points](#optional-extension-points--searchprovider-userdatahandler-pagerouteprovider).
785
814
 
786
815
  ## Contributing
787
816
  Contributions welcome — see [`CONTRIBUTING.md`](CONTRIBUTING.md). In short: `git commit -s` (DCO, required), SPDX header in new files, add tests.
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  * rejects a mismatch at startup (ARCHITECTURE §7.2). While the SDK is pre-1.0 a breaking change is
18
18
  * therefore a *minor* bump; from `1.0.0` on, breaking means major.
19
19
  */
20
- export declare const PLATFORM_API_VERSION: '0.9.0';
20
+ export declare const PLATFORM_API_VERSION: '0.9.1';
21
21
  /** A user's role (ARCHITECTURE §8.5). Anonymous visitors have no role (`user` is `null`). */
22
22
  export type Role = 'admin' | 'podcaster' | 'fan';
23
23
  /**
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@
19
19
  * rejects a mismatch at startup (ARCHITECTURE §7.2). While the SDK is pre-1.0 a breaking change is
20
20
  * therefore a *minor* bump; from `1.0.0` on, breaking means major.
21
21
  */
22
- export const PLATFORM_API_VERSION = '0.9.0';
22
+ export const PLATFORM_API_VERSION = '0.9.1';
23
23
  /**
24
24
  * The id every `user` data path carries: the literal `me`, mirroring the Java `Scope.SELF_ID`.
25
25
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mosaicast/plugin-sdk",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Versioned plugin contract for Mosaicast: the frontend PluginContext types, a Web Component base helper, an i18n helper, plus a test kit under the /testing subpath.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {