Skip to content

Package reference

The framework is distributed as six packages under the @drawnagency npm scope. All are published as ESM with declaration files. The build toolchain (tsup and typescript) is declared only in the root devDependencies — by design, so packages can build via pnpm’s workspace hoisting without each declaring the toolchain separately.

Each package’s build script runs:

Terminal window
tsup && rm -f tsconfig.tsbuildinfo && tsc --emitDeclarationOnly

tsup produces the Node-compatible ESM bundle with correct .js extensions; tsc --emitDeclarationOnly writes .d.ts files alongside it.


Purpose: The shared foundation. Contains the section registry, defineSection helper, all Zod schemas, shared React components (editor UI, viewer section shells), auth types, media types, and utility libraries. Every other package depends on this one.

Internal deps: None.

Key exports — one row per package.json exports subpath (40, as of 0.1.113; regenerate this table from exports when it changes):

Entry pathContents
. (root)Barrel: re-exports ./schemas, ./lib, ./auth, ./media, ./deploy, ./storage, ./collab/types, plus setCollabProvider/getCollabProvider
./schemasBarrel for all Zod schemas (sections, site-config, nav, media, audience, auth, link, documents, media-grid-options)
./schemas/authSessionSchema, AudienceSchema, SiteUserSchema, RoleSchema and their types
./schemas/audienceAudience naming/colour schemas, viewer credential + password schemas, normalizeUsername, slugifyAudienceName
./schemas/blockLayoutEnvelopeSchema, MAX_BLOCK_DEPTH, getBlockChildren, blockDepth, assertMaxDepth — the recursive container block model
./schemas/linkLinkValueSchema, LinkTargetSchema, isSafeHref, DEFAULT_LINK
./schemas/documentsDocumentManifestSchema, DOCUMENT_MANIFEST_PATH, git/bucket size caps, MIME↔extension maps, documentFileUrl/documentGitPath/documentBucketPath
./schemas/deckDeck JSON schema and constants (stage size, layout/component/media-kind enums, DECK_MAX_SLIDES)
./schemas/deck-metaDeckMetaSchema (grants sidecar), parseDeckMeta, serializeDeckMeta
./libBrowser-safe helper barrel: env, cn, audiencePasses, chatbot name/phrase resolvers, generateNavLinks, toSectionId, navEntries, pagesInNavOrder, groupOfPage, groupDisplayLabel, reconcileNav
./lib/dexieThe editor’s offline draft store (IndexedDB via Dexie): unsaved edits, working index/nav, pending media, content cache
./lib/envenv() — import.meta.env lookup with the guarded process.env fallback
./lib/video-urlparseVideoUrl, embedUrl for YouTube/Vimeo/Instagram links
./lib/platform-brokerplatformBroker, platformBrokerStream, isPlatformMode, PlatformBrokerError — the client-site side of the admin broker
./lib/audience-secretsealAudiencePassword/openAudiencePassword — reversible storage so an audience password can be shown again
./lib/registrySection registry: defineSection, createRegistry, registerSection, registerSchema, getSection, getAllSections, settings-schema types
./lib/index-opsIndexOpSchema, applyIndexOps — the pure structural-edit engine shared by the editor and the MCP connector
./lib/deck-accesscanCreateDeck, canAccessDeck, isDeckOwner, deckOwnerFor, deckDraftScope
./lib/deck-media-refsExtracts every portal media id a deck references (publish-time GC keep-set)
./lib/deck-toolupdateDeckTool — the update_deck LLM tool definition
./lib/deck-dexiePer-browser deck draft store (putDeckDraft, listDeckDrafts, markDeckDraftPublished, …)
./lib/deck-media-assetsdeckMediaAssets — the audience-filtered media catalog offered to deck chat
./lib/pdfjs-assetsURL contract for pdf.js runtime assets shared by the viewer and core’s dev/build hooks
./lib/media-foldersMedia-library folder tree helpers (buildFolderTree, canMoveFolder, uniqueFolderName, …)
./lib/migrate-sections-transformupgradeLegacySection — rewrites the three legacy layout section types into container
./collab/typesThe whole collab contract: peer/lock/update message schemas, CollabStatus, heartbeat and stale-lock constants
./collab/provider-registrysetCollabProvider/getCollabProvider singleton (wired by virtual:portal/collab in core)
./collab/rebasePure rebase merge: rebaseContent, reconcileRemoteIndex, applyRemoteIndexUpdate (409 auto-recovery and live save-sync)
./components/sections/registerensureSectionsRegistered() — registers every built-in section component
./components/sections/register-schemasensureSchemasRegistered() — registers built-in schemas only (server/MCP paths that never render)
./components/sections/brand-guidebrandGuideSectionDefs + ColorSpaceSchema/ColorItemSchema
./components/editorEditor chrome pieces: DragHandle, InsertButton, DeleteButton, SettingsButton, SettingsForm, IndicatorPill, …
./components/primitivesAuthoring surface for custom sections: EditableRichText, EditablePlainText, EditableList, EditableGrid, ImageDropZone, ResolvedMedia, …
./documentThe document (present-mode) engine and components — a sealed subtree: types, settings schema, paginate, harvest, DocPage, DocCoverPage, …
./deckThe deck runtime (DeckPlayer, DeckStage, layouts) — sealed subtree, never on a viewer page’s import graph
./deck-builderDeckBuilderApp, starterDeck, newDeckId — the /decks authoring surface, sealed like ./deck
./authderiveUiCapabilities, session/audience cookie signing and verification, requireSessionSecret, isSameOriginRequest, safeNextPath, auth errors
./mediaMedia provider registry (setMediaProvider/getMediaProvider), types, resolve/queue utils, generateVideoPoster, composePreviewImage
./storageStorageProvider types and errors (StorageConflictError, NothingToPublishError, IdempotencyLookupUnavailableError)
./types/databaseGenerated Supabase Database types (pnpm generate-types)

