A metadata application profile editor implementing the DC TAP (Dublin Core Tabular Application Profile) standard.
- Workspace Management: Create, duplicate, and delete workspaces
- Spreadsheet-like Interface: Edit metadata profiles in a familiar spreadsheet format
- Shape Management: Organize properties into shapes (entity types)
- Namespace Prefixes: Manage and auto-complete namespace prefixes
- Import/Export: Import from and export to CSV/TSV files
- Validation: Real-time validation with inline error display
- Clipboard Support: Copy, cut, and paste cells and ranges
- Undo/Redo: In-session undo/redo support
- Concurrent Editing: Polling-based updates for multi-tab usage
- Backend: Node.js, Express, TypeScript, SQLite (better-sqlite3)
- Frontend: Vue 3 (Options API), Vite, TypeScript
dctap-dancer/
├── backend/ # Express API server
│ ├── src/
│ │ ├── index.ts # Server entry point
│ │ ├── routes/ # API route handlers
│ │ ├── services/ # Business logic
│ │ │ ├── database.ts # SQLite operations
│ │ │ ├── validation.ts # DCTap validation
│ │ │ └── csv-parser.ts # Import/export logic
│ │ ├── middleware/ # Error handling
│ │ └── types/ # TypeScript types
│ └── data/ # SQLite database files
│
├── frontend/ # Vue 3 SPA
│ ├── src/
│ │ ├── views/ # Page components
│ │ ├── components/ # Reusable components
│ │ │ └── spreadsheet/ # Spreadsheet editor
│ │ ├── services/ # API client
│ │ └── types/ # TypeScript types
│ └── index.html
│
└── package.json # Monorepo workspace config
- Node.js 18+
- npm 9+
# Install dependencies
npm install
# Start development servers (backend + frontend)
npm run devThe frontend will be available at http://localhost:5173 The backend API will be at http://localhost:3000
# Run backend only
npm run dev:backend
# Run frontend only
npm run dev:frontend
# Build frontend for production
npm run build
# Start production backend
npm run start| Column | Description |
|---|---|
| shapeLabel | Human-readable name for the shape |
| propertyID | IRI/CURIE of the vocabulary term (e.g., dcterms:title) - Required |
| propertyLabel | Human-friendly display name |
| mandatory | Boolean: is this property required? |
| repeatable | Boolean: can this property appear multiple times? |
| valueNodeType | RDF node type: IRI, literal, or bnode |
| valueDataType | XSD datatype (e.g., xsd:string) - only for literals |
| valueShape | Reference to another shape - only for IRI/bnode |
| valueConstraint | Constraint value(s) |
| valueConstraintType | Type: picklist, IRIstem, pattern, etc. |
| note | Explanatory text |
GET /api/workspaces- List all workspacesPOST /api/workspaces- Create workspaceGET /api/workspaces/:id- Get workspacePUT /api/workspaces/:id- Update workspaceDELETE /api/workspaces/:id- Delete workspacePOST /api/workspaces/:id/duplicate- Duplicate workspace
GET /api/workspaces/:id/shapes- List shapesPOST /api/workspaces/:id/shapes- Create shapeGET /api/workspaces/:id/shapes/:shapeId- Get shapePUT /api/workspaces/:id/shapes/:shapeId- Update shapeDELETE /api/workspaces/:id/shapes/:shapeId- Delete shape
GET /api/workspaces/:id/shapes/:shapeId/rows- List rowsPOST /api/workspaces/:id/shapes/:shapeId/rows- Create rowPUT /api/workspaces/:id/shapes/:shapeId/rows/:rowId- Update rowPUT /api/workspaces/:id/shapes/:shapeId/rows- Bulk updateDELETE /api/workspaces/:id/shapes/:shapeId/rows/:rowId- Delete row
GET /api/workspaces/:id/namespaces- List namespacesPOST /api/workspaces/:id/namespaces- Create namespacePUT /api/workspaces/:id/namespaces/:prefix- Update namespaceDELETE /api/workspaces/:id/namespaces/:prefix- Delete namespace
GET /api/workspaces/:id/folders- List foldersPOST /api/workspaces/:id/folders- Create folderPUT /api/workspaces/:id/folders/:folderId- Update folderDELETE /api/workspaces/:id/folders/:folderId- Delete folder
POST /api/import- Import CSV/TSV fileGET /api/workspaces/:id/export?format=csv|tsv- Export workspace
GET /api/marva-profile/export/:id- Download Marva/Sinopia profile JSON
POST /api/starting-point/import/:id- Import LC Starting Point JSONGET /api/starting-point/export/:id- Download Starting Points JSONGET /api/starting-point/has/:id- Check if workspace has starting points
GET /api/serve/workspaces- List workspaces with URLs to all export formatsGET /api/serve/:id/profile- Serve Marva profile JSON (cached)GET /api/serve/:id/starting-points- Serve Starting Points JSON (cached)GET /api/serve/:id/csv- Serve CSV export (cached)GET /api/serve/:id/tsv- Serve TSV export (cached)GET /api/serve/cache/stats- Cache statistics (debug)
GET /api/health- Health check endpoint
| Shortcut | Action |
|---|---|
| Arrow keys | Navigate cells |
| Enter | Edit cell |
| Escape | Cancel editing |
| Tab | Move to next cell |
| Shift+Tab | Move to previous cell |
| Delete/Backspace | Clear selection |
| Ctrl/Cmd+C | Copy |
| Ctrl/Cmd+X | Cut |
| Ctrl/Cmd+V | Paste |
| Ctrl/Cmd+Z | Undo |
| Ctrl/Cmd+Shift+Z | Redo |
Build and run the full application (frontend + backend) in a single container:
# Build image
docker build -t dctap-dancer .
# Run container
docker run -d --name dctap -p 3000:3000 -v dctap-data:/app/data dctap-dancer
# Access at http://localhost:3000The -v dctap-data:/app/data flag persists the SQLite databases between container restarts.
Workspaces can be marked as read-only ("locked") to prevent modifications. Locked workspaces can still be viewed and duplicated, but cannot be edited or deleted. This is useful for public deployments where certain workspaces should remain stable as reference templates.
Locked workspaces are configured via a local locked-workspaces.json file (not tracked in git). Use the CLI tool to manage them:
# List all workspaces with lock status
npm run lock-workspace --workspace=backend list
# Lock a workspace by name or ID
npm run lock-workspace --workspace=backend lock "My Workspace"
# Unlock a workspace
npm run lock-workspace --workspace=backend unlock "My Workspace"
# Show current locked-workspaces.json config
npm run lock-workspace --workspace=backend show# Run backend tests
cd backend && npm test
# Run tests in watch mode
cd backend && npm run test:watchCreative Commons Zero v1.0 Universal