@biffo/cli 0.313.3 → 0.314.0

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.
@@ -80,8 +80,8 @@
80
80
  }
81
81
  ],
82
82
  "ui_components": [
83
- { "type": "nav-link", "label": "Example Plugin", "path": "/admin/example-plugin" },
84
- { "type": "page", "label": "Widgets", "path": "/admin/example-plugin/widgets" }
83
+ { "type": "nav-link", "label": "Example Plugin", "path": "/admin/plugins/example-plugin" },
84
+ { "type": "page", "label": "Widgets", "path": "/admin/plugins/example-plugin/widgets" }
85
85
  ],
86
86
  "seed": {
87
87
  "dir": "db/seed",
@@ -89,5 +89,17 @@
89
89
  },
90
90
  "dependencies": {
91
91
  "biffo-plugin-sdk": "^1.0"
92
+ },
93
+ "user_ingress": {
94
+ "required_group": "founder",
95
+ "app": "example_plugin.user_app:app"
96
+ },
97
+ "admin_ingress": {
98
+ "required_group": "admin",
99
+ "app": "example_plugin.admin_app:app"
100
+ },
101
+ "user_frontend": {
102
+ "dir": "web/dist",
103
+ "required_group": "founder"
92
104
  }
93
105
  }
@@ -15,6 +15,12 @@ dependencies = [
15
15
  "biffo-plugin-sdk>=1.0,<2.0",
16
16
  "aws-lambda-powertools[tracer]>=3.4.0",
17
17
  "httpx>=0.28.1",
18
+ # ASGI apps for the `user_ingress`/`admin_ingress` surfaces (ADR-0021) —
19
+ # the shared plugin host imports and mounts `<module>:app` directly, so this
20
+ # is the only new runtime dependency the two placeholder apps need (no
21
+ # mangum/pyjwt: the host provides the Lambda entry and enforces
22
+ # `required_group` itself — see user_app.py's docstring).
23
+ "fastapi>=0.115.0",
18
24
  ]
19
25
 
20
26
  [dependency-groups]
