seedorm 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -4
- package/dist/bin/seedorm.cjs +212 -44
- package/dist/{chunk-Y4EVX4PQ.js → chunk-3BYE5BOC.js} +34 -7
- package/dist/chunk-3BYE5BOC.js.map +1 -0
- package/dist/index.cjs +220 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -10
- package/dist/index.d.ts +55 -10
- package/dist/index.js +181 -22
- package/dist/index.js.map +1 -1
- package/dist/postgres-adapter-W2G6VHMX.js +7 -0
- package/package.json +1 -1
- package/dist/chunk-Y4EVX4PQ.js.map +0 -1
- package/dist/postgres-adapter-QBZYJX6P.js +0 -7
- /package/dist/{postgres-adapter-QBZYJX6P.js.map → postgres-adapter-W2G6VHMX.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/errors.ts","../src/adapters/postgres/pg-connection.ts","../src/query/operators.ts","../src/adapters/postgres/pg-query-builder.ts","../src/adapters/postgres/postgres-adapter.ts"],"sourcesContent":["// ── Field & Schema Types ──\n\nexport enum FieldType {\n String = \"string\",\n Number = \"number\",\n Boolean = \"boolean\",\n Date = \"date\",\n Json = \"json\",\n Array = \"array\",\n}\n\nexport interface FieldDefinition {\n type: FieldType;\n required?: boolean;\n unique?: boolean;\n index?: boolean;\n default?: unknown;\n minLength?: number;\n maxLength?: number;\n min?: number;\n max?: number;\n enum?: unknown[];\n}\n\nexport interface SchemaDefinition {\n [field: string]: FieldType | FieldDefinition;\n}\n\nexport interface NormalizedField {\n type: FieldType;\n required: boolean;\n unique: boolean;\n index: boolean;\n default?: unknown;\n minLength?: number;\n maxLength?: number;\n min?: number;\n max?: number;\n enum?: unknown[];\n}\n\nexport interface NormalizedSchema {\n [field: string]: NormalizedField;\n}\n\n// ── Document ──\n\nexport interface Document {\n id: string;\n createdAt: string;\n updatedAt: string;\n [key: string]: unknown;\n}\n\n// ── Query / Filter ──\n\nexport interface FilterOperators {\n $eq?: unknown;\n $ne?: unknown;\n $gt?: number | string | Date;\n $gte?: number | string | Date;\n $lt?: number | string | Date;\n $lte?: number | string | Date;\n $in?: unknown[];\n $nin?: unknown[];\n $like?: string;\n $exists?: boolean;\n}\n\nexport type FieldFilter = FilterOperators | unknown;\n\nexport interface FilterQuery {\n [field: string]: FieldFilter;\n}\n\nexport interface SortOption {\n [field: string]: 1 | -1;\n}\n\nexport interface FindOptions {\n filter?: FilterQuery;\n sort?: SortOption;\n limit?: number;\n offset?: number;\n include?: string[];\n}\n\n// ── Relations ──\n\nexport enum RelationType {\n HasOne = \"hasOne\",\n HasMany = \"hasMany\",\n BelongsTo = \"belongsTo\",\n ManyToMany = \"manyToMany\",\n}\n\nexport interface RelationDefinition {\n type: RelationType;\n model: string;\n foreignKey: string;\n joinCollection?: string;\n relatedKey?: string;\n}\n\nexport interface RelationsDefinition {\n [name: string]: RelationDefinition;\n}\n\n// ── Model Definition ──\n\nexport interface ModelDefinition {\n name: string;\n collection: string;\n schema: SchemaDefinition;\n timestamps?: boolean;\n prefix?: string;\n relations?: RelationsDefinition;\n}\n\n// ── Storage Adapter ──\n\nexport interface StorageAdapter {\n connect(): Promise<void>;\n disconnect(): Promise<void>;\n\n insert(collection: string, doc: Document): Promise<Document>;\n findById(collection: string, id: string): Promise<Document | null>;\n find(collection: string, options: FindOptions): Promise<Document[]>;\n count(collection: string, filter?: FilterQuery): Promise<number>;\n update(\n collection: string,\n id: string,\n data: Partial<Document>,\n ): Promise<Document | null>;\n delete(collection: string, id: string): Promise<boolean>;\n deleteMany(collection: string, filter: FilterQuery): Promise<number>;\n\n createCollection(collection: string, schema: NormalizedSchema): Promise<void>;\n dropCollection(collection: string): Promise<void>;\n listCollections(): Promise<string[]>;\n}\n\n// ── Config ──\n\nexport enum AdapterType {\n Json = \"json\",\n Postgres = \"postgres\",\n MySQL = \"mysql\",\n}\n\nexport interface JsonAdapterConfig {\n adapter: AdapterType.Json;\n path?: string;\n}\n\nexport interface PostgresAdapterConfig {\n adapter: AdapterType.Postgres;\n url: string;\n}\n\nexport interface MySQLAdapterConfig {\n adapter: AdapterType.MySQL;\n url: string;\n}\n\nexport type AdapterConfig =\n | JsonAdapterConfig\n | PostgresAdapterConfig\n | MySQLAdapterConfig;\n\nexport interface SeedORMConfig {\n adapter: AdapterConfig;\n migrationsDir?: string;\n}\n\n// ── Migration ──\n\nexport interface MigrationStep {\n type:\n | \"createCollection\"\n | \"dropCollection\"\n | \"addField\"\n | \"dropField\"\n | \"alterField\"\n | \"addIndex\"\n | \"dropIndex\";\n collection: string;\n field?: string;\n schema?: NormalizedField;\n oldSchema?: NormalizedField;\n}\n\nexport interface Migration {\n id: string;\n name: string;\n timestamp: number;\n up: MigrationStep[];\n down: MigrationStep[];\n}\n\nexport interface MigrationRecord {\n id: string;\n name: string;\n appliedAt: string;\n}\n","export class SeedORMError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SeedORMError\";\n }\n}\n\nexport class ValidationError extends SeedORMError {\n public field: string;\n public reason: string;\n\n constructor(field: string, reason: string) {\n super(`Validation error on \"${field}\": ${reason}`);\n this.name = \"ValidationError\";\n this.field = field;\n this.reason = reason;\n }\n}\n\nexport class AdapterError extends SeedORMError {\n public adapter: string;\n\n constructor(adapter: string, message: string) {\n super(`[${adapter}] ${message}`);\n this.name = \"AdapterError\";\n this.adapter = adapter;\n }\n}\n\nexport class CollectionNotFoundError extends SeedORMError {\n constructor(collection: string) {\n super(`Collection \"${collection}\" not found`);\n this.name = \"CollectionNotFoundError\";\n }\n}\n\nexport class DocumentNotFoundError extends SeedORMError {\n constructor(collection: string, id: string) {\n super(`Document \"${id}\" not found in \"${collection}\"`);\n this.name = \"DocumentNotFoundError\";\n }\n}\n\nexport class UniqueConstraintError extends SeedORMError {\n public field: string;\n public value: unknown;\n\n constructor(collection: string, field: string, value: unknown) {\n super(\n `Unique constraint violation on \"${collection}.${field}\": value ${JSON.stringify(value)} already exists`,\n );\n this.name = \"UniqueConstraintError\";\n this.field = field;\n this.value = value;\n }\n}\n","import { AdapterError } from \"../../errors.js\";\n\n// pg is an optional peer dependency\nlet pgModule: typeof import(\"pg\") | null = null;\n\nasync function loadPg(): Promise<typeof import(\"pg\")> {\n if (pgModule) return pgModule;\n try {\n pgModule = await import(\"pg\");\n return pgModule;\n } catch {\n throw new AdapterError(\n \"postgres\",\n 'The \"pg\" package is required for PostgreSQL. Install it with: npm install pg',\n );\n }\n}\n\nexport class PgConnection {\n private pool: import(\"pg\").Pool | null = null;\n private url: string;\n\n constructor(url: string) {\n this.url = url;\n }\n\n async connect(): Promise<void> {\n const pg = await loadPg();\n this.pool = new pg.Pool({ connectionString: this.url });\n // Test the connection\n const client = await this.pool.connect();\n client.release();\n }\n\n async disconnect(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n this.pool = null;\n }\n }\n\n async query<T extends Record<string, unknown> = Record<string, unknown>>(\n text: string,\n params?: unknown[],\n ): Promise<{ rows: T[]; rowCount: number }> {\n if (!this.pool) {\n throw new AdapterError(\"postgres\", \"Not connected\");\n }\n const result = await this.pool.query(text, params);\n return { rows: result.rows as T[], rowCount: result.rowCount ?? 0 };\n }\n}\n","import type { FilterOperators } from \"../types.js\";\n\ntype OperatorFn = (fieldValue: unknown, operand: unknown) => boolean;\n\nconst operators: Record<string, OperatorFn> = {\n $eq: (val, op) => val === op,\n\n $ne: (val, op) => val !== op,\n\n $gt: (val, op) => {\n if (typeof val === \"number\" && typeof op === \"number\") return val > op;\n if (typeof val === \"string\" && typeof op === \"string\") return val > op;\n return false;\n },\n\n $gte: (val, op) => {\n if (typeof val === \"number\" && typeof op === \"number\") return val >= op;\n if (typeof val === \"string\" && typeof op === \"string\") return val >= op;\n return false;\n },\n\n $lt: (val, op) => {\n if (typeof val === \"number\" && typeof op === \"number\") return val < op;\n if (typeof val === \"string\" && typeof op === \"string\") return val < op;\n return false;\n },\n\n $lte: (val, op) => {\n if (typeof val === \"number\" && typeof op === \"number\") return val <= op;\n if (typeof val === \"string\" && typeof op === \"string\") return val <= op;\n return false;\n },\n\n $in: (val, op) => {\n if (!Array.isArray(op)) return false;\n return op.includes(val);\n },\n\n $nin: (val, op) => {\n if (!Array.isArray(op)) return true;\n return !op.includes(val);\n },\n\n $like: (val, op) => {\n if (typeof val !== \"string\" || typeof op !== \"string\") return false;\n // Convert SQL-like pattern to regex: % → .*, _ → .\n const escaped = op.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const pattern = escaped.replace(/%/g, \".*\").replace(/_/g, \".\");\n return new RegExp(`^${pattern}$`, \"i\").test(val);\n },\n\n $exists: (val, op) => {\n const exists = val !== undefined && val !== null;\n return op ? exists : !exists;\n },\n};\n\nexport function applyOperator(\n key: string,\n fieldValue: unknown,\n operand: unknown,\n): boolean {\n const fn = operators[key];\n if (!fn) throw new Error(`Unknown operator: ${key}`);\n return fn(fieldValue, operand);\n}\n\nexport function isOperatorObject(value: unknown): value is FilterOperators {\n if (value === null || typeof value !== \"object\" || Array.isArray(value))\n return false;\n return Object.keys(value as object).some((k) => k.startsWith(\"$\"));\n}\n\nexport const OPERATORS = Object.keys(operators);\n","import type { FilterQuery, FindOptions, SortOption } from \"../../types.js\";\nimport { isOperatorObject } from \"../../query/operators.js\";\n\ninterface BuiltQuery {\n text: string;\n params: unknown[];\n}\n\nexport function buildSelect(\n collection: string,\n options: FindOptions,\n): BuiltQuery {\n const params: unknown[] = [];\n let text = `SELECT * FROM \"${collection}\"`;\n\n if (options.filter && Object.keys(options.filter).length > 0) {\n const where = buildWhere(options.filter, params);\n text += ` WHERE ${where}`;\n }\n\n if (options.sort) {\n text += ` ORDER BY ${buildOrderBy(options.sort)}`;\n }\n\n if (options.limit !== undefined) {\n params.push(options.limit);\n text += ` LIMIT $${params.length}`;\n }\n\n if (options.offset !== undefined) {\n params.push(options.offset);\n text += ` OFFSET $${params.length}`;\n }\n\n return { text, params };\n}\n\nexport function buildCount(\n collection: string,\n filter?: FilterQuery,\n): BuiltQuery {\n const params: unknown[] = [];\n let text = `SELECT COUNT(*) as count FROM \"${collection}\"`;\n\n if (filter && Object.keys(filter).length > 0) {\n const where = buildWhere(filter, params);\n text += ` WHERE ${where}`;\n }\n\n return { text, params };\n}\n\nfunction buildWhere(filter: FilterQuery, params: unknown[]): string {\n const conditions: string[] = [];\n\n for (const [field, condition] of Object.entries(filter)) {\n if (isOperatorObject(condition)) {\n for (const [op, value] of Object.entries(\n condition as Record<string, unknown>,\n )) {\n conditions.push(buildOperator(field, op, value, params));\n }\n } else {\n params.push(condition);\n conditions.push(`\"${field}\" = $${params.length}`);\n }\n }\n\n return conditions.join(\" AND \");\n}\n\nfunction buildOperator(\n field: string,\n op: string,\n value: unknown,\n params: unknown[],\n): string {\n switch (op) {\n case \"$eq\":\n params.push(value);\n return `\"${field}\" = $${params.length}`;\n case \"$ne\":\n params.push(value);\n return `\"${field}\" != $${params.length}`;\n case \"$gt\":\n params.push(value);\n return `\"${field}\" > $${params.length}`;\n case \"$gte\":\n params.push(value);\n return `\"${field}\" >= $${params.length}`;\n case \"$lt\":\n params.push(value);\n return `\"${field}\" < $${params.length}`;\n case \"$lte\":\n params.push(value);\n return `\"${field}\" <= $${params.length}`;\n case \"$in\":\n params.push(value);\n return `\"${field}\" = ANY($${params.length})`;\n case \"$nin\":\n params.push(value);\n return `\"${field}\" != ALL($${params.length})`;\n case \"$like\":\n params.push(value);\n return `\"${field}\" ILIKE $${params.length}`;\n case \"$exists\":\n return value\n ? `\"${field}\" IS NOT NULL`\n : `\"${field}\" IS NULL`;\n default:\n throw new Error(`Unknown operator: ${op}`);\n }\n}\n\nfunction buildOrderBy(sort: SortOption): string {\n return Object.entries(sort)\n .map(([field, dir]) => `\"${field}\" ${dir === 1 ? \"ASC\" : \"DESC\"}`)\n .join(\", \");\n}\n","import {\n FieldType,\n type Document,\n type FilterQuery,\n type FindOptions,\n type NormalizedField,\n type NormalizedSchema,\n type StorageAdapter,\n} from \"../../types.js\";\nimport { CollectionNotFoundError } from \"../../errors.js\";\nimport { PgConnection } from \"./pg-connection.js\";\nimport { buildSelect, buildCount } from \"./pg-query-builder.js\";\n\nfunction fieldToSQLType(field: NormalizedField): string {\n switch (field.type) {\n case FieldType.String:\n return field.maxLength ? `VARCHAR(${field.maxLength})` : \"TEXT\";\n case FieldType.Number:\n return \"DOUBLE PRECISION\";\n case FieldType.Boolean:\n return \"BOOLEAN\";\n case FieldType.Date:\n return \"TIMESTAMPTZ\";\n case FieldType.Json:\n case FieldType.Array:\n return \"JSONB\";\n default:\n return \"TEXT\";\n }\n}\n\nexport class PostgresAdapter implements StorageAdapter {\n private conn: PgConnection;\n\n constructor(url: string) {\n this.conn = new PgConnection(url);\n }\n\n async connect(): Promise<void> {\n await this.conn.connect();\n }\n\n async disconnect(): Promise<void> {\n await this.conn.disconnect();\n }\n\n async createCollection(\n collection: string,\n schema: NormalizedSchema,\n ): Promise<void> {\n const columns = [\n `\"id\" TEXT PRIMARY KEY`,\n `\"createdAt\" TIMESTAMPTZ NOT NULL DEFAULT NOW()`,\n `\"updatedAt\" TIMESTAMPTZ NOT NULL DEFAULT NOW()`,\n ];\n\n for (const [name, def] of Object.entries(schema)) {\n let col = `\"${name}\" ${fieldToSQLType(def)}`;\n if (def.required) col += \" NOT NULL\";\n if (def.unique) col += \" UNIQUE\";\n columns.push(col);\n }\n\n await this.conn.query(\n `CREATE TABLE IF NOT EXISTS \"${collection}\" (${columns.join(\", \")})`,\n );\n\n // Create indexes\n for (const [name, def] of Object.entries(schema)) {\n if (def.index && !def.unique) {\n await this.conn.query(\n `CREATE INDEX IF NOT EXISTS \"idx_${collection}_${name}\" ON \"${collection}\" (\"${name}\")`,\n );\n }\n }\n }\n\n async dropCollection(collection: string): Promise<void> {\n await this.conn.query(`DROP TABLE IF EXISTS \"${collection}\" CASCADE`);\n }\n\n async listCollections(): Promise<string[]> {\n const { rows } = await this.conn.query<{ tablename: string }>(\n `SELECT tablename FROM pg_tables WHERE schemaname = 'public'`,\n );\n return rows.map((r) => r.tablename);\n }\n\n async insert(collection: string, doc: Document): Promise<Document> {\n const keys = Object.keys(doc);\n const cols = keys.map((k) => `\"${k}\"`).join(\", \");\n const placeholders = keys.map((_, i) => `$${i + 1}`).join(\", \");\n const values = keys.map((k) => {\n const v = doc[k];\n return typeof v === \"object\" && v !== null && !(v instanceof Date)\n ? JSON.stringify(v)\n : v;\n });\n\n const { rows } = await this.conn.query<Document>(\n `INSERT INTO \"${collection}\" (${cols}) VALUES (${placeholders}) RETURNING *`,\n values,\n );\n return rows[0]!;\n }\n\n async findById(\n collection: string,\n id: string,\n ): Promise<Document | null> {\n const { rows } = await this.conn.query<Document>(\n `SELECT * FROM \"${collection}\" WHERE \"id\" = $1`,\n [id],\n );\n return rows[0] ?? null;\n }\n\n async find(\n collection: string,\n options: FindOptions,\n ): Promise<Document[]> {\n const q = buildSelect(collection, options);\n const { rows } = await this.conn.query<Document>(q.text, q.params);\n return rows;\n }\n\n async count(collection: string, filter?: FilterQuery): Promise<number> {\n const q = buildCount(collection, filter);\n const { rows } = await this.conn.query<{ count: string }>(\n q.text,\n q.params,\n );\n return parseInt(rows[0]!.count, 10);\n }\n\n async update(\n collection: string,\n id: string,\n data: Partial<Document>,\n ): Promise<Document | null> {\n const entries = Object.entries(data).filter(([k]) => k !== \"id\");\n if (entries.length === 0) return this.findById(collection, id);\n\n const sets = entries.map(([k], i) => `\"${k}\" = $${i + 1}`).join(\", \");\n const values = entries.map(([, v]) =>\n typeof v === \"object\" && v !== null && !(v instanceof Date)\n ? JSON.stringify(v)\n : v,\n );\n values.push(id);\n\n const { rows } = await this.conn.query<Document>(\n `UPDATE \"${collection}\" SET ${sets} WHERE \"id\" = $${values.length} RETURNING *`,\n values,\n );\n return rows[0] ?? null;\n }\n\n async delete(collection: string, id: string): Promise<boolean> {\n const { rowCount } = await this.conn.query(\n `DELETE FROM \"${collection}\" WHERE \"id\" = $1`,\n [id],\n );\n return rowCount > 0;\n }\n\n async deleteMany(\n collection: string,\n filter: FilterQuery,\n ): Promise<number> {\n const q = buildSelect(collection, { filter });\n // Convert SELECT to DELETE\n const deleteText = q.text.replace(/^SELECT \\* FROM/, \"DELETE FROM\");\n const { rowCount } = await this.conn.query(deleteText, q.params);\n return rowCount;\n }\n}\n"],"mappings":";AAEO,IAAK,YAAL,kBAAKA,eAAL;AACL,EAAAA,WAAA,YAAS;AACT,EAAAA,WAAA,YAAS;AACT,EAAAA,WAAA,aAAU;AACV,EAAAA,WAAA,UAAO;AACP,EAAAA,WAAA,UAAO;AACP,EAAAA,WAAA,WAAQ;AANE,SAAAA;AAAA,GAAA;AAuFL,IAAK,eAAL,kBAAKC,kBAAL;AACL,EAAAA,cAAA,YAAS;AACT,EAAAA,cAAA,aAAU;AACV,EAAAA,cAAA,eAAY;AACZ,EAAAA,cAAA,gBAAa;AAJH,SAAAA;AAAA,GAAA;AAuDL,IAAK,cAAL,kBAAKC,iBAAL;AACL,EAAAA,aAAA,UAAO;AACP,EAAAA,aAAA,cAAW;AACX,EAAAA,aAAA,WAAQ;AAHE,SAAAA;AAAA,GAAA;;;AChJL,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,kBAAN,cAA8B,aAAa;AAAA,EACzC;AAAA,EACA;AAAA,EAEP,YAAY,OAAe,QAAgB;AACzC,UAAM,wBAAwB,KAAK,MAAM,MAAM,EAAE;AACjD,SAAK,OAAO;AACZ,SAAK,QAAQ;AACb,SAAK,SAAS;AAAA,EAChB;AACF;AAEO,IAAM,eAAN,cAA2B,aAAa;AAAA,EACtC;AAAA,EAEP,YAAY,SAAiB,SAAiB;AAC5C,UAAM,IAAI,OAAO,KAAK,OAAO,EAAE;AAC/B,SAAK,OAAO;AACZ,SAAK,UAAU;AAAA,EACjB;AACF;AAEO,IAAM,0BAAN,cAAsC,aAAa;AAAA,EACxD,YAAY,YAAoB;AAC9B,UAAM,eAAe,UAAU,aAAa;AAC5C,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,wBAAN,cAAoC,aAAa;AAAA,EACtD,YAAY,YAAoB,IAAY;AAC1C,UAAM,aAAa,EAAE,mBAAmB,UAAU,GAAG;AACrD,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,wBAAN,cAAoC,aAAa;AAAA,EAC/C;AAAA,EACA;AAAA,EAEP,YAAY,YAAoB,OAAe,OAAgB;AAC7D;AAAA,MACE,mCAAmC,UAAU,IAAI,KAAK,YAAY,KAAK,UAAU,KAAK,CAAC;AAAA,IACzF;AACA,SAAK,OAAO;AACZ,SAAK,QAAQ;AACb,SAAK,QAAQ;AAAA,EACf;AACF;;;ACpDA,IAAI,WAAuC;AAE3C,eAAe,SAAuC;AACpD,MAAI,SAAU,QAAO;AACrB,MAAI;AACF,eAAW,MAAM,OAAO,IAAI;AAC5B,WAAO;AAAA,EACT,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,eAAN,MAAmB;AAAA,EAChB,OAAiC;AAAA,EACjC;AAAA,EAER,YAAY,KAAa;AACvB,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,MAAM,UAAyB;AAC7B,UAAM,KAAK,MAAM,OAAO;AACxB,SAAK,OAAO,IAAI,GAAG,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAEtD,UAAM,SAAS,MAAM,KAAK,KAAK,QAAQ;AACvC,WAAO,QAAQ;AAAA,EACjB;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,KAAK,MAAM;AACb,YAAM,KAAK,KAAK,IAAI;AACpB,WAAK,OAAO;AAAA,IACd;AAAA,EACF;AAAA,EAEA,MAAM,MACJ,MACA,QAC0C;AAC1C,QAAI,CAAC,KAAK,MAAM;AACd,YAAM,IAAI,aAAa,YAAY,eAAe;AAAA,IACpD;AACA,UAAM,SAAS,MAAM,KAAK,KAAK,MAAM,MAAM,MAAM;AACjD,WAAO,EAAE,MAAM,OAAO,MAAa,UAAU,OAAO,YAAY,EAAE;AAAA,EACpE;AACF;;;AC/CA,IAAM,YAAwC;AAAA,EAC5C,KAAK,CAAC,KAAK,OAAO,QAAQ;AAAA,EAE1B,KAAK,CAAC,KAAK,OAAO,QAAQ;AAAA,EAE1B,KAAK,CAAC,KAAK,OAAO;AAChB,QAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAU,QAAO,MAAM;AACpE,QAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAU,QAAO,MAAM;AACpE,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,CAAC,KAAK,OAAO;AACjB,QAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAU,QAAO,OAAO;AACrE,QAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAU,QAAO,OAAO;AACrE,WAAO;AAAA,EACT;AAAA,EAEA,KAAK,CAAC,KAAK,OAAO;AAChB,QAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAU,QAAO,MAAM;AACpE,QAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAU,QAAO,MAAM;AACpE,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,CAAC,KAAK,OAAO;AACjB,QAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAU,QAAO,OAAO;AACrE,QAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAU,QAAO,OAAO;AACrE,WAAO;AAAA,EACT;AAAA,EAEA,KAAK,CAAC,KAAK,OAAO;AAChB,QAAI,CAAC,MAAM,QAAQ,EAAE,EAAG,QAAO;AAC/B,WAAO,GAAG,SAAS,GAAG;AAAA,EACxB;AAAA,EAEA,MAAM,CAAC,KAAK,OAAO;AACjB,QAAI,CAAC,MAAM,QAAQ,EAAE,EAAG,QAAO;AAC/B,WAAO,CAAC,GAAG,SAAS,GAAG;AAAA,EACzB;AAAA,EAEA,OAAO,CAAC,KAAK,OAAO;AAClB,QAAI,OAAO,QAAQ,YAAY,OAAO,OAAO,SAAU,QAAO;AAE9D,UAAM,UAAU,GAAG,QAAQ,uBAAuB,MAAM;AACxD,UAAM,UAAU,QAAQ,QAAQ,MAAM,IAAI,EAAE,QAAQ,MAAM,GAAG;AAC7D,WAAO,IAAI,OAAO,IAAI,OAAO,KAAK,GAAG,EAAE,KAAK,GAAG;AAAA,EACjD;AAAA,EAEA,SAAS,CAAC,KAAK,OAAO;AACpB,UAAM,SAAS,QAAQ,UAAa,QAAQ;AAC5C,WAAO,KAAK,SAAS,CAAC;AAAA,EACxB;AACF;AAEO,SAAS,cACd,KACA,YACA,SACS;AACT,QAAM,KAAK,UAAU,GAAG;AACxB,MAAI,CAAC,GAAI,OAAM,IAAI,MAAM,qBAAqB,GAAG,EAAE;AACnD,SAAO,GAAG,YAAY,OAAO;AAC/B;AAEO,SAAS,iBAAiB,OAA0C;AACzE,MAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK;AACpE,WAAO;AACT,SAAO,OAAO,KAAK,KAAe,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC;AACnE;AAEO,IAAM,YAAY,OAAO,KAAK,SAAS;;;ACjEvC,SAAS,YACd,YACA,SACY;AACZ,QAAM,SAAoB,CAAC;AAC3B,MAAI,OAAO,kBAAkB,UAAU;AAEvC,MAAI,QAAQ,UAAU,OAAO,KAAK,QAAQ,MAAM,EAAE,SAAS,GAAG;AAC5D,UAAM,QAAQ,WAAW,QAAQ,QAAQ,MAAM;AAC/C,YAAQ,UAAU,KAAK;AAAA,EACzB;AAEA,MAAI,QAAQ,MAAM;AAChB,YAAQ,aAAa,aAAa,QAAQ,IAAI,CAAC;AAAA,EACjD;AAEA,MAAI,QAAQ,UAAU,QAAW;AAC/B,WAAO,KAAK,QAAQ,KAAK;AACzB,YAAQ,WAAW,OAAO,MAAM;AAAA,EAClC;AAEA,MAAI,QAAQ,WAAW,QAAW;AAChC,WAAO,KAAK,QAAQ,MAAM;AAC1B,YAAQ,YAAY,OAAO,MAAM;AAAA,EACnC;AAEA,SAAO,EAAE,MAAM,OAAO;AACxB;AAEO,SAAS,WACd,YACA,QACY;AACZ,QAAM,SAAoB,CAAC;AAC3B,MAAI,OAAO,kCAAkC,UAAU;AAEvD,MAAI,UAAU,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAC5C,UAAM,QAAQ,WAAW,QAAQ,MAAM;AACvC,YAAQ,UAAU,KAAK;AAAA,EACzB;AAEA,SAAO,EAAE,MAAM,OAAO;AACxB;AAEA,SAAS,WAAW,QAAqB,QAA2B;AAClE,QAAM,aAAuB,CAAC;AAE9B,aAAW,CAAC,OAAO,SAAS,KAAK,OAAO,QAAQ,MAAM,GAAG;AACvD,QAAI,iBAAiB,SAAS,GAAG;AAC/B,iBAAW,CAAC,IAAI,KAAK,KAAK,OAAO;AAAA,QAC/B;AAAA,MACF,GAAG;AACD,mBAAW,KAAK,cAAc,OAAO,IAAI,OAAO,MAAM,CAAC;AAAA,MACzD;AAAA,IACF,OAAO;AACL,aAAO,KAAK,SAAS;AACrB,iBAAW,KAAK,IAAI,KAAK,QAAQ,OAAO,MAAM,EAAE;AAAA,IAClD;AAAA,EACF;AAEA,SAAO,WAAW,KAAK,OAAO;AAChC;AAEA,SAAS,cACP,OACA,IACA,OACA,QACQ;AACR,UAAQ,IAAI;AAAA,IACV,KAAK;AACH,aAAO,KAAK,KAAK;AACjB,aAAO,IAAI,KAAK,QAAQ,OAAO,MAAM;AAAA,IACvC,KAAK;AACH,aAAO,KAAK,KAAK;AACjB,aAAO,IAAI,KAAK,SAAS,OAAO,MAAM;AAAA,IACxC,KAAK;AACH,aAAO,KAAK,KAAK;AACjB,aAAO,IAAI,KAAK,QAAQ,OAAO,MAAM;AAAA,IACvC,KAAK;AACH,aAAO,KAAK,KAAK;AACjB,aAAO,IAAI,KAAK,SAAS,OAAO,MAAM;AAAA,IACxC,KAAK;AACH,aAAO,KAAK,KAAK;AACjB,aAAO,IAAI,KAAK,QAAQ,OAAO,MAAM;AAAA,IACvC,KAAK;AACH,aAAO,KAAK,KAAK;AACjB,aAAO,IAAI,KAAK,SAAS,OAAO,MAAM;AAAA,IACxC,KAAK;AACH,aAAO,KAAK,KAAK;AACjB,aAAO,IAAI,KAAK,YAAY,OAAO,MAAM;AAAA,IAC3C,KAAK;AACH,aAAO,KAAK,KAAK;AACjB,aAAO,IAAI,KAAK,aAAa,OAAO,MAAM;AAAA,IAC5C,KAAK;AACH,aAAO,KAAK,KAAK;AACjB,aAAO,IAAI,KAAK,YAAY,OAAO,MAAM;AAAA,IAC3C,KAAK;AACH,aAAO,QACH,IAAI,KAAK,kBACT,IAAI,KAAK;AAAA,IACf;AACE,YAAM,IAAI,MAAM,qBAAqB,EAAE,EAAE;AAAA,EAC7C;AACF;AAEA,SAAS,aAAa,MAA0B;AAC9C,SAAO,OAAO,QAAQ,IAAI,EACvB,IAAI,CAAC,CAAC,OAAO,GAAG,MAAM,IAAI,KAAK,KAAK,QAAQ,IAAI,QAAQ,MAAM,EAAE,EAChE,KAAK,IAAI;AACd;;;ACzGA,SAAS,eAAe,OAAgC;AACtD,UAAQ,MAAM,MAAM;AAAA,IAClB;AACE,aAAO,MAAM,YAAY,WAAW,MAAM,SAAS,MAAM;AAAA,IAC3D;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AAAA,IACA;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEO,IAAM,kBAAN,MAAgD;AAAA,EAC7C;AAAA,EAER,YAAY,KAAa;AACvB,SAAK,OAAO,IAAI,aAAa,GAAG;AAAA,EAClC;AAAA,EAEA,MAAM,UAAyB;AAC7B,UAAM,KAAK,KAAK,QAAQ;AAAA,EAC1B;AAAA,EAEA,MAAM,aAA4B;AAChC,UAAM,KAAK,KAAK,WAAW;AAAA,EAC7B;AAAA,EAEA,MAAM,iBACJ,YACA,QACe;AACf,UAAM,UAAU;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,eAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,MAAM,GAAG;AAChD,UAAI,MAAM,IAAI,IAAI,KAAK,eAAe,GAAG,CAAC;AAC1C,UAAI,IAAI,SAAU,QAAO;AACzB,UAAI,IAAI,OAAQ,QAAO;AACvB,cAAQ,KAAK,GAAG;AAAA,IAClB;AAEA,UAAM,KAAK,KAAK;AAAA,MACd,+BAA+B,UAAU,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,IACnE;AAGA,eAAW,CAAC,MAAM,GAAG,KAAK,OAAO,QAAQ,MAAM,GAAG;AAChD,UAAI,IAAI,SAAS,CAAC,IAAI,QAAQ;AAC5B,cAAM,KAAK,KAAK;AAAA,UACd,mCAAmC,UAAU,IAAI,IAAI,SAAS,UAAU,OAAO,IAAI;AAAA,QACrF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,YAAmC;AACtD,UAAM,KAAK,KAAK,MAAM,yBAAyB,UAAU,WAAW;AAAA,EACtE;AAAA,EAEA,MAAM,kBAAqC;AACzC,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,KAAK;AAAA,MAC/B;AAAA,IACF;AACA,WAAO,KAAK,IAAI,CAAC,MAAM,EAAE,SAAS;AAAA,EACpC;AAAA,EAEA,MAAM,OAAO,YAAoB,KAAkC;AACjE,UAAM,OAAO,OAAO,KAAK,GAAG;AAC5B,UAAM,OAAO,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAChD,UAAM,eAAe,KAAK,IAAI,CAAC,GAAG,MAAM,IAAI,IAAI,CAAC,EAAE,EAAE,KAAK,IAAI;AAC9D,UAAM,SAAS,KAAK,IAAI,CAAC,MAAM;AAC7B,YAAM,IAAI,IAAI,CAAC;AACf,aAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,EAAE,aAAa,QACzD,KAAK,UAAU,CAAC,IAChB;AAAA,IACN,CAAC;AAED,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,KAAK;AAAA,MAC/B,gBAAgB,UAAU,MAAM,IAAI,aAAa,YAAY;AAAA,MAC7D;AAAA,IACF;AACA,WAAO,KAAK,CAAC;AAAA,EACf;AAAA,EAEA,MAAM,SACJ,YACA,IAC0B;AAC1B,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,KAAK;AAAA,MAC/B,kBAAkB,UAAU;AAAA,MAC5B,CAAC,EAAE;AAAA,IACL;AACA,WAAO,KAAK,CAAC,KAAK;AAAA,EACpB;AAAA,EAEA,MAAM,KACJ,YACA,SACqB;AACrB,UAAM,IAAI,YAAY,YAAY,OAAO;AACzC,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,KAAK,MAAgB,EAAE,MAAM,EAAE,MAAM;AACjE,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MAAM,YAAoB,QAAuC;AACrE,UAAM,IAAI,WAAW,YAAY,MAAM;AACvC,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,KAAK;AAAA,MAC/B,EAAE;AAAA,MACF,EAAE;AAAA,IACJ;AACA,WAAO,SAAS,KAAK,CAAC,EAAG,OAAO,EAAE;AAAA,EACpC;AAAA,EAEA,MAAM,OACJ,YACA,IACA,MAC0B;AAC1B,UAAM,UAAU,OAAO,QAAQ,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,IAAI;AAC/D,QAAI,QAAQ,WAAW,EAAG,QAAO,KAAK,SAAS,YAAY,EAAE;AAE7D,UAAM,OAAO,QAAQ,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,EAAE,KAAK,IAAI;AACpE,UAAM,SAAS,QAAQ;AAAA,MAAI,CAAC,CAAC,EAAE,CAAC,MAC9B,OAAO,MAAM,YAAY,MAAM,QAAQ,EAAE,aAAa,QAClD,KAAK,UAAU,CAAC,IAChB;AAAA,IACN;AACA,WAAO,KAAK,EAAE;AAEd,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,KAAK;AAAA,MAC/B,WAAW,UAAU,SAAS,IAAI,kBAAkB,OAAO,MAAM;AAAA,MACjE;AAAA,IACF;AACA,WAAO,KAAK,CAAC,KAAK;AAAA,EACpB;AAAA,EAEA,MAAM,OAAO,YAAoB,IAA8B;AAC7D,UAAM,EAAE,SAAS,IAAI,MAAM,KAAK,KAAK;AAAA,MACnC,gBAAgB,UAAU;AAAA,MAC1B,CAAC,EAAE;AAAA,IACL;AACA,WAAO,WAAW;AAAA,EACpB;AAAA,EAEA,MAAM,WACJ,YACA,QACiB;AACjB,UAAM,IAAI,YAAY,YAAY,EAAE,OAAO,CAAC;AAE5C,UAAM,aAAa,EAAE,KAAK,QAAQ,mBAAmB,aAAa;AAClE,UAAM,EAAE,SAAS,IAAI,MAAM,KAAK,KAAK,MAAM,YAAY,EAAE,MAAM;AAC/D,WAAO;AAAA,EACT;AACF;","names":["FieldType","RelationType","AdapterType"]}
|
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,36 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
|
+
// src/types.ts
|
|
34
|
+
var FieldType, RelationType, AdapterType;
|
|
35
|
+
var init_types = __esm({
|
|
36
|
+
"src/types.ts"() {
|
|
37
|
+
"use strict";
|
|
38
|
+
FieldType = /* @__PURE__ */ ((FieldType2) => {
|
|
39
|
+
FieldType2["String"] = "string";
|
|
40
|
+
FieldType2["Number"] = "number";
|
|
41
|
+
FieldType2["Boolean"] = "boolean";
|
|
42
|
+
FieldType2["Date"] = "date";
|
|
43
|
+
FieldType2["Json"] = "json";
|
|
44
|
+
FieldType2["Array"] = "array";
|
|
45
|
+
return FieldType2;
|
|
46
|
+
})(FieldType || {});
|
|
47
|
+
RelationType = /* @__PURE__ */ ((RelationType2) => {
|
|
48
|
+
RelationType2["HasOne"] = "hasOne";
|
|
49
|
+
RelationType2["HasMany"] = "hasMany";
|
|
50
|
+
RelationType2["BelongsTo"] = "belongsTo";
|
|
51
|
+
RelationType2["ManyToMany"] = "manyToMany";
|
|
52
|
+
return RelationType2;
|
|
53
|
+
})(RelationType || {});
|
|
54
|
+
AdapterType = /* @__PURE__ */ ((AdapterType2) => {
|
|
55
|
+
AdapterType2["Json"] = "json";
|
|
56
|
+
AdapterType2["Postgres"] = "postgres";
|
|
57
|
+
AdapterType2["MySQL"] = "mysql";
|
|
58
|
+
return AdapterType2;
|
|
59
|
+
})(AdapterType || {});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
33
63
|
// src/errors.ts
|
|
34
64
|
var SeedORMError, ValidationError, AdapterError, CollectionNotFoundError, DocumentNotFoundError, UniqueConstraintError;
|
|
35
65
|
var init_errors = __esm({
|
|
@@ -293,16 +323,16 @@ __export(postgres_adapter_exports, {
|
|
|
293
323
|
});
|
|
294
324
|
function fieldToSQLType(field) {
|
|
295
325
|
switch (field.type) {
|
|
296
|
-
case "string"
|
|
326
|
+
case "string" /* String */:
|
|
297
327
|
return field.maxLength ? `VARCHAR(${field.maxLength})` : "TEXT";
|
|
298
|
-
case "number"
|
|
328
|
+
case "number" /* Number */:
|
|
299
329
|
return "DOUBLE PRECISION";
|
|
300
|
-
case "boolean"
|
|
330
|
+
case "boolean" /* Boolean */:
|
|
301
331
|
return "BOOLEAN";
|
|
302
|
-
case "date"
|
|
332
|
+
case "date" /* Date */:
|
|
303
333
|
return "TIMESTAMPTZ";
|
|
304
|
-
case "json"
|
|
305
|
-
case "array"
|
|
334
|
+
case "json" /* Json */:
|
|
335
|
+
case "array" /* Array */:
|
|
306
336
|
return "JSONB";
|
|
307
337
|
default:
|
|
308
338
|
return "TEXT";
|
|
@@ -312,6 +342,7 @@ var PostgresAdapter;
|
|
|
312
342
|
var init_postgres_adapter = __esm({
|
|
313
343
|
"src/adapters/postgres/postgres-adapter.ts"() {
|
|
314
344
|
"use strict";
|
|
345
|
+
init_types();
|
|
315
346
|
init_pg_connection();
|
|
316
347
|
init_pg_query_builder();
|
|
317
348
|
PostgresAdapter = class {
|
|
@@ -426,11 +457,14 @@ var init_postgres_adapter = __esm({
|
|
|
426
457
|
var index_exports = {};
|
|
427
458
|
__export(index_exports, {
|
|
428
459
|
AdapterError: () => AdapterError,
|
|
460
|
+
AdapterType: () => AdapterType,
|
|
429
461
|
CollectionNotFoundError: () => CollectionNotFoundError,
|
|
430
462
|
DocumentNotFoundError: () => DocumentNotFoundError,
|
|
463
|
+
FieldType: () => FieldType,
|
|
431
464
|
JsonAdapter: () => JsonAdapter,
|
|
432
465
|
Model: () => Model,
|
|
433
466
|
PostgresAdapter: () => PostgresAdapter,
|
|
467
|
+
RelationType: () => RelationType,
|
|
434
468
|
SeedORM: () => SeedORM,
|
|
435
469
|
SeedORMError: () => SeedORMError,
|
|
436
470
|
UniqueConstraintError: () => UniqueConstraintError,
|
|
@@ -441,40 +475,43 @@ __export(index_exports, {
|
|
|
441
475
|
module.exports = __toCommonJS(index_exports);
|
|
442
476
|
|
|
443
477
|
// src/seedorm.ts
|
|
478
|
+
init_types();
|
|
444
479
|
init_errors();
|
|
445
480
|
|
|
446
481
|
// src/model/model.ts
|
|
447
482
|
var import_nanoid = require("nanoid");
|
|
483
|
+
init_types();
|
|
448
484
|
init_errors();
|
|
449
485
|
|
|
450
486
|
// src/model/schema.ts
|
|
451
487
|
init_errors();
|
|
452
488
|
|
|
453
489
|
// src/model/field-types.ts
|
|
490
|
+
init_types();
|
|
454
491
|
function validateFieldType(value, type) {
|
|
455
492
|
if (value === void 0 || value === null) return null;
|
|
456
493
|
switch (type) {
|
|
457
|
-
case "string"
|
|
494
|
+
case "string" /* String */:
|
|
458
495
|
if (typeof value !== "string") return `expected string, got ${typeof value}`;
|
|
459
496
|
break;
|
|
460
|
-
case "number"
|
|
497
|
+
case "number" /* Number */:
|
|
461
498
|
if (typeof value !== "number" || Number.isNaN(value))
|
|
462
499
|
return `expected number, got ${typeof value}`;
|
|
463
500
|
break;
|
|
464
|
-
case "boolean"
|
|
501
|
+
case "boolean" /* Boolean */:
|
|
465
502
|
if (typeof value !== "boolean")
|
|
466
503
|
return `expected boolean, got ${typeof value}`;
|
|
467
504
|
break;
|
|
468
|
-
case "date"
|
|
505
|
+
case "date" /* Date */:
|
|
469
506
|
if (typeof value === "string") {
|
|
470
507
|
if (Number.isNaN(Date.parse(value))) return `invalid date string`;
|
|
471
508
|
} else if (!(value instanceof Date)) {
|
|
472
509
|
return `expected date string or Date, got ${typeof value}`;
|
|
473
510
|
}
|
|
474
511
|
break;
|
|
475
|
-
case "json"
|
|
512
|
+
case "json" /* Json */:
|
|
476
513
|
break;
|
|
477
|
-
case "array"
|
|
514
|
+
case "array" /* Array */:
|
|
478
515
|
if (!Array.isArray(value)) return `expected array, got ${typeof value}`;
|
|
479
516
|
break;
|
|
480
517
|
default:
|
|
@@ -484,7 +521,7 @@ function validateFieldType(value, type) {
|
|
|
484
521
|
}
|
|
485
522
|
function coerceFieldValue(value, type) {
|
|
486
523
|
if (value === void 0 || value === null) return value;
|
|
487
|
-
if (type === "date" && value instanceof Date) {
|
|
524
|
+
if (type === "date" /* Date */ && value instanceof Date) {
|
|
488
525
|
return value.toISOString();
|
|
489
526
|
}
|
|
490
527
|
return value;
|
|
@@ -578,15 +615,19 @@ var Model = class {
|
|
|
578
615
|
collection;
|
|
579
616
|
schema;
|
|
580
617
|
prefix;
|
|
618
|
+
relations;
|
|
581
619
|
adapter;
|
|
582
620
|
timestamps;
|
|
583
|
-
|
|
621
|
+
db;
|
|
622
|
+
constructor(definition, adapter, db) {
|
|
584
623
|
this.name = definition.name;
|
|
585
624
|
this.collection = definition.collection;
|
|
586
625
|
this.schema = normalizeSchema(definition.schema);
|
|
587
626
|
this.prefix = definition.prefix ?? definition.collection.slice(0, 3);
|
|
588
627
|
this.adapter = adapter;
|
|
589
628
|
this.timestamps = definition.timestamps !== false;
|
|
629
|
+
this.relations = definition.relations ?? {};
|
|
630
|
+
this.db = db ?? null;
|
|
590
631
|
}
|
|
591
632
|
async init() {
|
|
592
633
|
await this.adapter.createCollection(this.collection, this.schema);
|
|
@@ -594,6 +635,111 @@ var Model = class {
|
|
|
594
635
|
generateId() {
|
|
595
636
|
return `${this.prefix}_${(0, import_nanoid.nanoid)(12)}`;
|
|
596
637
|
}
|
|
638
|
+
resolveRelation(relationName) {
|
|
639
|
+
const rel = this.relations[relationName];
|
|
640
|
+
if (!rel) {
|
|
641
|
+
throw new SeedORMError(`Unknown relation "${relationName}" on model "${this.name}"`);
|
|
642
|
+
}
|
|
643
|
+
if (!this.db) {
|
|
644
|
+
throw new SeedORMError("Cannot resolve relations without a SeedORM instance");
|
|
645
|
+
}
|
|
646
|
+
const relatedModel = this.db.getModel(rel.model);
|
|
647
|
+
if (!relatedModel) {
|
|
648
|
+
throw new SeedORMError(`Related model "${rel.model}" not found. Make sure it is defined before querying.`);
|
|
649
|
+
}
|
|
650
|
+
return { rel, relatedModel };
|
|
651
|
+
}
|
|
652
|
+
async populate(docs, includeList) {
|
|
653
|
+
if (includeList.length === 0 || docs.length === 0) return docs;
|
|
654
|
+
docs = docs.map((d) => ({ ...d }));
|
|
655
|
+
for (const relationName of includeList) {
|
|
656
|
+
const { rel, relatedModel } = this.resolveRelation(relationName);
|
|
657
|
+
switch (rel.type) {
|
|
658
|
+
case "hasMany" /* HasMany */: {
|
|
659
|
+
const parentIds = docs.map((d) => d.id);
|
|
660
|
+
const related = await this.adapter.find(relatedModel.collection, {
|
|
661
|
+
filter: { [rel.foreignKey]: { $in: parentIds } }
|
|
662
|
+
});
|
|
663
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
664
|
+
for (const r of related) {
|
|
665
|
+
const fk = r[rel.foreignKey];
|
|
666
|
+
if (!grouped.has(fk)) grouped.set(fk, []);
|
|
667
|
+
grouped.get(fk).push(r);
|
|
668
|
+
}
|
|
669
|
+
for (const doc of docs) {
|
|
670
|
+
doc[relationName] = grouped.get(doc.id) ?? [];
|
|
671
|
+
}
|
|
672
|
+
break;
|
|
673
|
+
}
|
|
674
|
+
case "hasOne" /* HasOne */: {
|
|
675
|
+
const parentIds = docs.map((d) => d.id);
|
|
676
|
+
const related = await this.adapter.find(relatedModel.collection, {
|
|
677
|
+
filter: { [rel.foreignKey]: { $in: parentIds } }
|
|
678
|
+
});
|
|
679
|
+
const map = /* @__PURE__ */ new Map();
|
|
680
|
+
for (const r of related) {
|
|
681
|
+
const fk = r[rel.foreignKey];
|
|
682
|
+
if (!map.has(fk)) map.set(fk, r);
|
|
683
|
+
}
|
|
684
|
+
for (const doc of docs) {
|
|
685
|
+
doc[relationName] = map.get(doc.id) ?? null;
|
|
686
|
+
}
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
case "belongsTo" /* BelongsTo */: {
|
|
690
|
+
const fkValues = [...new Set(docs.map((d) => d[rel.foreignKey]).filter(Boolean))];
|
|
691
|
+
if (fkValues.length === 0) {
|
|
692
|
+
for (const doc of docs) doc[relationName] = null;
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
const related = await this.adapter.find(relatedModel.collection, {
|
|
696
|
+
filter: { id: { $in: fkValues } }
|
|
697
|
+
});
|
|
698
|
+
const map = /* @__PURE__ */ new Map();
|
|
699
|
+
for (const r of related) {
|
|
700
|
+
map.set(r.id, r);
|
|
701
|
+
}
|
|
702
|
+
for (const doc of docs) {
|
|
703
|
+
const fk = doc[rel.foreignKey];
|
|
704
|
+
doc[relationName] = map.get(fk) ?? null;
|
|
705
|
+
}
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
case "manyToMany" /* ManyToMany */: {
|
|
709
|
+
if (!rel.joinCollection || !rel.relatedKey) {
|
|
710
|
+
throw new SeedORMError(
|
|
711
|
+
`manyToMany relation "${relationName}" requires joinCollection and relatedKey`
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
const parentIds = docs.map((d) => d.id);
|
|
715
|
+
const joinRows = await this.adapter.find(rel.joinCollection, {
|
|
716
|
+
filter: { [rel.foreignKey]: { $in: parentIds } }
|
|
717
|
+
});
|
|
718
|
+
const relatedIds = [...new Set(joinRows.map((r) => r[rel.relatedKey]))];
|
|
719
|
+
const relatedDocs = relatedIds.length > 0 ? await this.adapter.find(relatedModel.collection, {
|
|
720
|
+
filter: { id: { $in: relatedIds } }
|
|
721
|
+
}) : [];
|
|
722
|
+
const relatedMap = /* @__PURE__ */ new Map();
|
|
723
|
+
for (const r of relatedDocs) relatedMap.set(r.id, r);
|
|
724
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
725
|
+
for (const row of joinRows) {
|
|
726
|
+
const parentId = row[rel.foreignKey];
|
|
727
|
+
const relatedId = row[rel.relatedKey];
|
|
728
|
+
const relatedDoc = relatedMap.get(relatedId);
|
|
729
|
+
if (relatedDoc) {
|
|
730
|
+
if (!grouped.has(parentId)) grouped.set(parentId, []);
|
|
731
|
+
grouped.get(parentId).push(relatedDoc);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
for (const doc of docs) {
|
|
735
|
+
doc[relationName] = grouped.get(doc.id) ?? [];
|
|
736
|
+
}
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return docs;
|
|
742
|
+
}
|
|
597
743
|
async create(data) {
|
|
598
744
|
const validated = validateDocument(data, this.schema);
|
|
599
745
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -612,23 +758,32 @@ var Model = class {
|
|
|
612
758
|
}
|
|
613
759
|
return results;
|
|
614
760
|
}
|
|
615
|
-
async findById(id) {
|
|
616
|
-
|
|
761
|
+
async findById(id, options) {
|
|
762
|
+
const doc = await this.adapter.findById(this.collection, id);
|
|
763
|
+
if (!doc || !options?.include?.length) return doc;
|
|
764
|
+
const [populated] = await this.populate([doc], options.include);
|
|
765
|
+
return populated ?? null;
|
|
617
766
|
}
|
|
618
|
-
async findByIdOrThrow(id) {
|
|
619
|
-
const doc = await this.findById(id);
|
|
767
|
+
async findByIdOrThrow(id, options) {
|
|
768
|
+
const doc = await this.findById(id, options);
|
|
620
769
|
if (!doc) throw new DocumentNotFoundError(this.collection, id);
|
|
621
770
|
return doc;
|
|
622
771
|
}
|
|
623
|
-
async findOne(filter) {
|
|
772
|
+
async findOne(filter, options) {
|
|
624
773
|
const results = await this.adapter.find(this.collection, {
|
|
625
774
|
filter,
|
|
626
775
|
limit: 1
|
|
627
776
|
});
|
|
628
|
-
|
|
777
|
+
const doc = results[0] ?? null;
|
|
778
|
+
if (!doc || !options?.include?.length) return doc;
|
|
779
|
+
const [populated] = await this.populate([doc], options.include);
|
|
780
|
+
return populated ?? null;
|
|
629
781
|
}
|
|
630
782
|
async find(options = {}) {
|
|
631
|
-
|
|
783
|
+
const { include, ...adapterOptions } = options;
|
|
784
|
+
const docs = await this.adapter.find(this.collection, adapterOptions);
|
|
785
|
+
if (!include?.length) return docs;
|
|
786
|
+
return this.populate(docs, include);
|
|
632
787
|
}
|
|
633
788
|
async findAll() {
|
|
634
789
|
return this.adapter.find(this.collection, {});
|
|
@@ -654,6 +809,41 @@ var Model = class {
|
|
|
654
809
|
async deleteMany(filter) {
|
|
655
810
|
return this.adapter.deleteMany(this.collection, filter);
|
|
656
811
|
}
|
|
812
|
+
async associate(id, relationName, relatedId) {
|
|
813
|
+
const { rel } = this.resolveRelation(relationName);
|
|
814
|
+
if (rel.type !== "manyToMany" /* ManyToMany */) {
|
|
815
|
+
throw new SeedORMError(`associate() is only supported for manyToMany relations, got "${rel.type}"`);
|
|
816
|
+
}
|
|
817
|
+
if (!rel.joinCollection || !rel.relatedKey) {
|
|
818
|
+
throw new SeedORMError(
|
|
819
|
+
`manyToMany relation "${relationName}" requires joinCollection and relatedKey`
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
823
|
+
const joinDoc = {
|
|
824
|
+
id: `${this.prefix}rel_${(0, import_nanoid.nanoid)(12)}`,
|
|
825
|
+
[rel.foreignKey]: id,
|
|
826
|
+
[rel.relatedKey]: relatedId,
|
|
827
|
+
createdAt: now,
|
|
828
|
+
updatedAt: now
|
|
829
|
+
};
|
|
830
|
+
return this.adapter.insert(rel.joinCollection, joinDoc);
|
|
831
|
+
}
|
|
832
|
+
async dissociate(id, relationName, relatedId) {
|
|
833
|
+
const { rel } = this.resolveRelation(relationName);
|
|
834
|
+
if (rel.type !== "manyToMany" /* ManyToMany */) {
|
|
835
|
+
throw new SeedORMError(`dissociate() is only supported for manyToMany relations, got "${rel.type}"`);
|
|
836
|
+
}
|
|
837
|
+
if (!rel.joinCollection || !rel.relatedKey) {
|
|
838
|
+
throw new SeedORMError(
|
|
839
|
+
`manyToMany relation "${relationName}" requires joinCollection and relatedKey`
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
return this.adapter.deleteMany(rel.joinCollection, {
|
|
843
|
+
[rel.foreignKey]: id,
|
|
844
|
+
[rel.relatedKey]: relatedId
|
|
845
|
+
});
|
|
846
|
+
}
|
|
657
847
|
};
|
|
658
848
|
|
|
659
849
|
// src/adapters/json/json-adapter.ts
|
|
@@ -973,24 +1163,24 @@ var SeedORM = class {
|
|
|
973
1163
|
connected = false;
|
|
974
1164
|
constructor(config) {
|
|
975
1165
|
this.config = {
|
|
976
|
-
adapter: config?.adapter ?? { adapter: "json"
|
|
1166
|
+
adapter: config?.adapter ?? { adapter: "json" /* Json */, path: "./data" },
|
|
977
1167
|
migrationsDir: config?.migrationsDir ?? "./migrations"
|
|
978
1168
|
};
|
|
979
1169
|
}
|
|
980
1170
|
async createAdapter(adapterConfig) {
|
|
981
1171
|
switch (adapterConfig.adapter) {
|
|
982
|
-
case "json"
|
|
1172
|
+
case "json" /* Json */: {
|
|
983
1173
|
const dbPath = path2.resolve(
|
|
984
1174
|
adapterConfig.path ?? "./data",
|
|
985
1175
|
"seedorm.json"
|
|
986
1176
|
);
|
|
987
1177
|
return new JsonAdapter(dbPath);
|
|
988
1178
|
}
|
|
989
|
-
case "postgres"
|
|
1179
|
+
case "postgres" /* Postgres */: {
|
|
990
1180
|
const { PostgresAdapter: PostgresAdapter2 } = await Promise.resolve().then(() => (init_postgres_adapter(), postgres_adapter_exports));
|
|
991
1181
|
return new PostgresAdapter2(adapterConfig.url);
|
|
992
1182
|
}
|
|
993
|
-
case "mysql"
|
|
1183
|
+
case "mysql" /* MySQL */:
|
|
994
1184
|
throw new SeedORMError(
|
|
995
1185
|
'MySQL adapter requires the "mysql2" package. Install it with: npm install mysql2'
|
|
996
1186
|
);
|
|
@@ -1024,7 +1214,7 @@ var SeedORM = class {
|
|
|
1024
1214
|
"Not connected. Call db.connect() before defining models."
|
|
1025
1215
|
);
|
|
1026
1216
|
}
|
|
1027
|
-
const model = new Model(definition, this.adapter);
|
|
1217
|
+
const model = new Model(definition, this.adapter, this);
|
|
1028
1218
|
this.models.set(definition.name, model);
|
|
1029
1219
|
return model;
|
|
1030
1220
|
}
|
|
@@ -1044,15 +1234,19 @@ var SeedORM = class {
|
|
|
1044
1234
|
|
|
1045
1235
|
// src/index.ts
|
|
1046
1236
|
init_postgres_adapter();
|
|
1237
|
+
init_types();
|
|
1047
1238
|
init_errors();
|
|
1048
1239
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1049
1240
|
0 && (module.exports = {
|
|
1050
1241
|
AdapterError,
|
|
1242
|
+
AdapterType,
|
|
1051
1243
|
CollectionNotFoundError,
|
|
1052
1244
|
DocumentNotFoundError,
|
|
1245
|
+
FieldType,
|
|
1053
1246
|
JsonAdapter,
|
|
1054
1247
|
Model,
|
|
1055
1248
|
PostgresAdapter,
|
|
1249
|
+
RelationType,
|
|
1056
1250
|
SeedORM,
|
|
1057
1251
|
SeedORMError,
|
|
1058
1252
|
UniqueConstraintError,
|