* Grouped example API for Gantt persistence, reporting, and framework metadata.
* The recipes wrap REST, GraphQL, Supabase/PostgreSQL, JSON snapshot, and print
* helpers with injected state and transport dependencies. They are designed as
* copyable integration patterns for applications that own the backend, file
* picker, print shell, or framework-specific demo catalog.
import type { ProjectSnapshot } from '@revolist/gantt';
parseProjectSnapshotJson,
type ProjectSnapshotParseResult,
} from '@revolist/gantt';
ProjectSnapshotGraphqlAdapter,
type ProjectSnapshotGraphqlAdapterOptions,
type ProjectSnapshotGraphqlRequestOptions,
} from './graphql-adapter';
buildCreateProjectSnapshotTableSql,
buildLoadProjectSnapshotSql,
buildSaveProjectSnapshotSql,
ProjectSnapshotSupabaseAdapter,
type ProjectSnapshotPostgresOptions,
type ProjectSnapshotPostgresRow,
} from './postgres-persistence';
type GanttPrintDateRange,
} from './print-pdf-recipe';
ProjectSnapshotRestAdapter,
type ProjectSnapshotRestAdapterOptions,
type ProjectSnapshotRestRequestOptions,
export type GanttPersistenceRecipeId =
| 'rest-project-snapshot'
| 'graphql-project-snapshot'
| 'supabase-postgres-project-snapshot'
| 'postgres-sql-project-snapshot'
| 'json-snapshot-helpers'
| 'portal-resource-filter';
export type GanttPersistenceRecipeChannel =
export type GanttPersistenceRecipeFramework =
export interface GanttPersistenceRecipeMetadata {
readonly id: GanttPersistenceRecipeId;
readonly channel: GanttPersistenceRecipeChannel;
readonly frameworks: readonly GanttPersistenceRecipeFramework[];
readonly sourceFiles: readonly string[];
readonly dependencies: readonly string[];
readonly summary: string;
export interface ProjectSnapshotState {
getProject(): ProjectSnapshot;
setProject(project: ProjectSnapshot): void;
export interface ProjectSnapshotPersistenceRecipe {
readonly metadata: GanttPersistenceRecipeMetadata;
| ProjectSnapshotRestRequestOptions
| ProjectSnapshotGraphqlRequestOptions,
): Promise<ProjectSnapshot>;
| ProjectSnapshotRestRequestOptions
| ProjectSnapshotGraphqlRequestOptions,
export interface ProjectSnapshotStorageRecipe {
readonly metadata: GanttPersistenceRecipeMetadata;
load(projectId: string): Promise<ProjectSnapshot | null>;
save(): Promise<ProjectSnapshot>;
export interface ProjectSnapshotSqlExecutor {
statement: PostgresStatement,
): Promise<{ readonly rows: readonly T[] }>;
export interface ProjectSnapshotPostgresRecipe {
readonly metadata: GanttPersistenceRecipeMetadata;
createTable(): Promise<void>;
load(projectId: string): Promise<ProjectSnapshot | null>;
save(): Promise<ProjectSnapshot>;
buildReportingStatement(projectId: string): PostgresStatement;
export interface GanttPrintPdfRecipeOptions {
readonly target: Element | null | undefined;
readonly document?: Document;
readonly window?: GanttPrintOptions['window'];
readonly now?: () => Date | string;
export interface GanttPrintPdfRecipe {
readonly metadata: GanttPersistenceRecipeMetadata;
project: ProjectSnapshot,
range?: GanttPrintDateRange,
export interface ProjectSnapshotJsonRecipeOptions {
readonly readText?: () => string | Promise<string>;
) => void | Promise<void>;
readonly fileName?: (project: ProjectSnapshot) => string;
export interface ProjectSnapshotJsonRecipe {
readonly metadata: GanttPersistenceRecipeMetadata;
clone(project: ProjectSnapshot): ProjectSnapshot;
export(project: ProjectSnapshot, space?: number | string): string;
parse(json: string): ProjectSnapshotParseResult;
load(): Promise<ProjectSnapshot>;
save(project: ProjectSnapshot, space?: number | string): Promise<string>;
export const GANTT_PERSISTENCE_QUICK_WIN_EXAMPLES: readonly GanttPersistenceRecipeMetadata[] =
id: 'rest-project-snapshot',
title: 'REST project snapshot persistence',
frameworks: ['framework-neutral'],
'examples/components/src/components/gantt/recipes/rest-adapter.ts',
'packages/gantt/src/gantt/core/project-snapshot-json.ts',
'Loads and saves a serialized ProjectSnapshot through an injected fetch implementation.',
id: 'graphql-project-snapshot',
title: 'GraphQL project snapshot persistence',
frameworks: ['framework-neutral'],
'examples/components/src/components/gantt/recipes/graphql-adapter.ts',
'packages/gantt/src/gantt/core/project-snapshot-json.ts',
'Uses configurable GraphQL operations and variables to load and save ProjectSnapshot JSON.',
id: 'supabase-postgres-project-snapshot',
title: 'Supabase/PostgreSQL project snapshot persistence',
frameworks: ['framework-neutral'],
'examples/components/src/components/gantt/recipes/postgres-persistence.ts',
'packages/gantt/src/gantt/core/project-snapshot-json.ts',
dependencies: ['Supabase-like client'],
'Persists JSONB snapshots with a minimal Supabase-style client shape.',
id: 'postgres-sql-project-snapshot',
title: 'PostgreSQL SQL project snapshot persistence',
frameworks: ['framework-neutral'],
'examples/components/src/components/gantt/recipes/postgres-persistence.ts',
dependencies: ['PostgreSQL query executor'],
'Builds parameterized DDL, load, save, and reporting SQL without owning the database client.',
title: 'Print/PDF export',
frameworks: ['framework-neutral', 'vanilla'],
'examples/components/src/components/gantt/recipes/print-pdf-recipe.ts',
dependencies: ['DOM print API'],
'Prepares a printable Gantt container that users can route to browser print or Save as PDF.',
id: 'json-snapshot-helpers',
title: 'JSON snapshot helpers',
frameworks: ['framework-neutral'],
sourceFiles: ['packages/gantt/src/gantt/core/project-snapshot-json.ts'],
'Clones, serializes, parses, imports, and exports ProjectSnapshot payloads.',
id: 'portal-svelte-gantt',
title: 'Portal Svelte Gantt demo',
'apps/portal/src/components/gantt/GanttSvelteExample.svelte',
'apps/portal/src/components/gantt/GanttSvelteExample.md',
dependencies: ['@revolist/svelte-datagrid'],
'Existing portal reference showing Gantt usage through the Svelte wrapper.',
id: 'portal-resource-filter',
title: 'Portal resource filter demo',
frameworks: ['framework-neutral', 'vanilla'],
'apps/portal/src/components/gantt/GanttResourceFilterExample.ts',
dependencies: ['resourceFilterIds'],
'Existing portal reference for filtering projected Gantt rows by assigned resource IDs.',
export function createRestProjectPersistenceRecipe(
options: ProjectSnapshotRestAdapterOptions,
state: ProjectSnapshotState,
): ProjectSnapshotPersistenceRecipe {
const adapter = new ProjectSnapshotRestAdapter(options);
metadata: getPersistenceRecipeMetadata('rest-project-snapshot'),
const project = await adapter.load(
request as ProjectSnapshotRestRequestOptions | undefined,
state.setProject(project);
request as ProjectSnapshotRestRequestOptions | undefined,
export function createGraphqlProjectPersistenceRecipe(
options: ProjectSnapshotGraphqlAdapterOptions,
state: ProjectSnapshotState,
): ProjectSnapshotPersistenceRecipe {
const adapter = new ProjectSnapshotGraphqlAdapter(options);
metadata: getPersistenceRecipeMetadata('graphql-project-snapshot'),
const project = await adapter.load(
request as ProjectSnapshotGraphqlRequestOptions | undefined,
state.setProject(project);
request as ProjectSnapshotGraphqlRequestOptions | undefined,
export function createSupabasePostgresPersistenceRecipe(
client: SupabaseLikeClient<ProjectSnapshotPostgresRow>,
state: ProjectSnapshotState,
options: ProjectSnapshotPostgresOptions = {},
): ProjectSnapshotStorageRecipe {
const adapter = new ProjectSnapshotSupabaseAdapter(client, options);
metadata: getPersistenceRecipeMetadata(
'supabase-postgres-project-snapshot',
const project = await adapter.load(projectId);
state.setProject(project);
const project = await adapter.save(state.getProject());
state.setProject(project);
export function createPostgresSqlPersistenceRecipe(
executor: ProjectSnapshotSqlExecutor,
state: ProjectSnapshotState,
options: ProjectSnapshotPostgresOptions = {},
): ProjectSnapshotPostgresRecipe {
metadata: getPersistenceRecipeMetadata('postgres-sql-project-snapshot'),
await executor.query(buildCreateProjectSnapshotTableSql(options));
const result = await executor.query<{ readonly snapshot: unknown }>(
buildLoadProjectSnapshotSql(projectId, options),
const snapshot = result.rows[0]?.snapshot;
if (snapshot === undefined) {
const parsed = parseProjectSnapshotJson(
typeof snapshot === 'string' ? snapshot : JSON.stringify(snapshot),
if (parsed.ok === false) {
throw new Error(parsed.error);
state.setProject(parsed.project);
const project = state.getProject();
const result = await executor.query<ProjectSnapshotPostgresRow>(
buildSaveProjectSnapshotSql(project, options),
const persisted = result.rows[0]?.snapshot ?? project;
const parsed = parseProjectSnapshotJson(
typeof persisted === 'string' ? persisted : JSON.stringify(persisted),
if (parsed.ok === false) {
throw new Error(parsed.error);
state.setProject(parsed.project);
buildReportingStatement(projectId) {
" snapshot->>'id' as project_id,",
" snapshot->>'name' as project_name,",
" jsonb_array_length(snapshot->'tasks') as task_count,",
" jsonb_array_length(snapshot->'resources') as resource_count,",
" jsonb_array_length(snapshot->'dependencies') as dependency_count",
`from ${quoteQualifiedIdentifier(options.tableName ?? 'gantt_project_snapshots')}`,
'where project_id = $1;',
export function createPrintPdfExportRecipe(
options: GanttPrintPdfRecipeOptions,
metadata: getPersistenceRecipeMetadata('print-pdf-export'),
exportProject(project, range) {
return printGanttContainer(options.target, {
document: options.document,
subtitle: `${project.tasks.length} tasks | ${project.resources.length} resources`,
printedAt: options.now?.() ?? new Date(),
export function createJsonSnapshotRecipe(
options: ProjectSnapshotJsonRecipeOptions = {},
): ProjectSnapshotJsonRecipe {
metadata: getPersistenceRecipeMetadata('json-snapshot-helpers'),
clone: cloneProjectSnapshot,
return exportProjectSnapshot(project, { space });
parse: parseProjectSnapshotJson,
'Project snapshot JSON load requires an injected readText function.',
const parsed = parseProjectSnapshotJson(await options.readText());
if (parsed.ok === false) {
throw new Error(parsed.error);
async save(project, space) {
if (!options.writeText) {
'Project snapshot JSON save requires an injected writeText function.',
const contents = exportProjectSnapshot(project, { space });
options.fileName?.(project) ?? `${project.id}-gantt-project.json`;
await options.writeText(fileName, contents);
export function getPersistenceRecipeMetadata(
id: GanttPersistenceRecipeId,
): GanttPersistenceRecipeMetadata {
const metadata = GANTT_PERSISTENCE_QUICK_WIN_EXAMPLES.find(
(example) => example.id === id,
throw new Error(`Unknown Gantt persistence recipe "${id}".`);
function quoteQualifiedIdentifier(identifier: string): string {
const parts = identifier.split('.');
if (parts.some((part) => !/^[A-Za-z_][A-Za-z0-9_]*$/.test(part))) {
`PostgreSQL identifier "${identifier}" is not supported by this example.`,
return parts.map((part) => `"${part}"`).join('.');