JSPM

  • Created
  • Published
  • Downloads 115
  • Score
    100M100P100Q120828F
  • License MIT

A shared library for standardizing Firestore document schemas and paths across multiple projects

Package Exports

  • @jobsearch-works/firestore-models
  • @jobsearch-works/firestore-models/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@jobsearch-works/firestore-models) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

firestore-models

A shared library for standardizing Firestore document schemas and paths across multiple projects. This library provides:

  • Standardized document models and interfaces
  • Consistent path builders for Firestore collections and documents
  • Type-safe access to Firestore data
  • Shared validation and transformation utilities

Purpose

This library serves as a single source of truth for Firestore data structures across different applications. By centralizing the schema definitions, it:

  • Ensures consistency in data structure across projects
  • Reduces duplication of model definitions
  • Provides type safety when working with Firestore data
  • Makes it easier to maintain and update schemas across all applications

Installation

npm install @jsw/firestore-models
# or
yarn add @jsw/firestore-models

Project Structure

firestore-models/
├── src/
│   ├── models/           # All Firestore models
│   │   ├── Application.ts
│   │   ├── AuthUser.ts
│   │   ├── Client.ts
│   │   ├── ClientData.ts
│   │   ├── ClientLogin.ts
│   │   ├── Question.ts
│   │   ├── UserQuestion.ts
│   │   ├── Vacancy.ts
│   │   └── VacancySuggestion.ts
│   ├── BaseModel.ts      # Base interface for all models
│   └── index.ts          # Main entry point
├── dist/                 # Compiled output
└── package.json

Base Model

All models extend from the BaseModel interface:

interface BaseModel {
  id?: string; // Optional document ID
  createdAt?: Date | string; // Optional creation timestamp
  updatedAt?: Date | string; // Optional update timestamp
}

Models and Namespaces

Each model is defined within its own namespace, which includes:

  • The model interface
  • Path builders for Firestore collections and documents
  • Utility functions for working with the model

Client

namespace Client {
  interface Model extends BaseModel {
    name: string;
    email: string;
    status: "active" | "inactive";
    resume?: string;
  }

  // Path builders
  const collection = () => "clients";
  const document = (clientId: string) => `clients/${clientId}`;

  // Utility functions
  const formatClient = (client: Client.Model): string;
  const createNew = (name: string, email: string): Client.Model;
}

Application

namespace Application {
  interface Model extends BaseModel {
    vacancyId: string;
    clientId: string;
    status:
      | "new"
      | "submitted"
      | "interviewing"
      | "accepted"
      | "rejected"
      | "withdrawn"
      | "applying"
      | "suggested"
      | "approved";
    coverLetter?: string;
    resume?: string;
    // Denormalized fields from Vacancy
    company?: string;
    position?: string;
    location?: string;
    jobId?: string;
    advertisingUrl?: string;
    applicationUrl?: string;
    applicationDomain?: string;
    advertisingDomain?: string;
    description?: string;
    fullPageText?: string;
  }

  // Path builders
  const collection = (clientId: string) => `clients/${clientId}/applications`;
  const document = (clientId: string, applicationId: string) =>
    `clients/${clientId}/applications/${applicationId}`;

  // Utility functions
  const formatSummary = (application: Application.Model): string;
  const formatDate = (date: Date | string): string;
  const createNew = (
    clientId: string,
    vacancyId: string,
    vacancyData: Partial<Vacancy.Model>
  ): Application.Model;
  const updateStatus = (
    application: Application.Model,
    newStatus: Application.Model["status"]
  ): Application.Model;
}

AuthUser

namespace AuthUser {
  interface Model extends BaseModel {
    email: string;
    displayName?: string;
    photoURL?: string;
    lastSignIn?: Date | string;
    emailVerified?: boolean;
  }

  // Path builders
  const collection = () => "users";
  const document = (userId: string) => `users/${userId}`;

  // Utility functions
  const fromFirebaseUser = (user: any): AuthUser.Model;
  const formatDates = (user: AuthUser.Model): Record<string, string>;
  const toFirestore = (user: AuthUser.Model): Record<string, any>;
  const fromFirestore = (data: Record<string, any>): AuthUser.Model;
}

Vacancy

namespace Vacancy {
  interface Model extends BaseModel {
    company: string;
    position: string;
    location: string;
    jobId: string;
    advertisingUrl: string;
    applicationUrl: string;
    applicationDomain: string;
    advertisingDomain: string;
    description: string;
    fullPageText: string;
    status: "active" | "inactive";
  }

  // Path builders
  const collection = () => "vacancies";
  const document = (vacancyId: string) => `vacancies/${vacancyId}`;
}

Question

namespace Question {
  interface Model extends BaseModel {
    text: string;
    type: "text" | "multiple_choice" | "single_choice";
    options?: string[];
    required: boolean;
    order: number;
  }

  // Path builders
  const collection = () => "questions";
  const document = (questionId: string) => `questions/${questionId}`;
}

UserQuestion

namespace UserQuestion {
  interface Model extends BaseModel {
    clientId: string;
    questionId: string;
    answer: string;
  }

  // Path builders
  const collection = (clientId: string) => `clients/${clientId}/questions`;
  const document = (clientId: string, questionId: string) =>
    `clients/${clientId}/questions/${questionId}`;
}

ClientData

namespace ClientData {
  interface Model extends BaseModel {
    clientId: string;
    key: string;
    value: any;
  }

  // Path builders
  const collection = (clientId: string) => `clients/${clientId}/data`;
  const document = (clientId: string, key: string) =>
    `clients/${clientId}/data/${key}`;
}

ClientLogin

namespace ClientLogin {
  interface Model extends BaseModel {
    clientId: string;
    ipAddress: string;
    userAgent: string;
  }

  // Path builders
  const collection = (clientId: string) => `clients/${clientId}/logins`;
  const document = (clientId: string, loginId: string) =>
    `clients/${clientId}/logins/${loginId}`;
}

VacancySuggestion

namespace VacancySuggestion {
  interface Model extends BaseModel {
    clientId: string;
    vacancyId: string;
    status: "pending" | "accepted" | "rejected";
  }

  // Path builders
  const collection = (clientId: string) => `clients/${clientId}/suggestions`;
  const document = (clientId: string, suggestionId: string) =>
    `clients/${clientId}/suggestions/${suggestionId}`;
}

Services

For data access, use the provided services instead of directly accessing Firestore. Services provide a consistent interface for data operations and handle error cases.

ClientDataService

import { ClientDataService } from "@jobsearch-works/services";

// Get client data
const clientData = await ClientDataService.getData(userId);

Usage

Import the models and services in your application:

import { Client } from "@jsw/firestore-models";
import { ClientDataService } from "@jobsearch-works/services";

// Create a new client
const newClient = Client.createNew("John Doe", "john@example.com");

// Use services for data access
const clientData = await ClientDataService.getData("123");

// Use the utility functions
const formattedClient = Client.formatClient(newClient);

Contributing

When adding new models or modifying existing ones:

  1. Update the TypeScript interfaces
  2. Add corresponding path builders
  3. Update any validation or transformation utilities
  4. Update this documentation
  5. Ensure backward compatibility when possible

License

MIT