@rm30/stack 0.1.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 ADDED
@@ -0,0 +1,51 @@
1
+ # @rm30/stack
2
+
3
+ The RM30 version manifest, as data. One JSON of pinned versions per package class, read
4
+ by `rm doctor` (to grade a project) and `rm new` (to create one). Bumping the workspace
5
+ stack is one PR to one file.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pnpm add -D @rm30/stack
11
+ ```
12
+
13
+ ## Use
14
+
15
+ ```js
16
+ import stack, { classOf, versionOf, bannedReason } from '@rm30/stack'
17
+
18
+ classOf('expo') // 'platform'
19
+ versionOf('next') // '16.3.4'
20
+ bannedReason('mobx') // 'Zustand only. The one project still on it is migrating.'
21
+ stack.updated // '2026-09-08'
22
+ ```
23
+
24
+ The raw file is exported too, for tooling that would rather not import JS:
25
+
26
+ ```js
27
+ import manifest from '@rm30/stack/stack.json' with { type: 'json' }
28
+ ```
29
+
30
+ ## The three classes
31
+
32
+ A single "must be latest" gate over every dependency produces false alarms and then gets
33
+ ignored. So packages are graded by class:
34
+
35
+ | Class | Examples | Rule | `rm doctor` |
36
+ |---|---|---|---|
37
+ | **platform** | `expo`, `react-native`, `next`, `react`, `expo-router` | Version is set by the SDK, not chosen per package. Upgrade as one atomic move, at most one major behind. Never mix. | error |
38
+ | **toolchain** | `typescript`, `@biomejs/biome`, `pnpm`, `turbo`, `vitest` | Latest minor always. Latest major after a deliberate check — these break plugins. | warning, with the upgrade path named |
39
+ | **library** | `zod`, `zustand`, `drizzle-orm`, `viem`, `wagmi`, `firebase*` | Latest minor always, no ceremony. Majors reviewed. | info |
40
+
41
+ ## `banned`
42
+
43
+ Packages that must not enter an RM30 project, each with the reason. Some are paradigm
44
+ calls (`redux`, `mobx`, `nativewind`, `styled-components`); the rest are the concrete
45
+ blockers to TypeScript 7 — everything that consumes the TypeScript programmatic API,
46
+ which TS 7.0 does not ship.
47
+
48
+ ## Keeping it current
49
+
50
+ Versions were read from the npm registry on the date in `updated`. There is no automated
51
+ refresh yet; `rm doctor` will grow one.
package/index.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ export type PackageClass = 'platform' | 'toolchain' | 'library'
2
+
3
+ export interface ClassRule {
4
+ severity: 'error' | 'warn' | 'info'
5
+ policy: string
6
+ }
7
+
8
+ export interface Stack {
9
+ updated: string
10
+ rules: Record<PackageClass, ClassRule>
11
+ platform: Record<string, string>
12
+ toolchain: Record<string, string>
13
+ library: Record<string, string>
14
+ banned: Record<string, string>
15
+ }
16
+
17
+ export declare const stack: Stack
18
+ export default stack
19
+
20
+ export declare const CLASSES: readonly PackageClass[]
21
+ export declare function classOf(name: string): PackageClass | null
22
+ export declare function versionOf(name: string): string | null
23
+ export declare function bannedReason(name: string): string | null
package/index.js ADDED
@@ -0,0 +1,36 @@
1
+ import stack from './stack.json' with { type: 'json' }
2
+
3
+ export { stack }
4
+ export default stack
5
+
6
+ /** The three package classes, in the order `rm doctor` reports them. */
7
+ export const CLASSES = ['platform', 'toolchain', 'library']
8
+
9
+ /**
10
+ * The class a package belongs to, or `null` if the manifest does not track it.
11
+ * @param {string} name
12
+ * @returns {'platform' | 'toolchain' | 'library' | null}
13
+ */
14
+ export function classOf(name) {
15
+ return CLASSES.find((c) => name in stack[c]) ?? null
16
+ }
17
+
18
+ /**
19
+ * The pinned version for a package, or `null` if the manifest does not track it.
20
+ * @param {string} name
21
+ * @returns {string | null}
22
+ */
23
+ export function versionOf(name) {
24
+ const c = classOf(name)
25
+ return c ? stack[c][name] : null
26
+ }
27
+
28
+ /**
29
+ * Why a package is banned from RM30, or `null` if it is not banned.
30
+ * @param {string} name
31
+ * @returns {string | null}
32
+ */
33
+ export function bannedReason(name) {
34
+ if (name === '$comment') return null
35
+ return stack.banned[name] ?? null
36
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@rm30/stack",
3
+ "version": "0.1.0",
4
+ "description": "The RM30 stack manifest — pinned versions by package class, read by rm doctor and rm new.",
5
+ "license": "MIT",
6
+ "author": "Rafael Miziara",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/rafamiziara/cli.git",
10
+ "directory": "packages/stack"
11
+ },
12
+ "keywords": ["versions", "manifest", "dependencies"],
13
+ "type": "module",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./index.d.ts",
17
+ "default": "./index.js"
18
+ },
19
+ "./stack.json": "./stack.json"
20
+ },
21
+ "files": ["index.js", "index.d.ts", "stack.json", "README.md"],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ }
25
+ }
package/stack.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "$comment": "The RM30 stack manifest. Versions read from the npm registry on 2026-09-08. One class per rule: see rules[] for how rm doctor grades each.",
3
+ "updated": "2026-09-08",
4
+ "rules": {
5
+ "platform": {
6
+ "severity": "error",
7
+ "policy": "Version is set by the SDK, not chosen per package. Upgrade as one atomic move, at most one major behind. Never mix."
8
+ },
9
+ "toolchain": {
10
+ "severity": "warn",
11
+ "policy": "Latest minor always. Latest major after a deliberate check — these break plugins."
12
+ },
13
+ "library": {
14
+ "severity": "info",
15
+ "policy": "Latest minor always, no ceremony. Majors reviewed."
16
+ }
17
+ },
18
+ "platform": {
19
+ "expo": "57.0.20",
20
+ "react-native": "0.87.1",
21
+ "expo-router": "57.0.19",
22
+ "jest-expo": "57.0.5",
23
+ "next": "16.3.4",
24
+ "react": "19.2.8"
25
+ },
26
+ "toolchain": {
27
+ "typescript": "7.0.2",
28
+ "@biomejs/biome": "2.5.12",
29
+ "pnpm": "12.3.4",
30
+ "turbo": "2.10.12",
31
+ "vitest": "5.0.0",
32
+ "tailwindcss": "4.3.3",
33
+ "uniwind": "1.12.0",
34
+ "@testing-library/react-native": "14.0.1"
35
+ },
36
+ "library": {
37
+ "zod": "4.5.4",
38
+ "zustand": "5.0.15",
39
+ "firebase": "12.18.0",
40
+ "firebase-admin": "14.3.0",
41
+ "firebase-functions": "7.3.2",
42
+ "@mastra/core": "1.64.0",
43
+ "drizzle-orm": "0.45.2",
44
+ "wagmi": "3.7.7",
45
+ "viem": "2.56.3",
46
+ "hardhat": "3.16.0",
47
+ "ethers": "6.17.0",
48
+ "motion": "13.2.0"
49
+ },
50
+ "banned": {
51
+ "$comment": "Paradigms and packages that must not enter an RM30 project. rm doctor reports any occurrence as an error.",
52
+ "redux": "Zustand only.",
53
+ "@reduxjs/toolkit": "Zustand only.",
54
+ "mobx": "Zustand only. The one project still on it is migrating.",
55
+ "mobx-react-lite": "Zustand only. The one project still on it is migrating.",
56
+ "nativewind": "Uniwind, with web parity via Tailwind v4.",
57
+ "styled-components": "Tailwind v4.",
58
+ "moment": "Use the platform Intl APIs or date-fns.",
59
+ "eslint": "Biome replaces ESLint and Prettier.",
60
+ "prettier": "Biome replaces ESLint and Prettier.",
61
+ "@typescript-eslint/eslint-plugin": "Blocks TypeScript 7 — its supported range ends at <6.1.0.",
62
+ "@typescript-eslint/parser": "Blocks TypeScript 7 — its supported range ends at <6.1.0.",
63
+ "ts-jest": "Consumes the TypeScript programmatic API, which TS 7 does not ship. Use Vitest.",
64
+ "ts-node": "Consumes the TypeScript programmatic API, which TS 7 does not ship. Use tsx or Vitest."
65
+ }
66
+ }