@niroai/niro 0.17.0 → 1.0.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.
Files changed (177) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +123 -302
  3. package/bin/niro.js +12 -1
  4. package/docs/agent-antigravity.md +37 -0
  5. package/docs/agent-claude-code.md +48 -0
  6. package/docs/agent-claude-desktop.md +47 -0
  7. package/docs/agent-cline.md +42 -0
  8. package/docs/agent-codex.md +45 -0
  9. package/docs/agent-copilot-cli.md +42 -0
  10. package/docs/agent-cursor.md +44 -0
  11. package/docs/agent-factory.md +42 -0
  12. package/docs/agent-gemini-cli.md +42 -0
  13. package/docs/agent-goose.md +42 -0
  14. package/docs/agent-kiro.md +37 -0
  15. package/docs/agent-opencode.md +37 -0
  16. package/docs/agent-vscode.md +45 -0
  17. package/docs/agent-windsurf.md +38 -0
  18. package/docs/answer-shapes.md +69 -0
  19. package/docs/builds-and-updates.md +68 -0
  20. package/docs/cloud-README.md +372 -0
  21. package/docs/configuring-a-folder.md +51 -0
  22. package/docs/connect-ci.md +24 -0
  23. package/docs/connect-overview.md +20 -0
  24. package/docs/connect-review-bots.md +17 -0
  25. package/docs/connect-team-server.md +12 -0
  26. package/docs/connect-terminal.md +49 -0
  27. package/docs/connect-web-agents.md +9 -0
  28. package/docs/connect-your-ai-assistant.md +59 -175
  29. package/docs/coverage-audit-and-consent.md +51 -0
  30. package/docs/evaluate-what-niro-did.md +271 -0
  31. package/docs/faq-assistants-and-mcp.md +122 -0
  32. package/docs/faq-feedback-and-receipts.md +33 -0
  33. package/docs/faq-indexing.md +127 -0
  34. package/docs/faq-install-and-setup.md +103 -0
  35. package/docs/faq-licence-and-plans.md +86 -0
  36. package/docs/faq-login-and-account.md +69 -0
  37. package/docs/faq-offline.md +34 -0
  38. package/docs/faq-performance.md +33 -0
  39. package/docs/faq-privacy-and-security.md +113 -0
  40. package/docs/faq-uninstall.md +47 -0
  41. package/docs/faq-updates.md +59 -0
  42. package/docs/faq-worktrees-and-branches.md +59 -0
  43. package/docs/faq-wrong-or-missing-answers.md +275 -0
  44. package/docs/hosted-only.md +33 -0
  45. package/docs/how-the-graph-works.md +47 -0
  46. package/docs/index.md +157 -0
  47. package/docs/licence-and-plans.md +72 -0
  48. package/docs/lifecycle-debug.md +48 -0
  49. package/docs/lifecycle-onboarding.md +32 -0
  50. package/docs/lifecycle-overview.md +77 -0
  51. package/docs/lifecycle-plan-and-design.md +44 -0
  52. package/docs/lifecycle-review.md +30 -0
  53. package/docs/lifecycle-security-and-privacy.md +35 -0
  54. package/docs/lifecycle-test.md +28 -0
  55. package/docs/lifecycle-write-code.md +35 -0
  56. package/docs/llms-full.txt +8015 -0
  57. package/docs/llms.txt +126 -0
  58. package/docs/playbook-add-a-feature.md +108 -0
  59. package/docs/playbook-change-safely.md +93 -0
  60. package/docs/playbook-data-and-events.md +107 -0
  61. package/docs/playbook-debug-after-a-change.md +96 -0
  62. package/docs/playbook-debug-config.md +95 -0
  63. package/docs/playbook-debug-exception.md +124 -0
  64. package/docs/playbook-debug-failing-service-call.md +119 -0
  65. package/docs/playbook-debug-slow-path.md +80 -0
  66. package/docs/playbook-debug-wrong-value.md +137 -0
  67. package/docs/playbook-onboard.md +95 -0
  68. package/docs/playbook-review-a-change.md +72 -0
  69. package/docs/playbook-test-a-change.md +73 -0
  70. package/docs/reference-cli.md +113 -0
  71. package/docs/reference-config-files.md +42 -0
  72. package/docs/reference-engine-commands.md +56 -0
  73. package/docs/reference-environment-variables.md +89 -0
  74. package/docs/reference-exit-codes.md +67 -0
  75. package/docs/reference-limits.md +75 -0
  76. package/docs/reference-mcp-tools.md +326 -0
  77. package/docs/reference-messages.md +180 -0
  78. package/docs/secrets-and-masking.md +48 -0
  79. package/docs/supported-c.md +130 -0
  80. package/docs/supported-cpp.md +157 -0
  81. package/docs/supported-csharp.md +211 -0
  82. package/docs/supported-go.md +258 -0
  83. package/docs/supported-java.md +226 -0
  84. package/docs/supported-javascript.md +223 -0
  85. package/docs/supported-kotlin.md +124 -0
  86. package/docs/supported-overview.md +89 -0
  87. package/docs/supported-php.md +157 -0
  88. package/docs/supported-python.md +224 -0
  89. package/docs/supported-ruby.md +162 -0
  90. package/docs/supported-rust.md +150 -0
  91. package/docs/supported-swift.md +133 -0
  92. package/docs/supported-typescript.md +220 -0
  93. package/docs/tokens-speed-and-accuracy.md +99 -0
  94. package/docs/troubleshooting.md +42 -0
  95. package/docs/uninstall.md +74 -0
  96. package/docs/what-leaves-your-machine.md +71 -0
  97. package/docs/where-things-are-stored.md +59 -0
  98. package/docs/worktrees-and-branches.md +55 -0
  99. package/package.json +5 -3
  100. package/src/commands/add-repo.js +5 -0
  101. package/src/commands/auditSend.js +67 -0
  102. package/src/commands/build.js +6 -0
  103. package/src/commands/docsSend.js +73 -0
  104. package/src/commands/engine.js +120 -0
  105. package/src/commands/engineRoutes.js +105 -0
  106. package/src/commands/guide.js +254 -0
  107. package/src/commands/init.js +6 -2
  108. package/src/commands/initLocal.js +96 -0
  109. package/src/commands/journalSend.js +47 -0
  110. package/src/commands/login.js +89 -12
  111. package/src/commands/mcp.js +22 -6
  112. package/src/commands/plan.js +99 -0
  113. package/src/commands/repoPush.js +33 -0
  114. package/src/commands/requestIntegration.js +38 -0
  115. package/src/commands/share.js +56 -0
  116. package/src/commands/start.js +451 -371
  117. package/src/commands/status.js +57 -0
  118. package/src/commands/takeover.js +6 -0
  119. package/src/commands/uninstall.js +180 -0
  120. package/src/commands/upgrade.js +38 -8
  121. package/src/commands/watch.js +420 -6
  122. package/src/index.js +174 -3
  123. package/src/lib/agentApi.js +32 -1
  124. package/src/lib/api.js +20 -6
  125. package/src/lib/auditUpload.js +293 -0
  126. package/src/lib/branchDrift.js +149 -0
  127. package/src/lib/choose.js +116 -0
  128. package/src/lib/cloudGuard.js +74 -0
  129. package/src/lib/credentials.js +2 -2
  130. package/src/lib/docsMarked.js +55 -0
  131. package/src/lib/docsState.js +213 -0
  132. package/src/lib/docsUpload.js +481 -0
  133. package/src/lib/engine.js +619 -0
  134. package/src/lib/folderSync.js +2 -1
  135. package/src/lib/graphCheck.js +40 -0
  136. package/src/lib/guide.js +293 -0
  137. package/src/lib/guideSearch.js +252 -0
  138. package/src/lib/integrationRequest.js +89 -0
  139. package/src/lib/journalUpload.js +147 -0
  140. package/src/lib/languages.js +129 -0
  141. package/src/lib/lease.js +375 -0
  142. package/src/lib/localBuild.js +285 -0
  143. package/src/lib/localEngine.js +210 -0
  144. package/src/lib/localOnboard.js +124 -0
  145. package/src/lib/localWorktrees.js +243 -0
  146. package/src/lib/machineCheck.js +58 -0
  147. package/src/lib/mcpClients.js +10 -1
  148. package/src/lib/migrationNotice.js +118 -0
  149. package/src/lib/modelFetch.js +153 -0
  150. package/src/lib/nestedOutput.js +23 -9
  151. package/src/lib/onboardingLog.js +34 -0
  152. package/src/lib/planUsage.js +165 -0
  153. package/src/lib/progressBar.js +46 -0
  154. package/src/lib/prompt.js +1 -1
  155. package/src/lib/redact.js +155 -0
  156. package/src/lib/release.js +33 -0
  157. package/src/lib/repoPush.js +495 -0
  158. package/src/lib/secret.js +1 -1
  159. package/src/lib/service.js +372 -63
  160. package/src/lib/shareCode.js +367 -0
  161. package/src/lib/shareExclusions.js +114 -0
  162. package/src/lib/shareFiles.js +129 -0
  163. package/src/lib/spinner.js +9 -5
  164. package/src/lib/watchDaemon.js +33 -10
  165. package/src/lib/watchState.js +12 -1
  166. package/src/lib/windowsPaths.js +67 -0
  167. package/src/mcp/lineCheck.js +382 -0
  168. package/src/mcp/lineName.js +92 -0
  169. package/src/mcp/localBackend.js +751 -0
  170. package/src/mcp/localMode.js +383 -0
  171. package/src/mcp/localSkill.js +189 -0
  172. package/src/mcp/server.js +610 -79
  173. package/src/mcp/setup.js +519 -131
  174. package/src/mcp/shellSearchHook.js +509 -0
  175. package/src/mcp/teardown.js +173 -91
  176. package/src/mcp/usingNiro.js +39 -0
  177. package/docs/working-on-branches.md +0 -162