@@ -0,0 +1,50 @@
1
+ """Placeholder ASGI app backing this plugin's `admin_ingress` surface (ADR-0021).
2
+
3
+ `biffo.plugin.json`'s `admin_ingress.app` names this module's `app` object. The
4
+ **shared plugin host** imports it, provides the Lambda entry, and mounts it at
5
+ `/api/v1/plugins/<name>/admin/*`, enforcing `admin_ingress.required_group`
6
+ (ADR-0011) before a request ever reaches here — see `user_app.py`'s docstring
7
+ for why this file does no auth of its own.
8
+
9
+ **This is also `web-admin/`'s one reader (biffo-template#2008).** The skeleton
10
+ shipped a full admin frontend at `web-admin/` that no manifest field referenced
11
+ — an orphan directory nothing would ever build or serve. `AdminIngress` in the
12
+ SDK is explicitly "the plugin's admin-gated API and optional static UI bundle":
13
+ unlike `user_frontend` (a separate declarative `dir`, served directly by the
14
+ host with no plugin code involved), the admin surface's static bundle is served
15
+ *by the app this field names* — `web-admin/`'s own `vite.config.ts` already
16
+ targets exactly this mount point (`base:
17
+ '/api/v1/plugins/example-plugin/admin/'`).
18
+
19
+ `web-admin/dist/` is the Vite build output — gitignored, and not built by this
20
+ repo's CI (that wiring is follow-up work; #2008's scope is declaring the
21
+ surfaces, not shipping the built bundle, mirroring `user_frontend.dir` above
22
+ pointing at `web/dist` before that directory exists either — see #2009). The
23
+ mount below is skipped when `dist/` is absent, so a fresh checkout and `pytest`
24
+ need no build step and no edits (this issue's "done when" criterion) — once
25
+ `web-admin` is built, dropping its `dist/` in place is enough to serve it, with
26
+ no further code change.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from pathlib import Path
32
+
33
+ from fastapi import FastAPI
34
+ from fastapi.staticfiles import StaticFiles
35
+
36
+ app = FastAPI(title="example-plugin admin ingress")
37
+
38
+ # src/example_plugin/admin_app.py -> src/example_plugin -> src -> repo root
39
+ # (plugin root, where web-admin/ lives alongside biffo.plugin.json).
40
+ WEB_ADMIN_DIST = Path(__file__).resolve().parent.parent.parent / "web-admin" / "dist"
41
+
42
+
43
+ @app.get("/ping")
44
+ def ping() -> dict[str, str]:
45
+ """Identifies the plugin, so the mount can be proven real rather than guessed at."""
46
+ return {"plugin": "example-plugin", "surface": "admin_ingress"}
47
+
48
+
49
+ if WEB_ADMIN_DIST.is_dir():
50
+ app.mount("/", StaticFiles(directory=str(WEB_ADMIN_DIST), html=True), name="web-admin")
@@ -0,0 +1,31 @@
1
+ """Placeholder ASGI app backing this plugin's `user_ingress` surface (ADR-0021).
2
+
3
+ `biffo.plugin.json`'s `user_ingress.app` names this module's `app` object. The
4
+ **shared plugin host** imports it, provides the Lambda entry, and mounts it at
5
+ `/api/v1/plugins/<name>/*` — a plugin declaring `user_ingress` ships no Lambda
6
+ handler and no infrastructure of its own for this surface (see `terraform/`:
7
+ nothing here needs a per-plugin resource).
8
+
9
+ **No auth in this file, on purpose.** The host enforces `user_ingress.required_group`
10
+ (ADR-0011) before a request ever reaches a mounted app — see
11
+ `biffo_plugin_sdk.user_serving`'s module docstring: its `require_group` dependency
12
+ is only for an `isolated: true` plugin running in its own Lambda outside the
13
+ shared host. Adding a second check here would be redundant at best and a
14
+ divergent enforcement path at worst.
15
+
16
+ This is intentionally a placeholder (biffo-template#2008): one route that
17
+ identifies the plugin, enough to prove the mount is real, not enough to be
18
+ mistaken for a feature. Replace it with your own user-facing routes.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from fastapi import FastAPI
24
+
25
+ app = FastAPI(title="example-plugin user ingress")
26
+
27
+
28
+ @app.get("/ping")
29
+ def ping() -> dict[str, str]:
30
+ """Identifies the plugin, so the mount can be proven real rather than guessed at."""
31
+ return {"plugin": "example-plugin", "surface": "user_ingress"}
@@ -0,0 +1,119 @@
1
+ """Pins the three platform surfaces `biffo.plugin.json` declares (biffo-template#2008).
2
+
3
+ Before this, `_skeletons/plugin-template` declared none of `user_ingress`,
4
+ `admin_ingress` or `user_frontend` — the platform already serves all three
5
+ (the working reference is `keiranholloway/biffo-plugin-ideation`'s manifest),
6
+ but every scaffolded plugin started life exposing none of them. This test
7
+ guards three things at once:
8
+
9
+ 1. the manifest actually validates against the SDK's real `PluginManifest`
10
+ (`extra="forbid"`, so a typo in a block's keys fails loudly rather than
11
+ silently dropping the surface — see `README.md`'s "Manifest validation"
12
+ section for why that model, not `registry-schema.json`, is authoritative);
13
+ 2. each `*_ingress.app` reference actually resolves to a real, importable ASGI
14
+ app — not merely a string that looks right; and
15
+ 3. each placeholder app answers with something that identifies the plugin,
16
+ so the mount is real rather than guessed at (this issue's own "done when").
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import importlib
22
+
23
+ from biffo_plugin_sdk.plugin import load_manifest
24
+ from fastapi import FastAPI
25
+ from fastapi.testclient import TestClient
26
+
27
+ from example_plugin.manifest import MANIFEST_PATH
28
+
29
+
30
+ def _resolve_app(app_ref: str) -> FastAPI:
31
+ """Resolve a manifest `*_ingress.app` string ('<module>:<attr>') the same
32
+ way the shared plugin host would — a plain dynamic import, not a literal
33
+ reference to this test's own imports, so a manifest string that drifted
34
+ from the real module path would be caught here rather than passing by
35
+ coincidence."""
36
+ module_name, _, attr = app_ref.partition(":")
37
+ module = importlib.import_module(module_name)
38
+ return getattr(module, attr)
39
+
40
+
41
+ class TestManifestDeclaresAllThreeSurfaces:
42
+ def test_manifest_validates_and_declares_user_ingress(self) -> None:
43
+ manifest = load_manifest(MANIFEST_PATH)
44
+ assert manifest.user_ingress is not None
45
+ assert manifest.user_ingress.required_group == "founder"
46
+ assert manifest.user_ingress.app == "example_plugin.user_app:app"
47
+
48
+ def test_manifest_declares_admin_ingress(self) -> None:
49
+ manifest = load_manifest(MANIFEST_PATH)
50
+ assert manifest.admin_ingress is not None
51
+ assert manifest.admin_ingress.required_group == "admin"
52
+ assert manifest.admin_ingress.app == "example_plugin.admin_app:app"
53
+
54
+ def test_manifest_declares_user_frontend(self) -> None:
55
+ manifest = load_manifest(MANIFEST_PATH)
56
+ assert manifest.user_frontend is not None
57
+ assert manifest.user_frontend.dir == "web/dist"
58
+ assert manifest.user_frontend.required_group == "founder"
59
+
60
+ def test_ui_components_no_longer_point_at_a_dead_admin_path(self) -> None:
61
+ """The old `/admin/example-plugin` paths matched none of the three real
62
+ surfaces (this issue's own finding) — pin that they now sit under the
63
+ portal's real installed-plugin route instead."""
64
+ manifest = load_manifest(MANIFEST_PATH)
65
+ paths = {c.path for c in manifest.ui_components}
66
+ assert paths == {
67
+ "/admin/plugins/example-plugin",
68
+ "/admin/plugins/example-plugin/widgets",
69
+ }
70
+
71
+
72
+ class TestIngressAppsResolveAndIdentifyThePlugin:
73
+ def test_user_ingress_app_resolves_and_answers(self) -> None:
74
+ manifest = load_manifest(MANIFEST_PATH)
75
+ assert manifest.user_ingress is not None
76
+ app = _resolve_app(manifest.user_ingress.app)
77
+
78
+ response = TestClient(app).get("/ping")
79
+
80
+ assert response.status_code == 200
81
+ assert response.json() == {"plugin": "example-plugin", "surface": "user_ingress"}
82
+
83
+ def test_admin_ingress_app_resolves_and_answers(self) -> None:
84
+ manifest = load_manifest(MANIFEST_PATH)
85
+ assert manifest.admin_ingress is not None
86
+ app = _resolve_app(manifest.admin_ingress.app)
87
+
88
+ response = TestClient(app).get("/ping")
89
+
90
+ assert response.status_code == 200
91
+ assert response.json() == {"plugin": "example-plugin", "surface": "admin_ingress"}
92
+
93
+
94
+ class TestAdminIngressWebAdminOrphanResolution:
95
+ """`web-admin/` had no manifest field referencing it (this issue's own
96
+ finding). `admin_app.py` is now its one reader — these pin that the
97
+ reference is real (the directory `admin_app` looks for is exactly
98
+ `web-admin/`, not a typo'd sibling) and that a repo state with no built
99
+ `dist/` yet (true of this skeleton and every fresh checkout, since `dist/`
100
+ is gitignored and nothing builds it here — see admin_app.py's docstring)
101
+ still imports and serves cleanly rather than raising at import time.
102
+ """
103
+
104
+ def test_web_admin_dist_path_points_at_the_real_directory(self) -> None:
105
+ from example_plugin.admin_app import WEB_ADMIN_DIST
106
+
107
+ assert WEB_ADMIN_DIST.parent.name == "web-admin"
108
+ assert WEB_ADMIN_DIST.name == "dist"
109
+ assert WEB_ADMIN_DIST.parent.is_dir(), "web-admin/ itself must still exist"
110
+
111
+ def test_static_mount_is_skipped_when_dist_is_absent(self) -> None:
112
+ from example_plugin.admin_app import WEB_ADMIN_DIST, app
113
+
114
+ assert not WEB_ADMIN_DIST.is_dir(), (
115
+ "this test asserts the no-build-yet behaviour; if dist/ has been "
116
+ "built locally, the skip path this test pins is not being exercised"
117
+ )
118
+ response = TestClient(app).get("/some/never-built/asset.js")
119
+ assert response.status_code == 404
@@ -2,6 +2,15 @@ version = 1
2
2
  revision = 3
3
3
  requires-python = ">=3.13"
4
4
 
5
+ [[package]]
6
+ name = "annotated-doc"
7
+ version = "0.0.5"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" }
10
+ wheels = [
11
+ { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" },
12
+ ]
13
+
5
14
  [[package]]
6
15
  name = "annotated-types"
7
16
  version = "0.8.0"
@@ -70,6 +79,7 @@ source = { editable = "." }
70
79
  dependencies = [
71
80
  { name = "aws-lambda-powertools", extra = ["tracer"] },
72
81
  { name = "biffo-plugin-sdk" },
82
+ { name = "fastapi" },
73
83
  { name = "httpx" },
74
84
  ]
75
85
 
@@ -88,6 +98,7 @@ dev = [
88
98
  requires-dist = [
89
99
  { name = "aws-lambda-powertools", extras = ["tracer"], specifier = ">=3.4.0" },
90
100
  { name = "biffo-plugin-sdk", specifier = ">=1.0,<2.0" },
101
+ { name = "fastapi", specifier = ">=0.115.0" },
91
102
  { name = "httpx", specifier = ">=0.28.1" },
92
103
  ]
93
104
 
@@ -398,6 +409,22 @@ wheels = [
398
409
  { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" },
399
410
  ]
400
411
 
412
+ [[package]]
413
+ name = "fastapi"
414
+ version = "0.141.1"
415
+ source = { registry = "https://pypi.org/simple" }
416
+ dependencies = [
417
+ { name = "annotated-doc" },
418
+ { name = "pydantic" },
419
+ { name = "starlette" },
420
+ { name = "typing-extensions" },
421
+ { name = "typing-inspection" },
422
+ ]
423
+ sdist = { url = "https://files.pythonhosted.org/packages/8a/02/91e3416a8fdd715abb903a952a6bec7cdd8d14eed55d415fc8595524c319/fastapi-0.141.1.tar.gz", hash = "sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1", size = 425799, upload-time = "2026-07-29T17:18:05.568Z" }
424
+ wheels = [
425
+ { url = "https://files.pythonhosted.org/packages/cb/03/10388a42375ee7e4ac9b94eb2c5c569c8b5795e377e701c9ac3ad63de890/fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3", size = 131954, upload-time = "2026-07-29T17:18:04.364Z" },
426
+ ]
427
+
401
428
  [[package]]
402
429
  name = "filelock"
403
430
  version = "3.32.3"
@@ -1005,6 +1032,18 @@ wheels = [
1005
1032
  { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" },
1006
1033
  ]
1007
1034
 
1035
+ [[package]]
1036
+ name = "starlette"
1037
+ version = "1.6.0"
1038
+ source = { registry = "https://pypi.org/simple" }
1039
+ dependencies = [
1040
+ { name = "anyio" },
1041
+ ]
1042
+ sdist = { url = "https://files.pythonhosted.org/packages/b5/b4/205b0d5241d934e8add0c38aa924c4f9fb7330834ff11e5444db964ec3f9/starlette-1.6.0.tar.gz", hash = "sha256:d4e3ac5e546444960c710297a3c9fc3f7ebae1b7e963f3d36173b49da535be9b", size = 2716969, upload-time = "2026-08-08T18:27:57.512Z" }
1043
+ wheels = [
1044
+ { url = "https://files.pythonhosted.org/packages/c8/cb/6a6a47d5b464bd08695d254f3da6e7986cc70c9fa5d778eda57538edfe56/starlette-1.6.0-py3-none-any.whl", hash = "sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c", size = 75969, upload-time = "2026-08-08T18:27:56.196Z" },
1045
+ ]
1046
+
1008
1047
  [[package]]
1009
1048
  name = "tomli"
1010
1049
  version = "2.4.1"
@@ -18,7 +18,7 @@
18
18
  "dependencies": {
19
19
  "@biffo/design-tokens": "^0.248.2",
20
20
  "amazon-cognito-identity-js": "^6.3.12",
21
- "next": "^15.5.22",
21
+ "next": "^15.5.25",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
24
24
  },
@@ -33,7 +33,7 @@
33
33
  "@types/react-dom": "^19.0.3",
34
34
  "@vitejs/plugin-react": "^4.7.0",
35
35
  "eslint": "^9.18.0",
36
- "eslint-config-next": "^15.5.22",
36
+ "eslint-config-next": "^15.5.25",
37
37
  "jsdom": "^30.0.1",
38
38
  "prettier": "^3.4.2",
39
39
  "typescript": "^5.7.3",
@@ -24,8 +24,8 @@ importers:
24
24
  specifier: ^6.3.12
25
25
  version: 6.3.18
26
26
  next:
27
- specifier: ^15.5.22
28
- version: 15.5.22(@babel/core@7.29.7)(@playwright/test@1.62.1)(@types/node@22.20.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
27
+ specifier: ^15.5.25
28
+ version: 15.5.25(@babel/core@7.29.7)(@playwright/test@1.62.1)(@types/node@22.20.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
29
29
  react:
30
30
  specifier: ^19.0.0
31
31
  version: 19.2.7
@@ -64,8 +64,8 @@ importers:
64
64
  specifier: ^9.18.0
65
65
  version: 9.39.4
66
66
  eslint-config-next:
67
- specifier: ^15.5.22
68
- version: 15.5.22(eslint@9.39.4)(typescript@5.9.3)
67
+ specifier: ^15.5.25
68
+ version: 15.5.25(eslint@9.39.4)(typescript@5.9.3)
69
69
  jsdom:
70
70
  specifier: ^30.0.1
71
71
  version: 30.0.1
@@ -641,56 +641,56 @@ packages:
641
641
  '@emnapi/core': ^1.7.1
642
642
  '@emnapi/runtime': ^1.7.1
643
643
 
644
- '@next/env@15.5.22':
645
- resolution: {integrity: sha512-O5BlKb3KtsHkvO0gjjV66PuJnAgCtIEIzwkt50HRAHsQkU1t77eksIXSZV84/WMtZJjWrnDUPKHVRi0D62nSAA==}
644
+ '@next/env@15.5.25':
645
+ resolution: {integrity: sha512-42h1lLr07vl4gawALP1hsgRZjHB1xYa58JfUfHwr0f7jG/zhPakh5GHkADHXOC9ZxUvlQFOPIrp7s6qX4DezPQ==}
646
646
 
647
- '@next/eslint-plugin-next@15.5.22':
648
- resolution: {integrity: sha512-SB8PGBmpAiVmOtMOIfObnI2VA3MN3jUGg0iYTh4MO2efxQIV/IL0U8iYOok/8DldCEKARyCOJJix+Vvgzaic/Q==}
647
+ '@next/eslint-plugin-next@15.5.25':
648
+ resolution: {integrity: sha512-dAzOqZQCAOgIq5yQpsuMBZcwxK0AtzGqHMQpxNWYLQlvf79rsP3SDwdGPNQC4ySaMSihOQXMO/VXubQ3JWW+3A==}
649
649
 
650
- '@next/swc-darwin-arm64@15.5.22':
651
- resolution: {integrity: sha512-/VISwtffSg8+fVvBbXdglsvruCsdbBC4dG25iU6xascKVqfQKsj/OtjGnOEkIS7pX5GB9e9/r5QprpicsGL3gw==}
650
+ '@next/swc-darwin-arm64@15.5.25':
651
+ resolution: {integrity: sha512-w+RR0v/QuApnWEjRGm1z6gcObKwGMb5YPA7V3bzBEVSBpMFUXprer0tS27UxjUcEnqbhL7Zuzohej79B6rYmBg==}
652
652
  engines: {node: '>= 10'}
653
653
  cpu: [arm64]
654
654
  os: [darwin]
655
655
 
656
- '@next/swc-darwin-x64@15.5.22':
657
- resolution: {integrity: sha512-NiA9ve8hbiuhG/Q17a2mZDRVxMTtg3rTOgjLnDaLlE+AEPAQlkkuKrfePEbeOrgYmX0U2KGX4EVEn09hXU5GlQ==}
656
+ '@next/swc-darwin-x64@15.5.25':
657
+ resolution: {integrity: sha512-QiGGBUSakt8S1H4Lt9Ehsh6Ja87axiBnQQgysOObvCbI7iUfJnRGntF1P64S4/ijuHFnSB8KLsEddkY3nN26uw==}
658
658
  engines: {node: '>= 10'}
659
659
  cpu: [x64]
660
660
  os: [darwin]
661
661
 
662
- '@next/swc-linux-arm64-gnu@15.5.22':
663
- resolution: {integrity: sha512-vAPa9vltW+UW/KWtjXeSUFgV3wb1x9d/BeyC6WFI6eBpL0D2f70oGwtOp6193mNW3qusrpgBzMQferPf+Zh8Dw==}
662
+ '@next/swc-linux-arm64-gnu@15.5.25':
663
+ resolution: {integrity: sha512-ehLos/66zo0d/mJCU5u96a/VDcr01aaUrX0o/i16UdInxz8qPTCDSxGtjk/Lps1sIr1RJFdiX3hxc0fxJo+cPA==}
664
664
  engines: {node: '>= 10'}
665
665
  cpu: [arm64]
666
666
  os: [linux]
667
667
 
668
- '@next/swc-linux-arm64-musl@15.5.22':
669
- resolution: {integrity: sha512-iknK80pWlNDnkdSr13bd8mMuG3Z2oTxODwsZHvuMY7caMk77+rBLdHVWsy8v2EVa3ZojJ/+wJX5fnq8va6Gv8A==}
668
+ '@next/swc-linux-arm64-musl@15.5.25':
669
+ resolution: {integrity: sha512-ZVMrqLiJ7DiChgmbkQwFtdhAnUkSH/4p7tB29QY+giATb0Q/XGHNRSKAb/B8XGDHRUaA67NepOW5W8u3ZRJBAA==}
670
670
  engines: {node: '>= 10'}
671
671
  cpu: [arm64]
672
672
  os: [linux]
673
673
 
674
- '@next/swc-linux-x64-gnu@15.5.22':
675
- resolution: {integrity: sha512-penuEdkwU2OOAiS+n4LE8T/VIoCfAI01QcLZTJ2xc3+l4Q22L/DzURocmI2LU1b+8BMQoLAP1Sze3uYAZT05Bg==}
674
+ '@next/swc-linux-x64-gnu@15.5.25':
675
+ resolution: {integrity: sha512-UOewtDGkTMJTiODrEdeLZ50yGb59xCZSriNpXkfPMxRRgwDkGc7i8mLWqV5076wEdb+Ca/XN7MhJyM3CupyNyQ==}
676
676
  engines: {node: '>= 10'}
677
677
  cpu: [x64]
678
678
  os: [linux]
679
679
 
680
- '@next/swc-linux-x64-musl@15.5.22':
681
- resolution: {integrity: sha512-ZM0BKJm3FZ+guG6WT6PcyOLtp6paZ5tngcJC/uUKvLW4Y0TQnnVi1+UGdo8Q6Yxp5gaS82pmC1rD/oFlhkWB3g==}
680
+ '@next/swc-linux-x64-musl@15.5.25':
681
+ resolution: {integrity: sha512-UBHwA8AhkCZgtRfU1aJpunuAJe/6gZv6jDESQe4p5MjTb5V0YEeJBWCdNqx15Vj3x+5jmauRfeMJSjfQj9HGFQ==}
682
682
  engines: {node: '>= 10'}
683
683
  cpu: [x64]
684
684
  os: [linux]
685
685
 
686
- '@next/swc-win32-arm64-msvc@15.5.22':
687
- resolution: {integrity: sha512-rY/YaumrZaS0//94BnHLF5VSRp0GFUO4GvXNuoCBb0cGSci96yO+p1JaNL2aq9YZAYv9cuZRziV02x5IQH/wjg==}
686
+ '@next/swc-win32-arm64-msvc@15.5.25':
687
+ resolution: {integrity: sha512-QcFcPRr16djk5IqK5+e8O80eZfgWzIvVBXfitIq0tQ/uc+eyfdoZ0NmKc0cnbIJyfVwREapKuG97YcxWA9gcpA==}
688
688
  engines: {node: '>= 10'}
689
689
  cpu: [arm64]
690
690
  os: [win32]
691
691
 
692
- '@next/swc-win32-x64-msvc@15.5.22':
693
- resolution: {integrity: sha512-s5IA4cyrbR2XK/5NWcu5dp8CfPBiKME+UhvNperia7uQybEgg5+LIhGMiY37WQE4rcI4owsDcU4IVUjLoTuDkA==}
692
+ '@next/swc-win32-x64-msvc@15.5.25':
693
+ resolution: {integrity: sha512-zREeykps3ndWr9egJgvJKqVkkDuaw6Zrrg23cYBos0ygydFkAWYU4+PaPVwXzP1eAYQJe53ShSK45iDM529BOg==}
694
694
  engines: {node: '>= 10'}
695
695
  cpu: [x64]
696
696
  os: [win32]
@@ -1271,9 +1271,6 @@ packages:
1271
1271
  resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
1272
1272
  engines: {node: '>=6'}
1273
1273
 
1274
- caniuse-lite@1.0.30001800:
1275
- resolution: {integrity: sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==}
1276
-
1277
1274
  caniuse-lite@1.0.30001810:
1278
1275
  resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==}
1279
1276
 
@@ -1446,8 +1443,8 @@ packages:
1446
1443
  resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
1447
1444
  engines: {node: '>=10'}
1448
1445
 
1449
- eslint-config-next@15.5.22:
1450
- resolution: {integrity: sha512-HhZsB3dvBsYwQpnymZm/Ps9NVaSYiSzDSXkR02CjyOcQutCuea3cF7NxE8i8Drs2+Dgw95WUzK2CMBIHd5YbMw==}
1446
+ eslint-config-next@15.5.25:
1447
+ resolution: {integrity: sha512-hB1oClZzRO3vMAeMmBMch0FHNJ4irH74LvQWDEMHkJtG/Us9+SAsg1f5Pvcw9jz4tc8wSGnvbmF050BRAAIbBg==}
1451
1448
  peerDependencies:
1452
1449
  eslint: ^7.23.0 || ^8.0.0 || ^9.0.0
1453
1450
  typescript: '>=3.3.1'
@@ -1539,6 +1536,7 @@ packages:
1539
1536
  eslint@9.39.4:
1540
1537
  resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==}
1541
1538
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
1539
+ deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
1542
1540
  hasBin: true
1543
1541
  peerDependencies:
1544
1542
  jiti: '*'
@@ -2001,8 +1999,8 @@ packages:
2001
1999
  natural-compare@1.4.0:
2002
2000
  resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
2003
2001
 
2004
- next@15.5.22:
2005
- resolution: {integrity: sha512-mrtal1sRxO4YrlDS98sDuIvGZivKbFix8w7oAL9ZynfOgc3cADQOQgvwtMooc18Qr8bKzvQAcHwHZ0mbJ7zcfQ==}
2002
+ next@15.5.25:
2003
+ resolution: {integrity: sha512-OMWNulIIqKM2ykvC2qMjIt0IoavB4UB2SCs4iXJ6z6847FvyH8jBmBWcvrF5iuhTu8Przh20Fo/aoszIdqx4PA==}
2006
2004
  engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
2007
2005
  hasBin: true
2008
2006
  peerDependencies:
@@ -3109,34 +3107,34 @@ snapshots:
3109
3107
  '@tybys/wasm-util': 0.10.3
3110
3108
  optional: true
3111
3109
 
3112
- '@next/env@15.5.22': {}
3110
+ '@next/env@15.5.25': {}
3113
3111
 
3114
- '@next/eslint-plugin-next@15.5.22':
3112
+ '@next/eslint-plugin-next@15.5.25':
3115
3113
  dependencies:
3116
3114
  fast-glob: 3.3.1
3117
3115
 
3118
- '@next/swc-darwin-arm64@15.5.22':
3116
+ '@next/swc-darwin-arm64@15.5.25':
3119
3117
  optional: true
3120
3118
 
3121
- '@next/swc-darwin-x64@15.5.22':
3119
+ '@next/swc-darwin-x64@15.5.25':
3122
3120
  optional: true
3123
3121
 
3124
- '@next/swc-linux-arm64-gnu@15.5.22':
3122
+ '@next/swc-linux-arm64-gnu@15.5.25':
3125
3123
  optional: true
3126
3124
 
3127
- '@next/swc-linux-arm64-musl@15.5.22':
3125
+ '@next/swc-linux-arm64-musl@15.5.25':
3128
3126
  optional: true
3129
3127
 
3130
- '@next/swc-linux-x64-gnu@15.5.22':
3128
+ '@next/swc-linux-x64-gnu@15.5.25':
3131
3129
  optional: true
3132
3130
 
3133
- '@next/swc-linux-x64-musl@15.5.22':
3131
+ '@next/swc-linux-x64-musl@15.5.25':
3134
3132
  optional: true
3135
3133
 
3136
- '@next/swc-win32-arm64-msvc@15.5.22':
3134
+ '@next/swc-win32-arm64-msvc@15.5.25':
3137
3135
  optional: true
3138
3136
 
3139
- '@next/swc-win32-x64-msvc@15.5.22':
3137
+ '@next/swc-win32-x64-msvc@15.5.25':
3140
3138
  optional: true
3141
3139
 
3142
3140
  '@nodelib/fs.scandir@2.1.5':
@@ -3716,8 +3714,6 @@ snapshots:
3716
3714
 
3717
3715
  callsites@3.1.0: {}
3718
3716
 
3719
- caniuse-lite@1.0.30001800: {}
3720
-
3721
3717
  caniuse-lite@1.0.30001810: {}
3722
3718
 
3723
3719
  chai@6.2.2: {}
@@ -3974,16 +3970,16 @@ snapshots:
3974
3970
 
3975
3971
  escape-string-regexp@4.0.0: {}
3976
3972
 
3977
- eslint-config-next@15.5.22(eslint@9.39.4)(typescript@5.9.3):
3973
+ eslint-config-next@15.5.25(eslint@9.39.4)(typescript@5.9.3):
3978
3974
  dependencies:
3979
- '@next/eslint-plugin-next': 15.5.22
3975
+ '@next/eslint-plugin-next': 15.5.25
3980
3976
  '@rushstack/eslint-patch': 1.16.1
3981
3977
  '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)
3982
3978
  '@typescript-eslint/parser': 8.62.1(eslint@9.39.4)(typescript@5.9.3)
3983
3979
  eslint: 9.39.4
3984
3980
  eslint-import-resolver-node: 0.3.10
3985
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4))(eslint@9.39.4)
3986
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4))(eslint@9.39.4))(eslint@9.39.4)
3981
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4)
3982
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4)
3987
3983
  eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4)
