Appearance
Architecture
Runtime boundaries
- Laravel 13: auth, workspace membership, template metadata, generation records, API, billing, storage, and queues.
- React 19 + Inertia 3 + shadcn/ui: authenticated workspace and document forms. Page navigation uses standard server requests where possible; JavaScript is reserved for forms, drag-and-drop, menus, and live queue completion signals.
- Node.js worker: parses placeholders, extracts visible text, rewrites exact smart-field candidates across Word text runs, and renders DOCX with
docxtemplater; invokes LibreOffice for PDF and LibreOffice plus Poppler for first-page PNG previews. - Private storage:
DOCUMENTS_DISKselects local development storage or production S3. - Redis: production cache, sessions, and queues.
- Stripe: Cashier uses the workspace (
Teaminternally) as its customer. - OpenAI: optional Responses API integration returns strict structured candidates for smart template parsing; request storage is disabled.
- Admin: an
is_admingate protects aggregate operational metrics and database-backed operational failure notifications.
Document flow
- A member uploads a DOCX to the current workspace.
- Laravel checks quota, writes to the private disk, and creates a template record.
ParseDocumentTemplatecalls Node and stores unique placeholders such as[first name]before dispatchingGenerateTemplatePreviewto write a private first-page PNG. Preview errors are non-fatal and tracked separately from parsing errors.- Optional smart parsing extracts text, receives schema-constrained candidates, and asks Node to replace only exact source spans with stable placeholders.
- Field definitions select a resolver (provided input or an internal variable) before rendering.
- A submitted form or XLSX mapping creates standard generation records. Single fills dispatch
GenerateDocument; bulk fills run through a trackedGenerationBatchwith a plan row cap and optional ZIP archive. - Node renders DOCX, optionally converts to PDF, and Laravel stores the result privately.
- Grid cards, list rows, and the template detail page request previews through an authorized workspace route; storage paths are never sent to the client.
- While work is processing, the browser holds one authenticated server-sent event connection. Laravel signals completion and the browser performs a full page refresh; there is no client polling loop.
- An authorized member downloads through Laravel, or a recipient uses a temporary signed email-share URL. Object paths are never public.
Internal naming
Laravel's starter kit calls collaborative workspaces “teams”. Stable internal names (Team, team_members) remain while the product and public documentation use “workspace”.
Extension seams
WorkspaceStorage owns quotas and disk selection. DocumentWorker owns the PHP/Node contract. FieldValueResolverRegistry is the plugin seam for input, internal variables, and future intelligence providers. PlanCapabilities centralizes smart-parse and bulk-run entitlements. Queue jobs isolate heavy work. Enum-backed formats/statuses allow expansion. Cashier is workspace-scoped for two later paid plans.