Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .changeset/one-major-per-line-layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@codama/spec': major
---

Restructure the package for the one-major-per-line release model: the root entrypoint now hosts the current major's spec surface from `src/spec/` (formerly `src/v1/`), the `./v1` subpath export is removed (the v1 spec remains available as `@codama/spec@^1` from the `1.x` line), and the generated artifacts move from `v1/` to the repository root (`spec.json`, `schema.json`, `docs/`). The generated docs landing page now links to the docs of previous majors on their maintenance branches.
17 changes: 9 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ pnpm test

See the [README](./README.md) for a top-level tour. The short version:

- `src/` — package source (the `@codama/spec` public surface, including the meta-model API and the latest-major spec data).
- `src/` — package source (the `@codama/spec` public surface: the meta-model API under `src/api/` and this line's spec content under `src/spec/`).
- `tests/` — package tests.
- `generators/` — internal codegen orchestrator and per-target generators. Not exported from the package; produces the `v<n>/` artifacts.
- `v1/` — generated artifacts mirroring the `@codama/spec/v1` surface (`spec.json`, `schema.json`, `docs/`). Treat as machine output; edit the spec or the generators, not these files.
- `generators/` — internal codegen orchestrator and per-target generators. Not exported from the package; produces the generated artifacts.
- `spec.json`, `schema.json`, `docs/` — generated artifacts mirroring the `@codama/spec` surface. Treat as machine output; edit the spec or the generators, not these files.
- `.changeset/` — release intent files managed by [`@changesets/cli`](https://github.com/changesets/changesets).

## Making changes
Expand Down Expand Up @@ -106,10 +106,10 @@ train).

The package version in `package.json` is the single source of truth, bumped by
changesets at release time. The spec version (`SPEC_VERSION` in
`src/v1/version.ts`, embedded in `v1/spec.json` and the generated docs) always
`src/spec/version.ts`, embedded in `spec.json` and the generated docs) always
mirrors it: the Release package PR runs `pnpm release:version`, which calls
`changeset version`, syncs `src/v1/version.ts` via
`scripts/sync-spec-version.ts`, and regenerates the `v1/` artifacts — all in
`changeset version`, syncs `src/spec/version.ts` via
`scripts/sync-spec-version.ts`, and regenerates the artifacts — all in
the same commit. Never edit `SPEC_VERSION` by hand; a unit test fails if it
drifts from the package version.

