{"_id":"@cofoundrng/slintorm","name":"@cofoundrng/slintorm","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@cofoundrng/slintorm","version":"1.0.0","description":"Minimal fully typed orm for typescript","license":"MIT","author":{"name":"Joseph Christopher"},"type":"module","homepage":"https://github.com/emeraldlinks/slintorm","bugs":{"url":"https://github.com/emeraldlinks/slintormssues"},"main":"./dist/src/index.js","types":"./dist/src/index.d.ts","exports":{".":{"types":"./dist/src/index.d.ts","import":"./dist/src/index.js","require":"./dist/src/index.js"}},"repository":{"type":"git","url":"git+https://github.com/emeraldlinks/slintorm.git"},"scripts":{"build":"tsc && tsc-alias","test":"node --loader ts-node/esm --experimental-specifier-resolution=node src/example.ts"},"dependencies":{"mongodb":"^6.20.0","mysql2":"^3.15.3","path":"^0.12.7","pg":"^8.16.3","sqlite":"^5.1.1","sqlite3":"^5.1.7"},"devDependencies":{"@types/node":"^24.9.1","@types/pg":"^8.15.5","ts-node":"^10.9.2","tsc-alias":"^1.8.16","typescript":"^5.9.3"},"_id":"@cofoundrng/slintorm@1.0.0","gitHead":"0403ded064bfb08861d48253fb12a9e1a8e4c955","_nodeVersion":"24.8.0","_npmVersion":"11.6.0","dist":{"integrity":"sha512-pYhmhjPh1rzPcyXIhVJIMU8YGiQ0XM8YcwBeCShycpmvMNKrW4ls7vf7Ac/M7LVqI2B1ONAaNucQ6l3O/+vRzg==","shasum":"919760e73bba806a474131f2ea02c16db0e91729","tarball":"https://registry.npmjs.org/@cofoundrng/slintorm/-/slintorm-1.0.0.tgz","fileCount":26,"unpackedSize":164288,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIF5LmdotNumNSirjxbghwzRZk9iqBCwlCSzQSEFthHIcAiAaK6jzzEkssyTtIGr61MkMK7R6IEgLGaR4iHGSpHLtXg=="}]},"_npmUser":{"name":"joechristophers","email":"joechristophersc@gmail.com"},"directories":{},"maintainers":[{"name":"joechristophers","email":"joechristophersc@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/slintorm_1.0.0_1780257580847_0.23264659698500711"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-31T19:59:40.372Z","1.0.0":"2026-05-31T19:59:40.987Z","modified":"2026-05-31T19:59:41.203Z"},"maintainers":[{"name":"joechristophers","email":"joechristophersc@gmail.com"}],"description":"Minimal fully typed orm for typescript","homepage":"https://github.com/emeraldlinks/slintorm","repository":{"type":"git","url":"git+https://github.com/emeraldlinks/slintorm.git"},"author":{"name":"Joseph Christopher"},"bugs":{"url":"https://github.com/emeraldlinks/slintormssues"},"license":"MIT","readme":"# Simple TypeScript ORM\n\nA lightweight TypeScript ORM for SQLite, PostgreSQL, and MySQL.  \nInspired by Go's GORM, this ORM focuses on **simplicity, type safety, and full-featured query building** while keeping the API intuitive for TypeScript developers.\n\nIt is designed for:\n\n- Rapid development with auto table creation and migrations\n- Fully type-safe model definitions\n- Easy handling of relationships: one-to-one, one-to-many, and many-to-many\n- Advanced query building with joins, aggregates, subqueries, window functions, and preloads\n- Minimal configuration with sensible defaults\n\n---\n## Installation\n\n```bash\nnpm install slintorm\n\n```\n\n---\n\n\n\n\n## Model Interfaces\n\n```ts\n\n/** Post table */\ninterface Post {\n  // @index;\n  id?: number;\n  // @length:255;not null;comment:Post title\n  title: string;\n  // @nullable;comment:Author user ID\n  userId?: number;\n  // @relation manytoone:User;foreignKey:userId;onDelete:SET NULL\n  user?: User;\n  // @json;nullable;comment:Extra post data\n  meta?: Record<string, any>;\n  createdAt?: string;\n  updatedAt?: string;\n  // @softDelete\n  deletedAt?: string;\n  // @enum:(draft,published,archived)\n  status?: \"draft\" | \"published\" | \"archived\";\n}\n\n/** User table */\ninterface User {\n  // @index;auto;comment:primary key\n  id?: number;\n  // @nullable;length:100;comment:First name\n  firstName?: string;\n  // @length:100;not null;comment:Last name\n  name: string;\n  // @nullable;length:100;comment:Last name\n  lastname?: string;\n  // unique;comment:Email\n  email?: string;\n  // @relationship onetomany:Post;foreignKey:userId\n  posts?: Post[];\n  // @relationship onetoone:Profile;foreignKey:userId;onDelete:CASCADE\n  profile?: Profile;\n  // @json;nullable;comment:Extra user info\n  meta?: Record<string, any>;\n  createdAt?: string;\n  updatedAt?: string;\n  // @softDelete\n  deletedAt?: string;\n  // @enum:(active,inactive,banned)\n  status?: \"active\" | \"inactive\" | \"banned\";\n}\n\n/** Profile table */\ninterface Profile {\n  // @index;auto;comment:primary key\n  id?: number;\n  // @relation onetoone:User;foreignKey:userId\n  user?: User;\n  userId: number;\n  // @json;nullable;comment:Extra profile data\n  meta?: Record<string, any>;\n  createdAt?: string;\n  updatedAt?: string;\n  // @softDelete\n  deletedAt?: string;\n  // @enum:(male,female, other)\n  gender?: \"male\" | \"female\" | \"other\";\n}\n\n/** Todo table */\ninterface Todo {\n  // @index;auto;comment:primary key\n  id?: number;\n  // @length:255;not null\n  title: string;\n  // @nullable;length:1000\n  detail: string;\n  createdAt?: string;\n  updatedAt?: string;\n  // @softDelete\n  deletedAt?: string;\n  // @json;nullable\n  meta?: Record<string, any>;\n  // @enum:(low,medium,high)\n  priority?: \"low\" | \"medium\" | \"high\";\n}\n\n/** Task table */\ninterface Task {\n  // @index;auto\n  id?: number;\n  // @length:255;not null\n  title: string;\n  // @nullable;length:1000\n  detail: string;\n  createdAt?: string;\n  updatedAt?: string;\n  // @softDelete\n  deletedAt?: string;\n  // @json;nullable\n  meta?: Record<string, any>;\n  // @enum:(todo,inprogress,done)\n  status?: \"todo\" | \"inprogress\" | \"done\";\n} \n\n/** Tasksx table */\ninterface Tasksx {\n  // @index;auto\n  id?: number;\n  // @length:255;not null\n  title: string;\n  // @nullable;length:1000\n  detail: string;\n  createdAt?: string;\n  updatedAt?: string;\n  // @softDelete\n  deletedAt?: string;\n  // @json;nullable\n  meta?: Record<string, any>;\n  // @enum:(todo, inprogress, done)\n  status?: \"todo\" | \"inprogress\" | \"done\";\n}\n\n/** Team table */\ninterface Team {\n  // @index;auto\n  id?: number;\n  // @length:255;not null\n  title: string;\n  // @nullable;length:1000\n  detail: string;\n  // @nullable\n  open?: boolean;\n  // @nullable\n  tested?: boolean;\n  // @json;nullable\n  meta?: Record<string, any>;\n  createdAt?: string;\n  updatedAt?: string;\n  // @softDelete\n  deletedAt?: string;\n  // @enum:(active,archived)\n  status?: \"active\" | \"archived\";\n}\n```\n\n\n\n## Initialization\n\n```ts\nimport ORMManager from \"slintorm\";\n\n// Initialize ORM\nconst orm = new ORMManager({\n  driver: \"sqlite\",\n  databaseUrl: \"./test.db\",\n});\n\n// Run migrations automatically\nawait orm.migrate();\n```\n---\n\n## Define Models\n\n```ts\n\nconst Users = await orm.defineModel<User>(\"users\", \"User\");\nconst Posts = await orm.defineModel<Post>(\"posts\", \"Post\");\nconst Todos = await orm.defineModel<Todo>(\"todos\", \"Todo\");\nconst Profiles = await orm.defineModel<Profile>(\"profiles\", \"Profile\");\nconst Tasks = await orm.defineModel<Task>(\"tasks\", \"Task\");\n  const Teams = await orm.defineModel<Team>(\"team\", \"Team\", {\n    onCreateBefore(item) {\n      console.log(\"before create Team: \", item)\n    },\n    onCreateAfter(item) {\n      console.log(\"after create: \", item)\n    },\n    onUpdateAfter(oldData, newData) {\n\n    },\n  });\n```\n---\n---\n\n## Basic CRUD Examples\n\n```ts\n\n// Insert\nawait Todos.insert({\n  title: \"To watch plates\",\n  detail: \"Wash all plates\",\n  createdAt: new Date().toISOString(),\n});\n\n// Fetch all\nconst allTodos = await Todos.getAll();\n\n// Fetch one\nconst user = await Users.get({ id: 1 });\n\n// Update\nawait Users.update({ id: 1 }, { name: \"Amike Catherine\" });\n\n// Update instance\nconst fetchedUser = await Users.get({ id: 1 });\nawait fetchedUser?.update({ name: \"Amike Egwamene\" });\n\n// Delete\nawait Posts.delete({ id: 3 });\n\n```\n\n---\n\n## Query Builder Examples\n```ts\n// Preload relationships and filter\n  const postWithUser = await Posts.query()\n    .exclude(\"title\")\n    .preload(\"user\")\n    .preload(\"user.posts\")\n    .preload(\"user.profile\")\n    .preload(\"user.posts.user\")\n    .exclude(\"user.lastname\")\n    .first();\n\n\nconst userWithRelations = await Users.query()\n  .preload(\"posts\")\n  .preload(\"profile\")\n  .first(\"id = 2\");\n  // .first({id: 2}); both are valid\n\n// Nested preloads\nconst postWithUser = await Posts.query()\n  .preload(\"user\")\n  .preload(\"user.posts\")\n  .preload(\"user.profile\")\n  .get();\n\n// Filtering, ordering, and limiting\nconst todos = await Todos.query()\n  .where(\"title\", \"LIKE\", \"%plates%\")\n  .orderBy(\"createdAt\", \"desc\")\n  .limit(5)\n  .get();\n\n// Distinct and aggregates\nconst counts = await Users.query()\n  .count(\"id\")\n  .groupBy(\"lastname\")\n  .ILike(\"name\", \"jane\")\n  .get();\n\n// Window function example\nconst rankedUsers = await Users.query()\n  .window(\"ROW_NUMBER()\", \"PARTITION BY lastname ORDER BY id ASC\")\n  .get();\n\n// Subquery example\nconst subquery = Users.query().select(\"id\").where(\"name\", \"=\", \"Amike\");\nconst usersFromSub = await Users.query()\n  .selectSubquery(subquery, \"sub_id\")\n  .get();\n\n// EXISTS / NOT EXISTS example\nconst existsQuery = await Posts.query()\n  .exists(subquery)\n  .get();\n\n\n   try {\n    await Posts.delete({ id: 3 });\n  } catch (err) {\n    console.log(\"Delete error:\", err);\n  }\n\n\n  const user = await Users.get({ id: 1 })\n  // console.log(\"user: \", user)\n\n\n  try {\n    const uup = await Users.update({ id: 1 }, { name: \"Amike Catherine\" })\n    //  console.log(\"immediate update: \", uup)\n\n    const upuser = await Users.get({ id: 1 })\n    // console.log(\"fetched updated user: \", upuser)\n    const excupuser = await Users.query().exclude(\"profile\")\n    // .preload(\"posts\").exclude(\"posts.user\")\n    .first()\n\n    const updated = await upuser?.update({ name: \"Amike Egwamene\" });\n    console.log(\"Updated user:\", updated);\n    console.log(\"excluded user fields:\", excupuser);\n\n  } catch (err) {\n    console.log(\"error updated user: \", err)\n\n  }\n\n\n  const pp = await Profiles.query()\n  .preload(\"user\").preload(\"user.profile\").preload(\"user.profile.user\")\n  .exclude(\"user.name\")\n  .first(`userId = ${2}`)\n  console.log(\"profile: \", pp)\n\n\n  const nnew = await Teams.insert({\n    title: \"To watch dishes\",\n    detail: \"Wash all dishes\",\n    open: true,\n    tested: false\n  });\n  console.log(\"Teams:\", nnew);\n\n\n\n```\n---\n\n## Relationships\n\n* One-to-many: @relation onetomany:Post;foreignKey:userId\n* Many-to-one: @relation manytoone:User;foreignKey:userId\n* One-to-one: @relationship onetoone:Profile;foreignKey:userId\n* Many-to-many: Use a through table in schema metadata\n\n---\n\n## Migrations\n\nThe ORM automatically ensures that tables exist and applies schema changes based on your model metadata. Use:\n\n```ts\nawait orm.migrate();\n```\nto synchronize your database schema.\n\n---\n\n\n## Why use Simple TypeScript ORM?\n\nMany TypeScript ORMs are either minimal but lack features (like Drizzle) or extremely heavy (like Prisma). Simple TypeScript ORM balances **ease of use, flexibility, and performance**, making it ideal for projects that require quick iteration, full control over queries, and GORM-inspired patterns in TypeScript.\n\n| Feature                         | Simple TypeScript ORM | Drizzle        | Prisma        |\n|---------------------------------|---------------------|----------------|---------------|\n| Auto table creation & migration  | ✅ Automatic and zero-config migrations | ❌ Manual or CLI-based | ✅ CLI-based migrations |\n| Type-safe queries                | ✅ Full TypeScript support | ✅ Type-safe | ✅ Type-safe |\n| Relationships (1:1,1:N,N:M)     | ✅ Fully supported with preloads | ✅ Supported via join tables | ✅ Supported via relations |\n| Query builder with joins & HAVING| ✅ Advanced SQL capabilities | ❌ Limited | ✅ Limited to client API, raw SQL for complex cases |\n| Aggregates & window functions    | ✅ COUNT, SUM, AVG, custom window functions | ❌ Limited | ✅ Raw SQL required for window functions |\n| Subquery support                 | ✅ Easy subquery integration | ❌ Limited | ✅ Possible via raw SQL |\n| Preload / eager loading          | ✅ Preload nested relations | ❌ Not supported | ✅ Supports select/include |\n| Lightweight & minimal boilerplate| ✅ Minimal setup, single import | ✅ Minimal | ❌ Requires Prisma Client generation and schema setup |\n| Inspiration / design             | GORM-like (Go ORM) | TypeScript-only | Prisma Engine with schema DSL |\n| Learning curve                   | ✅ Very low, intuitive | ✅ Low | ❌ Medium, requires learning schema DSL and client |\n| Flexibility / raw SQL            | ✅ Direct SQL injection when needed | ✅ Limited raw SQL | ✅ Raw SQL available but requires Prisma Client |\n| Ideal use case                   | Rapid prototyping, small to medium apps, GORM-style workflow | Type-safe lightweight projects | Large-scale apps, strong typing, ecosystem-heavy projects |\n\n---\n\n### Summary\n\nSlintORM is **best suited for developers who want a GORM-inspired workflow in TypeScript**: minimal setup, automatic migrations, and full SQL query control.  \nDrizzle is lightweight and type-safe but lacks advanced query features.  \nPrisma is powerful and production-ready, but heavier and requires more boilerplate and tooling setup.  \n\nSlintORM fills the niche for **quick iteration, flexible queries, and minimal friction**, making it perfect for both learning and production projects.\n\n\n## Notes\n\n- Supports SQLite, PostgreSQL, and MySQL.\n- MongoDB support is limited to basic CRUD via the adapter.\n- All queries are type-safe and return mapped Boolean fields and excluded columns if configured.\n- Advanced query builder supports joins, group by, having, distinct, window functions, subqueries, and preloads.\n","readmeFilename":"README.md","_rev":"1-085e6a906f7e1cd6403ebd7262160d24"}