The package ships both dist/ (compiled) and src/ (source). The Astro integration aliases all primitives import paths to src/ during dev and SSR builds — see SSR / Netlify gotchas.


Purpose: The Cloudflare R2 storage adapter — the optional bucket half of the storage model (videos, large documents). Ships a dependency-free SigV4 signer, so it pulls in no AWS SDK. Imported by the template’s default portal.config.mjs.

Internal deps: @drawnagency/primitives (workspace:^) — for the AssetStore / DocumentStore interfaces.

Key exports:

Entry pathContents
. (root)r2Assets() → AssetStore, r2Documents() → DocumentStore

Standalone (“.env”) mode reads R2_ACCOUNT_ID, R2_BUCKET, the access key pair and PORTAL_SITE_ID; platform mode goes through the broker instead. A portal may have no bucket at all — GitHub-backed media works without one, so never assume these are configured. See the storage-model notes in the SSR / Netlify rules.


Purpose: CLI tooling and Claude skills for populating client sites. Provides the authoring binary (used via pnpm exec authoring) with subcommands validate, process-images and upload-video. Also ships the /populate-site skill into client repos via postinstall.

Internal deps: @drawnagency/primitives (workspace:^), @drawnagency/assets-r2 (workspace:^ — the R2 signer behind upload-video).

Key exports:

Entry pathContents
. (root)Public API for authoring utilities
bin/authoringCLI binary: validate --project <path>, process-images --project <path>, upload-video <file> --project <path> --width <n> --height <n>

files in package.json includes dist/, skills/, and scripts/. The skills/ directory contains the /populate-site skill; scripts/link-skills.mjs is run by client repos’ postinstall to symlink it into .claude/skills/.


Purpose: GitHub storage adapter. Handles reading and writing content files and media to a GitHub repository via the GitHub REST API (@octokit/rest). Exports githubStorage(), the StorageProvider implementation used in portal.config.mjs.

Internal deps: @drawnagency/primitives (workspace:^).

Key exports:

Entry pathContents
. (root)githubStorage() — returns a StorageProvider

Purpose: Supabase auth adapter. Wraps @supabase/supabase-js and @supabase/ssr to implement the AuthProvider interface from @drawnagency/primitives. Handles OAuth sign-in, session cookies, audience management, and invite flows.

Internal deps: @drawnagency/primitives (workspace:^).

Key exports:

Entry pathContents
. (root)supabaseAuth() — returns an AuthProvider; createSupabaseAuth() for advanced use

Purpose: The Astro integration and all server-side page/API routes. Injects viewer and editor routes, middleware, API endpoints, and the Vite plugin configuration into the client Astro project. Also exports the defineConfig helper and the password-only auth adapter.

Internal deps: @drawnagency/primitives (workspace:^), @drawnagency/github (workspace:^).

Key exports:

Entry pathContents
. (root)portalIntegration (the Astro integration), defineConfig, supabaseDeployStatus, netlifyDeployStatus, githubMedia. Do not import this in browser code or portal.config.mjs — it pulls in node:url, node:fs, etc.
./configdefineConfig() and PortalConfig — the import for portal.config.mjs
./passwordcreatePasswordAuth() — the password-only auth adapter
./content-opsvalidateContent, applyContentWrite, publishContent — the single write path shared by /api/save and the MCP connector
./brand-chatassembleGroundingCorpus, assembleBrandChat, stripPresentation — pure, audience-scoped prompt assembly for the brand chatbot
./lib/loaderloadContentFromGitHub, loadMediaManifestFromGitHub, loadDocumentManifestFromGitHub, getContentSnapshot, plus the primitives loader re-exports
./styles/base.cssBase Tailwind CSS; client repos @import this from their own src/styles/base.css

The ./config, ./password, ./content-ops, ./brand-chat and ./lib/loader entry points resolve to src/ files directly (not dist/) so they can be used in contexts where dist/ may not have been built (e.g. apps/admin uses only import type from core; portal.config.mjs must import defineConfig from ./config specifically).


@drawnagency/platform (private — not published)

Section titled “@drawnagency/platform (private — not published)”

Purpose: The platform-side provisioning and access core shared by apps/admin and apps/mcp. Contains provisionSite/teardownSite, the GitHub App/Netlify/Cloudflare provider clients, the service-role Supabase admin client, and checkPlatformAccess (the platform_users/allowed_signups gate).

Internal deps: @drawnagency/primitives — import type only (no runtime import, so no primitives dist/ is required by consumers).

Key difference from the published six: every export points at ./src/*.ts — there is no build step and no dist/. apps/admin bundles it from source via vite.ssr.noExternal (its Netlify build runs without build:packages); apps/mcp bundles it with esbuild. CI’s check-admin-isolation gate fails the build if this package ever grows a dist-pointing export. It is excluded from scripts/publish.sh and has no build script, so “build all six packages” remains literally six.