3988
3984
  eslint-plugin-react: 7.37.5(eslint@9.39.4)
3989
3985
  eslint-plugin-react-hooks: 5.2.0(eslint@9.39.4)
@@ -4002,7 +3998,7 @@ snapshots:
4002
3998
  transitivePeerDependencies:
4003
3999
  - supports-color
4004
4000
 
4005
- eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4))(eslint@9.39.4):
4001
+ eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4):
4006
4002
  dependencies:
4007
4003
  '@nolyfill/is-core-module': 1.0.39
4008
4004
  debug: 4.4.3
@@ -4013,22 +4009,22 @@ snapshots:
4013
4009
  tinyglobby: 0.2.17
4014
4010
  unrs-resolver: 1.12.2
4015
4011
  optionalDependencies:
4016
- eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4))(eslint@9.39.4))(eslint@9.39.4)
4012
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4)
4017
4013
  transitivePeerDependencies:
4018
4014
  - supports-color
4019
4015
 
4020
- eslint-module-utils@2.14.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4))(eslint@9.39.4))(eslint@9.39.4):
4016
+ eslint-module-utils@2.14.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4):
4021
4017
  dependencies:
4022
4018
  debug: 3.2.7
4023
4019
  optionalDependencies:
4024
4020
  '@typescript-eslint/parser': 8.62.1(eslint@9.39.4)(typescript@5.9.3)
