**Problem:** - player-ownership.ts imported drizzle-orm and @/db at top level - When RoomMemoryPairsProvider imported client-safe utilities, Webpack bundled ALL imports including database code - This caused hydration error: "The 'original' argument must be of type Function" - Node.js util.promisify was being called in browser context **Solution:** 1. Created player-ownership.client.ts with ONLY client-safe utilities - No database imports - Safe to import from 'use client' components - Contains: buildPlayerOwnershipFromRoomData(), buildPlayerMetadata(), helper functions 2. Updated player-ownership.ts to re-export client utilities and add server-only functions - Re-exports everything from .client.ts - Adds buildPlayerOwnershipMap() (async, database-backed) - Safe to import from server components/API routes 3. Updated RoomMemoryPairsProvider to import from .client.ts **Result:** - No more hydration errors on /arcade/room - Client bundle doesn't include database code - Server code can still use both client and server utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
5.9 KiB
5.9 KiB
Soroban Flashcard Generator - Node.js API Documentation
Installation
npm install python-shell
Basic Usage
import { SorobanGenerator } from "./soroban-generator-bridge";
const generator = new SorobanGenerator();
const result = await generator.generate({
range: "0-99",
cardsPerPage: 6,
});
API Reference
SorobanGenerator
The main class for generating flashcards from Node.js/TypeScript.
Constructor
new SorobanGenerator(projectRoot?: string)
projectRoot(optional): Path to the soroban-abacus-flashcards directory. Defaults to../../from the module location.
Methods
generate(config: FlashcardConfig): Promise<FlashcardResult>
Generate flashcards with the specified configuration.
Parameters:
config: Configuration object (see FlashcardConfig below)
Returns: Promise resolving to:
{
pdf: string; // Base64 encoded PDF
count: number; // Number of flashcards generated
numbers: number[]; // Array of numbers (limited to first 100)
}
generateBuffer(config: FlashcardConfig): Promise<Buffer>
Generate flashcards and return as Node.js Buffer.
Parameters:
config: Configuration object
Returns: Promise resolving to Buffer containing PDF data
initialize(): Promise<void>
Initialize a persistent Python process for better performance when generating multiple PDFs.
close(): Promise<void>
Clean up the persistent Python process.
Configuration Interface
interface FlashcardConfig {
// Required
range: string; // e.g., "0-99" or "1,2,5,10"
// Optional
step?: number; // Increment (default: 1)
cardsPerPage?: number; // 1-30+ (default: 6)
paperSize?: "us-letter" | "a4" | "a3" | "a5";
orientation?: "portrait" | "landscape";
margins?: {
top?: string; // e.g., "0.5in"
bottom?: string;
left?: string;
right?: string;
};
gutter?: string; // Space between cards (default: "5mm")
shuffle?: boolean; // Randomize order
seed?: number; // Random seed for deterministic shuffle
showCutMarks?: boolean; // Show cutting guides
showRegistration?: boolean; // Show alignment marks
fontFamily?: string; // Font name (default: "DejaVu Sans")
fontSize?: string; // Font size (default: "48pt")
columns?: string | number; // "auto" or specific number
showEmptyColumns?: boolean;
hideInactiveBeads?: boolean;
beadShape?: "diamond" | "circle" | "square";
colorScheme?: "monochrome" | "place-value" | "heaven-earth" | "alternating";
coloredNumerals?: boolean; // Color numerals to match beads
scaleFactor?: number; // 0.1 to 1.0 (default: 0.9)
}
Examples
Basic Generation
const generator = new SorobanGenerator();
// Simple 0-9 flashcards
const result = await generator.generate({
range: "0-9",
});
Skip Counting
// Count by 5s from 0 to 100
const result = await generator.generate({
range: "0-100",
step: 5,
cardsPerPage: 12,
});
Educational Colors
// Place-value coloring for learning
const result = await generator.generate({
range: "0-999",
colorScheme: "place-value",
coloredNumerals: true,
showCutMarks: true,
});
Express.js Route
app.post("/api/flashcards", async (req, res) => {
try {
const generator = new SorobanGenerator();
const config = {
range: req.body.range || "0-9",
cardsPerPage: req.body.cardsPerPage || 6,
colorScheme: req.body.colorScheme || "monochrome",
...req.body,
};
const result = await generator.generate(config);
if (req.query.format === "json") {
// Return metadata
res.json({
count: result.count,
numbers: result.numbers,
});
} else {
// Return PDF
const pdfBuffer = Buffer.from(result.pdf, "base64");
res.contentType("application/pdf");
res.setHeader(
"Content-Disposition",
"attachment; filename=flashcards.pdf",
);
res.send(pdfBuffer);
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});
Next.js API Route
// pages/api/flashcards.ts
import { NextApiRequest, NextApiResponse } from "next";
import { SorobanGenerator } from "@/lib/soroban-generator-bridge";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
try {
const generator = new SorobanGenerator();
const result = await generator.generate(req.body);
const pdfBuffer = Buffer.from(result.pdf, "base64");
res.setHeader("Content-Type", "application/pdf");
res.setHeader("Content-Disposition", "attachment; filename=flashcards.pdf");
res.send(pdfBuffer);
} catch (error) {
res.status(500).json({ error: error.message });
}
}
Performance Optimization
For generating multiple PDFs, use persistent mode:
const generator = new SorobanGenerator();
// Initialize once
await generator.initialize();
// Generate multiple PDFs quickly
for (const config of configs) {
const result = await generator.generate(config);
// Process result...
}
// Clean up when done
await generator.close();
Requirements
- Node.js 14+
- Python 3.8+
- Typst (installed via
brew install typst) - qpdf (optional, for PDF optimization)
Error Handling
The generator will throw errors for:
- Missing Python installation
- Missing Typst installation
- Invalid configuration
- Typst compilation errors
Always wrap calls in try/catch blocks:
try {
const result = await generator.generate(config);
} catch (error) {
console.error("Generation failed:", error.message);
}
TypeScript Types
All interfaces and types are included in the module. Import them as needed:
import {
SorobanGenerator,
FlashcardConfig,
FlashcardResult,
} from "./soroban-generator-bridge";