Frequently Asked Questions¶
Architecture¶
Why 4 packages instead of 1?¶
Different consumers need different things:
- A static website only needs
@cyber-eco/typesfor TypeScript interfaces (zero runtime cost) - A lightweight app needs
@cyber-eco/types+@cyber-eco/auth(for SSO hooks) - A full CyberEco app needs all four packages
- A testing environment needs
@cyber-eco/types+@cyber-eco/serviceswith aMockStorageAdapter(no Firebase at all)
A single monolithic package would force every consumer to install Firebase, React, jsonwebtoken, and the entire service layer even if they only need type definitions.
Why Turborepo instead of Nx?¶
The main cybereco-monorepo uses Nx — and that's the right choice for an app monorepo with code generation, executors, and complex build graphs.
This repository is a library-only monorepo (no apps, just packages). Turborepo's sweet spot is exactly this: near-zero configuration, automatic topological builds via dependsOn: ["^build"], and excellent caching. For 4 packages with a linear dependency chain, the entire turbo.json is ~15 lines.
Using a different tool also creates clean separation: Nx manages apps, Turborepo manages published packages.
Why not publish to public npm?¶
Not yet. The packages are in active development (pre-1.0) and the API is still stabilizing. Publishing to GitHub Packages (private, scoped to @cyber-eco) lets us iterate quickly without the social contract of a public npm package.
When the API stabilizes post-1.0, we plan to publish to public npm.
Why doesn't @cyber-eco/services depend on @cyber-eco/firebase?¶
This is the most important architectural decision in the entire project.
If @cyber-eco/services imported @cyber-eco/firebase, then every consumer of the services package would transitively depend on Firebase. This would make it impossible to:
- Test services without Firebase mocks
- Run services against a different storage backend
- Migrate to IPFS/blockchain without rewriting all services
Instead, services depend only on the StorageAdapter interface from @cyber-eco/types. The consumer wires the FirebaseStorageAdapter at runtime. This is classic dependency inversion.
Usage¶
How do I add a new StorageAdapter?¶
- Create a new package (e.g.,
@cyber-eco/postgres) - Add
@cyber-eco/typesas a dependency - Create a class that implements
StorageAdapter - Implement all methods (
getDocument,setDocument,query, etc.) - Pass an instance to
createDataLayer({ adapter: new PostgresAdapter(...) })
All domain services will work unchanged.
How do I add a new domain service?¶
- Create a new file in
packages/services/src/domain/(e.g.,VotingService.ts) - Accept
IDataLayerServicein the constructor - Use
this.dataLayer.get(),this.dataLayer.create(),this.dataLayer.query()for all data operations - Add the service to the
createDataLayerfactory return type - Export from
packages/services/src/index.ts
How do I run tests without Firebase emulators?¶
Use the MockStorageAdapter:
import { MockStorageAdapter } from '@cyber-eco/services/testing';
import { createDataLayer } from '@cyber-eco/services';
const adapter = new MockStorageAdapter();
adapter.seed('users', 'user1', { name: 'Alice', email: 'alice@example.com' });
const dataLayer = createDataLayer({ adapter });
const user = await dataLayer.sharedData.getSharedUserProfile('user1', 'user1');
// Returns { id: 'user1', name: 'Alice', email: 'alice@example.com' }
No Firebase SDK, no emulators, no network. Tests run in milliseconds.
What happens when types change — do I need to update all packages?¶
All 4 packages use fixed versioning via Changesets. When @cyber-eco/types changes, all packages get the same version bump. Consumers install the same version of all packages and compatibility is guaranteed.
How does the permission bootstrap (circular dependency) work?¶
The DataLayerService needs a permission checker function. The PermissionService needs the DataLayerService to read user data. This is a circular dependency.
The createDataLayer() factory solves it in three steps:
1. Create DataLayerService without permission checking
2. Create PermissionService using the DataLayerService
3. Wire the PermissionService.evaluatePermission back into the DataLayerService
After step 3, all operations go through permission checks.
Can I use @cyber-eco/auth without @cyber-eco/services?¶
Yes. @cyber-eco/auth provides authentication utilities (SSO, JWT, hooks, 2FA) that work independently. It does not depend on @cyber-eco/services.
Common standalone use cases:
- useHubAuth() hook in a lightweight app
- SharedAuthState for cross-app SSO
- AuthTokenService for URL-based token processing
- Zod validation schemas for input validation
Migration¶
What's the migration path from the current monorepo?¶
- Install the published packages alongside existing
libs/directories - Update
.npmrcto include the GitHub Packages registry - Migrate one app at a time (start with Website — fewest imports)
- For each app: replace
@cyber-eco/shared-types->@cyber-eco/types,@cyber-eco/firebase-config->@cyber-eco/firebase - Replace direct Firestore imports in services with
createDataLayer()factory - Verify tests pass
- Once all apps are migrated, delete the old
libs/directories