4025
4021
  eslint: 9.39.4
4026
4022
  eslint-import-resolver-node: 0.3.10
4027
- eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4))(eslint@9.39.4)
4023
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4)
4028
4024
  transitivePeerDependencies:
4029
4025
  - supports-color
4030
4026
 
4031
- eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4))(eslint@9.39.4))(eslint@9.39.4):
4027
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4):
4032
4028
  dependencies:
4033
4029
  '@rtsao/scc': 1.1.0
4034
4030
  array-includes: 3.1.9
@@ -4039,7 +4035,7 @@ snapshots:
4039
4035
  doctrine: 2.1.0
4040
4036
  eslint: 9.39.4
4041
4037
  eslint-import-resolver-node: 0.3.10
4042
- eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4))(eslint@9.39.4))(eslint@9.39.4)
4038
+ eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.62.1(eslint@9.39.4)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4)
4043
4039
  hasown: 2.0.4
4044
4040
  is-core-module: 2.16.2
4045
4041
  is-glob: 4.0.3
@@ -4607,24 +4603,24 @@ snapshots:
4607
4603
 
4608
4604
  natural-compare@1.4.0: {}
4609
4605
 
4610
- next@15.5.22(@babel/core@7.29.7)(@playwright/test@1.62.1)(@types/node@22.20.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
4606
+ next@15.5.25(@babel/core@7.29.7)(@playwright/test@1.62.1)(@types/node@22.20.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
4611
4607
  dependencies:
4612
- '@next/env': 15.5.22
4608
+ '@next/env': 15.5.25
4613
4609
  '@swc/helpers': 0.5.15
4614
- caniuse-lite: 1.0.30001800
4610
+ caniuse-lite: 1.0.30001810
4615
4611
  postcss: 8.5.23
4616
4612
  react: 19.2.7
4617
4613
  react-dom: 19.2.7(react@19.2.7)
4618
4614
  styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.7)
4619
4615
  optionalDependencies:
4620
- '@next/swc-darwin-arm64': 15.5.22
4621
- '@next/swc-darwin-x64': 15.5.22
4622
- '@next/swc-linux-arm64-gnu': 15.5.22
4623
- '@next/swc-linux-arm64-musl': 15.5.22
4624
- '@next/swc-linux-x64-gnu': 15.5.22
4625
- '@next/swc-linux-x64-musl': 15.5.22
4626
- '@next/swc-win32-arm64-msvc': 15.5.22
4627
- '@next/swc-win32-x64-msvc': 15.5.22
4616
+ '@next/swc-darwin-arm64': 15.5.25
4617
+ '@next/swc-darwin-x64': 15.5.25
4618
+ '@next/swc-linux-arm64-gnu': 15.5.25
4619
+ '@next/swc-linux-arm64-musl': 15.5.25
4620
+ '@next/swc-linux-x64-gnu': 15.5.25
4621
+ '@next/swc-linux-x64-musl': 15.5.25
4622
+ '@next/swc-win32-arm64-msvc': 15.5.25
4623
+ '@next/swc-win32-x64-msvc': 15.5.25
4628
4624
  '@playwright/test': 1.62.1
4629
4625
  sharp: 0.35.3(@types/node@22.20.0)
4630
4626
  transitivePeerDependencies:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.313.3",
3
+ "version": "0.314.0",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",