Expand All @@ -120,8 +120,9 @@ drifts from the package version.
[`@solana-config/oxc`](https://www.npmjs.com/package/@solana-config/oxc) as
the shared base. `pnpm lint` runs both (with type-aware rules enabled);
`pnpm lint:fix` applies their autofixes.
- Generated files under `v1/` are committed but treated as machine output —
edit the spec or the generators, not the generated files.
- Generated files (`spec.json`, `schema.json`, `docs/`) are committed but
treated as machine output — edit the spec or the generators, not the
generated files.

## Reporting issues

Expand Down
32 changes: 17 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ The canonical Codama node specification.

Codama is a standard for describing on-chain Solana programs as a graph of typed nodes (accounts, instructions, types, …). This repository contains:

- **The spec.** A machine-readable description of every node in the Codama node graph, authored in TypeScript under `src/` and emitted as `v1/spec.json`. Future Codama majors will land alongside as `v2/spec.json`, `v3/spec.json`, …
- **The spec.** A machine-readable description of every node in the Codama node graph, authored in TypeScript under `src/spec/` and emitted as `spec.json`.
- **The meta-model API.** Authoring helpers (`defineNode`, `attribute`, primitives, compounds, …) exposed at `@codama/spec/api` for hand-authoring specs and test fixtures.
- **Internal codegen.** Generators under `generators/` produce the public artifacts that mirror each spec major (`v<n>/spec.json`, `v<n>/schema.json`, `v<n>/docs/`). They are not exported from the `@codama/spec` package; they exist as internal tooling for this repo.
- **Internal codegen.** Generators under `generators/` produce the public artifacts (`spec.json`, `schema.json`, `docs/`). They are not exported from the `@codama/spec` package; they exist as internal tooling for this repo.

Each release line hosts exactly one spec major: this branch carries the current major, and previous majors live on their own maintenance branches (e.g. [`1.x`](https://github.com/codama-idl/spec/tree/1.x)) and publish as their own npm versions (e.g. `@codama/spec@^1`). See [RELEASING.md](./RELEASING.md).

Reference implementations (TypeScript node types, node factories, visitors, validators, renderers, the CLI) live in [codama-idl/codama](https://github.com/codama-idl/codama) and consume the published `@codama/spec` package. The Rust reference implementation lives in [codama-idl/codama-rs](https://github.com/codama-idl/codama-rs).

Expand All @@ -21,20 +23,19 @@ pnpm add @codama/spec

## Quickstart

`@codama/spec` exposes three entrypoints:
`@codama/spec` exposes two entrypoints:

- `@codama/spec` — the latest stable major's public surface. Re-exports `@codama/spec/v1` today; will track future majors.
- `@codama/spec/v1` — the v1 spec data, accessors (`getSpec`, `getNode`, `getUnion`, `getEnumeration`), and the version-agnostic types (`NodeSpec`, `UnionSpec`, …).
- `@codama/spec` — the spec data of this release line's major: accessors (`getSpec`, `getNode`, `getUnion`, `getEnumeration`), the `SPEC_VERSION` constant, and the version-agnostic types (`NodeSpec`, `UnionSpec`, …).
- `@codama/spec/api` — the meta-model authoring API (`defineNode`, `attribute`, primitives, compounds, …) for hand-authoring specs and test fixtures.

### Read the spec

```ts
import { getSpec, getNode, SPEC_VERSION } from '@codama/spec/v1';
import { getSpec, getNode, SPEC_VERSION } from '@codama/spec';

const spec = getSpec();
console.log(spec.version); // → '1.8.0'
console.log(SPEC_VERSION); // → '1.8.0'
console.log(spec.version); // → '2.0.0'
console.log(SPEC_VERSION); // → '2.0.0'

const account = getNode('accountNode');
console.log(account?.attributes.map(a => a.name));
Expand Down Expand Up @@ -78,16 +79,17 @@ Preserving declaration order keeps encoded IDLs readable: the identifying scalar

```
src/ # package source (the @codama/spec public surface)
api/ # the meta-model authoring API (@codama/spec/api)
spec/ # the current major's spec content (re-exported by the root)
tests/ # package tests
generators/ # internal codegen orchestrator + per-target generators
index.ts # runs every registered generator sequentially
json-spec/ # emits v<n>/spec.json
json-schema/ # emits v<n>/schema.json (stub)
docs/ # renders the spec as markdown and emits v<n>/docs/
v1/ # generated artifacts mirroring the @codama/spec/v1 surface
spec.json
schema.json
docs/
json-spec/ # emits spec.json
json-schema/ # emits schema.json (stub)
docs/ # renders the spec as markdown and emits docs/
spec.json # generated: the encoded spec
schema.json # generated: JSON Schema (stub)
docs/ # generated: browsable markdown docs
.changeset/ # release intent files (managed by @changesets/cli)
```

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion v1/docs/README.md → docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The canonical Codama node specification.

Spec version: 1.9.2
Spec version: 1.9.2 · Other majors: [v1](https://github.com/codama-idl/spec/blob/1.x/v1/docs/README.md)

Pages marked _(abstract)_ document unions: sets of nodes that can be used interchangeably. Pages marked _(recursive)_ document nested unions: wrapper nodes that may nest before reaching a base type.

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
11 changes: 6 additions & 5 deletions generators/docs/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/**
* `docs` generator.
*
* Runs the docs generator over the v1 spec, then writes the emitted markdown tree to `v1/docs/`
* Runs the docs generator over the spec, then writes the emitted markdown tree to `docs/`
* (GitHub-flavoured pages, relative `.md` links, `README` landing pages per folder). CI re-runs this
* and fails if the result differs from what is committed, keeping the docs artifact in lockstep with
* the spec source.
* the spec source. Previous majors' docs live on their own maintenance branches; the root landing
* page links to them (see `PREVIOUS_MAJOR_DOCS`).
*/

import path from 'node:path';
Expand All @@ -21,7 +22,7 @@ import {
writeRenderMap,
} from '@codama/fragments';

import { getSpec } from '../../src/v1';
import { getSpec } from '../../src/spec';
import { PAGE_EXTENSION } from './constants';
import { generateDocs } from './generateDocs';
import type { DocModel } from './types';
Expand All @@ -43,8 +44,8 @@ export function generate(): void {
const model = generateDocs(getSpec());
const docsMap = getDocsRenderMap(model);

const outDir = joinPath(REPO_ROOT, 'v1', 'docs');
const outDir = joinPath(REPO_ROOT, 'docs');
deleteDirectory(outDir);
writeRenderMap(docsMap, outDir);
process.stdout.write(`wrote ${docsMap.size} docs files to v1/docs\n`);
process.stdout.write(`wrote ${docsMap.size} docs files to docs/\n`);
}
9 changes: 9 additions & 0 deletions generators/docs/render/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ export const ROOT_TITLE = 'Codama Spec';
/** Root-page description. */
export const ROOT_DESCRIPTION = 'The canonical Codama node specification.';

/**
* Docs landing pages of previous spec majors, linked from the root page's version line.
* Each release line hosts exactly one major, so older docs live on their own maintenance
* branches; append one entry here at each major transition.
*/
export const PREVIOUS_MAJOR_DOCS: readonly { label: string; url: string }[] = [
{ label: 'v1', url: 'https://github.com/codama-idl/spec/blob/1.x/v1/docs/README.md' },
];

/** The display heading for each category group kind - a rendering concern, kept out of the CategoryGroup data. */
export const GROUP_TITLES: Record<CategoryGroup['kind'], string> = {
node: 'Nodes',
Expand Down
14 changes: 11 additions & 3 deletions generators/docs/render/renderPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import type {
MarkupRenderer,
NavRegistry,
} from '../types';
import { BLOCK_SEPARATOR, GROUP_TITLES, ROOT_DESCRIPTION, ROOT_TITLE } from './constants';
import { BLOCK_SEPARATOR, GROUP_TITLES, PREVIOUS_MAJOR_DOCS, ROOT_DESCRIPTION, ROOT_TITLE } from './constants';
import { isDocChild, linkedEntity, renderType } from './renderType';

/** Shared context threaded through every page renderer. `link` resolves a relative href between two pages. */
Expand Down Expand Up @@ -202,6 +202,14 @@ export function renderCategoryIndexPage(category: CategorySpec, ctx: RenderCtx):
};
}

/** The root page's version line: the current spec version, plus links to the docs of previous majors. */
function specVersionLine(version: string, markup: MarkupRenderer): string {
const current = `Spec version: ${version}`;
if (PREVIOUS_MAJOR_DOCS.length === 0) return current;
const others = PREVIOUS_MAJOR_DOCS.map(major => markup.link(major.label, major.url)).join(', ');
return `${current} · Other majors: ${others}`;
}

export function renderRootIndexPage(spec: Spec, ctx: RenderCtx): DocPage {
const { markup } = ctx;
const ref: DocRef = { kind: 'rootIndex' };
Expand All @@ -228,8 +236,8 @@ export function renderRootIndexPage(spec: Spec, ctx: RenderCtx): DocPage {
markup.heading(1, ROOT_TITLE),
// description
markup.paragraph(ROOT_DESCRIPTION),
// version
markup.paragraph(`Spec version: ${spec.version}`),
// version, with a switcher to the docs of previous majors (hosted on their maintenance branches)
markup.paragraph(specVersionLine(spec.version, markup)),
// legend for the (abstract)/(recursive) heading suffixes
markup.paragraph(
`Pages marked ${markup.italic('(abstract)')} document unions: sets of nodes that can be used ` +
Expand Down
6 changes: 4 additions & 2 deletions generators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
*
* Runs every registered generator sequentially. Each generator exposes
* a `generate()` function (sync or async) from its `index.ts`, owns its
* target output paths, and writes to a versioned subfolder
* (`v1/...`, future `v2/...`, …) at the repo root.
* target output paths, and writes its artifacts (`spec.json`, `schema.json`,
* `docs/`) at the repo root. Each release line hosts exactly one spec major,
* so the artifacts are unversioned; previous majors live on their own
* maintenance branches.
*
* Adding a new generator: drop a folder under `generators/`, export a
* `generate()` from its `index.ts`, then register it in the `GENERATORS`
Expand Down
2 changes: 1 addition & 1 deletion generators/json-schema/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* `json-schema` generator.
*
* Will emit the public `v1/schema.json` JSON Schema artifact from the
* Will emit the public `schema.json` JSON Schema artifact from the
* encoded spec for use by editor tooling and third-party consumers.
*
* Currently a no-op stub; the orchestrator runs it but it does nothing
Expand Down
17 changes: 7 additions & 10 deletions generators/json-spec/index.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
/**
* `json-spec` generator.
*
* Emits the canonical `v1/spec.json` artifact from the encoded v1 spec.
* Emits the canonical `spec.json` artifact from the encoded spec.
* CI re-runs this and fails the build if the resulting file differs from
* what's committed, ensuring the JSON artifact and the TypeScript source
* stay in lockstep.
*
* Future Codama majors add a second `writeSpec('v2', getV2Spec())` line.
* Each release line hosts exactly one spec major, so there is exactly one
* `spec.json`; previous majors live on their own maintenance branches.
*/

import { writeFile } from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';

import type { Spec } from '../../src/api';
import { getSpec as getV1Spec } from '../../src/v1';
import { getSpec } from '../../src/spec';

const HERE = path.dirname(fileURLToPath(import.meta.url));
// here = <repo>/generators/json-spec → repoRoot is two levels up.
const REPO_ROOT = path.resolve(HERE, '../..');

async function writeSpec(major: string, spec: Spec): Promise<void> {
const out = path.join(REPO_ROOT, major, 'spec.json');
export async function generate(): Promise<void> {
const spec = getSpec();
const out = path.join(REPO_ROOT, 'spec.json');
const json = JSON.stringify(spec, null, 4) + '\n';
await writeFile(out, json, 'utf8');
const nodeCount = spec.categories.reduce((acc, c) => acc + c.nodes.length, 0);
Expand All @@ -32,7 +33,3 @@ async function writeSpec(major: string, spec: Spec): Promise<void> {
`wrote ${path.relative(REPO_ROOT, out)} (${nodeCount} nodes, ${unionCount} unions, ${enumCount} enumerations)\n`,
);
}

export async function generate(): Promise<void> {
await writeSpec('v1', getV1Spec());
}
5 changes: 4 additions & 1 deletion oxfmt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ const solanaFmt = require('@solana-config/oxc/oxfmt');

const ignorePatterns = [
'**/dist/',
'v[0-9]*/**',
// generated artifacts (machine output)
'spec.json',
'schema.json',
'docs/**',
'.changeset/**',
'CHANGELOG.md',
'pnpm-lock.yaml',
Expand Down
5 changes: 4 additions & 1 deletion oxlint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ const solanaConfig = require('@solana-config/oxc/oxlint');

const ignorePatterns = [
'**/dist/',
'v[0-9]*/**',
// generated artifacts (machine output)
'spec.json',
'schema.json',
'docs/**',
'.changeset/**',
'CHANGELOG.md',
'pnpm-lock.yaml',
Expand Down
14 changes: 1 addition & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
"files": [
"./dist/types",
"./dist/index.*",
"./dist/api.*",
"./dist/v1.*"
"./dist/api.*"
],
"type": "commonjs",
"sideEffects": false,
Expand Down Expand Up @@ -50,17 +49,6 @@
"import": "./dist/api.node.mjs",
"require": "./dist/api.node.cjs"
}
},
"./v1": {
"types": "./dist/types/v1/index.d.ts",
"browser": {
"import": "./dist/v1.browser.mjs",
"require": "./dist/v1.browser.cjs"
},
"node": {
"import": "./dist/v1.node.mjs",
"require": "./dist/v1.node.cjs"
}
}
},
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion v1/schema.json → schema.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$comment": "Generated artifact. Placeholder until the json-schema generator lands.",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/codama-idl/spec/raw/main/v1/schema.json",
"$id": "https://github.com/codama-idl/spec/raw/main/schema.json",
"title": "Codama IDL",
"type": "object"
}
6 changes: 3 additions & 3 deletions scripts/sync-spec-version.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* Syncs `SPEC_VERSION` (src/v1/version.ts) with the `version` field of package.json.
* Syncs `SPEC_VERSION` (src/spec/version.ts) with the `version` field of package.json.
*
* Run by the release flow (`pnpm release:version`) right after `changeset version`, so the
* spec version lands in the same commit as the package bump. `pnpm generate` then refreshes
* the `v1/` artifacts that embed the version. A unit test asserts the two values never drift.
* the generated artifacts that embed the version. A unit test asserts the two values never drift.
*/

import { readFileSync, writeFileSync } from 'node:fs';
Expand All @@ -12,7 +12,7 @@ import process from 'node:process';
import { fileURLToPath } from 'node:url';

const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const VERSION_FILE = path.join(REPO_ROOT, 'src', 'v1', 'version.ts');
const VERSION_FILE = path.join(REPO_ROOT, 'src', 'spec', 'version.ts');

const { version } = JSON.parse(readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf8')) as { version: string };
const content = readFileSync(VERSION_FILE, 'utf8');
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
* Public types describing the shape of a Codama spec.
*
* These types are version-agnostic — the same shape describes a Codama v1
* spec, a v2 spec, etc. Versioned content (concrete nodes, enumerations,
* unions, categories) lives under `src/v<n>/`. The first half of
* spec, a v2 spec, etc. The current major's content (concrete nodes,
* enumerations, unions, categories) lives under `src/spec/`. The first half of
* this file declares the type-expression vocabulary; the second half
* declares the spec-content shape (attributes, nodes, unions,
* enumerations, nested unions, categories).
Expand Down
12 changes: 5 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
/**
* `@codama/spec` — entry point.
*
* Re-exports the latest stable Codama major's public surface. Today that's
* `@codama/spec/v1`; future Codama majors will add their own subpaths
* (`@codama/spec/v2`, …) and the entry point will track the latest.
*
* Consumers wanting a stable, version-pinned import should always use the
* subpath form (`@codama/spec/v1`).
* Re-exports the spec surface of this release line's Codama major from
* `./spec`. Each release line hosts exactly one major: older majors are
* published from their own maintenance branches and obtained via npm
* versions (e.g. `@codama/spec@^1` for the v1 spec) — see RELEASING.md.
*/

export * from './v1';
export * from './spec';
2 changes: 1 addition & 1 deletion src/v1/enumerations.ts → src/spec/enumerations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Named enumerations for the Codama v1 spec.
* Named enumerations for the Codama spec.
*
* Anywhere a node attribute would otherwise be an inline string-literal union
* (e.g. `endian: 'be' | 'le'`), the union is lifted to a named enumeration
Expand Down
Loading