Multi-project platform for creating and sharing microapps. Each sub-project is an independent repo with its own toolchain — there is no root-level package.json or shared build system.
| Directory | Description | Stack |
|---|---|---|
api/ |
REST API backend | Express, Mongoose, Mocha, Node 22 |
hub/ |
Web builder + viewer | React 18, Express, Webpack 5, Jest, Node 22 |
electron-hub/ |
Electron desktop app (offline viewer) | Electron 35, TypeScript, React 18, better-sqlite3, Node 22 |
mosaic/ |
Shared JS SDK (enums, API client, permissions) | Babel, Node 22 |
tiled-ui/ |
Shared React component library | TypeScript, Rollup, Storybook |
admin/ |
Internal admin dashboard | React 18, CRA, MUI |
figma-plugin/ |
Figma integration | TypeScript, webpack |
xd-plugin/ |
Adobe XD integration | React 16, Webpack 4, Jest |
native-clients/ |
Mobile app (iOS + Android) | React Native 0.74, Expo 51, Redux, Realm, Node 18 |
pdf-extractor/ |
PDF slide extraction microservice | Python 3.8, Flask, PyMuPDF, Gunicorn |
mortar/ |
Real-time engine | socket.io |
custom-tile/ |
Custom tile dev environment | webpack |
- Prettier:
printWidth: 120,singleQuote: true— consistent across all projects - ESLint: Airbnb base + Prettier integration in every project. Run
npm run lintbefore committing. - Indentation: 2 spaces, LF line endings, UTF-8, trim trailing whitespace
- npm exclusively (no yarn/pnpm). Use
npm cifor reproducible installs.- Exception:
xd-pluginusesyarninternally in its build scripts — global yarn installation required.
- Exception:
- Most projects: Node 22.16.0 (use nvm —
.nvmrcfiles are present) native-clients/: Node 18.19.0 (locked — do not upgrade)pdf-extractor/: Python 3.8.10 (no Node — entirely Python)- Always check
.nvmrcorenginesinpackage.jsonwhen switching projects.
All projects share enums, API client, and permissions through mosaic. Never redefine enums or constants locally.
import api from 'mosaic';
import { TileType, UserRole } from 'mosaic/enums';
import { hasPermission } from 'mosaic/permissions';- Installed as a private GitHub dependency (git+https URL with OAuth token)
- After modifying mosaic source, run
npm run buildin the mosaic directory — it compilessrc/to the package root via Babel - Other projects must
npm installafter mosaic is updated to pick up changes
Shared React components consumed by hub, admin, figma-plugin.
import { Box, Flex } from 'tiled-ui';- TypeScript source, Rollup build (dual CJS + ESM)
- Storybook available:
npm run storybook(port 6006)
| Project | Framework | Command |
|---|---|---|
| api | Mocha + Chai | npm test |
| hub | Jest (UI + server) | npm test |
| electron-hub | Jest + ts-jest | npm test (rebuilds better-sqlite3 for system Node via pretest) |
| tiled-ui | Jest + Testing Library | npm test |
| native-clients | Jest | npm test |
| xd-plugin | Jest + Testing Library | npm test |
| pdf-extractor | (none) | N/A — Python service, no test suite |
- api + hub: AWS CodeDeploy, pm2, release via
./scripts/release.sh stage|master - Environment config via
.env(copy from.env.sample)
api/,hub/,admin/, andelectron-hub/have dedicated.github/copilot-instructions.mdfiles with detailed architecture, code patterns, and linting gotchas. These are loaded automatically by Copilot when working in those directories.
- CommonJS throughout. Express + Mongoose.
- Multi-environment: dev, test, hotfix, pp, prod, env01–env20 (K8S staging)
- Dual server processes:
index.jsruns the private API on port 3002 (npm run dev);index-public.jsruns a separate public API on port 3003 (npm run dev:public) - Dual database: MongoDB/Mongoose for app data (
DB_MONGO_URL), PostgreSQL/Sequelize for analytics (tiled_analytics,tiled_arbiter). Separate migration commands for each. - Queue system: Bull (Redis-backed) for async jobs — PDF conversion, microapp import/export, analytics export, HubSpot sync, user anonymization, trial bootstrap/downgrade. Redis is optional (
REDIS_DISABLEDenv var skips queue creation). Config:REDIS_HOST,REDIS_PORT,REDIS_AUTH_PASS,REDIS_TLS - Feature flags: Unleash (
unleash-client) with custom activation strategies:InternalUseOnly,DesignerAccountsOnly,CreativeAccountsOnly(defined inapi/strategies/). Config:UNLEASH_KEY - Auth/security: Cookie-based JWTs (
JWT_SECRET,access_token/refresh_tokencookies),express-rate-limit,helmet,cors,bcrypt,sanitize-html. OAuth providers: Google. SAML SSO (saml2-js). SCIM provisioning (/scimdirectory). - Storage backends: S3 (default), Azure Blob Storage, CloudFront (signed URLs).
STORAGE_SERVICEenv var selects S3 vs Azure. - On-prem mode:
DEPLOY_TYPE=on-premchanges auth flows, cookie names (tiled_prefix), storage config, and required env vars. - Observability: Sentry (
SENTRY_DSN_API/SENTRY_DSN_PUBLIC), New Relic (on-prem only), Winston logger + LogEntries - v2 routes: All REST routes under
/v2. Key domains: accounts, analytics, assets, auth, config-sets, custom-tiles, dashboard, document-sets, documents, feature-flags, fonts, groups, instances, libraries, microapp, roles, rooms, saml-providers, scim-config, sheets, tags, tds, tenants, users, short-urls - API docs: JSDoc-generated (
npm run build:docs) + OpenAPI/Redoc (npm run build:redocfrompublic/docs/openapi.yaml) pre-commithook runs lintcheck + build:docsexpress-http-contextfor request-scoped context threading through middleware
- Create React App with MUI. Internal tool.
- Consumes
mosaicandtiled-ui
- Express server (port 3001) serves templates that bootstrap React apps; the server serves interpolated HTML shells, not server-rendered React.
- Four webpack app bundles:
hub,viewer,analytics,join— each a separate React entry point, plus a sharedcomponentsbundle all appsdependOn. - Webpack import aliases (for
importstatements):api,actions,components,component-library,asset-library,common,colorpicker,hooks,styles,utl(NOTutil),util-ts,images,audio,clientConfig,reducers - Jest three separate projects:
server(Node env, matchesserver/test/**/*-test.js),ui(jsdom env, matchessrc/**),eslint(linting as a Jest project). Run server tests alone:npm run test:server - Dev server uses pm2 +
webpack-hot-middleware(not a standalone webpack-dev-server). Hot reload injected as an entry point prefix.
- Figma plugin - syncs screens, hotspots, and prototypes from XD into Tiled microapps
- webpack build, Figma API types in
figma.d.ts - Consumes
mosaicandtiled-ui
- Adobe XD plugin — syncs screens, hotspots, and prototypes from XD into Tiled microapps
- React 16 + Webpack 4, outputs a
.xdxarchive (main.js + manifest.json + images) - Uses
yarnin build scripts (exception to the npm-only rule) - Environment-specific builds:
npm run build:dev,build:test,build:pp,build:prod mosaicversion is v1.x (much older than other projects) — be aware of enum/API differences
- React Native 0.74 + Expo 51 mobile app for iOS and Android
- Node 18.19.0 locked — do not upgrade
- State: Redux 5 + redux-thunk. Offline data: Realm 12
- Navigation:
@react-navigation(native-stack, drawer, bottom-tabs) - CI/CD: Azure Pipelines, builds via EAS (Expo Application Services)
patch-packageused — runnpm run postinstallafter patching
- Python Flask microservice — no Node.js, no package.json
- Receives PDF via HTTP POST, extracts slides/media/layout using PyMuPDF, uploads results to S3
- Deploy: AWS CodeDeploy (
appspec.yml+ci-cd/scripts), runs behind nginx + Gunicorn - Install:
pip install -r requirements.txt - Endpoint:
POST /extractwith body{"output_folder": "<account>/<app>", "key": "<s3-key>"}
- Electron 35 desktop app — offline microapp viewer with background sync
- Native modules:
better-sqlite3must be rebuilt for the correct runtime. Runnpm run prestart(useselectron-rebuild) before launching with Electron, andpretest(usesnpm rebuild) before running Jest. Both run automatically via npm lifecycle hooks. - Two webpack bundles:
webpack.main.config.js(main + preload) andwebpack.renderer.config.js(library UI React app) - Custom protocol:
tiled://registered viaregisterSchemesAsPrivileged. Routes:tiled://library/→ renderer bundle,tiled://viewer/page/{id}→ in-memory HTML store,tiled://viewer/assets/*→ hub-dist/,tiled://viewer/cached/*→ userData/assets/ - Hub bundles: Run
npm run sync-hub-buildto copy viewer + components JS fromhub/dist/intohub-dist/and writehub-dist/manifest.json(version, filenames). Must be re-run after hub builds. - Packaging:
npm run package:mac/npm run package:win. electron-builder must run from insideelectron-hub/— it fails from the workspace root. Full pipeline:bash scripts/build-release.sh [mac|win|all] - IPC: All channels defined in
src/shared/types.tsIPCconst. Never hardcode channel strings. Preload exposes full typedElectronAPIviacontextBridge. - SQLite schema: 7 tables —
libraries,microapps,published_documents,assets,analytics_queue,sync_state,migrations. Schema insrc/main/db.ts. - Analytics: Events queued locally in
analytics_queuewhen offline, flushed to API on reconnect (30s periodic flush).hub/src/viewer.jspasseswindow.__tiledElectronAnalyticsastrackOrEnqueueto mosaicactionsConfig.