Package Exports
- record-sculptor
 - record-sculptor/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 (record-sculptor) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Record Sculptor
A flexible library for serializing records into customizable views written in TypeScript. Inspired by Blueprinter, Record Sculptor aims to provide a simple yet powerful way to present and manipulate data objects.
Installation and Usage
TODO: actually published this package, but it will likely look something like this:
Using npm:
npm install --save record-sculptorSimplistic Usage
See examples/README.md for a more realistic example.
TODO: validate this code
// src/serializers/user-serialiser.ts
import recordSculptor from "record-sculptor"
import { User, Role } from "@/models"
import { RoleSerializer } from "@/serializers"
class UserSerializer extends recordSculptor.Base<User> {}
 // Default view, put common stuff here, but prefer named views for anything specific or complex
UserSerializer.addView((view) => {
  view.addFields("id", "email", "firstName", "lastName" "isAdmin", "createdAt")
  view.addField("displayName", (user: User): string => `${user.firstName} ${user.lastName}`)
})
// Reuses all the fields from the default view, and adds a new roles field
UserSerializer.addView("table", (view) => {
  view.addField("roles", (user: User): string[] => user.roles.map((r) => r.name))
})
// Reuses all the fields from the default view, and makes use of another serializer
UserSerializer.addView("detailed", (view) => {
  view.addField("roles", (user: User) => RoleSerializer.serialize(user.roles))
})// src/serializers/role-serializer.ts
import recordSculptor from "record-sculptor"
import { Role } from "@/models"
class RoleSerializer extends recordSculptor.Base<Role> {}
UserSerializer.addView((view) => {
  view.addFields("id", "userId", "name")
})// src/routes.ts
import express, { type Request, type Response } from "express"
import { User } from "@/models"
import { UserSerializer } from "@/serializers"
export const router = express.Router()
router.get("/users", async (request: Request, response: Response) => {
  await const users = await User.findAll() // Retrieval from database, using Sequelize in this example
  const serializedUsers = UserSerializer.serialize(users, { view: "table" }) // Data presentation/serialization
  return response.status(200).json({ users: serializedUsers }) // Send data
})
router.post("/users", async (request: Request, response: Response) => {
  const newAttributes = request.body
  return User.create(newAttributes).then(user => { // Save to database, using Sequelize in this example
    const serializedUser = UserSerializer.serialize(user, { view: "detailed" }) // Data presentation/serialization
    return response.status(201).json({ user: serializedUser }) // Send data
  }).catch(error => {
    return response.status(422).json({ error: error.message }) // Handle errors
  })
})
router.get("/users/:id", async (request: Request, response: Response) => {
  const id = request.params.id
  const user = await User.findByPk(id) // Retrieval from database, using Sequelize in this example
  if (user === null) {
    return response.status(404).json({ message: "User not found" }) // Handle errors
  }
  const serializedUser = UserSerializer.serialize(user, { view: "detailed" }) // Data presentation/serialization
  return response.status(200).json({ user: serializedUser }) // Send data
})
router.put("/users/:id", async (request: Request, response: Response) => {
  const id = request.params.id
  const user = await User.findByPk(id) // Retrieval from database, using Sequelize in this example
  if (user === null) {
    return response.status(404).json({ message: "User not found" }) // Handle errors
  }
  const newAttributes = request.body
  return user.update(newAttributes).then((updatedUser) => {
    const serializedUser = UserSerializer.serialize(updatedUser, { view: "detailed" }) // Data presentation/serialization
    return response.status(200).json({ user: serializedUser }) // Send data
  }).catch(error => {
    return response.status(422).json({ error: error.message }) // Handle errors
  })
})
// Delete does use serializer as there is no point in returning anything
router.delete("/users/:id", async (request: Request, response: Response) => {
  const id = request.params.id
  // You _could_ peform a direct delete with Sequelize, but that makes the code harder to read,
  // and the optimization probably isn't worth it anyway.
  const user = await User.findByPk(id) // Retrieval from database, using Sequelize in this example
  if (user === null) {
    return response.status(404).json({ message: "User not found" }) // Handle errors
  }
  return user.destroy().then(() => {
    return response.status(204).send() // Send empty response implies success
  }).catch(error => {
    return response.status(422).json({ error: error.message }) // Handle errors
  })
})// src/models/user.ts
import { Role } from "@/models"
class User {
  constructor(
    public id: number,
    public email: string,
    public firstName: string,
    public lastName: string,
    public isAdmin?: boolean,
    public createdAt?: Date,
    public roles?: Array<Role>,
  ) {}
}// src/modles/role.ts
class Role {
  constructor(
    public id: number,
    public userId: number,
    public name: string,
  ) {}
}Development
Install
asdffrom https://asdf-vm.com/guide/getting-started.htmlInstall the
nodejsplugin for asdf viaasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.gitInstall the appropriate local
nodejsversion viaasdf install nodejsInstall the project dependencies via
npm installRun the test suite via
npm run test
Publishing
Testing Publishability
See https://www.freecodecamp.org/news/how-to-create-and-publish-your-first-npm-package/
Build this project via
npm run clean && npm run buildMake a directory relative to the project directory called
test-record-sculptor-importChange into the new project directory.
Run
npm link ../record-sculptorCreate a
test-record-sculptor-import.tsfile in the new project with this code in it.// test-record-sculptor-import/test-record-sculptor-import.ts import { Serializer } from "record-sculptor" class User { constructor( public id: number, public email: string, public firstName: string, public lastName: string, public isAdmin?: boolean, public createdAt?: Date, // public roles?: Array<Role> ) {} } const UserSerializer = Serializer.define<User>(({ addView }) => { addView((view) => { view.addFields("id", "email", "firstName", "lastName", "isAdmin", "createdAt") view.addField("displayName", (user: User): string => `${user.firstName} ${user.lastName}`) }) }) console.log( UserSerializer.serialize( new User(1, "john.doe@example.com", "John", "Doe", true, new Date("2021-01-01T12:00:00Z")), ), )
Run
npx ts-node test-record-sculpture-import.tsand check that it prints the appropriate record info.
Publishing the Repo
See https://www.freecodecamp.org/news/how-to-create-and-publish-your-first-npm-package/
Future Development
TODO: move away from lodash after I've built the basics, so as to keep this project light.