Stone.jsDocs
Paradigm

Start here

Troubleshooting & FAQ

Most first-run problems come from one of a few places: the decorator toolchain, ESM, the .stone codegen, or workspace type resolution. Here is how to recognise and fix each, plus answers to the questions that come up most.

#Decorators do nothing, or `SetupError: This decorator can only be applied to...`

Stone.js uses TC39 stage-3 decorators (the 2023-11 standard) with Symbol.metadata, never the legacy TypeScript ones and never reflect-metadata. Almost every problem here comes from one rule:

Why the flag is required. The published method-decorator signatures are legacy-shaped, because that is the only shape TypeScript knows, while the bodies require a 2023-11 context. Without the flag, a method decorator does not typecheck (TS1241, TS1270); with it, it compiles and Babel makes sure the legacy form never exists at run time. Class decorators alone do not need it.

tsconfig.jsonjson
{
  "compilerOptions": {
    // Appeases the compiler for method decorators. Babel emits 2023-11 at build time,
    // so the legacy form never reaches the runtime.
    "experimentalDecorators": true,
    "emitDecoratorMetadata": false
  }
}

The Stone.js CLI configures Babel for you. If you run your own build, or a test runner, add the plugin yourself:

babel (only if you bypass the Stone CLI)json
{
  "plugins": [["@babel/plugin-proposal-decorators", { "version": "2023-11" }]]
}

#Testing a real application with Vitest

Vitest transforms with esbuild, so a decorated class imported in a test hits the rule above. Add Babel to the test transform and the whole application boots in-memory, handlers, services, error handlers and routes included:

vitest.config.tsts
import babel from 'vite-plugin-babel'
import { defineConfig } from 'vitest/config'

export default defineConfig({
  plugins: [
    babel({
      filter: /\.[jt]sx?$/,
      babelConfig: {
        babelrc: false,
        configFile: false,
        presets: ['@babel/preset-typescript'],
        plugins: [['@babel/plugin-proposal-decorators', { version: '2023-11' }]]
      }
    })
  ]
})

#Checking metadata by hand

Metadata keys are symbols, so JSON.stringify hides them: JSON.stringify(MyClass[Symbol.metadata]) prints {} even when everything is correct. Use Reflect.ownKeys() instead.

nodejs
Reflect.ownKeys(MyClass[Symbol.metadata] ?? {})   // the real keys
JSON.stringify(MyClass[Symbol.metadata])          // always "{}", so do not trust it

#Stale build after editing code (the .stone folder)

The CLI generates a .stone/ directory (module manifest, route table, entry) at build time. If a newly added handler, page or command is not picked up, the codegen cache is stale. Clear it and rebuild:

terminalbash
stone cache clear      # drop the .stone codegen cache
npm run dev            # or: npm run build

Commit nothing from .stone/ or dist/: both are generated.

#any types or unresolved @stone-js/* imports in a monorepo

Type-aware tools (your editor, tsc, type-aware lint) read a dependency's types from its built dist/*.d.ts. In a fresh workspace clone where nothing is built yet, imports from sibling @stone-js/* packages can resolve to any. Build once so the declaration files exist:

terminalbash
pnpm build             # build every package (topological)
# or a single graph:  pnpm --filter @stone-js/core... build

#"No response was returned"

A handler must return a value; the kernel resolves that value into a response per event. An InitializationError: No response was returned means a handler path returned undefined. Return the payload (or an explicit response), and remember that the status code belongs to the platform layer, not your domain: a bare returned value becomes a 200 over HTTP, an exit code on the CLI.

#A route 404s unexpectedly

A missed match is a not-found the error handler maps to 404, never a crash. Check precedence (static beats dynamic), host/domain constraints, and the HTTP method. Add a fallback route for a friendly page. See Matching & precedence.

#Translations answer their own keys

t('SOME_KEY') returns SOME_KEY. Nothing failed, which is the whole problem: it reads like a missing entry rather than a missing module, and it survives every in-process test. The application says so at boot now, and the causes are worth knowing in order.

CauseTypeDescription
A configuration replaced the bucketmost commonblueprint.set('stone.i18n', { … }) overwrites what the build injected. Set keys one at a time: stone.i18n.locale.
The scan found nothingCatalogs must sit at <root>/**/i18n/<locale>/<namespace>.json. The build line says how many it found.
The build plugin did not run@stone-js/i18n must be a direct dependency of the app being built, and stone.builder.autoDiscover must not be false.
Locales are unknownwrong languageNegotiation is skipped when the list is empty, so every caller gets the fallback. The build declares it; check the line it prints.

#Unknown file extension ".ts" when running tests

Discovery imports your application's modules at run time, and an installed package doing that sits outside the runner's transform, so Node is asked to load a .ts file directly. stone test inlines the framework in the config it generates, which is what puts those imports back through the transform. If you maintain your own Vitest config, keep server.deps.inline with '@stone-js/' in it, as a string and not a regular expression: the generated config is written as JSON, where a RegExp becomes {} and the runner then finds no tests at all.

#FAQ

#Do I have to use decorators?

No. Every declarative decorator has an imperative define* equivalent, at parity. Pick either; mix if you like.

#Which adapter do I choose?

You do not choose one, you stack the ones you target (@NodeHttp, @Fetch,@AwsLambdaHttp, @Mcp…). The runtime that receives the request collapses the choice; the domain is written once.

#TypeScript or JavaScript?

Both. The JavaScript variants keep stage-3 decorators and strip types; there is no second source to maintain.

#Is it production-ready?

The framework is in beta ahead of a 1.0. See the versioning policy for what stability the current line promises before you adopt it for a critical workload.


Stone.js

Your app exists in every runtime. Until you run it.

An open-source project by Stone Foundation
Created by Mr. Stone (Evens Pierre)