@@ -0,0 +1,223 @@
1
+ # Supported in JavaScript
2
+
3
+ <!-- GENERATED by scripts/gen-supported-docs.js. Do not edit by hand: run the script again. -->
4
+ <!-- source: catalog/javascript.json; catalog/javascript.structure.json -->
5
+ <!-- measured: coverage corpus 1bb4ebb, niro-local 2b30d4fd -->
6
+
7
+ This page lists the JavaScript libraries, frameworks and build tools Niro was tested against, and whether it recognised each one.
8
+
9
+ **What the marks mean.**
10
+
11
+ - **Supported:** Niro recognised every way of using it that the test code tries.
12
+ - **Partly:** Niro recognised some of those ways, or recognised one only in part.
13
+ - **Not yet:** Niro did not recognise it in the test code.
14
+
15
+ The test code is made up for the purpose: one small service per library, with the names invented. So "supported" means Niro recognised it there. It is good evidence, but it is not a promise about every codebase. If Niro misses something in your code, the coverage audit reports the gap on its own (see [coverage-audit-and-consent.md](coverage-audit-and-consent.md)).
16
+ <!-- source: niro-coverage-corpus README.md ("Synthetic test code ... All names are made up"); catalog status merged pairs one to one with measured found -->
17
+
18
+ The test catalog also holds 8 look-alike cases for JavaScript: code that Niro must NOT take for a route, a call or a store. They test that Niro ignores the look-alike, so they are left out of this page and its counts.
19
+ <!-- source: catalog/javascript.json and catalog/javascript.structure.json, rows marked as traps (scripts/gen-supported-docs.js isTrap) -->
20
+
21
+ ## Web frameworks and HTTP servers (the routes your code serves)
22
+
23
+ 23 supported, 3 partly, 4 not yet.
24
+
25
+ | Name | Status | Ways of using it that were tested |
26
+ |---|---|---|
27
+ | @apollo/server | supported | yes: app.use('/graphql', expressMiddleware(server)) |
28
+ | @azure/functions (v3 programming model) | supported | yes: <FunctionName>/function.json httpTrigger route + methods, code in index.js as module.exports = async function (context, req)<br>yes: function.json httpTrigger with no route and no methods: every verb at /api/<FunctionName> |
29
+ | @fastify/autoload | supported | yes: the fastify-cli generated layout: app.js registers AutoLoad for plugins/ and routes/; routes/<folder>/index.js gets the folder prefix |
30
+ | @feathersjs/feathers | not yet | no: app.use('/messages', new Service()) gives REST find/get/create/remove |
31
+ | @google-cloud/functions-framework | supported | yes: functions.http('name', handler) |
32
+ | @hapi/hapi | partly | yes: server.route({ method, path, handler })<br>yes: server.route([ {...}, {...} ]) with an array of route objects<br>no: plugin registered with { routes: { prefix } } |
33
+ | @koa/router | supported | yes: const router = new Router(); router.get(path, h)<br>yes: new Router({ prefix }) in its own file, then router.get/post<br>yes: a Router held in a variable not called router (audit), mounted with router.use(path, audit.routes()) |
34
+ | @remix-run/node (Remix v2) | supported | yes: app/routes/<dotted>.js resource route with loader and action, in JavaScript |
35
+ | @sveltejs/kit | supported | yes: src/routes/<path>/+server.js in JavaScript with JSDoc types, a [param], a group and a [...rest] |
36
+ | @tinyhttp/app | supported | yes: new App().get().post() (ES module) |
37
+ | Cloudflare Pages Functions | supported | yes: functions/<path>.js exporting function onRequestGet / onRequestPost ...<br>yes: functions/<dir>/[param].js and [[param]].js |
38
+ | connect | not yet | no: connect().use(path, handler): any verb |
39
+ | express | supported | yes: app.get/app.post(path, handler) on the app object<br>yes: Router() in its own file, module.exports, mounted with app.use(prefix, router)<br>yes: app.route(path).put(h).delete(h) chain<br>yes: router mounted inside a router: app.use("/v2", v2); v2.use("/promos", promos) |
40
+ | express-file-routing | supported | yes: await createRouter(app) binding routes/ directly to the app, in JavaScript (ESM) |
41
+ | fastify | partly | yes: fastify.get(path, handler)<br>yes: fastify.route({ method, url, handler })<br>yes: fastify.route({ method: [PUT, PATCH], url })<br>no: plugin in another file registered with { prefix } |
42
+ | firebase-functions | partly | yes: exports.name = functions.https.onRequest(handler): path is the export name<br>yes: v2 onRequest(options, handler)<br>partly: Express app passed to onRequest: routes under the export name |
43
+ | Gatsby Functions (gatsby) | supported | yes: src/api/<path>.js default export handler(req, res) |
44
+ | koa-router | supported | yes: legacy koa-router package, router.get and router.del |
45
+ | micro + microrouter | not yet | no: module.exports = router(get(path, h), post(path, h)) |
46
+ | Netlify Edge Functions | supported | yes: netlify/edge-functions/<name>.js with export const config = { path } |
47
+ | Netlify Functions (@netlify/functions) | supported | yes: netlify/functions/<name>.mjs default export with export const config = { path } |
48
+ | Netlify Functions (@netlify/functions), Lambda-compatible | supported | yes: netlify/functions/<name>.js with exports.handler = async (event, context): /.netlify/functions/<name> |
49
+ | next | supported | yes: pages/api/auth/[...nextauth].js: export default NextAuth(options), a catch-all API route in JavaScript<br>yes: pages/api/<path>/[[...slug]].js beside a fixed file in the same folder (fixed routes win)<br>yes: app/<path>/route.js in JavaScript, with a dynamic segment and a route group |
50
+ | next (pages router) | supported | yes: pages/api/<path>.js default export handler, verb from req.method<br>yes: pages/api/catalog/[id].js dynamic segment |
51
+ | polka | supported | yes: polka().get(...).post(...) chained on the constructor call |
52
+ | restify | supported | yes: server.get/post/del(path, handler) |
53
+ | sails | not yet | no: config/routes.js object map 'VERB /path': 'Controller.action' |
54
+ | serverless framework (AWS Lambda) | supported | yes: serverless.yml functions.<name>.events http/httpApi path+method, handler file.export |
55
+ | Vercel Functions (@vercel/node), no framework | supported | yes: api/<name>.js with module.exports = (req, res) => {}: every verb<br>yes: api/<name>.mjs exporting GET, POST: the export name is the verb<br>yes: api/<dir>/[param].js: one segment, value in req.query |
56
+ | ws | supported | yes: new WebSocketServer({ port, path }); wss.on('connection') |
57
+
58
+ ## The language's own HTTP server
59
+
60
+ 0 supported, 0 partly, 5 not yet.
61
+
62
+ | Name | Status | Ways of using it that were tested |
63
+ |---|---|---|
64
+ | Bun.serve | not yet | no: Bun.serve({ fetch(req) }) with new URL(req.url).pathname comparison<br>no: Bun.serve({ routes: { "/path/:id": { GET, DELETE } } }) route table |
65
+ | node:http | not yet | no: http.createServer(handler) with if/else on req.method and new URL(req.url).pathname<br>no: switch over `${req.method} ${req.url}` with one case per route<br>no: new http.Server() then server.on('request', handler) |
66
+ | node:http2 | not yet | no: http2.createSecureServer().on('stream') reading headers[':path'] |
67
+ | node:https | not yet | no: https.createServer(tlsOptions, handler) with req.url comparison |
68
+ | node:net | not yet | no: net.createServer(socket => ...).listen(port): a raw TCP service (not HTTP) |
69
+
70
+ ## HTTP client libraries (the calls your code makes to other services)
71
+
72
+ 14 supported, 5 partly, 2 not yet.
73
+
74
+ | Name | Status | Ways of using it that were tested |
75
+ |---|---|---|
76
+ | @apollo/client | supported | yes: useQuery(gql`...`) and useMutation in JSX, uri set on ApolloClient in another module |
77
+ | axios | partly | no: axios.create({ baseURL: import.meta.env.X ?? default }) exported from one module, api.get(path) in another<br>yes: axios.get(`${BASE}/path`)<br>yes: axios.post(literalUrl, body)<br>no: axios({ method, url, data }) called as a function<br>yes: const api = axios.create({ baseURL }); api.get(path)<br>partly: axios.request({ url, method })<br>partly: this.http = axios.create({ baseURL: env }) in a constructor; this.http.get(path) |
78
+ | axios (any client) | supported | yes: the call sits in a const arrow function: const f = () => axios.get(url)<br>yes: the call sits in a method of an object literal<br>yes: the call sits in exports.name = () => ... |
79
+ | bent | not yet | no: bent(base, 'json') returns a function called with the path |
80
+ | cross-fetch | supported | yes: crossFetch(url) |
81
+ | got | supported | yes: got(url).json() called as a function<br>yes: got.post(url, { json })<br>yes: got.extend({ prefixUrl }) then client.get(relative) |
82
+ | graphql-request | partly | no: request(endpoint, query)<br>yes: new GraphQLClient(endpoint).request(query) |
83
+ | isomorphic-fetch | supported | yes: require('isomorphic-fetch') then the global fetch(url) |
84
+ | make-fetch-happen | not yet | no: require(...).defaults(opts) then fetch(url) |
85
+ | needle | supported | yes: needle('get', url): verb as the first argument<br>yes: needle.post(url, data) |
86
+ | node-fetch | supported | yes: fetch(url, { method }) imported from node-fetch under another name |
87
+ | ofetch | partly | yes: ofetch(url)<br>no: ofetch.create({ baseURL }) then api(path) |
88
+ | phin | supported | yes: phin({ url, method, data }) |
89
+ | react-router-dom | supported | yes: createBrowserRouter([{ path, loader({ params }) { fetch(...) } }])<br>yes: route action({ request }) posting with fetch |
90
+ | redaxios | supported | yes: redaxios.get(url) |
91
+ | request (deprecated) | supported | yes: request.get(url, cb)<br>yes: request({ uri, method }, cb) |
92
+ | superagent | supported | yes: superagent.get(url)<br>yes: superagent.delete(url)<br>yes: superagent.post(url).send(body).set(h) chain |
93
+ | swr | partly | yes: useSWR(url, fetcher) in a JSX component<br>partly: useSWRMutation(url, (url, { arg }) => fetch(url, { method: 'POST' })) |
94
+ | undici | partly | yes: request(url) from undici<br>yes: undici.fetch(url)<br>partly: new Pool(origin).request({ path, method }) |
95
+ | vue (single-file component) | supported | yes: <script setup> in a .vue file calling axios.get |
96
+ | wretch | supported | yes: wretch(url).delete().res()<br>yes: wretch(base).url(path).post(body) |
97
+
98
+ ## The language's own HTTP client
99
+
100
+ 8 supported, 1 partly, 2 not yet.
101
+
102
+ | Name | Status | Ways of using it that were tested |
103
+ |---|---|---|
104
+ | @sveltejs/kit | supported | yes: export async function load({ fetch }) in +page.js using the fetch it is given |
105
+ | fetch (any client) | supported | yes: the call sits in an async arrow with a block body<br>yes: the call sits in a class field holding an arrow function |
106
+ | fetch (browser) | supported | yes: fetch(url) in an async function inside useEffect, in a .jsx file |
107
+ | fetch (global) | partly | yes: fetch(literalUrl)<br>yes: fetch(`${BASE}/path`, { method }) with BASE from process.env ?? default<br>partly: fetch(new Request(url, { method }))<br>yes: a project wrapper api(path, opts) around fetch, called with the path |
108
+ | node:http | supported | yes: http.request({ hostname, port, path, method }, cb)<br>yes: http.request(urlString, { method }, cb)<br>yes: http.get(url, cb) |
109
+ | node:http2 | not yet | no: http2.connect(origin).request({ ':path', ':method' }) |
110
+ | node:https | supported | yes: https.get(url, cb)<br>yes: https.request({ host, port, path, method }, cb) |
111
+ | node:net | not yet | no: net.createConnection({ host, port }): raw TCP client |
112
+ | svelte (component) | supported | yes: fetch inside <script> of a .svelte component |
113
+ | vue (single-file component) | supported | yes: <script setup> in a .vue file calling fetch with a method |
114
+ | WebSocket (global) | supported | yes: new WebSocket('ws://host/path') (global since Node 22) |
115
+
116
+ ## gRPC
117
+
118
+ 3 supported, 2 partly, 0 not yet.
119
+
120
+ | Name | Status | Ways of using it that were tested |
121
+ |---|---|---|
122
+ | @grpc/grpc-js | partly | partly: server streaming: client.method(req).on('data')<br>no: promisify(client.method).bind(client) passed as a value (gRPC plan 8.1: code, later)<br>yes: literal_path: new grpc.Client(addr).makeUnaryRequest('/pkg.S/M', ...) |
123
+ | @grpc/grpc-js (static codegen) | supported | yes: constructor: new services.{S}Client(addr, creds) from *_grpc_pb |
124
+ | @grpc/grpc-js (static grpc-tools codegen) | supported | yes: server.addService(services.{S}Service, { method }) with *_grpc_pb.js checked in, paths written in the generated object |
125
+ | @grpc/grpc-js + @grpc/proto-loader | supported | yes: grpc-node-addservice: server.addService(proto.{S}.service, { method: impl }) after loadSync(proto)<br>yes: constructor: new proto.{S}(addr, creds) from loadPackageDefinition, then client.method(req, cb) |
126
+ | grpc (deprecated native package) | partly | yes: grpc.load(proto); server.addService(proto.pkg.{S}.service, impl)<br>partly: constructor: new pkg.{S}(addr, creds) after grpc.load |
127
+
128
+ ## Databases and data stores
129
+
130
+ 23 supported, 1 partly, 4 not yet.
131
+
132
+ | Name | Status | Ways of using it that were tested |
133
+ |---|---|---|
134
+ | @aws-sdk/client-dynamodb | supported | yes: client.send(new PutItemCommand({ TableName })) |
135
+ | @aws-sdk/client-s3 | supported | yes: s3.send(new PutObjectCommand({ Bucket, Key })) |
136
+ | @aws-sdk/lib-dynamodb | supported | yes: DynamoDBDocumentClient.from(client).send(new GetCommand({ TableName })) |
137
+ | @elastic/elasticsearch | supported | yes: es.search({ index }) / es.index({ index, document }) |
138
+ | aws-sdk (v2) | supported | yes: new AWS.DynamoDB.DocumentClient().put({ TableName }).promise() |
139
+ | better-sqlite3 | supported | yes: db.prepare(sql).get()/run(); db.exec(sql) |
140
+ | bookshelf | not yet | no: bookshelf.model('Book', { tableName }) then fetch()/fetchAll() |
141
+ | cassandra-driver | supported | yes: client.execute(cql, params, { prepare }) |
142
+ | dynamoose | supported | yes: dynamoose.model('Device', schema); Device.get/create |
143
+ | firebase-admin | supported | yes: admin.firestore().collection('x').doc(id).get()/set(); collection('x').add() |
144
+ | ioredis | supported | yes: redis.get/set/hset/incr on new Redis(url)<br>yes: main reads a key another service writes |
145
+ | knex | supported | yes: knex('table').where().select() / insert()<br>yes: knex.select('*').from('table')<br>yes: knex.raw(sql)<br>yes: knex.schema.createTable('t', fn) (DDL) |
146
+ | lowdb | not yet | no: JSONFilePreset('db.json', defaults); db.data.x.push(); db.write() |
147
+ | mongodb | supported | yes: const col = db.collection('x'); col.find()/insertOne()/updateOne()/aggregate()<br>yes: client.db('x').collection('y').deleteOne() in one chain<br>yes: main reads a collection another service writes |
148
+ | mongoose | supported | yes: mongoose.model('Cart', schema): collection is the plural, lower-cased name<br>yes: mongoose.model('Audit', schema, 'audit_trail') third argument |
149
+ | mssql | supported | yes: pool.request().input(...).query(sql)<br>yes: sql.query`...` tagged template |
150
+ | mysql | supported | yes: pool.query(sql, params, callback) (legacy mysql package) |
151
+ | mysql2 | supported | yes: mysql2/promise pool.execute(sql, params) |
152
+ | nano | not yet | no: nano.db.use('db').get()/insert() |
153
+ | neo4j-driver | supported | yes: session.run(cypher) and session.executeWrite(tx => tx.run(cypher)) |
154
+ | objection | not yet | no: class X extends Model { static get tableName() }; X.query().findById() |
155
+ | oracledb | supported | yes: conn.execute(sql, binds) |
156
+ | pg | supported | yes: pool.query(sqlText, params) for SELECT, INSERT, DELETE<br>yes: client.query({ text, values }) config object<br>yes: table name from a constant in a template literal<br>yes: main reads a table another service writes (cross-service data link) |
157
+ | pg-promise | supported | yes: db.any/none/one(sql, params) |
158
+ | postgres (postgres.js) | supported | yes: sql`SELECT ... FROM t WHERE id = ${id}` tagged template<br>yes: sql.unsafe(sqlText) |
159
+ | redis | supported | yes: createClient({ url }); client.set/get/hGetAll (v4 camelCase) |
160
+ | sequelize | partly | no: sequelize.define('Model', attrs, { tableName }) then Model.findAll()<br>no: class X extends Model {}; X.init(attrs, { sequelize, tableName }); X.create()<br>yes: sequelize.query(sql, { type }) |
161
+ | sqlite3 | supported | yes: db.all/db.run/db.get(sql, params, cb) |
162
+
163
+ ## Messaging and events (queues, topics, streams)
164
+
165
+ 9 supported, 16 partly, 1 not yet.
166
+
167
+ | Name | Status | Ways of using it that were tested |
168
+ |---|---|---|
169
+ | @aws-sdk/client-sns | supported | yes: sns.send(new PublishCommand({ TopicArn })) |
170
+ | @aws-sdk/client-sqs | partly | partly: sqs.send(new ReceiveMessageCommand({ QueueUrl })) polling loop<br>partly: sqs.send(new SendMessageCommand({ QueueUrl: env ?? default })) |
171
+ | @azure/event-hubs | partly | no: new EventHubConsumerClient(group, conn, 'hub').subscribe({...})<br>yes: new EventHubProducerClient(conn, 'hub').sendBatch(batch) |
172
+ | @azure/service-bus | partly | no: sb.createReceiver('queue').subscribe({ processMessage })<br>yes: sb.createSender('queue').sendMessages({ body }) |
173
+ | @google-cloud/pubsub | partly | no: pubsub.subscription('sub').on('message', h)<br>yes: pubsub.topic('t').publishMessage({ json }) |
174
+ | @stomp/stompjs | partly | no: client.subscribe('/queue/x', h)<br>partly: client.publish({ destination, body }) |
175
+ | agenda | supported | yes: agenda.define('job-name', handler)<br>yes: agenda.now('job', data); agenda.every(interval, 'job') |
176
+ | amqplib | partly | partly: ch.consume('queue', handler)<br>partly: assertExchange + bindQueue(q, exchange, pattern) + consume(q)<br>partly: ch.sendToQueue('queue', buf)<br>partly: ch.publish('exchange', 'routing.key', buf) |
177
+ | aws-sdk (v2) | partly | partly: new AWS.SQS().sendMessage({ QueueUrl }).promise() |
178
+ | bee-queue | partly | partly: new BeeQueue('name').process(handler)<br>partly: queue.createJob(data).save() |
179
+ | bull | partly | partly: new Queue('name', redisUrl).process(handler)<br>yes: new Queue('name', url).add(data) |
180
+ | bullmq | partly | yes: new Worker('queue', processor, { connection })<br>partly: new Queue('name', { connection }).add('job', data) |
181
+ | events (EventEmitter) | supported | yes: class X extends EventEmitter; bus.on('name', h); bus.emit('name', data) |
182
+ | firebase-functions | supported | yes: Firestore trigger onDocumentCreated('collection/{id}') |
183
+ | ioredis | supported | yes: sub.subscribe('channel'); sub.on('message', h)<br>yes: redis.publish('channel', msg) |
184
+ | kafkajs | partly | yes: consumer.subscribe({ topic }) then consumer.run({ eachMessage })<br>yes: consumer.subscribe({ topics: [a, b] })<br>yes: producer.send({ topic, messages })<br>no: producer.sendBatch({ topicMessages: [{ topic }] })<br>yes: producer.send({ topic: literal }) in main, consumed by kafkajs-svc<br>yes: producer.send({ topic: TOPICS.X }) with the topic in a constant object |
185
+ | mqtt | supported | yes: client.subscribe('topic'); client.on('message', h)<br>yes: client.publish('topic', payload) |
186
+ | nats | partly | yes: nc.subscribe('subject') with for await<br>no: nc.subscribe('subject', { queue, callback })<br>yes: request side nc.request; reply side subscribe + m.respond<br>yes: nc.publish('subject', data)<br>yes: await nc.request('subject', data) |
187
+ | node-rdkafka | supported | yes: consumer.subscribe(['topic']); consumer.on('data', h)<br>yes: producer.produce('topic', partition, buffer) |
188
+ | pg | partly | no: client.query('LISTEN channel'); client.on('notification', h)<br>partly: SELECT pg_notify('channel', payload) |
189
+ | redis | supported | yes: await subscriber.subscribe('channel', listener)<br>yes: client.xReadGroup(group, consumer, [{ key }]) (streams)<br>yes: client.publish('channel', msg); client.xAdd('stream', '*', fields) |
190
+ | rhea | supported | yes: connection.open_receiver('address'); container.on('message', h)<br>yes: connection.open_sender('address').send({ body }) |
191
+ | socket.io | partly | partly: io.on('connection', s => s.on('event', h)); io.emit('event') |
192
+ | socket.io-client | partly | partly: io(url).emit('event', data); socket.on('event', h) |
193
+ | sqs-consumer | not yet | no: Consumer.create({ queueUrl, handleMessage }) |
194
+ | zeromq | partly | yes: new zmq.Subscriber(); sock.subscribe('topic')<br>partly: new zmq.Publisher(); pub.send(['topic', msg]) |
195
+
196
+ ## Build tools and package managers
197
+
198
+ 9 supported, 0 partly, 0 not yet.
199
+
200
+ | Name | Status | Ways of using it that were tested |
201
+ |---|---|---|
202
+ | Bun workspaces | supported | yes: package.json workspaces with bun.lock, an app using Bun.serve and no server framework dependency |
203
+ | Lerna | supported | yes: lerna.json "packages": ["packages/*"] with fixed versioning; packages named by their package.json |
204
+ | npm | supported | yes: one package.json, src/ code, test/ tests, dist/ output gitignored |
205
+ | npm workspaces | supported | yes: root package.json "workspaces": ["apps/*", "packages/*"], sibling dependency by name with "*" |
206
+ | pnpm workspaces | supported | yes: pnpm-workspace.yaml packages list, "workspace:*" protocol, catalog: versions |
207
+ | Rush | supported | yes: rush.json "projects": [{ packageName, projectFolder }] with folders that are not one glob; common/ holds tool config |
208
+ | webpack | supported | yes: webpack.config.js resolve.alias { Utilities: path.resolve(__dirname, "src/utilities/") } imports, a minified vendor file in public/ |
209
+ | yarn 1 (classic) workspaces | supported | yes: "workspaces": { "packages": [...], "nohoist": [...] } object form, yarn.lock v1 |
210
+ | yarn 2+ (berry) with Plug n Play | supported | yes: .yarnrc.yml nodeLinker pnp, "workspace:^" protocol, generated .pnp.cjs and .yarn/ folder |
211
+
212
+ ## Project and folder layouts
213
+
214
+ 6 supported, 0 partly, 0 not yet.
215
+
216
+ | Name | Status | Ways of using it that were tested |
217
+ |---|---|---|
218
+ | Babel or tsc output folders | supported | yes: source in src/, CommonJS output in lib/ and ESM output in es/ (both gitignored), "main": "lib/index.js" |
219
+ | Electron | supported | yes: one package with main/ (Node process), preload/ and renderer/ (browser), electron-builder output in release/ |
220
+ | Jest, Mocha, Cypress | supported | yes: __tests__/ folders, colocated *.test.js, test/ for Mocha, cypress/e2e, and coverage/lcov-report output |
221
+ | Next.js (pages router) | supported | yes: pages/ and pages/api/ at the root, lib/ helpers, .next/ build output and out/ static export |
222
+ | Node module systems | supported | yes: "type": "module" package with .mjs and .cjs files, createRequire, an "exports" map with subpaths and conditions, "imports" #aliases |
223
+ | Yeoman | supported | yes: generator-<name>/generators/<sub>/index.js with templates/ holding EJS-templated JS that is not valid code |
@@ -0,0 +1,124 @@
1
+ # Supported in Kotlin
2
+
3
+ <!-- GENERATED by scripts/gen-supported-docs.js. Do not edit by hand: run the script again. -->
4
+ <!-- source: catalog/kotlin.json; catalog/kotlin.structure.json -->
5
+ <!-- measured: coverage corpus 1bb4ebb, niro-local 2b30d4fd -->
6
+
7
+ This page lists the Kotlin libraries, frameworks and build tools Niro was tested against, and whether it recognised each one.
8
+
9
+ **What the marks mean.**
10
+
11
+ - **Supported:** Niro recognised every way of using it that the test code tries.
12
+ - **Partly:** Niro recognised some of those ways, or recognised one only in part.
13
+ - **Not yet:** Niro did not recognise it in the test code.
14
+
15
+ The test code is made up for the purpose: one small service per library, with the names invented. So "supported" means Niro recognised it there. It is good evidence, but it is not a promise about every codebase. If Niro misses something in your code, the coverage audit reports the gap on its own (see [coverage-audit-and-consent.md](coverage-audit-and-consent.md)).
16
+ <!-- source: niro-coverage-corpus README.md ("Synthetic test code ... All names are made up"); catalog status merged pairs one to one with measured found -->
17
+
18
+ The test catalog also holds 4 look-alike cases for Kotlin: code that Niro must NOT take for a route, a call or a store. They test that Niro ignores the look-alike, so they are left out of this page and its counts.
19
+ <!-- source: catalog/kotlin.json and catalog/kotlin.structure.json, rows marked as traps (scripts/gen-supported-docs.js isTrap) -->
20
+
21
+ ## Web frameworks and HTTP servers (the routes your code serves)
22
+
23
+ 4 supported, 0 partly, 2 not yet.
24
+
25
+ | Name | Status | Ways of using it that were tested |
26
+ |---|---|---|
27
+ | http4k | not yet | no: routes("path" bind METHOD to handler) |
28
+ | Javalin in Kotlin | supported | yes: app.get(path) { ctx -> } with a trailing lambda |
29
+ | Ktor Resources (type-safe routing) | not yet | no: @Resource(path) classes and get<Res> { } |
30
+ | Ktor server | supported | yes: routing { get(path) { } ; post(path) { } } inside Application.module()<br>yes: route(prefix) { get { } ; post(sub) { } }<br>yes: routes declared in a fun Route.x() extension called from routing { } |
31
+ | Spring Web MVC in Kotlin | supported | yes: @RestController + @RequestMapping prefix + @GetMapping in Kotlin |
32
+ | Spring WebFlux Kotlin DSL | supported | yes: coRouter { GET(path, handler::fn); "/prefix".nest { GET(sub, h) } }<br>yes: router { GET(path) { .. } } |
33
+
34
+ ## The language's own HTTP server
35
+
36
+ 2 supported, 0 partly, 0 not yet.
37
+
38
+ | Name | Status | Ways of using it that were tested |
39
+ |---|---|---|
40
+ | com.sun.net.httpserver from Kotlin | supported | yes: server.createContext(path) { exchange -> } |
41
+ | Servlet API from Kotlin | supported | yes: @WebServlet(path) class X : HttpServlet() { override fun doGet } |
42
+
43
+ ## HTTP client libraries (the calls your code makes to other services)
44
+
45
+ 3 supported, 2 partly, 1 not yet.
46
+
47
+ | Name | Status | Ways of using it that were tested |
48
+ |---|---|---|
49
+ | Fuel | partly | no: "url".httpGet().responseString() string extension<br>yes: Fuel.post(url).body(..) |
50
+ | Ktor client | partly | yes: client.get("url/$id") / client.post(url) { setBody(..) }<br>partly: client.request(url) { method = HttpMethod.Post }<br>no: client.get { url { protocol; host; port; path(..) } }<br>yes: HttpClient { defaultRequest { url(base) } } then relative get(path) |
51
+ | OkHttp from Kotlin | supported | yes: client.newCall(Request.Builder().url(..).post(json.toRequestBody()).build()).execute() |
52
+ | Retrofit with Kotlin suspend functions | not yet | no: interface with suspend @GET fun, Retrofit.Builder().baseUrl(base) |
53
+ | Spring RestTemplate Kotlin extension | supported | yes: restTemplate.getForObject<T>(url) |
54
+ | Spring WebClient with coroutines | supported | yes: webClient.get().uri(path).retrieve().awaitBody<T>() |
55
+
56
+ ## The language's own HTTP client
57
+
58
+ 1 supported, 1 partly, 1 not yet.
59
+
60
+ | Name | Status | Ways of using it that were tested |
61
+ |---|---|---|
62
+ | java.net.http from Kotlin | partly | partly: HttpRequest.newBuilder(URI(x)).GET().build(); http.send(..) |
63
+ | java.net.HttpURLConnection from Kotlin | not yet | no: (URL(x).openConnection() as HttpURLConnection) with requestMethod = ".." |
64
+ | kotlin.io URL.readText() (stdlib extension) | supported | yes: URL(x).readText() |
65
+
66
+ ## gRPC
67
+
68
+ 5 supported, 0 partly, 0 not yet.
69
+
70
+ | Name | Status | Ways of using it that were tested |
71
+ |---|---|---|
72
+ | grpc-java base in Kotlin with net.devh @GrpcService | supported | yes: @GrpcService class X : {S}Grpc.{S}ImplBase() |
73
+ | grpc-java client from Kotlin | supported | yes: {S}Grpc.newBlockingStub(channel).rpc(req), channel forTarget("host:port") |
74
+ | grpc-kotlin | supported | yes: class X : {S}GrpcKt.{S}CoroutineImplBase() with override suspend fun (and Flow for streams) |
75
+ | grpc-kotlin client | supported | yes: {S}GrpcKt.{S}CoroutineStub(channel).rpc(req), streams collected as Flow |
76
+ | net.devh @GrpcClient in Kotlin | supported | yes: @GrpcClient("name") private lateinit var stub: {S}BlockingStub |
77
+
78
+ ## Databases and data stores
79
+
80
+ 1 supported, 1 partly, 6 not yet.
81
+
82
+ | Name | Status | Ways of using it that were tested |
83
+ |---|---|---|
84
+ | Android Room | partly | partly: @Entity(tableName) + @Dao interface with @Query(SQL) / @Insert / @Delete |
85
+ | Exposed (JetBrains) | not yet | no: object X : Table("name"); transaction { X.selectAll() / X.insert { } / X.deleteWhere { } } |
86
+ | Exposed (JetBrains) DAO | not yet | no: object X : IntIdTable("name") with an IntEntity; Entity.all() / Entity.new { } |
87
+ | JDBC from Kotlin | not yet | no: DriverManager.getConnection(..).use { it.prepareStatement(SQL) } |
88
+ | Ktorm | not yet | no: object X : Table<Nothing>("name"); database.from(X).select() / database.insert(X) { } |
89
+ | MongoDB Kotlin coroutine driver | not yet | no: database.getCollection<T>("name").find(..) / insertOne |
90
+ | Spring Data (Kotlin coroutines) | supported | yes: interface : CoroutineCrudRepository<T, Id>, @Table data class |
91
+ | SQLDelight | not yet | no: .sq file with CREATE TABLE and labelled queries; generated xQueries.label() calls |
92
+
93
+ ## Messaging and events (queues, topics, streams)
94
+
95
+ 2 supported, 1 partly, 0 not yet.
96
+
97
+ | Name | Status | Ways of using it that were tested |
98
+ |---|---|---|
99
+ | Apache Kafka clients from Kotlin | partly | partly: KafkaProducer(props).use { it.send(ProducerRecord(t, k, v)) }; consumer.subscribe(listOf(t)) |
100
+ | Spring AMQP in Kotlin | supported | yes: @RabbitListener(queues = ["literal"]) and rabbitTemplate.convertAndSend |
101
+ | Spring for Apache Kafka in Kotlin | supported | yes: @KafkaListener(topics = ["literal"]) and kafkaTemplate.send |
102
+
103
+ ## Build tools and package managers
104
+
105
+ 8 supported, 0 partly, 0 not yet.
106
+
107
+ | Name | Status | Ways of using it that were tested |
108
+ |---|---|---|
109
+ | Amper (JetBrains) | supported | yes: project.yaml listing modules, module.yaml with product: jvm/app or jvm/lib, flat src/ and test/ |
110
+ | Android Gradle Plugin | supported | yes: com.android.application module and com.android.library module; Kotlin under src/main/java; test and androidTest |
111
+ | Gradle buildSrc (Kotlin DSL) | supported | yes: buildSrc with `kotlin-dsl` precompiled script plugins, plus a .main.kts script |
112
+ | Gradle Kotlin DSL (multi-project) | supported | yes: settings include(...), a Kotlin application, a Spring Boot Kotlin service and a plain kotlin("jvm") library |
113
+ | Gradle Kotlin DSL (single project) | supported | yes: one build.gradle.kts with kotlin("jvm") and application, src/main/kotlin and src/test/kotlin |
114
+ | Gradle version catalog (Kotlin) | supported | yes: alias(libs.plugins.ktor) and implementation(projects.moneyLib) |
115
+ | Kotlin Multiplatform | supported | yes: shared module with commonMain, jvmMain, jsMain (expect/actual) and commonTest; a JVM server depending on it |
116
+ | Maven + kotlin-maven-plugin | supported | yes: <sourceDirectory>src/main/kotlin</sourceDirectory> with kotlin-maven-plugin; Spring Boot service and Kotlin library |
117
+
118
+ ## Project and folder layouts
119
+
120
+ 1 supported, 0 partly, 0 not yet.
121
+
122
+ | Name | Status | Ways of using it that were tested |
123
+ |---|---|---|
124
+ | Gradle with flat Kotlin source folders | supported | yes: kotlin.srcDir("src") and srcDir("test") instead of src/main/kotlin |
@@ -0,0 +1,89 @@
1
+ # What Niro supports
2
+
3
+ <!-- GENERATED by scripts/gen-supported-docs.js. Do not edit by hand: run the script again. -->
4
+ <!-- source: niro_local/languages.py LANGUAGES; profiles/local/unparsed-languages.json; catalog/*.json -->
5
+ <!-- measured: coverage corpus 1bb4ebb, niro-local 2b30d4fd -->
6
+
7
+ This page says which computers Niro runs on, which languages it reads, and how much of each language's libraries it recognises. Each language has its own page with the full list.
8
+
9
+ ## Computers
10
+
11
+ - **macOS and Linux**, on Intel (x64) and ARM (arm64) processors.
12
+ <!-- source: niro-cli src/lib/engine.js:79-90 (engine labels macos-x86_64, macos-arm64, linux-x86_64, linux-arm64) -->
13
+ - **Windows:** supported. The details are coming.
14
+ <!-- source: Niraj, 2026-11-10 -->
15
+ - **Node.js 18 or newer** runs the `niro` command.
16
+ <!-- source: niro-cli src/lib/machineCheck.js:13 (MIN_NODE_MAJOR) -->
17
+ - **Disk:** `niro start` warns when less than 2 GB is free, because a large repository's graph can need more.
18
+ <!-- source: niro-cli src/lib/machineCheck.js:14 (LOW_DISK_BYTES); src/commands/start.js:175 -->
19
+
20
+ ## Languages Niro reads
21
+
22
+ Niro reads a file when its ending (its extension) is one of these:
23
+
24
+ | Language | File endings |
25
+ |---|---|
26
+ | Java | `.java` |
27
+ | JavaScript | `.js` `.jsx` `.mjs` `.cjs` `.html` `.htm` `.vue` `.svelte` |
28
+ | TypeScript | `.ts` `.mts` `.cts` `.astro` |
29
+ | TSX | `.tsx` |
30
+ | Python | `.py` `.pyw` `.pyi` |
31
+ | C# | `.cs` `.csx` |
32
+ | Kotlin | `.kt` `.kts` |
33
+ | Go | `.go` |
34
+ | PHP | `.php` `.phtml` `.php3` `.php4` `.php5` |
35
+ | Ruby | `.rb` `.rake` `.gemspec` |
36
+ | Swift | `.swift` |
37
+ | Rust | `.rs` |
38
+ | C | `.c` `.h` |
39
+ | C++ | `.cpp` `.cc` `.cxx` `.hpp` `.hxx` `.h++` `.c++` `.hh` `.tcc` `.ipp` `.inl` `.tpp` |
40
+ <!-- source: niro-local src/niro_local/languages.py LANGUAGES -->
41
+
42
+ Notes on the table:
43
+
44
+ - In `.html` and `.htm` files, and in `.vue` and `.svelte` components, Niro reads only the script blocks inside the page.
45
+ <!-- source: niro-local src/niro_local/languages.py, the comment above the javascript entry ("only the page's inline script blocks are read") -->
46
+ - A `.h` header that holds C++ is read as C++, not as C.
47
+ <!-- source: niro-local src/niro_local/languages.py, the comment above the c entry -->
48
+
49
+ ## Languages Niro counts but does not read
50
+
51
+ Niro has no reader for these. It counts their files, and says so in a build's summary and in `get_index_status`, but nothing in them reaches the graph.
52
+
53
+ | File ending | Language |
54
+ |---|---|
55
+ | `.fs` | F# |
56
+ | `.fsi` | F# |
57
+ | `.fsx` | F# |
58
+ | `.vb` | Visual Basic |
59
+ <!-- source: niro-local src/niro_local/profiles/local/unparsed-languages.json -->
60
+
61
+ ## Libraries recognised, per language
62
+
63
+ **What the marks mean.**
64
+
65
+ - **Supported:** Niro recognised every way of using it that the test code tries.
66
+ - **Partly:** Niro recognised some of those ways, or recognised one only in part.
67
+ - **Not yet:** Niro did not recognise it in the test code.
68
+
69
+ The test code is made up for the purpose: one small service per library, with the names invented. So "supported" means Niro recognised it there. It is good evidence, but it is not a promise about every codebase. If Niro misses something in your code, the coverage audit reports the gap on its own (see [coverage-audit-and-consent.md](coverage-audit-and-consent.md)).
70
+ <!-- source: niro-coverage-corpus README.md ("Synthetic test code ... All names are made up"); catalog status merged pairs one to one with measured found -->
71
+
72
+ | Language | Libraries supported | Partly | Not yet | Full list |
73
+ |---|---|---|---|---|
74
+ | C | 13 | 4 | 34 | [supported-c.md](supported-c.md) |
75
+ | C++ | 24 | 6 | 48 | [supported-cpp.md](supported-cpp.md) |
76
+ | C# | 61 | 25 | 36 | [supported-csharp.md](supported-csharp.md) |
77
+ | Go | 72 | 36 | 61 | [supported-go.md](supported-go.md) |
78
+ | Java | 108 | 19 | 17 | [supported-java.md](supported-java.md) |
79
+ | JavaScript | 95 | 28 | 18 | [supported-javascript.md](supported-javascript.md) |
80
+ | Kotlin | 27 | 5 | 10 | [supported-kotlin.md](supported-kotlin.md) |
81
+ | PHP | 41 | 18 | 19 | [supported-php.md](supported-php.md) |
82
+ | Python | 91 | 28 | 26 | [supported-python.md](supported-python.md) |
83
+ | Ruby | 34 | 25 | 24 | [supported-ruby.md](supported-ruby.md) |
84
+ | Rust | 38 | 11 | 19 | [supported-rust.md](supported-rust.md) |
85
+ | Swift | 21 | 10 | 20 | [supported-swift.md](supported-swift.md) |
86
+ | TypeScript | 82 | 17 | 32 | [supported-typescript.md](supported-typescript.md) |
87
+
88
+ PowerShell has one entry in the test catalog and no reader, so it has no page.
89
+ <!-- source: catalog/powershell.json (1 row, pending) -->
@@ -0,0 +1,157 @@
1
+ # Supported in PHP
2
+
3
+ <!-- GENERATED by scripts/gen-supported-docs.js. Do not edit by hand: run the script again. -->
4
+ <!-- source: catalog/php.json; catalog/php.structure.json -->
5
+ <!-- measured: coverage corpus 1bb4ebb, niro-local 2b30d4fd -->
6
+
7
+ This page lists the PHP libraries, frameworks and build tools Niro was tested against, and whether it recognised each one.
8
+
9
+ **What the marks mean.**
10
+
11
+ - **Supported:** Niro recognised every way of using it that the test code tries.
12
+ - **Partly:** Niro recognised some of those ways, or recognised one only in part.
13
+ - **Not yet:** Niro did not recognise it in the test code.
14
+
15
+ The test code is made up for the purpose: one small service per library, with the names invented. So "supported" means Niro recognised it there. It is good evidence, but it is not a promise about every codebase. If Niro misses something in your code, the coverage audit reports the gap on its own (see [coverage-audit-and-consent.md](coverage-audit-and-consent.md)).
16
+ <!-- source: niro-coverage-corpus README.md ("Synthetic test code ... All names are made up"); catalog status merged pairs one to one with measured found -->
17
+
18
+ ## Web frameworks and HTTP servers (the routes your code serves)
19
+
20
+ 6 supported, 2 partly, 8 not yet.
21
+
22
+ | Name | Status | Ways of using it that were tested |
23
+ |---|---|---|
24
+ | api-platform/core | not yet | no: #[ApiResource(operations: [new Get(), new GetCollection(), new Post()])] on an entity |
25
+ | cakephp/cakephp | not yet | no: config/routes.php: $builder->connect(path, [controller, action]), ->get, ->resources |
26
+ | codeigniter4/framework | not yet | no: app/Config/Routes.php: $routes->get('vendors', 'Vendors::index'), group, resource |
27
+ | laminas/laminas-mvc | not yet | no: module.config.php router routes with type Literal and 'route' => path |
28
+ | Laravel Folio | supported | yes: resources/views/pages/<path>.blade.php is a GET route at /<path>; nested folders nest the path<br>yes: index.blade.php is the root of its folder: pages/index.blade.php -> /, pages/plants/index.blade.php -> /plants<br>yes: a bracketed file name is a path parameter: [id], a model binding [Plant], or a keyed binding [Plant:slug] (on Windows [Plant-slug])<br>yes: [...name].blade.php captures several segments<br>yes: Folio::path(resource_path('views/<folder>'))->uri('/prefix') in a service provider mounts another pages folder under a prefix |
29
+ | laravel/framework | partly | yes: Route::get(path, closure) in routes/web.php<br>yes: Route::post(path, [Controller::class, 'method'])<br>yes: Route::put(path, 'Controller@method') legacy string<br>yes: Route::prefix('admin')->group(closure)<br>no: Route::resource(name, Controller::class) expands to seven routes<br>yes: routes/api.php served under /api by the framework<br>yes: Route::match(['get','post'], path, handler) |
30
+ | laravel/lumen-framework | not yet | no: $router->get(path, 'Controller@method') in routes/web.php<br>no: $router->group(['prefix' => 'v2'], closure) |
31
+ | league/route | supported | yes: $router->map('GET', path, [Controller::class, 'm']) and ->group |
32
+ | mezzio/mezzio | supported | yes: config/routes.php: $app->get(path, Handler::class) and ->route(path, H, [verbs]) |
33
+ | nikic/fast-route | supported | yes: $r->addRoute('GET', path, handler) in simpleDispatcher<br>yes: $r->get(path) and $r->addGroup(prefix, closure) |
34
+ | slim/slim | supported | yes: $app->get(path, closure)<br>yes: $app->group(prefix, fn (RouteCollectorProxy $group) => $group->post(...))<br>yes: $app->map(['GET','POST'], path, closure) |
35
+ | spatie/laravel-route-attributes | supported | yes: #[Prefix] on class, #[Get]/#[Post] on methods |
36
+ | symfony/routing | partly | yes: #[Route(path, methods: [...])] on a controller method<br>yes: class-level #[Route('/catalog')] prefixes every method route<br>no: legacy docblock @Route("/path", methods={"GET"})<br>no: config/routes.yaml path + controller + methods |
37
+ | WordPress admin-ajax | not yet | no: add_action('wp_ajax_shop_refund', fn) served at POST /wp-admin/admin-ajax.php?action=shop_refund |
38
+ | WordPress REST API | not yet | no: register_rest_route(namespace, path, ['methods' => 'GET', 'callback' => fn]) on rest_api_init |
39
+ | yiisoft/yii2 | not yet | no: config urlManager rules 'GET loyalty' => 'loyalty/index' |
40
+
41
+ ## The language's own HTTP server
42
+
43
+ 1 supported, 1 partly, 1 not yet.
44
+
45
+ | Name | Status | Ways of using it that were tested |
46
+ |---|---|---|
47
+ | PHP built-in ($_SERVER) | not yet | no: front controller: if on REQUEST_METHOD and parse_url(REQUEST_URI) path<br>no: switch ($path) { case '/health': ... }<br>no: preg_match('#^/receipts/(\d+)$#', $path) for a path parameter |
48
+ | PHP built-in (web server maps files) | partly | partly: one script per path: public/api/coupons.php checks REQUEST_METHOD |
49
+ | PHP built-in server / any document-root web server | supported | yes: <docroot>/<path>/index.php answers /<path>/ (a request that names no file gets the folder's index.php); the docroot comes from php -S -t, a Dockerfile or the web server config |
50
+
51
+ ## HTTP client libraries (the calls your code makes to other services)
52
+
53
+ 3 supported, 4 partly, 2 not yet.
54
+
55
+ | Name | Status | Ways of using it that were tested |
56
+ |---|---|---|
57
+ | guzzlehttp/guzzle | partly | yes: new Client(['base_uri' => ...]) then $client->get(relative path)<br>yes: $client->request('POST', absolute url, options)<br>yes: $client->getAsync(url)->wait()<br>no: $client->send(new Request('DELETE', url))<br>yes: ClientInterface injected into a typed property, $this->http->post(url)<br>partly: base_uri read from getenv() set in docker-compose<br>yes: $client->requestAsync('GET', url) |
58
+ | kriswallsmith/buzz | supported | yes: $browser->get(url) on Buzz\Browser |
59
+ | laravel/framework (Http facade) | partly | yes: Http::get(absolute url)<br>partly: Http::withToken($t)->post(url) and other option chains<br>partly: Http::baseUrl(config('services.x.url'))->get(path), config from env()<br>no: Http::pool(fn (Pool $pool) => [$pool->get(url), ...]) |
60
+ | psr/http-client (PSR-18) | not yet | no: $client->sendRequest($factory->createRequest('GET', url)) |
61
+ | rmccue/requests | supported | yes: WpOrg\Requests\Requests::get(url) and ::post(url, headers, data) |
62
+ | saloonphp/saloon | not yet | no: Connector::resolveBaseUrl() plus Request $method and resolveEndpoint() |
63
+ | several (main project) | supported | yes: main calls a server in each framework through a different client form |
64
+ | symfony/http-client | partly | yes: HttpClient::create()->request('GET', url)<br>yes: HttpClientInterface injected, $this->client->request('POST', url)<br>yes: HttpClient::createForBaseUri(base)->request('GET', path)<br>partly: scoped client base_uri in framework.yaml, injected by name |
65
+ | WordPress HTTP API | partly | partly: wp_remote_get(url), wp_remote_post(url, args), wp_remote_request(url, ['method' => ...]) |
66
+
67
+ ## The language's own HTTP client
68
+
69
+ 1 supported, 1 partly, 0 not yet.
70
+
71
+ | Name | Status | Ways of using it that were tested |
72
+ |---|---|---|
73
+ | ext-curl | partly | yes: curl_init(url) then curl_exec<br>partly: curl_init() then curl_setopt($ch, CURLOPT_URL, url) and CURLOPT_POST<br>no: curl_setopt_array($ch, [CURLOPT_URL => url, CURLOPT_CUSTOMREQUEST => 'PATCH'])<br>yes: curl_multi_add_handle with handles from curl_init(url) |
74
+ | PHP streams (allow_url_fopen) | supported | yes: file_get_contents(absolute url)<br>yes: file_get_contents(url, false, stream_context_create(['http' => ['method' => 'POST']]))<br>yes: fopen(url, 'r') then stream_get_contents |
75
+
76
+ ## gRPC
77
+
78
+ 2 supported, 0 partly, 0 not yet.
79
+
80
+ | Name | Status | Ways of using it that were tested |
81
+ |---|---|---|
82
+ | grpc/grpc | supported | yes: new {S}Client($addr, ['credentials' => ...]) then $client->PlaceOrder($req)->wait()<br>yes: $call = $client->ListOrders($req); foreach ($call->responses() as $x)<br>yes: hand-written BaseStub subclass calling $this->_simpleRequest('/pkg.S/M', ...) |
83
+ | spiral/roadrunner-grpc | supported | yes: class X implements {S}Interface, registered with $server->registerService({S}Interface::class, new X()) |
84
+
85
+ ## Databases and data stores
86
+
87
+ 13 supported, 4 partly, 3 not yet.
88
+
89
+ | Name | Status | Ways of using it that were tested |
90
+ |---|---|---|
91
+ | aws/aws-sdk-php (DynamoDB) | supported | yes: $ddb->putItem(['TableName' => 'carts', ...]) and query |
92
+ | aws/aws-sdk-php (S3) | supported | yes: $s3->putObject(['Bucket' => 'shop-invoices', ...]) and getObject |
93
+ | catfan/medoo | supported | yes: $db->select('newsletters', [...]) and $db->insert('newsletters', [...]) |
94
+ | cycle/orm | not yet | no: #[Entity(table: 'loyalty_points')] and $orm->getRepository(X::class)->findAll() |
95
+ | doctrine/dbal | partly | yes: $conn->executeQuery(sql) and fetchAllAssociative(sql)<br>yes: $conn->insert('stock_moves', [...]) with the table as argument 0<br>no: $conn->createQueryBuilder()->select('*')->from('suppliers') |
96
+ | doctrine/orm | supported | yes: $em->getRepository(Product::class)->findBy([...]) with #[ORM\Table(name: 'products')]<br>yes: $em->persist($product); $em->flush()<br>yes: custom repository createQueryBuilder and createQuery(DQL) |
97
+ | elasticsearch/elasticsearch | supported | yes: $client->index(['index' => 'products', ...]) and search |
98
+ | ext-memcached | supported | yes: $m = new Memcached(); $m->addServer(...); $m->set(key, value) |
99
+ | ext-pgsql (built-in) | supported | yes: pg_query($conn, sql) and pg_query_params($conn, sql, params) |
100
+ | ext-redis (phpredis) | supported | yes: $r = new Redis(); $r->connect('redis', 6379); $r->hSet(...) |
101
+ | illuminate/database (Eloquent) | partly | yes: Order::where(...)->get() on a model with protected $table<br>yes: Order::create($data)<br>yes: model without $table: table is the plural snake case of the class (Coupon -> coupons)<br>no: DB::table('ledger_entries')->insert([...])<br>no: DB::select('SELECT * FROM refunds WHERE ...', [...]) |
102
+ | laminas/laminas-db | supported | yes: new TableGateway('gift_cards', $adapter); $table->select([...]) |
103
+ | laravel/framework (Cache facade) | not yet | no: Cache::put(key, value) and Cache::remember(key, ttl, fn) |
104
+ | laravel/framework (Redis facade) | supported | yes: Redis::incr('visits') |
105
+ | laravel/framework (Storage facade) | supported | yes: Storage::disk('s3')->put(path, contents) |
106
+ | mongodb/mongodb | not yet | no: $client->shop->reviews->insertOne(...) (database and collection as properties)<br>no: $client->selectCollection('shop', 'ratings')->find(...) |
107
+ | mysqli (built-in) | supported | yes: $db = new mysqli(...); $db->query('SELECT * FROM customers')<br>yes: $db->prepare(sql); bind_param; execute<br>yes: mysqli_query($link, sql) procedural form |
108
+ | PDO (built-in) | partly | yes: $pdo->query('SELECT ... FROM orders')<br>no: $stmt = $pdo->prepare(sql); $stmt->execute([...])<br>partly: $pdo->exec('DELETE FROM carts ...')<br>no: new PDO('pgsql:...') then prepare/execute an UPDATE |
109
+ | predis/predis | supported | yes: $redis = new Predis\Client('tcp://redis:6379'); $redis->set(key), get, lpush |
110
+ | several (main project) | partly | partly: main writes tables and topics that services read |
111
+
112
+ ## Messaging and events (queues, topics, streams)
113
+
114
+ 4 supported, 6 partly, 5 not yet.
115
+
116
+ | Name | Status | Ways of using it that were tested |
117
+ |---|---|---|
118
+ | aws/aws-sdk-php (SNS) | supported | yes: $sns->publish(['TopicArn' => arn, 'Message' => ...]) |
119
+ | aws/aws-sdk-php (SQS) | partly | partly: $sqs->sendMessage(['QueueUrl' => url, ...]) and receiveMessage |
120
+ | bernard/bernard | not yet | no: $producer->produce(new PlainMessage('ResizeImage', [...])) |
121
+ | enqueue/enqueue | not yet | no: $context->createProducer()->send($context->createQueue('audit'), $message) |
122
+ | ext-rdkafka | supported | yes: $producer->newTopic('payments')->produce(RD_KAFKA_PARTITION_UA, 0, $payload)<br>yes: $consumer->subscribe(['payments-settled']) then consume() |
123
+ | google/cloud-pubsub | supported | yes: $pubsub->topic('loyalty-events')->publish([...]) and subscription(...)->pull() |
124
+ | laravel/framework (events) | supported | yes: event(new OrderShipped($order)) helper<br>yes: OrderShipped::dispatch($id) and Event::dispatch(new OrderShipped)<br>yes: Event::listen(OrderShipped::class, [Listener::class, 'handle']) and $listen map |
125
+ | laravel/framework (queues) | not yet | no: SendInvoiceEmail::dispatch($order)->onQueue('emails')<br>no: dispatch(new SendInvoiceEmail($id)) global helper<br>no: Queue::pushOn('emails', new Job)<br>no: class implements ShouldQueue with handle() |
126
+ | pda/pheanstalk | partly | partly: $p->useTube('thumbnails'); $p->put(data) and watch/reserve |
127
+ | php-amqplib/php-amqplib | not yet | no: $channel->basic_publish($msg, 'orders', 'order.created')<br>no: $channel->basic_consume('invoice-requests', '', ..., $callback) in a worker script |
128
+ | php-mqtt/client | partly | partly: $mqtt->publish('devices/scale/weight', ...) and subscribe('devices/+/weight', fn) |
129
+ | predis/predis | partly | partly: $redis->publish('price-changes', ...) and $redis->pubSubLoop()->subscribe('price-changes') |
130
+ | symfony/event-dispatcher | partly | yes: $dispatcher->dispatch(new StockLow($sku), 'stock.low')<br>partly: #[AsEventListener(event: 'stock.low')]<br>partly: EventSubscriberInterface::getSubscribedEvents() returning ['stock.low' => 'onStockLow'] |
131
+ | symfony/messenger | partly | yes: $this->bus->dispatch(new OrderPlaced($id)) on MessageBusInterface<br>partly: #[AsMessageHandler] class with __invoke(OrderPlaced $m)<br>yes: class implements MessageHandlerInterface (before 6.2) |
132
+ | twilio/sdk | not yet | no: $twilio->messages->create($to, ['from' => ..., 'body' => ...]) |
133
+
134
+ ## Build tools and package managers
135
+
136
+ 3 supported, 0 partly, 0 not yet.
137
+
138
+ | Name | Status | Ways of using it that were tested |
139
+ |---|---|---|
140
+ | composer | supported | yes: one composer.json, PSR-4 src/, public/index.php, tests/, vendor/<br>yes: apps/* require a library from packages/* through a path repository<br>yes: autoload classmap of a lib/ folder (no namespaces) and a files autoload of global functions<br>yes: a console tool: composer.json "bin" names bin/shopctl; no front controller |
141
+ | composer (symplify/monorepo-builder) | supported | yes: root composer.json maps every package namespace and replaces the packages; each package has its own composer.json |
142
+ | none (plain PHP) | supported | yes: plain PHP without Composer: index.php with require_once of lib/*.php and includes/*.inc |
143
+
144
+ ## Project and folder layouts
145
+
146
+ 8 supported, 0 partly, 0 not yet.
147
+
148
+ | Name | Status | Ways of using it that were tested |
149
+ |---|---|---|
150
+ | Drupal | supported | yes: Drupal recommended-project: core in web/core, contrib in web/modules/contrib, own code in web/modules/custom/<m> with a .module hooks file |
151
+ | laravel | supported | yes: a Laravel app: artisan, app/, routes/, bootstrap/cache (generated), storage/framework/views (compiled Blade) |
152
+ | laravel + nwidart/laravel-modules | supported | yes: Modules/<Name>/ each with module.json and composer.json, loaded into one Laravel app |
153
+ | Magento 2 | supported | yes: app/code/<Vendor>/<Module> with registration.php and etc/module.xml; generated/ holds interceptors |
154
+ | PHPUnit, Pest, Codeception, phpspec | supported | yes: a library with tests/Unit (PHPUnit), tests/Feature (Pest), tests/Acceptance (Codeception Cest) and spec/ (phpspec) |
155
+ | symfony | supported | yes: a Symfony app (bin/console, public/index.php) with a local bundle through a path repository; var/cache holds the generated container<br>yes: apps/orders-api (HTTP), apps/billing-worker (bin/console messenger:consume only), packages/shared-kernel |
156
+ | WordPress | supported | yes: a WordPress site: core in wp-includes/ and wp-admin/, own plugin and theme in wp-content/, a third-party plugin beside it |
157
+ | WordPress (roots/bedrock) | supported | yes: Bedrock: composer.json installs core to web/wp and plugins to web/app/plugins; own code in web/app/mu-plugins |