# https://github.com/datopian/portaljs Project Manual

Generated at: 2026-07-05 04:54:06 UTC

## Table of Contents

- [Overview](#page-overview)
- [Lib](#page-site-lib)
- [Lib](#page-packages-ckan-src-lib)
- [Src](#page-packages-core-src)
- [Src](#page-packages-ckan-src)
- [Src](#page-cloud-auth-src)
- [Src](#page-cloud-api-src)
- [Src](#page-packages-ckan-api-client-js-src)

<a id='page-overview'></a>

## Overview

### Related Pages

Related topics: [Lib](#page-site-lib)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/datopian/portaljs/blob/main/README.md)
- [cloud/api/package.json](https://github.com/datopian/portaljs/blob/main/cloud/api/package.json)
- [cloud/auth/package.json](https://github.com/datopian/portaljs/blob/main/cloud/auth/package.json)
- [cloud/worker/package.json](https://github.com/datopian/portaljs/blob/main/cloud/worker/package.json)
- [giftless/README.md](https://github.com/datopian/portaljs/blob/main/giftless/README.md)
- [giftless/cloudflare/README.md](https://github.com/datopian/portaljs/blob/main/giftless/cloudflare/README.md)
</details>

# Overview

PortalJS is an open-source framework for building modern data portals. Originally forked and rebranded from the Recline.js data explorer, it has grown into a full-stack toolkit that covers portal scaffolding, frontend data exploration, CKAN-compatible APIs, authentication, background processing, and Git LFS object storage. The repository is organized as a monorepo that groups several independently versioned packages under one source tree.

## Purpose and Scope

The repository serves two distinct but related audiences:

- **Data portal builders** who want to launch an end-user data portal with dataset catalogs, previews, charts, and search.
- **Platform operators** who need the backend services that power those portals: a REST API, an auth layer, background workers, and large-file storage.

According to `README.md`, PortalJS positions itself as a "framework for creating data portals" with components, examples, and integrations spanning the full stack. The community has confirmed the migration track from older CKAN / Datahub templates to Next.js (App Router) for portal UIs, as tracked in `issues/1618`.

## Repository Layout

The top-level directories each correspond to a deployable unit:

- `cloud/api/` — the public REST surface used by portals. `Source: [cloud/api/package.json:1-40]()` names this package `@portaljs/cloud-api` and wires Express, CKAN-compatible endpoints, and Prisma for persistence.
- `cloud/auth/` — authentication service exposed as `@portaljs/cloud-auth`. `Source: [cloud/auth/package.json:1-30]()` shows NextAuth integration with Prisma adapters and OAuth providers.
- `cloud/worker/` — a worker process for async jobs (indexing, exports). `Source: [cloud/worker/package.json:1-30]()` declares a BullMQ-based queue consuming from Redis.
- `giftless/` — a standalone Git LFS server used to back large dataset files. `Source: [giftless/README.md:1-60]()` describes its role as the storage layer behind the portal's data APIs.
- `giftless/cloudflare/` — a Cloudflare Workers deployment variant of Giftless. `Source: [giftless/cloudflare/README.md:1-40]()` documents the edge runtime, Durable Object backends, and KV/WebAssembly storage adapters.

A simplified view of how these pieces relate:

```mermaid
flowchart LR
    Browser[Portal Frontend] --> API[cloud/api]
    Browser --> Auth[cloud/auth]
    API --> DB[(Postgres via Prisma)]
    API --> Giftless[giftless LFS]
    Worker[cloud/worker] --> DB
    Worker --> Giftless
    Giftless --> Storage[(Object Storage)]
```

## Component Highlights

### Portal Frontend

The user-facing portal is built on Next.js and reuses `@portaljs/components` for dataset tables, charts, and map previews. Recent component releases focus on robustness — for example, `@portaljs/components@1.2.3` "breaks lines at invalid / missing values" in LineChart, and `@portaljs/components@1.2.2` adds `yearmonth` to `xAxisTimeUnit`. The community also tracks PDF styling fixes (e.g., `@portaljs/components@0.3.1`).

### CKAN API Client

`@portaljs/ckan-api-client-js@1.4.0` provides a typed client for CKAN endpoints. This is the contract that lets PortalJS Cloud interoperate with existing CKAN integrations without breaking downstream consumers, a point emphasized in the FAQ added under `issues/1502`.

### Auth, Worker, and Storage

- `cloud/auth` handles session management and provider callbacks.
- `cloud/worker` offloads long-running operations such as dataset indexing and exports to a queue.
- `giftless` implements the Git LFS protocol so portals can version and stream large files; the Cloudflare variant runs the same protocol on the edge with Durable Object state.

### Scaffolding and Skills

`create-portaljs` is the official scaffolder (`npm create portaljs@latest`). Recent releases bundle agentic "skills" that guide AI assistants when bootstrapping or extending a portal. `create-portaljs@0.6.0` standardized the `portaljs-` prefix for all skills (e.g., `/portaljs-new-portal`, `/portaljs-add-dataset`); earlier releases introduced `/migrate` (`@0.5.0`), bundled skills into `.claude/` (`@0.4.0`), and added the branded cyclone loader (`@0.3.0`).

## Community Signals

Community activity aligns with the architecture above:

- `issues/520` traces the historical rename of Recline.js to Portal.JS, framing it as a modern React-based data portal framework.
- `issues/1138` discusses how LLMs could automate schema work — a direction now reflected in the agentic skills shipped with `create-portaljs`.
- `issues/1059` surfaces a remark-wiki-link bug, indicating the docs site reuses Markdown pipelines that PortalJS contributors should be aware of.
- `issues/1095` and `issues/819` are operational issues (a 404 on an airports code list, and a stale `xlsx` / SheetJS registry) that illustrate the kind of dataset-quality friction portals must handle.

Together, the packages, scripts, and community tracks make PortalJS a full-stack framework rather than a single library.

---

<a id='page-site-lib'></a>

## Lib

### Related Pages

Related topics: [Overview](#page-overview), [Lib](#page-packages-ckan-src-lib)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [site/lib/common.ts](https://github.com/datopian/portaljs/blob/main/site/lib/common.ts)
- [site/lib/computeFields.ts](https://github.com/datopian/portaljs/blob/main/site/lib/computeFields.ts)
- [site/lib/getAuthorsDetails.ts](https://github.com/datopian/portaljs/blob/main/site/lib/getAuthorsDetails.ts)
- [site/lib/getGitHubData.ts](https://github.com/datopian/portaljs/blob/main/site/lib/getGitHubData.ts)
- [site/lib/getPageData.js](https://github.com/datopian/portaljs/blob/main/site/lib/getPageData.js)
- [site/lib/gtag.js](https://github.com/datopian/portaljs/blob/main/site/lib/gtag.js)
</details>

# Lib

The `site/lib/` directory in the PortalJS repository hosts the server-side and client-side helper modules used by the marketing and documentation site (Next.js app) that lives under `site/`. It is not the core portal framework itself (which is provided by `@portaljs/components` and `@portaljs/ckan-api-client-js`); rather, it is a thin utility layer that exposes configuration constants, content-fetching helpers, analytics wiring, and small transformation functions reused across pages, components, and static generation routines.

## Scope and High-Level Role

The files in `site/lib/` fall into four functional groups:

1. **Shared constants and primitives** — `common.ts` exports site-wide configuration values used by both client and server contexts.
2. **Data transformation utilities** — `computeFields.ts` provides pure functions that reshape raw records into computed fields before they are rendered.
3. **Remote data fetchers** — `getGitHubData.ts`, `getAuthorsDetails.ts`, and `getPageData.js` fetch external state (GitHub metadata, author bios, CMS-driven page content) at build time or request time.
4. **Analytics integration** — `gtag.js` integrates Google Analytics, initializing the tracker and exposing helpers for page views and events.

Together, these modules decouple page-level code from configuration, HTTP concerns, and analytics plumbing so that UI components can focus on rendering.

Source: [site/lib/common.ts]() and [site/lib/getPageData.js]() act as the structural backbone for the rest of the site.

## Configuration and Shared Constants

`site/lib/common.ts` centralizes constants such as the GitHub repository coordinates used by social cards and "star us" CTAs, the canonical site URL, Open Graph defaults, and any feature flags consumed by multiple pages. Centralizing these values prevents drift between server-rendered HTML, metadata, and the client-side runtime; when the project URL or repo slug changes, only this file needs editing. Both `getGitHubData.ts` and `getAuthorsDetails.ts` read from `common.ts` so that the GitHub owner/name pair stays consistent across contributors listings and star-count widgets.

Source: [site/lib/common.ts]() defines the canonical constants consumed by every fetcher and UI component in the site.

## Data Fetching Helpers

The fetcher layer is split by responsibility to keep error handling local and to make each helper independently mockable in tests.

`getGitHubData.ts` wraps the GitHub REST API (repository metadata such as `stargazers_count`, default branch, and license) and returns a normalized object that downstream components consume without re-parsing the raw response. It is invoked during static generation for the homepage and the contributors page so that the marketing surface always shows up-to-date numbers without making client requests.

`getAuthorsDetails.ts` complements the GitHub fetcher: for each contributor handle it resolves a richer profile (avatar URL, bio, social links) so the page can render author cards alongside blog posts and changelog entries. Centralizing the resolution makes it easy to swap data sources (e.g. switching from GitHub to a CMS) without touching every page.

`getPageData.js` is the generic CMS page resolver used by the documentation, blog, and changelog routes. It accepts a slug and a content type, calls the configured headless CMS endpoint, and returns the parsed payload. Because it is the single entry point for content retrieval, error logging, caching headers, and TypeScript/JSDoc type guards are written once and reused everywhere, which is consistent with PortalJS's modular packaging philosophy visible in releases such as `@portaljs/ckan-api-client-js@1.4.0`.

Source: [site/lib/getGitHubData.ts](), [site/lib/getAuthorsDetails.ts](), and [site/lib/getPageData.js]() form the three fetchers used by static and dynamic routes.

## Transformations and Analytics

`computeFields.ts` is a small, side-effect-free transformer used after a fetcher returns raw data. For example, given a blog post payload it can compute reading time, formatted publish dates, and trimmed excerpts from a longer body. Keeping this logic out of page components makes it straightforward to unit test the transformation in isolation and to reuse it across different routes (the blog index, post detail page, and RSS feed all share the same `computeFields` pipeline).

`gtag.js` is the analytics layer. It initializes `gtag('js', new Date(), 'G-XXXX')`, exposes a `pageview` helper that the Next.js `_app` or `app/` router invokes on route change, and provides a typed `event` helper for tracking CTA clicks on the homepage and documentation CTAs. The file is deliberately framework-agnostic so it can be reused regardless of whether the site is rendered with the Pages Router or the App Router that the `research.portaljs.com` migration referenced in community issue #1618 adopts.

Source: [site/lib/computeFields.ts]() keeps rendering logic separate from data logic, while [site/lib/gtag.js]() wires up analytics.

## Architectural Sketch

```mermaid
flowchart LR
  A[Page / Route] --> B[common.ts constants]
  A --> C[getPageData.js]
  A --> D[getGitHubData.ts]
  A --> E[getAuthorsDetails.ts]
  C --> F[computeFields.ts]
  D --> F
  E --> F
  F --> G[UI Components]
  A --> H[gtag.js]
  H --> I[Google Analytics]
```

Pages pull constants from `common.ts`, fetch content through one of the typed fetchers, normalize the result via `computeFields.ts`, render it with shared UI components, and report engagement through `gtag.js`.

## How to Extend

When adding a new helper, prefer placement in `site/lib/` rather than inside a component file so it can be imported by both server and client code paths without bundling accidental dependencies. Follow the existing convention of one concern per file (fetchers separated from transformers, analytics isolated from data), match the typed signatures used by `getGitHubData.ts` and `computeFields.ts`, and register any new constant in `common.ts` to keep configuration discoverable.

Source: directory conventions observed across [site/lib/common.ts](), [site/lib/computeFields.ts](), [site/lib/getGitHubData.ts](), [site/lib/getPageData.js](), and [site/lib/gtag.js]().

---

<a id='page-packages-ckan-src-lib'></a>

## Lib

### Related Pages

Related topics: [Lib](#page-site-lib), [Src](#page-packages-core-src)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [packages/ckan/src/lib/ckanapi.tsx](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/lib/ckanapi.tsx)
- [packages/ckan/src/lib/index.tsx](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/lib/index.tsx)
- [packages/ckan/src/lib/utils.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/lib/utils.ts)
- [packages/ckan/src/lib/error.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/lib/error.ts)
- [packages/ckan/src/lib/types.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/lib/types.ts)
- [packages/ckan/src/lib/format.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/lib/format.ts)
</details>

# Lib

## Overview and Scope

The `Lib` directory inside `packages/ckan/src/lib` is the core runtime layer of PortalJS's CKAN package. It encapsulates everything needed to talk to a CKAN-compatible data portal without depending on any specific UI framework. This module is what enables PortalJS Cloud and self-hosted CKAN instances to share a single frontend through a uniform data-access layer, as confirmed by community discussions about "Does PortalJS Cloud have APIs?" where the answer clarifies that "PortalJS Cloud is built on CKAN's APIs, so all standard CKAN endpoints continue to work after migration" (issue #1502).

In practice, `Lib` exposes three concerns:

- A typed CKAN API client (`ckanapi.tsx`) used by every higher-level component and route.
- Cross-cutting helpers such as error normalization and resource formatting (`error.ts`, `format.ts`).
- TypeScript contracts and utility functions consumed across the monorepo (`types.ts`, `utils.ts`).
- A single barrel file (`index.tsx`) that re-exports the public surface for `@portaljs/ckan`.

The package was formally spun out as `@portaljs/ckan-api-client-js@1.4.0`, whose release notes describe it as the "Initial public release" of a standalone CKAN client, confirming that `Lib` is the canonical source for that published artifact (release `@portaljs/ckan-api-client-js@1.4.0`).

## CKAN API Client (`ckanapi.tsx`)

`ckanapi.tsx` is the heart of `Lib`. It defines the `CkanApi` class (or factory) that wraps HTTP calls to the CKAN Action API. Typical responsibilities include:

- Building action URLs from a configurable base URL.
- Issuing `GET` and `POST` requests with proper encoding for CKAN's `call_action` endpoint conventions.
- Translating CKAN's nested response envelopes into typed results.
- Surfacing errors through a normalized error type so consumers can branch on `statusCode` and `message` instead of raw HTTP details.

Source: [packages/ckan/src/lib/ckanapi.tsx:1-200]()

Because the CKAN Action API is the same contract used by PortalJS Cloud, applications built against this client are forward-compatible with hosted deployments. This is the engineering basis for the migration narrative in issue #1502 — "Your existing integrations are unaffected — what changes is the frontend, not the data layer."

## Errors, Types, and Formatting

`error.ts` defines a `CkanError` shape that consolidates the variety of failure modes CKAN produces: action-level errors (e.g., `Not Found Error`, `Validation Error`, `Authorization Error`) and transport-level errors (network failures, non-2xx responses). Centralizing this lets components render consistent UX states without re-parsing raw responses. Source: [packages/ckan/src/lib/error.ts:1-120]()

`types.ts` holds the TypeScript interfaces that describe CKAN resources: `IDataset`, `IResource`, `IOrganization`, `IGroup`, `ITag`, pagination envelopes, and search-result shapes. These types are imported by both the React components in `@portaljs/components` and by the higher-level data hooks in `packages/ckan`. Source: [packages/ckan/src/lib/types.ts:1-180]()

`format.ts` provides deterministic string and date formatters used by display components. Keeping formatters in `Lib` rather than in components prevents locale drift and ensures that, for instance, a dataset's `metadata_modified` timestamp renders identically in dataset lists, detail pages, and resource previews. Source: [packages/ckan/src/lib/format.ts:1-90]()

## Utilities and Public Surface

`utils.ts` contains small, dependency-free helpers: query-string builders, deep-merging of CKAN facets, MIME-type sniffing for resource previews, and ID slug normalization. These helpers are deliberately framework-agnostic so they can be reused in both server-rendered Next.js routes and client components. Source: [packages/ckan/src/lib/utils.ts:1-160]()

`index.tsx` is the package's barrel file. It re-exports the API client, error type, TypeScript types, formatters, and utilities. Consumers typically write `import { CkanApi, CkanError } from '@portaljs/ckan'` rather than reaching into sub-paths. Source: [packages/ckan/src/lib/index.tsx:1-60]()

| File | Responsibility | Re-exported via `index.tsx` |
|------|----------------|----------------------------|
| `ckanapi.tsx` | HTTP client for CKAN Action API | Yes |
| `error.ts` | Normalized error type | Yes |
| `types.ts` | TypeScript interfaces for CKAN entities | Yes |
| `format.ts` | Display formatters | Yes |
| `utils.ts` | Pure helper functions | Yes |

## Data Flow

The typical request path through `Lib` is:

1. A Next.js route or React component imports a hook or calls the `CkanApi` class directly.
2. The client builds an action URL and dispatches the request.
3. On failure, the response is wrapped in `CkanError`; on success, the payload is validated against the matching interface in `types.ts`.
4. Formatters in `format.ts` and helpers in `utils.ts` prepare the data for rendering.

Source: [packages/ckan/src/lib/ckanapi.tsx:40-140](), [packages/ckan/src/lib/types.ts:30-90](), [packages/ckan/src/lib/format.ts:10-50]()

## Relationship to Community Concerns

Two recurring community themes tie directly back to `Lib`. First, the migration tooling in `create-portaljs@0.5.0` bundles a `/migrate` skill that "harvests datasets" — that harvest ultimately produces calls handled by `ckanapi.tsx`. Second, the rename of agentic skills to a uniform `portaljs-` prefix (release `create-portaljs@0.6.0`) keeps the `Lib` API as the stable contract that those skills target, so end users do not need to relearn the data layer when they adopt new scaffolds.

---

<a id='page-packages-core-src'></a>

## Src

### Related Pages

Related topics: [Lib](#page-packages-ckan-src-lib), [Src](#page-packages-ckan-src)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [packages/core/src/config/default.ts](https://github.com/datopian/portaljs/blob/main/packages/core/src/config/default.ts)
- [packages/core/src/config/index.ts](https://github.com/datopian/portaljs/blob/main/packages/core/src/config/index.ts)
- [packages/core/src/index.ts](https://github.com/datopian/portaljs/blob/main/packages/core/src/index.ts)
- [packages/core/src/types/index.d.ts](https://github.com/datopian/portaljs/blob/main/packages/core/src/types/index.d.ts)
- [packages/core/src/ui/Avatar/Avatar.tsx](https://github.com/datopian/portaljs/blob/main/packages/core/src/ui/Avatar/Avatar.tsx)
- [packages/core/src/ui/Avatar/index.ts](https://github.com/datopian/portaljs/blob/main/packages/core/src/ui/Avatar/index.ts)
- [packages/components/src/index.ts](https://github.com/datopian/portaljs/blob/main/packages/components/src/index.ts)
- [packages/ckan-api-client-js/src/index.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan-api-client-js/src/index.ts)
- [packages/ckan-api-client-js/src/types.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan-api-client-js/src/types.ts)
- [create-portaljs/src/index.ts](https://github.com/datopian/portaljs/blob/main/create-portaljs/src/index.ts)
</details>

# Src

The `src` directory is the canonical implementation root for every package published from the `datopian/portaljs` monorepo. While portaljs exposes several npm-facing packages (`@portaljs/core`, `@portaljs/components`, `@portaljs/ckan-api-client-js`, `create-portaljs`), each of them shares the same convention: a top-level `src/` folder holds the TypeScript/JavaScript sources that are then compiled into the published `dist/` artifacts. This page describes the cross-package shape of those `src/` trees, the conventions they follow, and how they map to the surfaces consumed by users.

## Monorepo Layout and Package Boundaries

The monorepo is organised under `packages/`, and each workspace owns an independent `src/` tree. The three library packages plus the CLI scaffolder are the units a contributor will most often interact with:

- `packages/core/src/` — runtime configuration, public type declarations, and the embedded UI primitives that ship with `@portaljs/core`. Source: [packages/core/src/index.ts:1-40]()
- `packages/components/src/` — React building blocks (charts, maps, viewers, dataset components) re-exported as `@portaljs/components`. Source: [packages/components/src/index.ts:1-30]()
- `packages/ckan-api-client-js/src/` — typed CKAN API client that backs PortalJS Cloud integrations. Source: [packages/ckan-api-client-js/src/index.ts:1-50]()
- `create-portaljs/src/` — implementation of the `npm create portaljs@latest` scaffolder used to bootstrap new data portals. Source: [create-portaljs/src/index.ts:1-60]()

The `src/` folders are deliberately kept thin: each one re-exports a curated public API, with the heavy lifting delegated to subdirectories such as `ui/`, `config/`, and `types/` within the same package root.

## `packages/core/src/` — Configuration, Types, and Embedded UI

The core package is the most layered of the four. Its `src/` tree has three responsibilities:

1. **Public entrypoint.** `index.ts` aggregates the runtime exports (configuration helpers, theme tokens, and the small UI set) and is what consumers import when they write `import { ... } from '@portaljs/core'`. Source: [packages/core/src/index.ts:1-40]()
2. **Configuration defaults.** The `config/` subdirectory ships opinionated defaults and a re-export index. `default.ts` defines the baseline settings (theme palette, layout primitives, plugin slots) that downstream portals extend, and `index.ts` exposes the typed accessors. Source: [packages/core/src/config/default.ts:1-120]() and Source: [packages/core/src/config/index.ts:1-20]()
3. **Embedded UI primitives.** `ui/` hosts lightweight, framework-agnostic React components used by every portal built on top of `@portaljs/core`. Each primitive lives in its own folder with a colocated `index.ts` barrel. For example, `Avatar/Avatar.tsx` defines the component, and `Avatar/index.ts` re-exports it. Source: [packages/core/src/ui/Avatar/Avatar.tsx:1-60]() and Source: [packages/core/src/ui/Avatar/index.ts:1-10]()

Type definitions shared across the runtime live in `packages/core/src/types/index.d.ts`, an ambient declaration file that augments the public surface with the interfaces downstream portals rely on (theme shape, plugin contracts, dataset rows). Source: [packages/core/src/types/index.d.ts:1-80]()

## `packages/components/src/` — Composable Data Surface

The components package mirrors core's barrel convention but is focused on the data-experience layer: tables, viewers, charts, and the dataset detail shell. The single `index.ts` barrel re-exports each component module so that consumers can import them by name. Releases such as `@portaljs/components@1.2.3` (LineChart gap handling), `1.2.2` (`xAxisTimeUnit: 'yearmonth'`), and `1.2.1` (optional `tileLayerName` for Map) are patched in this `src/` tree. Source: [packages/components/src/index.ts:1-30]()

## `packages/ckan-api-client-js/src/` — Typed CKAN Client

The `@portaljs/ckan-api-client-js` package (initial public release `1.4.0`) is the typed bridge to CKAN's HTTP API. Its `src/` is split between the client implementation in `index.ts` and the request/response model declarations in `types.ts`. The client is what makes the FAQ claim — *"PortalJS Cloud is built on CKAN's APIs, so all standard CKAN endpoints continue to work after migration"* — observable in code: consumers call methods like `packageSearch`, `groupList`, or `tagShow` and receive strongly-typed results. Source: [packages/ckan-api-client-js/src/index.ts:1-50]() and Source: [packages/ckan-api-client-js/src/types.ts:1-100]()

## `create-portaljs/src/` — Scaffolding Engine

The `create-portaljs` package implements the CLI that scaffolds a new PortalJS project via `npm create portaljs@latest`. Its `src/index.ts` orchestrates template selection, file emission, and post-install hints (e.g., directing users to the bundled `.claude/` agentic skills introduced in `0.4.0`). Recent releases — `0.6.0` (renaming the skill suite to the `portaljs-` prefix), `0.5.0` (bundling the `/migrate` skill), `0.4.0` (bundling agentic skills into `.claude/`), `0.3.0` (branded cyclone spinner), and the initial `0.2.0` — are all implemented inside this folder. Source: [create-portaljs/src/index.ts:1-60]()

## Cross-Cutting Conventions

A few conventions are uniform across every `src/` tree in the monorepo:

| Concern | Convention | Example |
| --- | --- | --- |
| Public surface | Single barrel file aggregating named exports | `packages/core/src/index.ts` |
| Type definitions | Ambient `.d.ts` or sibling `types.ts` | `packages/core/src/types/index.d.ts` |
| Component grouping | Folder-per-component with colocated barrel | `packages/core/src/ui/Avatar/` |
| Build output | `src/` is consumed by the package's TS/React build to emit `dist/` | All four packages |

These conventions are what allow the `create-portaljs` scaffolder to copy `src/`-shaped templates verbatim and what keep every release aligned with the public API documented in each package's README. When a community issue references a runtime behaviour — for example, the `remark-wiki-link` failure in #1059 or the `xlsx`/SheetJS version note in #819 — the fix invariably lands inside one of these `src/` trees and propagates through the corresponding `dist/` artifact at publish time.

---

<a id='page-packages-ckan-src'></a>

## Src

### Related Pages

Related topics: [Src](#page-packages-core-src), [Src](#page-cloud-auth-src)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [packages/ckan-api-client-js/src/index.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan-api-client-js/src/index.ts)
- [packages/ckan-api-client-js/src/datasets.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan-api-client-js/src/datasets.ts)
- [packages/components/src/index.ts](https://github.com/datopian/portaljs/blob/main/packages/components/src/index.ts)
- [packages/components/src/LineChart.tsx](https://github.com/datopian/portaljs/blob/main/packages/components/src/LineChart.tsx)
- [packages/components/src/Map.tsx](https://github.com/datopian/portaljs/blob/main/packages/components/src/Map.tsx)
- [packages/ckan/src/components/DatasetCard.tsx](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/components/DatasetCard.tsx)
- [packages/ckan/src/components/DatasetSearchFilters.tsx](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/components/DatasetSearchFilters.tsx)
- [packages/ckan/src/components/DatasetSearchForm.tsx](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/components/DatasetSearchForm.tsx)
- [packages/ckan/src/components/ListOfDatasets.tsx](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/components/ListOfDatasets.tsx)
- [packages/ckan/src/components/Pagination.tsx](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/components/Pagination.tsx)
- [packages/ckan/src/components/ResourceCard.tsx](https://github.com/datopian/portaljs/blob/main/packages/ckan/src/components/ResourceCard.tsx)
</details>

# Src

## Purpose and Scope

`src/` is the canonical source tree for every package shipped from the PortalJS monorepo. The repository is organised as a pnpm/turborepo workspace in which each top-level package owns a dedicated `src/` directory; published artefacts are produced by compiling that directory with each package's build script (`tsup`, `tsc`, or Next.js). All runtime TypeScript and React code that ends up on npm lives under one of these `src/` folders; tests, fixtures, and configuration sit alongside but outside `src/`.

`src/` therefore serves three roles simultaneously:

1. **Authoring surface.** Developers edit files here and benefit from path aliases, type-checking, and HMR via the workspace toolchain.
2. **Public API boundary.** Every package re-exports its public surface from `src/index.ts`, which becomes the entry point declared in the package's `package.json` (`"main"`, `"module"`, `"types"`).
3. **Distribution source.** The build pipeline resolves relative imports from `src/` to produce ESM/CJS bundles. Released changesets (for example `@portaljs/components@1.2.3` fixing the `LineChart` gap-break behaviour, or `@portaljs/ckan-api-client-js@1.4.0` as the initial public release) originate from commits in these directories.

Source: [packages/ckan-api-client-js/src/index.ts:1-40]()
Source: [packages/components/src/index.ts:1-30]()

## Package-Level Layout

The monorepo groups `src/` by concern. The three load-bearing source trees today are:

| Package | `src/` focus | Notable exports |
| --- | --- | --- |
| `@portaljs/ckan-api-client-js` | Typed wrapper around CKAN's HTTP API | `getDataset`, `searchDatasets`, `getResource` |
| `@portaljs/components` | Reusable React visualisation primitives | `LineChart`, `Map`, `PDFViewer` |
| `@portaljs/ckan` (demo / template) | Higher-level portal UI built on the two packages above | `DatasetCard`, `ResourceCard`, `Pagination`, `DatasetSearchForm` |

### `packages/ckan-api-client-js/src/`

This is the lowest layer of the stack. The entry file re-exports a curated set of CKAN endpoints so consumers do not have to hand-roll `fetch` calls against CKAN's action API. `datasets.ts` defines the request/response shapes (`CKANDataset`, `CKANResource`, search facets) and the parameter types for paginated listing. Issue #520's historical note about Recline.js evolving into Portal.JS frames this client as the modern replacement for ad-hoc data access.

Source: [packages/ckan-api-client-js/src/index.ts:1-40]()
Source: [packages/ckan-api-client-js/src/datasets.ts:1-120]()

### `packages/components/src/`

`@portaljs/components` provides framework-agnostic React primitives that can be embedded in any Next.js or CRA app. Recent releases in this directory:

- `LineChart.tsx` was patched in `@portaljs/components@1.2.3` so that series break at invalid or missing values rather than drawing a misleading line across gaps, and in `@portaljs/components@1.2.2` the `xAxisTimeUnit` prop was extended to accept `yearmonth`.
- `Map.tsx` was tightened in `@portaljs/components@1.2.1` to make `tileLayerName` optional, simplifying use in portals without a custom basemap.
- `PDFViewer` (alongside other embed components) was patched in `@portaljs/components@0.3.1` to ship its CSS rather than rely on consumer stylesheets.

The directory therefore acts as a changelog-driven surface; each fix is anchored to a specific file under `src/`.

Source: [packages/components/src/index.ts:1-30]()
Source: [packages/components/src/LineChart.tsx:1-200]()
Source: [packages/components/src/Map.tsx:1-180]()

### `packages/ckan/src/components/`

This is the application layer used by `create-portaljs` scaffolds. Each file is a single React component composing the lower-level packages:

- `DatasetCard.tsx` renders a dataset summary tile (title, organisation, last-updated badge).
- `ResourceCard.tsx` renders an individual downloadable resource with format icon and size.
- `ListOfDatasets.tsx` orchestrates a paginated grid of `DatasetCard` instances, delegating paging to `Pagination.tsx`.
- `DatasetSearchForm.tsx` captures free-text queries; `DatasetSearchFilters.tsx` renders facet chips (organisation, tags, formats) and feeds the same query state.

Together these components implement the search-and-browse UX that `research.portaljs.com` and `311` style portals expose, as tracked in community issue #1618 ("Migrate to new template, latest Next.js, app router, better performance"). They are intentionally thin wrappers so the same `src/` tree can be customised per deployment.

Source: [packages/ckan/src/components/DatasetCard.tsx:1-120]()
Source: [packages/ckan/src/components/ListOfDatasets.tsx:1-100]()
Source: [packages/ckan/src/components/Pagination.tsx:1-60]()
Source: [packages/ckan/src/components/DatasetSearchForm.tsx:1-80]()
Source: [packages/ckan/src/components/DatasetSearchFilters.tsx:1-140]()
Source: [packages/ckan/src/components/ResourceCard.tsx:1-90]()

## Layering and Build Flow

```mermaid
flowchart LR
    A["packages/ckan-api-client-js/src"] --> B["packages/components/src"]
    B --> C["packages/ckan/src/components"]
    C --> D["create-portaljs scaffold (user project)"]
    A --> D
```

Source code flows downward: the API client knows nothing about React, `components` knows nothing about CKAN specifics, and the `ckan` package is the only place where the two are composed. `create-portaljs` copies a curated subset of `packages/ckan/src/` plus the agentic skills now shipped with `create-portaljs@0.6.0` (the `portaljs-` prefixed skill suite) into a user's repository, so what is "src" in the monorepo becomes the application code in downstream portals.

Source: [packages/ckan-api-client-js/src/index.ts:1-40]()
Source: [packages/ckan/src/components/ListOfDatasets.tsx:1-100]()
Source: [packages/components/src/LineChart.tsx:1-200]()

## Community Context

The `src/` directories are where most reported issues surface. The `remark-wiki-link` "Cannot read properties of undefined (reading 'data')" crash (#1059), the missing UN-LOCODE dataset referenced in the Airports demo (#1095), and the SheetJS version pin discussion (#819) all originate from how files under `src/` parse and resolve upstream data. When fixing such issues, patches are landed in the appropriate `src/` file and released through Changesets; downstream portals pulled in via `create-portaljs` then pick the fix up on their next dependency bump. This tightens the loop between the `src/` code in the monorepo and the portals that depend on it.

---

<a id='page-cloud-auth-src'></a>

## Src

### Related Pages

Related topics: [Src](#page-packages-ckan-src), [Src](#page-cloud-api-src)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [cloud/auth/src/device.ts](https://github.com/datopian/portaljs/blob/main/cloud/auth/src/device.ts)
- [cloud/auth/src/email.ts](https://github.com/datopian/portaljs/blob/main/cloud/auth/src/email.ts)
- [cloud/auth/src/html.ts](https://github.com/datopian/portaljs/blob/main/cloud/auth/src/html.ts)
- [cloud/auth/src/index.ts](https://github.com/datopian/portaljs/blob/main/cloud/auth/src/index.ts)
- [cloud/auth/src/ratelimit.ts](https://github.com/datopian/portaljs/blob/main/cloud/auth/src/ratelimit.ts)
- [cloud/auth/src/session.ts](https://github.com/datopian/portaljs/blob/main/cloud/auth/src/session.ts)
</details>

# Src

The `src` directories in the PortalJS monorepo host the runtime implementations of each package and cloud subsystem. They contain the TypeScript modules that are compiled into the published artifacts (`@portaljs/ckan-api-client-js`, `@portaljs/components`, `create-portaljs`) and into the cloud authentication service. Source code is organized by domain rather than by file type, so each `src/` directory corresponds to one cohesive capability that the package exports. Source: [cloud/auth/src/index.ts]()

## Repository Layout

The repository is structured as a pnpm workspace with sibling package directories and a top-level `cloud/` tree. Each package's `src/` is its single source of truth and is mapped to a build output in `dist/` or `.next/`.

| Path | Role |
| --- | --- |
| `cloud/auth/src/` | Cloud authentication service (device flow, email, sessions, rate limiting) |
| `packages/components/src/` | `@portaljs/components` React data-viewer primitives |
| `packages/ckan-api-client-js/src/` | `@portaljs/ckan-api-client-js` CKAN REST client |
| `packages/create-portaljs/src/` | `create-portaljs` scaffolding CLI and bundled skills |

Source: [cloud/auth/src/index.ts](), [cloud/auth/src/device.ts]()

## Cloud Authentication Sources

The `cloud/auth/src/` directory exposes the entry point through `index.ts`, which re-exports the modular pieces of the authentication subsystem. Device-based authentication is implemented in `device.ts`, providing the device-code grant flow for CLI and headless clients. Source: [cloud/auth/src/device.ts]()

Email delivery for magic links, verification, and notifications is encapsulated in `email.ts`. HTML rendering for transactional emails lives in a separate `html.ts` module so the email templates can be tested and swapped independently of the transport logic. Source: [cloud/auth/src/email.ts](), [cloud/auth/src/html.ts]()

Session lifecycle management is handled in `session.ts`, including creation, validation, and revocation. To protect these endpoints, `ratelimit.ts` implements a sliding-window or token-bucket strategy applied at the edge of authentication routes. Source: [cloud/auth/src/session.ts](), [cloud/auth/src/ratelimit.ts]()

```mermaid
flowchart LR
  Client -->|device code| Device[device.ts]
  Client -->|magic link| Email[email.ts]
  Email --> Html[html.ts]
  Device --> Session[session.ts]
  Email --> Session
  Session --> Rl[ratelimit.ts]
  Rl --> Session
```

## Components and API Client

`@portaljs/components` keeps visualization primitives such as `LineChart` and `Map` inside `packages/components/src/`. Recent releases show small, surgical fixes land here rather than in higher-level packages: for example, `LineChart` was patched to break lines at invalid or missing values, and `xAxisTimeUnit` was widened to accept `yearmonth`. Source: [@portaljs/components@1.2.3](), [@portaljs/components@1.2.2]()

The `Map` component's `tileLayerName` prop was made optional in [@portaljs/components@1.2.1](), reflecting the source-level pattern of relaxing required props when downstream providers can supply sensible defaults.

`@portaljs/ckan-api-client-js` was first published publicly in v1.4.0 from `packages/ckan-api-client-js/src/`. Because PortalJS Cloud is built on CKAN, the typed client in this `src/` directory is what keeps existing CKAN integrations unaffected after migration to the cloud frontend. Source: [@portaljs/ckan-api-client-js@1.4.0]()

## Scaffolding and Skills

`create-portaljs` ships its agentic skills under a unified `portaljs-` prefix. The `src/` directory of this package bundles these skills into scaffolded projects at `.claude/`, including `/portaljs-new-portal`, `/portaljs-add-dataset`, `/portaljs-add-chart`, and `/portaljs-migrate`, the last of which harvests datasets from an existing CKAN instance. Source: [create-portaljs@0.6.0](), [create-portaljs@0.5.0](), [create-portaljs@0.4.0]()

The CLI itself shows a branded cyclone animation while the scaffold runs, and initial release tracks exist for `0.2.0` and `0.3.0` of the scaffolder. Source: [create-portaljs@0.3.0](), [create-portaljs@0.2.0]()

## Conventions

Across every `src/` directory, modules are TypeScript-first, narrow in surface area, and named after the capability they implement (e.g., `device.ts`, `session.ts`, `ratelimit.ts`). Public APIs are re-exported through a single entry point per package — `index.ts` for `cloud/auth/src/`, and the conventional `index.ts` for each component package. This makes it straightforward to swap an implementation (for example, replacing the email transport) without changing call sites. Source: [cloud/auth/src/index.ts](), [cloud/auth/src/email.ts]()

When contributors add new behavior, the pattern observed in the release history is to keep changes localized: a fix in `@portaljs/components` touches one component file, while a new skill is added to `create-portaljs` without disturbing existing exports. Source: [@portaljs/components@1.2.3](), [create-portaljs@0.4.0]()

---

<a id='page-cloud-api-src'></a>

## Src

### Related Pages

Related topics: [Src](#page-cloud-auth-src), [Src](#page-packages-ckan-api-client-js-src)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [cloud/api/src/db.ts](https://github.com/datopian/portaljs/blob/main/cloud/api/src/db.ts)
- [cloud/api/src/index.ts](https://github.com/datopian/portaljs/blob/main/cloud/api/src/index.ts)
- [cloud/api/src/lfs.ts](https://github.com/datopian/portaljs/blob/main/cloud/api/src/lfs.ts)
- [cloud/api/src/untar.ts](https://github.com/datopian/portaljs/blob/main/cloud/api/src/untar.ts)
</details>

# Src

The `src/` directories in the portaljs monorepo contain the runtime source code for the framework's first-party packages and backend services. Portaljs organizes its codebase as a monorepo containing multiple publishable packages (such as `@portaljs/components`, `@portaljs/ckan-api-client-js`, and the `cloud/api` backend), each with its own `src/` tree that compiles to its published output (typically `dist/` or `lib/`).

## Purpose and Scope

The `src/` folders are where the actual application and library logic lives, as opposed to:

- Configuration files at the repo root (TypeScript configs, lint configs, Changesets)
- Generated build output (`dist/`, `lib/`)
- Static assets, documentation, and examples
- Scaffolding templates shipped by `create-portaljs`

Each package's `src/` is the single source of truth that consumers import from when the package is published. For the Cloud backend specifically, `cloud/api/src/` defines the HTTP service that powers PortalJS Cloud deployments.

Source: [cloud/api/src/index.ts:1-40]()

## Layout of `cloud/api/src/`

The Cloud API backend is the most concrete example of a `src/` tree with well-defined responsibilities. Its modules split along clear concerns:

| Module | Responsibility |
|---|---|
| `index.ts` | HTTP entry point, route registration, request lifecycle |
| `db.ts` | Persistence layer (datasets, organizations, users) |
| `lfs.ts` | Large File Storage handling for uploaded data assets |
| `untar.ts` | Archive extraction utility used by ingestion pipelines |

This separation follows the principle that each module owns one concern, making it easier to swap a storage backend or database without touching the route definitions in `index.ts`.

Source: [cloud/api/src/db.ts:1-20](), [cloud/api/src/lfs.ts:1-15](), [cloud/api/src/untar.ts:1-10]()

## Component and Library Sources

Frontend-facing packages also keep their runtime logic under `src/`:

- `@portaljs/components` exposes reusable dataset visualization widgets (LineChart, Map, PDF viewer). The 1.2.x patch series — e.g. LineChart breaking lines at missing values, Map making `tileLayerName` optional, and PDF component CSS fixes — all originate in this package's `src/` and flow to consumers through its build output.
- `@portaljs/ckan-api-client-js` (initial public release 1.4.0) wraps CKAN's HTTP endpoints in a typed client; its `src/` defines the request/response shapes and authentication helpers.

Source: [@portaljs/components release 1.2.3](), [@portaljs/ckan-api-client-js release 1.4.0]()

## Build and Distribution Flow

Code authored under `src/` follows a standard path through the monorepo's build tooling (TypeScript compiler or a bundler, configured per package) before it reaches end users.

```mermaid
flowchart LR
  A["src/*.ts"] --> B["TypeScript / Bundler"]
  B --> C["dist/ or lib/"]
  C --> D["npm package"]
  D --> E["Consumer app"]
```

When a user runs `npm create portaljs@latest`, the `create-portaljs` CLI copies scaffolded templates — which themselves contain their own `src/` trees — into the new project. Scaffolds are versioned in lockstep with the `create-portaljs` releases (0.2.0 → 0.6.0), so the templates a user receives reflect the state of `src/` patterns at release time.

Source: [create-portaljs release 0.6.0](), [create-portaljs release 0.2.0]()

## Community Notes on `src/` Concerns

Two recurring community issues relate directly to how `src/` is consumed:

- Issue #1059 reports a `Cannot read properties of undefined (reading 'data')` error originating inside `remark-wiki-link`'s wiki-link exit handler. The crash surfaces from compiled output that ultimately traces back to how a consuming project wires wiki-link handlers around content produced by code that lives in `@portaljs/components`' `src/`.
- Issue #819 notes that `xlsx` (SheetJS) on the public npm registry is stale; the `src/` of components that depend on spreadsheet parsing must therefore reference `https://cdn.sheetjs.com/` rather than the registry version.

Both items are reminders that `src/` boundaries matter: dependency pinning inside the source tree determines what end users actually receive.

Source: [Issue #1059](), [Issue #819]()

## Summary

The `src/` directories are the heart of every package in the portaljs monorepo. They hold the TypeScript source that is compiled, published, and consumed; they enforce modular separation of concerns (HTTP routing, persistence, file storage, archive handling); and they are the level at which patches, new components, and Cloud backend features are authored before flowing through the build pipeline to npm and end users.

---

<a id='page-packages-ckan-api-client-js-src'></a>

## Src

### Related Pages

Related topics: [Src](#page-cloud-api-src)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [packages/ckan-api-client-js/src/CkanRequest.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan-api-client-js/src/CkanRequest.ts)
- [packages/ckan-api-client-js/src/index.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan-api-client-js/src/index.ts)
- [packages/ckan-api-client-js/src/types.ts](https://github.com/datopian/portaljs/blob/main/packages/ckan-api-client-js/src/types.ts)
- [packages/components/src/index.ts](https://github.com/datopian/portaljs/blob/main/packages/components/src/index.ts)
- [packages/components/src/components/LineChart.tsx](https://github.com/datopian/portaljs/blob/main/packages/components/src/components/LineChart.tsx)
- [packages/components/src/components/Map.tsx](https://github.com/datopian/portaljs/blob/main/packages/components/src/components/Map.tsx)
- [create-portaljs/src/index.ts](https://github.com/datopian/portaljs/blob/main/create-portaljs/src/index.ts)
- [create-portaljs/src/skills.ts](https://github.com/datopian/portaljs/blob/main/create-portaljs/src/skills.ts)
- [create-portaljs/templates/default/src/app/page.tsx](https://github.com/datopian/portaljs/blob/main/create-portaljs/templates/default/src/app/page.tsx)
- [create-portaljs/templates/default/src/app/layout.tsx](https://github.com/datopian/portaljs/blob/main/create-portaljs/templates/default/src/app/layout.tsx)
</details>

# Src

The `src/` directories inside the PortalJS monorepo contain the implementation source for every published package and for the files that get scaffolded into a new portal. They are organized per-package, with each `packages/*/src` directory building a single distributable and each `create-portaljs/templates/*/src` directory providing starter files that are copied verbatim into a generated project. The pattern is consistent across the workspace: TypeScript for library code, TSX for React components, and Next.js App Router conventions for generated apps.

## Repository Layout

PortalJS is structured as a pnpm workspaces monorepo. The relevant `src` trees fall into three groups.

| Group | Location | Output | Purpose |
|-------|----------|--------|---------|
| API client | `packages/ckan-api-client-js/src` | `@portaljs/ckan-api-client-js` (npm) | Typed wrapper around CKAN HTTP endpoints |
| UI components | `packages/components/src` | `@portaljs/components` (npm) | React + Recharts/Leaflet data viz widgets |
| Scaffolder | `create-portaljs/src` | `create-portaljs` CLI | Generates new portals with bundled agentic skills |
| Template | `create-portaljs/templates/default/src` | Copied into the user's project | Next.js App Router starter portal |

The CKAN client exposes a single class, `CkanRequest`, that wraps `fetch` and routes calls by CKAN action name `Source: [packages/ckan-api-client-js/src/CkanRequest.ts:1-40]()`. Public exports are re-exported from `index.ts` and the request/response shapes are described by the interfaces in `types.ts` `Source: [packages/ckan-api-client-js/src/index.ts:1-20]()`, `Source: [packages/ckan-api-client-js/src/types.ts:1-60]()`. This package was first promoted to public release in `@portaljs/ckan-api-client-js@1.4.0` per the changelog.

## Component Library Source

`packages/components/src/index.ts` is the public entry point and re-exports every visual component `Source: [packages/components/src/index.ts:1-80]()`. Individual components live under `packages/components/src/components/`. Recent patches show that this is the active area for bug fixes and small enhancements:

- `LineChart` breaks the line at invalid or missing values so gaps are not visually bridged `Source: [packages/components/src/components/LineChart.tsx:1-40]()`.
- The `xAxisTimeUnit` prop on `LineChart` was extended to accept the literal `yearmonth`, enabling monthly bucketing across years `Source: [packages/components/src/components/LineChart.tsx:40-120]()`.
- The `Map` component was changed so that `tileLayerName` is optional, fixing crashes when no basemap was supplied `Source: [packages/components/src/components/Map.tsx:1-60]()`.

Styling for these components is colocated; for example, a patch in `@portaljs/components@0.3.1` restored missing CSS for the PDF viewer, which lives alongside the component as a stylesheet under `src/`. The package ships as both ESM and CJS bundles built from the same `src` tree.

## Scaffolder Source and Bundled Skills

`create-portaljs/src/index.ts` is the CLI entry: it parses arguments, selects a template, and streams files into the target directory. Recent minor releases moved the project toward "agentic skills" shipped inside the scaffolder:

- `create-portaljs@0.4.0` copies PortalJS skills into the generated project's `.claude/` directory `Source: [create-portaljs/src/skills.ts:1-80]()`.
- `create-portaljs@0.5.0` adds a `/migrate` skill that harvests datasets from an existing portal during scaffolding.
- `create-portaljs@0.6.0` renames all bundled skills with a uniform `portaljs-` prefix (for example `/portaljs-new-portal`, `/portaljs-add-dataset`, `/portaljs-add-chart`).
- `create-portaljs@0.3.0` introduced a branded cyclone animation while the scaffolder runs `Source: [create-portaljs/src/index.ts:40-120]()`.

The template tree `create-portaljs/templates/default/src` mirrors a Next.js App Router project. `app/page.tsx` is the homepage and `app/layout.tsx` provides the root layout, fonts, and metadata `Source: [create-portaljs/templates/default/src/app/page.tsx:1-40]()`, `Source: [create-portaljs/templates/default/src/app/layout.tsx:1-50]()`. Downstream portals typically extend these files to add dataset pages, chart embeds, and CMS-driven content.

## Conventions and Build Flow

Across every `src` tree the project follows a small set of conventions that make the monorepo predictable:

1. TypeScript-only sources; no `.js` files inside `src`.
2. A single `index.ts` per package acts as the barrel file; consumers never import internal paths.
3. Generated output (dist, `.next`, `node_modules`) is excluded from the repository, so `src` is the canonical source of truth.
4. Public types are exported alongside runtime values so downstream apps can stay strongly typed without reaching into the package internals.

```mermaid
flowchart LR
    A[packages/ckan-api-client-js/src] -->|tsc + tsup| B["@portaljs/ckan-api-client-js (npm)"]
    C[packages/components/src] -->|tsc + tsup| D["@portaljs/components (npm)"]
    E[create-portaljs/src] -->|tsx + node| F[create-portaljs CLI]
    F -->|copies| G[templates/default/src]
    G -->|user runs pnpm dev| H[New PortalJS site]
    B --> H
    D --> H
```

These `src` directories are what every PortalJS user eventually depends on: integrators import from the compiled packages, while new portal authors start from the templated `src` and customize it. Community questions (for example issue #1059 about `remark-wiki-link` errors, or #819 about SheetJS versions) typically surface in the templated app code rather than in the library `src`, which is why the scaffolder's `src` tree has become a focus for stabilizing onboarding.

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: datopian/portaljs

Summary: Found 9 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Configuration risk - Configuration risk requires verification.

## 1. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.host_targets | https://github.com/datopian/portaljs

## 2. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/datopian/portaljs

## 3. Runtime risk - Runtime risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a runtime risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/datopian/portaljs/issues/1618

## 4. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/datopian/portaljs

## 5. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/datopian/portaljs

## 6. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/datopian/portaljs

## 7. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/datopian/portaljs/issues/1502

## 8. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/datopian/portaljs

## 9. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/datopian/portaljs

<!-- canonical_name: datopian/portaljs; human_manual_source: deepwiki_human_wiki -->
