@produtype/core 0.70.0 → 0.72.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.
package/README.md
CHANGED
|
@@ -174,7 +174,7 @@ prodkit plan ../my-app --output prodkit-plan.md
|
|
|
174
174
|
|
|
175
175
|
_Generated from the analyzer itself — run `npm run docs:stacks` after changing a detector._
|
|
176
176
|
|
|
177
|
-
- **Backend:** Express, Next.js, NestJS, Fastify, Hono, Elysia, Koa, AdonisJS, SvelteKit, Remix, Nuxt, Nitro, Astro, Django, Flask, FastAPI, aiohttp, Litestar, Sanic, Tornado, Starlette, Streamlit, Gradio, Dash, Chainlit, Gin, Echo, Fiber, chi, Gorilla, Beego, Go, Axum, Actix Web, Rocket, Warp, Tide, Poem, Salvo, Tower HTTP, Spring Boot, Quarkus, Micronaut, Ktor, Javalin, Vert.x, Dropwizard, Helidon, Rails, Sinatra, Hanami, Roda, Grape, Ruby, Laravel, Symfony, Slim, CodeIgniter, CakePHP, Yii, PHP, ASP.NET Core, .NET
|
|
177
|
+
- **Backend:** Express, Next.js, NestJS, Fastify, Hono, Elysia, Koa, AdonisJS, SvelteKit, Remix, Nuxt, Nitro, Astro, Django, Flask, FastAPI, aiohttp, Litestar, Sanic, Tornado, Starlette, Streamlit, Gradio, Dash, Chainlit, Gin, Echo, Fiber, chi, Gorilla, Beego, Go, Axum, Actix Web, Rocket, Warp, Tide, Poem, Salvo, Tower HTTP, Hyper, Spring Boot, Quarkus, Micronaut, Ktor, Javalin, Vert.x, Dropwizard, Helidon, Rails, Sinatra, Hanami, Roda, Grape, Ruby, Laravel, Symfony, Slim, CodeIgniter, CakePHP, Yii, PHP, ASP.NET Core, .NET
|
|
178
178
|
- **Frontend:** React, Vite, Vue, Nuxt, Svelte, Angular, Astro, Solid, Qwik, Preact, Remix, htmx, Tailwind CSS, Electron
|
|
179
179
|
- **Mobile:** Flutter, React Native, iOS (native), Android (native)
|
|
180
180
|
- **Databases:** Postgres, MySQL, SQLite, SQL Server, MongoDB, Redis, Firestore, DynamoDB, Convex
|
|
@@ -59,6 +59,8 @@ exports.RUST_BACKEND_FRAMEWORKS = [
|
|
|
59
59
|
['poem', ['poem']],
|
|
60
60
|
['salvo', ['salvo']],
|
|
61
61
|
['tower-http', ['tower-http']],
|
|
62
|
+
// What a Rust service uses when it uses no framework: the HTTP layer itself.
|
|
63
|
+
['hyper', ['hyper']],
|
|
62
64
|
];
|
|
63
65
|
/**
|
|
64
66
|
* JVM frameworks, read from pom.xml and build.gradle alike.
|
|
@@ -211,6 +213,7 @@ const LABELS = {
|
|
|
211
213
|
poem: 'Poem',
|
|
212
214
|
salvo: 'Salvo',
|
|
213
215
|
'tower-http': 'Tower HTTP',
|
|
216
|
+
hyper: 'Hyper',
|
|
214
217
|
rust: 'Rust',
|
|
215
218
|
rails: 'Rails',
|
|
216
219
|
sinatra: 'Sinatra',
|
|
@@ -109,24 +109,28 @@ async function detectBackend(ctx) {
|
|
|
109
109
|
evidence.push({ type: 'dependency', value: dep });
|
|
110
110
|
}
|
|
111
111
|
/** Rust, read from Cargo.toml. */
|
|
112
|
-
let namedRustFramework = false;
|
|
113
112
|
for (const [framework, deps] of catalogue_1.RUST_BACKEND_FRAMEWORKS) {
|
|
114
113
|
const hits = (0, detectContext_1.hasAnyRuntimeRustDep)(ctx, deps);
|
|
115
114
|
if (!hits.length)
|
|
116
115
|
continue;
|
|
117
|
-
namedRustFramework = true;
|
|
118
116
|
frameworks.push(framework);
|
|
119
117
|
for (const dep of hits)
|
|
120
118
|
evidence.push({ type: 'dependency', value: dep });
|
|
121
119
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
120
|
+
/**
|
|
121
|
+
* No fallback for Rust, and removing it is a correction of my own over-reach.
|
|
122
|
+
*
|
|
123
|
+
* This was written to mirror Go's, whose justification is that plenty of production
|
|
124
|
+
* services use `net/http` and nothing else. Rust's standard library has no HTTP
|
|
125
|
+
* server at all, so the mirror does not hold: a crate with no web framework is
|
|
126
|
+
* overwhelmingly a library, a parser or a command-line tool.
|
|
127
|
+
*
|
|
128
|
+
* ruff is the measurement. A linter with `Cargo.toml` at its root reported
|
|
129
|
+
* `backend: rust`, and "a backend disqualifies a library" then profiled it as a
|
|
130
|
+
* client application — so teaching this analyzer to read Rust made it worse at
|
|
131
|
+
* reading the best-known Rust project in the corpus. `hyper` joins the framework
|
|
132
|
+
* list instead: it is what a Rust service uses when it uses no framework.
|
|
133
|
+
*/
|
|
130
134
|
/** Ruby, read from the Gemfile. */
|
|
131
135
|
let namedRubyFramework = false;
|
|
132
136
|
for (const [framework, deps] of catalogue_1.RUBY_BACKEND_FRAMEWORKS) {
|
|
@@ -138,7 +142,23 @@ async function detectBackend(ctx) {
|
|
|
138
142
|
for (const dep of hits)
|
|
139
143
|
evidence.push({ type: 'dependency', value: dep });
|
|
140
144
|
}
|
|
141
|
-
|
|
145
|
+
/**
|
|
146
|
+
* A Gemfile is not a Ruby backend on its own.
|
|
147
|
+
*
|
|
148
|
+
* WordPress-iOS and BlueWallet both reported `backend: ruby`. Neither ships a Ruby
|
|
149
|
+
* server: both keep a Gemfile for fastlane and CocoaPods, which is how the iOS
|
|
150
|
+
* world runs its build. WordPress-iOS has 23 Ruby files among 2675 — under one per
|
|
151
|
+
* cent — and the backend it did not have then excluded it from the mobile profile,
|
|
152
|
+
* so an iOS application with 2649 Swift files was judged as a B2B SaaS and told it
|
|
153
|
+
* needed a health endpoint.
|
|
154
|
+
*
|
|
155
|
+
* The same share test Go already uses, and the reason Rust's fallback was removed
|
|
156
|
+
* outright a release ago: a language has to be a real part of what is written here
|
|
157
|
+
* before it names the backend.
|
|
158
|
+
*/
|
|
159
|
+
if (!namedRubyFramework
|
|
160
|
+
&& languageShare(ctx, /\.rb$/) >= MINIMUM_BACKEND_SHARE
|
|
161
|
+
&& ctx.files.all.some((f) => /(^|\/)Gemfile$/.test(f))) {
|
|
142
162
|
frameworks.push('ruby');
|
|
143
163
|
evidence.push({ type: 'note', value: 'a Gemfile with no web framework in it' });
|
|
144
164
|
}
|
|
@@ -164,7 +164,23 @@ const RULES = [
|
|
|
164
164
|
* Half is the line, and it sits in the gap between those two rather than in the
|
|
165
165
|
* middle of a distribution.
|
|
166
166
|
*/
|
|
167
|
-
|
|
167
|
+
/**
|
|
168
|
+
* A tenant word in a client model is not a tenant boundary.
|
|
169
|
+
*
|
|
170
|
+
* WordPress-iOS carries `organizationID` in `RemoteBlog.swift` and
|
|
171
|
+
* `RemoteReaderSiteInfo.swift` — data classes deserialised from WordPress.com's
|
|
172
|
+
* JSON. The application consumes an organization; it does not host one, and it
|
|
173
|
+
* could not: enforcing a boundary between tenants takes a server, and this is
|
|
174
|
+
* 2649 Swift files with none.
|
|
175
|
+
*
|
|
176
|
+
* It was excluded from the mobile profile on that word and judged as a B2B SaaS,
|
|
177
|
+
* which asked it for a health endpoint, security headers and a GDPR export
|
|
178
|
+
* route. Tenancy still disqualifies a phone application that ships a server
|
|
179
|
+
* alongside it, because then the boundary is the repository's to keep.
|
|
180
|
+
*/
|
|
181
|
+
admissible: (f) => f.mobilePlatforms.length > 0
|
|
182
|
+
&& f.mobileShare >= 0.5
|
|
183
|
+
&& !(f.tenancy && f.productBackend),
|
|
168
184
|
signals: [
|
|
169
185
|
{ identifies: true, label: 'a mobile project in the repository', weight: 5, holds: (f) => f.mobilePlatforms.length > 0 },
|
|
170
186
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produtype/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.0",
|
|
4
4
|
"description": "Deterministic CLI and library that analyzes a web application repository and reports how far it is from production-ready for the kind of product it is meant to be.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|