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
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install seedorm
|
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
```typescript
|
|
16
|
-
import { SeedORM } from "seedorm";
|
|
16
|
+
import { SeedORM, FieldType } from "seedorm";
|
|
17
17
|
|
|
18
18
|
const db = new SeedORM();
|
|
19
19
|
await db.connect();
|
|
@@ -22,9 +22,9 @@ const User = db.model({
|
|
|
22
22
|
name: "User",
|
|
23
23
|
collection: "users",
|
|
24
24
|
schema: {
|
|
25
|
-
name: { type:
|
|
26
|
-
email: { type:
|
|
27
|
-
role: { type:
|
|
25
|
+
name: { type: FieldType.String, required: true },
|
|
26
|
+
email: { type: FieldType.String, unique: true },
|
|
27
|
+
role: { type: FieldType.String, enum: ["admin", "user"], default: "user" },
|
|
28
28
|
},
|
|
29
29
|
});
|
|
30
30
|
await User.init();
|
|
@@ -48,6 +48,51 @@ await User.delete(alice.id);
|
|
|
48
48
|
await db.disconnect();
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
## Relations
|
|
52
|
+
|
|
53
|
+
Define relationships between models and populate them at query time.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { SeedORM, FieldType, RelationType } from "seedorm";
|
|
57
|
+
|
|
58
|
+
const User = db.model({
|
|
59
|
+
name: "User",
|
|
60
|
+
collection: "users",
|
|
61
|
+
schema: {
|
|
62
|
+
name: { type: FieldType.String, required: true },
|
|
63
|
+
email: { type: FieldType.String, unique: true },
|
|
64
|
+
},
|
|
65
|
+
relations: {
|
|
66
|
+
posts: { type: RelationType.HasMany, model: "Post", foreignKey: "authorId" },
|
|
67
|
+
profile: { type: RelationType.HasOne, model: "Profile", foreignKey: "userId" },
|
|
68
|
+
roles: { type: RelationType.ManyToMany, model: "Role", joinCollection: "user_roles", foreignKey: "userId", relatedKey: "roleId" },
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const Post = db.model({
|
|
73
|
+
name: "Post",
|
|
74
|
+
collection: "posts",
|
|
75
|
+
schema: {
|
|
76
|
+
title: { type: FieldType.String, required: true },
|
|
77
|
+
authorId: { type: FieldType.String, required: true },
|
|
78
|
+
},
|
|
79
|
+
relations: {
|
|
80
|
+
author: { type: RelationType.BelongsTo, model: "User", foreignKey: "authorId" },
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Populate relations with include
|
|
85
|
+
const user = await User.findById("usr_abc123", {
|
|
86
|
+
include: ["posts", "profile", "roles"],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Manage many-to-many links
|
|
90
|
+
await User.associate("roles", "usr_abc123", "rol_editor");
|
|
91
|
+
await User.dissociate("roles", "usr_abc123", "rol_editor");
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Relation types:** `hasOne`, `hasMany`, `belongsTo`, `manyToMany`
|
|
95
|
+
|
|
51
96
|
## Switch to a real database
|
|
52
97
|
|
|
53
98
|
Change your config. That's it.
|
|
@@ -66,10 +111,21 @@ Change your config. That's it.
|
|
|
66
111
|
|
|
67
112
|
Your models, queries, and application logic stay exactly the same. SeedORM currently supports PostgreSQL, with MySQL, SQLite, and more adapters coming soon.
|
|
68
113
|
|
|
114
|
+
## Enums
|
|
115
|
+
|
|
116
|
+
SeedORM exports string enums for type-safe definitions. Plain strings also work since the enums are string-backed.
|
|
117
|
+
|
|
118
|
+
| Enum | Values |
|
|
119
|
+
|------|--------|
|
|
120
|
+
| `FieldType` | `String`, `Number`, `Boolean`, `Date`, `Json`, `Array` |
|
|
121
|
+
| `RelationType` | `HasOne`, `HasMany`, `BelongsTo`, `ManyToMany` |
|
|
122
|
+
| `AdapterType` | `Json`, `Postgres`, `MySQL` |
|
|
123
|
+
|
|
69
124
|
## Features
|
|
70
125
|
|
|
71
126
|
- **Zero-config start** — data lives in a JSON file, no database setup needed
|
|
72
127
|
- **Schema validation** — type checking, required fields, unique constraints, min/max, enums
|
|
128
|
+
- **Relations** — `hasOne`, `hasMany`, `belongsTo`, `manyToMany` with eager loading via `include`
|
|
73
129
|
- **Query operators** — `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$like`, `$exists`
|
|
74
130
|
- **CLI tools** — `seedorm init`, `seedorm start` (REST server), `seedorm studio` (visual UI)
|
|
75
131
|
- **Migration engine** — `migrate create`, `migrate up`, `migrate to` (SQL export)
|
package/dist/bin/seedorm.cjs
CHANGED
|
@@ -3033,6 +3033,13 @@ var require_commander = __commonJS({
|
|
|
3033
3033
|
}
|
|
3034
3034
|
});
|
|
3035
3035
|
|
|
3036
|
+
// src/types.ts
|
|
3037
|
+
var init_types = __esm({
|
|
3038
|
+
"src/types.ts"() {
|
|
3039
|
+
"use strict";
|
|
3040
|
+
}
|
|
3041
|
+
});
|
|
3042
|
+
|
|
3036
3043
|
// src/errors.ts
|
|
3037
3044
|
var SeedORMError, ValidationError, AdapterError, CollectionNotFoundError, DocumentNotFoundError, UniqueConstraintError;
|
|
3038
3045
|
var init_errors = __esm({
|
|
@@ -3956,16 +3963,16 @@ __export(postgres_adapter_exports, {
|
|
|
3956
3963
|
});
|
|
3957
3964
|
function fieldToSQLType(field) {
|
|
3958
3965
|
switch (field.type) {
|
|
3959
|
-
case "string"
|
|
3966
|
+
case "string" /* String */:
|
|
3960
3967
|
return field.maxLength ? `VARCHAR(${field.maxLength})` : "TEXT";
|
|
3961
|
-
case "number"
|
|
3968
|
+
case "number" /* Number */:
|
|
3962
3969
|
return "DOUBLE PRECISION";
|
|
3963
|
-
case "boolean"
|
|
3970
|
+
case "boolean" /* Boolean */:
|
|
3964
3971
|
return "BOOLEAN";
|
|
3965
|
-
case "date"
|
|
3972
|
+
case "date" /* Date */:
|
|
3966
3973
|
return "TIMESTAMPTZ";
|
|
3967
|
-
case "json"
|
|
3968
|
-
case "array"
|
|
3974
|
+
case "json" /* Json */:
|
|
3975
|
+
case "array" /* Array */:
|
|
3969
3976
|
return "JSONB";
|
|
3970
3977
|
default:
|
|
3971
3978
|
return "TEXT";
|
|
@@ -3975,6 +3982,7 @@ var PostgresAdapter;
|
|
|
3975
3982
|
var init_postgres_adapter = __esm({
|
|
3976
3983
|
"src/adapters/postgres/postgres-adapter.ts"() {
|
|
3977
3984
|
"use strict";
|
|
3985
|
+
init_types();
|
|
3978
3986
|
init_pg_connection();
|
|
3979
3987
|
init_pg_query_builder();
|
|
3980
3988
|
PostgresAdapter = class {
|
|
@@ -4140,6 +4148,7 @@ ${BOLD}${msg}${RESET}
|
|
|
4140
4148
|
// src/cli/util/config-loader.ts
|
|
4141
4149
|
var fs = __toESM(require("fs"), 1);
|
|
4142
4150
|
var path = __toESM(require("path"), 1);
|
|
4151
|
+
init_types();
|
|
4143
4152
|
var CONFIG_FILENAMES = [
|
|
4144
4153
|
"seedorm.config.json",
|
|
4145
4154
|
"seedorm.json"
|
|
@@ -4155,22 +4164,23 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
4155
4164
|
const configPath = findConfigFile(cwd);
|
|
4156
4165
|
if (!configPath) {
|
|
4157
4166
|
return {
|
|
4158
|
-
adapter: { adapter: "json"
|
|
4167
|
+
adapter: { adapter: "json" /* Json */, path: "./data" },
|
|
4159
4168
|
migrationsDir: "./migrations"
|
|
4160
4169
|
};
|
|
4161
4170
|
}
|
|
4162
4171
|
const raw = fs.readFileSync(configPath, "utf-8");
|
|
4163
4172
|
const parsed = JSON.parse(raw);
|
|
4164
4173
|
return {
|
|
4165
|
-
adapter: parsed.adapter ?? { adapter: "json"
|
|
4174
|
+
adapter: parsed.adapter ?? { adapter: "json" /* Json */, path: "./data" },
|
|
4166
4175
|
migrationsDir: parsed.migrationsDir ?? "./migrations"
|
|
4167
4176
|
};
|
|
4168
4177
|
}
|
|
4169
4178
|
|
|
4170
4179
|
// src/cli/commands/init.ts
|
|
4180
|
+
init_types();
|
|
4171
4181
|
var DEFAULT_CONFIG = {
|
|
4172
4182
|
adapter: {
|
|
4173
|
-
adapter: "json"
|
|
4183
|
+
adapter: "json" /* Json */,
|
|
4174
4184
|
path: "./data"
|
|
4175
4185
|
},
|
|
4176
4186
|
migrationsDir: "./migrations"
|
|
@@ -4203,40 +4213,43 @@ function initCommand(options) {
|
|
|
4203
4213
|
var http = __toESM(require("http"), 1);
|
|
4204
4214
|
|
|
4205
4215
|
// src/seedorm.ts
|
|
4216
|
+
init_types();
|
|
4206
4217
|
init_errors();
|
|
4207
4218
|
|
|
4208
4219
|
// src/model/model.ts
|
|
4209
4220
|
init_nanoid();
|
|
4221
|
+
init_types();
|
|
4210
4222
|
init_errors();
|
|
4211
4223
|
|
|
4212
4224
|
// src/model/schema.ts
|
|
4213
4225
|
init_errors();
|
|
4214
4226
|
|
|
4215
4227
|
// src/model/field-types.ts
|
|
4228
|
+
init_types();
|
|
4216
4229
|
function validateFieldType(value, type) {
|
|
4217
4230
|
if (value === void 0 || value === null) return null;
|
|
4218
4231
|
switch (type) {
|
|
4219
|
-
case "string"
|
|
4232
|
+
case "string" /* String */:
|
|
4220
4233
|
if (typeof value !== "string") return `expected string, got ${typeof value}`;
|
|
4221
4234
|
break;
|
|
4222
|
-
case "number"
|
|
4235
|
+
case "number" /* Number */:
|
|
4223
4236
|
if (typeof value !== "number" || Number.isNaN(value))
|
|
4224
4237
|
return `expected number, got ${typeof value}`;
|
|
4225
4238
|
break;
|
|
4226
|
-
case "boolean"
|
|
4239
|
+
case "boolean" /* Boolean */:
|
|
4227
4240
|
if (typeof value !== "boolean")
|
|
4228
4241
|
return `expected boolean, got ${typeof value}`;
|
|
4229
4242
|
break;
|
|
4230
|
-
case "date"
|
|
4243
|
+
case "date" /* Date */:
|
|
4231
4244
|
if (typeof value === "string") {
|
|
4232
4245
|
if (Number.isNaN(Date.parse(value))) return `invalid date string`;
|
|
4233
4246
|
} else if (!(value instanceof Date)) {
|
|
4234
4247
|
return `expected date string or Date, got ${typeof value}`;
|
|
4235
4248
|
}
|
|
4236
4249
|
break;
|
|
4237
|
-
case "json"
|
|
4250
|
+
case "json" /* Json */:
|
|
4238
4251
|
break;
|
|
4239
|
-
case "array"
|
|
4252
|
+
case "array" /* Array */:
|
|
4240
4253
|
if (!Array.isArray(value)) return `expected array, got ${typeof value}`;
|
|
4241
4254
|
break;
|
|
4242
4255
|
default:
|
|
@@ -4246,7 +4259,7 @@ function validateFieldType(value, type) {
|
|
|
4246
4259
|
}
|
|
4247
4260
|
function coerceFieldValue(value, type) {
|
|
4248
4261
|
if (value === void 0 || value === null) return value;
|
|
4249
|
-
if (type === "date" && value instanceof Date) {
|
|
4262
|
+
if (type === "date" /* Date */ && value instanceof Date) {
|
|
4250
4263
|
return value.toISOString();
|
|
4251
4264
|
}
|
|
4252
4265
|
return value;
|
|
@@ -4340,15 +4353,19 @@ var Model = class {
|
|
|
4340
4353
|
collection;
|
|
4341
4354
|
schema;
|
|
4342
4355
|
prefix;
|
|
4356
|
+
relations;
|
|
4343
4357
|
adapter;
|
|
4344
4358
|
timestamps;
|
|
4345
|
-
|
|
4359
|
+
db;
|
|
4360
|
+
constructor(definition, adapter, db) {
|
|
4346
4361
|
this.name = definition.name;
|
|
4347
4362
|
this.collection = definition.collection;
|
|
4348
4363
|
this.schema = normalizeSchema(definition.schema);
|
|
4349
4364
|
this.prefix = definition.prefix ?? definition.collection.slice(0, 3);
|
|
4350
4365
|
this.adapter = adapter;
|
|
4351
4366
|
this.timestamps = definition.timestamps !== false;
|
|
4367
|
+
this.relations = definition.relations ?? {};
|
|
4368
|
+
this.db = db ?? null;
|
|
4352
4369
|
}
|
|
4353
4370
|
async init() {
|
|
4354
4371
|
await this.adapter.createCollection(this.collection, this.schema);
|
|
@@ -4356,6 +4373,111 @@ var Model = class {
|
|
|
4356
4373
|
generateId() {
|
|
4357
4374
|
return `${this.prefix}_${nanoid(12)}`;
|
|
4358
4375
|
}
|
|
4376
|
+
resolveRelation(relationName) {
|
|
4377
|
+
const rel = this.relations[relationName];
|
|
4378
|
+
if (!rel) {
|
|
4379
|
+
throw new SeedORMError(`Unknown relation "${relationName}" on model "${this.name}"`);
|
|
4380
|
+
}
|
|
4381
|
+
if (!this.db) {
|
|
4382
|
+
throw new SeedORMError("Cannot resolve relations without a SeedORM instance");
|
|
4383
|
+
}
|
|
4384
|
+
const relatedModel = this.db.getModel(rel.model);
|
|
4385
|
+
if (!relatedModel) {
|
|
4386
|
+
throw new SeedORMError(`Related model "${rel.model}" not found. Make sure it is defined before querying.`);
|
|
4387
|
+
}
|
|
4388
|
+
return { rel, relatedModel };
|
|
4389
|
+
}
|
|
4390
|
+
async populate(docs, includeList) {
|
|
4391
|
+
if (includeList.length === 0 || docs.length === 0) return docs;
|
|
4392
|
+
docs = docs.map((d) => ({ ...d }));
|
|
4393
|
+
for (const relationName of includeList) {
|
|
4394
|
+
const { rel, relatedModel } = this.resolveRelation(relationName);
|
|
4395
|
+
switch (rel.type) {
|
|
4396
|
+
case "hasMany" /* HasMany */: {
|
|
4397
|
+
const parentIds = docs.map((d) => d.id);
|
|
4398
|
+
const related = await this.adapter.find(relatedModel.collection, {
|
|
4399
|
+
filter: { [rel.foreignKey]: { $in: parentIds } }
|
|
4400
|
+
});
|
|
4401
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4402
|
+
for (const r of related) {
|
|
4403
|
+
const fk = r[rel.foreignKey];
|
|
4404
|
+
if (!grouped.has(fk)) grouped.set(fk, []);
|
|
4405
|
+
grouped.get(fk).push(r);
|
|
4406
|
+
}
|
|
4407
|
+
for (const doc of docs) {
|
|
4408
|
+
doc[relationName] = grouped.get(doc.id) ?? [];
|
|
4409
|
+
}
|
|
4410
|
+
break;
|
|
4411
|
+
}
|
|
4412
|
+
case "hasOne" /* HasOne */: {
|
|
4413
|
+
const parentIds = docs.map((d) => d.id);
|
|
4414
|
+
const related = await this.adapter.find(relatedModel.collection, {
|
|
4415
|
+
filter: { [rel.foreignKey]: { $in: parentIds } }
|
|
4416
|
+
});
|
|
4417
|
+
const map = /* @__PURE__ */ new Map();
|
|
4418
|
+
for (const r of related) {
|
|
4419
|
+
const fk = r[rel.foreignKey];
|
|
4420
|
+
if (!map.has(fk)) map.set(fk, r);
|
|
4421
|
+
}
|
|
4422
|
+
for (const doc of docs) {
|
|
4423
|
+
doc[relationName] = map.get(doc.id) ?? null;
|
|
4424
|
+
}
|
|
4425
|
+
break;
|
|
4426
|
+
}
|
|
4427
|
+
case "belongsTo" /* BelongsTo */: {
|
|
4428
|
+
const fkValues = [...new Set(docs.map((d) => d[rel.foreignKey]).filter(Boolean))];
|
|
4429
|
+
if (fkValues.length === 0) {
|
|
4430
|
+
for (const doc of docs) doc[relationName] = null;
|
|
4431
|
+
break;
|
|
4432
|
+
}
|
|
4433
|
+
const related = await this.adapter.find(relatedModel.collection, {
|
|
4434
|
+
filter: { id: { $in: fkValues } }
|
|
4435
|
+
});
|
|
4436
|
+
const map = /* @__PURE__ */ new Map();
|
|
4437
|
+
for (const r of related) {
|
|
4438
|
+
map.set(r.id, r);
|
|
4439
|
+
}
|
|
4440
|
+
for (const doc of docs) {
|
|
4441
|
+
const fk = doc[rel.foreignKey];
|
|
4442
|
+
doc[relationName] = map.get(fk) ?? null;
|
|
4443
|
+
}
|
|
4444
|
+
break;
|
|
4445
|
+
}
|
|
4446
|
+
case "manyToMany" /* ManyToMany */: {
|
|
4447
|
+
if (!rel.joinCollection || !rel.relatedKey) {
|
|
4448
|
+
throw new SeedORMError(
|
|
4449
|
+
`manyToMany relation "${relationName}" requires joinCollection and relatedKey`
|
|
4450
|
+
);
|
|
4451
|
+
}
|
|
4452
|
+
const parentIds = docs.map((d) => d.id);
|
|
4453
|
+
const joinRows = await this.adapter.find(rel.joinCollection, {
|
|
4454
|
+
filter: { [rel.foreignKey]: { $in: parentIds } }
|
|
4455
|
+
});
|
|
4456
|
+
const relatedIds = [...new Set(joinRows.map((r) => r[rel.relatedKey]))];
|
|
4457
|
+
const relatedDocs = relatedIds.length > 0 ? await this.adapter.find(relatedModel.collection, {
|
|
4458
|
+
filter: { id: { $in: relatedIds } }
|
|
4459
|
+
}) : [];
|
|
4460
|
+
const relatedMap = /* @__PURE__ */ new Map();
|
|
4461
|
+
for (const r of relatedDocs) relatedMap.set(r.id, r);
|
|
4462
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4463
|
+
for (const row of joinRows) {
|
|
4464
|
+
const parentId = row[rel.foreignKey];
|
|
4465
|
+
const relatedId = row[rel.relatedKey];
|
|
4466
|
+
const relatedDoc = relatedMap.get(relatedId);
|
|
4467
|
+
if (relatedDoc) {
|
|
4468
|
+
if (!grouped.has(parentId)) grouped.set(parentId, []);
|
|
4469
|
+
grouped.get(parentId).push(relatedDoc);
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
for (const doc of docs) {
|
|
4473
|
+
doc[relationName] = grouped.get(doc.id) ?? [];
|
|
4474
|
+
}
|
|
4475
|
+
break;
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
return docs;
|
|
4480
|
+
}
|
|
4359
4481
|
async create(data) {
|
|
4360
4482
|
const validated = validateDocument(data, this.schema);
|
|
4361
4483
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4374,23 +4496,32 @@ var Model = class {
|
|
|
4374
4496
|
}
|
|
4375
4497
|
return results;
|
|
4376
4498
|
}
|
|
4377
|
-
async findById(id) {
|
|
4378
|
-
|
|
4499
|
+
async findById(id, options) {
|
|
4500
|
+
const doc = await this.adapter.findById(this.collection, id);
|
|
4501
|
+
if (!doc || !options?.include?.length) return doc;
|
|
4502
|
+
const [populated] = await this.populate([doc], options.include);
|
|
4503
|
+
return populated ?? null;
|
|
4379
4504
|
}
|
|
4380
|
-
async findByIdOrThrow(id) {
|
|
4381
|
-
const doc = await this.findById(id);
|
|
4505
|
+
async findByIdOrThrow(id, options) {
|
|
4506
|
+
const doc = await this.findById(id, options);
|
|
4382
4507
|
if (!doc) throw new DocumentNotFoundError(this.collection, id);
|
|
4383
4508
|
return doc;
|
|
4384
4509
|
}
|
|
4385
|
-
async findOne(filter) {
|
|
4510
|
+
async findOne(filter, options) {
|
|
4386
4511
|
const results = await this.adapter.find(this.collection, {
|
|
4387
4512
|
filter,
|
|
4388
4513
|
limit: 1
|
|
4389
4514
|
});
|
|
4390
|
-
|
|
4515
|
+
const doc = results[0] ?? null;
|
|
4516
|
+
if (!doc || !options?.include?.length) return doc;
|
|
4517
|
+
const [populated] = await this.populate([doc], options.include);
|
|
4518
|
+
return populated ?? null;
|
|
4391
4519
|
}
|
|
4392
4520
|
async find(options = {}) {
|
|
4393
|
-
|
|
4521
|
+
const { include, ...adapterOptions } = options;
|
|
4522
|
+
const docs = await this.adapter.find(this.collection, adapterOptions);
|
|
4523
|
+
if (!include?.length) return docs;
|
|
4524
|
+
return this.populate(docs, include);
|
|
4394
4525
|
}
|
|
4395
4526
|
async findAll() {
|
|
4396
4527
|
return this.adapter.find(this.collection, {});
|
|
@@ -4416,6 +4547,41 @@ var Model = class {
|
|
|
4416
4547
|
async deleteMany(filter) {
|
|
4417
4548
|
return this.adapter.deleteMany(this.collection, filter);
|
|
4418
4549
|
}
|
|
4550
|
+
async associate(id, relationName, relatedId) {
|
|
4551
|
+
const { rel } = this.resolveRelation(relationName);
|
|
4552
|
+
if (rel.type !== "manyToMany" /* ManyToMany */) {
|
|
4553
|
+
throw new SeedORMError(`associate() is only supported for manyToMany relations, got "${rel.type}"`);
|
|
4554
|
+
}
|
|
4555
|
+
if (!rel.joinCollection || !rel.relatedKey) {
|
|
4556
|
+
throw new SeedORMError(
|
|
4557
|
+
`manyToMany relation "${relationName}" requires joinCollection and relatedKey`
|
|
4558
|
+
);
|
|
4559
|
+
}
|
|
4560
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4561
|
+
const joinDoc = {
|
|
4562
|
+
id: `${this.prefix}rel_${nanoid(12)}`,
|
|
4563
|
+
[rel.foreignKey]: id,
|
|
4564
|
+
[rel.relatedKey]: relatedId,
|
|
4565
|
+
createdAt: now,
|
|
4566
|
+
updatedAt: now
|
|
4567
|
+
};
|
|
4568
|
+
return this.adapter.insert(rel.joinCollection, joinDoc);
|
|
4569
|
+
}
|
|
4570
|
+
async dissociate(id, relationName, relatedId) {
|
|
4571
|
+
const { rel } = this.resolveRelation(relationName);
|
|
4572
|
+
if (rel.type !== "manyToMany" /* ManyToMany */) {
|
|
4573
|
+
throw new SeedORMError(`dissociate() is only supported for manyToMany relations, got "${rel.type}"`);
|
|
4574
|
+
}
|
|
4575
|
+
if (!rel.joinCollection || !rel.relatedKey) {
|
|
4576
|
+
throw new SeedORMError(
|
|
4577
|
+
`manyToMany relation "${relationName}" requires joinCollection and relatedKey`
|
|
4578
|
+
);
|
|
4579
|
+
}
|
|
4580
|
+
return this.adapter.deleteMany(rel.joinCollection, {
|
|
4581
|
+
[rel.foreignKey]: id,
|
|
4582
|
+
[rel.relatedKey]: relatedId
|
|
4583
|
+
});
|
|
4584
|
+
}
|
|
4419
4585
|
};
|
|
4420
4586
|
|
|
4421
4587
|
// src/adapters/json/json-adapter.ts
|
|
@@ -4735,24 +4901,24 @@ var SeedORM = class {
|
|
|
4735
4901
|
connected = false;
|
|
4736
4902
|
constructor(config) {
|
|
4737
4903
|
this.config = {
|
|
4738
|
-
adapter: config?.adapter ?? { adapter: "json"
|
|
4904
|
+
adapter: config?.adapter ?? { adapter: "json" /* Json */, path: "./data" },
|
|
4739
4905
|
migrationsDir: config?.migrationsDir ?? "./migrations"
|
|
4740
4906
|
};
|
|
4741
4907
|
}
|
|
4742
4908
|
async createAdapter(adapterConfig) {
|
|
4743
4909
|
switch (adapterConfig.adapter) {
|
|
4744
|
-
case "json"
|
|
4910
|
+
case "json" /* Json */: {
|
|
4745
4911
|
const dbPath = path4.resolve(
|
|
4746
4912
|
adapterConfig.path ?? "./data",
|
|
4747
4913
|
"seedorm.json"
|
|
4748
4914
|
);
|
|
4749
4915
|
return new JsonAdapter(dbPath);
|
|
4750
4916
|
}
|
|
4751
|
-
case "postgres"
|
|
4917
|
+
case "postgres" /* Postgres */: {
|
|
4752
4918
|
const { PostgresAdapter: PostgresAdapter2 } = await Promise.resolve().then(() => (init_postgres_adapter(), postgres_adapter_exports));
|
|
4753
4919
|
return new PostgresAdapter2(adapterConfig.url);
|
|
4754
4920
|
}
|
|
4755
|
-
case "mysql"
|
|
4921
|
+
case "mysql" /* MySQL */:
|
|
4756
4922
|
throw new SeedORMError(
|
|
4757
4923
|
'MySQL adapter requires the "mysql2" package. Install it with: npm install mysql2'
|
|
4758
4924
|
);
|
|
@@ -4786,7 +4952,7 @@ var SeedORM = class {
|
|
|
4786
4952
|
"Not connected. Call db.connect() before defining models."
|
|
4787
4953
|
);
|
|
4788
4954
|
}
|
|
4789
|
-
const model = new Model(definition, this.adapter);
|
|
4955
|
+
const model = new Model(definition, this.adapter, this);
|
|
4790
4956
|
this.models.set(definition.name, model);
|
|
4791
4957
|
return model;
|
|
4792
4958
|
}
|
|
@@ -5117,20 +5283,21 @@ var fs5 = __toESM(require("fs"), 1);
|
|
|
5117
5283
|
var path7 = __toESM(require("path"), 1);
|
|
5118
5284
|
|
|
5119
5285
|
// src/migration/exporters/postgres-exporter.ts
|
|
5286
|
+
init_types();
|
|
5120
5287
|
function fieldToSQLType2(field) {
|
|
5121
5288
|
switch (field.type) {
|
|
5122
|
-
case "string"
|
|
5289
|
+
case "string" /* String */:
|
|
5123
5290
|
if (field.maxLength) return `VARCHAR(${field.maxLength})`;
|
|
5124
5291
|
return "TEXT";
|
|
5125
|
-
case "number"
|
|
5292
|
+
case "number" /* Number */:
|
|
5126
5293
|
return "DOUBLE PRECISION";
|
|
5127
|
-
case "boolean"
|
|
5294
|
+
case "boolean" /* Boolean */:
|
|
5128
5295
|
return "BOOLEAN";
|
|
5129
|
-
case "date"
|
|
5296
|
+
case "date" /* Date */:
|
|
5130
5297
|
return "TIMESTAMPTZ";
|
|
5131
|
-
case "json"
|
|
5298
|
+
case "json" /* Json */:
|
|
5132
5299
|
return "JSONB";
|
|
5133
|
-
case "array"
|
|
5300
|
+
case "array" /* Array */:
|
|
5134
5301
|
return "JSONB";
|
|
5135
5302
|
default:
|
|
5136
5303
|
return "TEXT";
|
|
@@ -5200,8 +5367,9 @@ function generateExportSQL(collection, schema, docs) {
|
|
|
5200
5367
|
}
|
|
5201
5368
|
|
|
5202
5369
|
// src/cli/commands/migrate-to.ts
|
|
5370
|
+
init_types();
|
|
5203
5371
|
async function migrateToCommand(target, options) {
|
|
5204
|
-
if (target !== "postgres") {
|
|
5372
|
+
if (target !== "postgres" /* Postgres */) {
|
|
5205
5373
|
logger.error(`Unsupported target: ${target}. Currently only "postgres" is supported.`);
|
|
5206
5374
|
return;
|
|
5207
5375
|
}
|
|
@@ -5250,18 +5418,18 @@ function inferSchemaFromDocs(docs) {
|
|
|
5250
5418
|
return schema;
|
|
5251
5419
|
}
|
|
5252
5420
|
function inferType(value) {
|
|
5253
|
-
if (value === null || value === void 0) return "string"
|
|
5254
|
-
if (typeof value === "boolean") return "boolean"
|
|
5255
|
-
if (typeof value === "number") return "number"
|
|
5256
|
-
if (Array.isArray(value)) return "array"
|
|
5257
|
-
if (typeof value === "object") return "json"
|
|
5421
|
+
if (value === null || value === void 0) return "string" /* String */;
|
|
5422
|
+
if (typeof value === "boolean") return "boolean" /* Boolean */;
|
|
5423
|
+
if (typeof value === "number") return "number" /* Number */;
|
|
5424
|
+
if (Array.isArray(value)) return "array" /* Array */;
|
|
5425
|
+
if (typeof value === "object") return "json" /* Json */;
|
|
5258
5426
|
if (typeof value === "string") {
|
|
5259
5427
|
if (!Number.isNaN(Date.parse(value)) && /^\d{4}-\d{2}-\d{2}/.test(value)) {
|
|
5260
|
-
return "date"
|
|
5428
|
+
return "date" /* Date */;
|
|
5261
5429
|
}
|
|
5262
|
-
return "string"
|
|
5430
|
+
return "string" /* String */;
|
|
5263
5431
|
}
|
|
5264
|
-
return "string"
|
|
5432
|
+
return "string" /* String */;
|
|
5265
5433
|
}
|
|
5266
5434
|
|
|
5267
5435
|
// src/studio/server.ts
|
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var FieldType = /* @__PURE__ */ ((FieldType2) => {
|
|
3
|
+
FieldType2["String"] = "string";
|
|
4
|
+
FieldType2["Number"] = "number";
|
|
5
|
+
FieldType2["Boolean"] = "boolean";
|
|
6
|
+
FieldType2["Date"] = "date";
|
|
7
|
+
FieldType2["Json"] = "json";
|
|
8
|
+
FieldType2["Array"] = "array";
|
|
9
|
+
return FieldType2;
|
|
10
|
+
})(FieldType || {});
|
|
11
|
+
var RelationType = /* @__PURE__ */ ((RelationType2) => {
|
|
12
|
+
RelationType2["HasOne"] = "hasOne";
|
|
13
|
+
RelationType2["HasMany"] = "hasMany";
|
|
14
|
+
RelationType2["BelongsTo"] = "belongsTo";
|
|
15
|
+
RelationType2["ManyToMany"] = "manyToMany";
|
|
16
|
+
return RelationType2;
|
|
17
|
+
})(RelationType || {});
|
|
18
|
+
var AdapterType = /* @__PURE__ */ ((AdapterType2) => {
|
|
19
|
+
AdapterType2["Json"] = "json";
|
|
20
|
+
AdapterType2["Postgres"] = "postgres";
|
|
21
|
+
AdapterType2["MySQL"] = "mysql";
|
|
22
|
+
return AdapterType2;
|
|
23
|
+
})(AdapterType || {});
|
|
24
|
+
|
|
1
25
|
// src/errors.ts
|
|
2
26
|
var SeedORMError = class extends Error {
|
|
3
27
|
constructor(message) {
|
|
@@ -232,16 +256,16 @@ function buildOrderBy(sort) {
|
|
|
232
256
|
// src/adapters/postgres/postgres-adapter.ts
|
|
233
257
|
function fieldToSQLType(field) {
|
|
234
258
|
switch (field.type) {
|
|
235
|
-
case "string"
|
|
259
|
+
case "string" /* String */:
|
|
236
260
|
return field.maxLength ? `VARCHAR(${field.maxLength})` : "TEXT";
|
|
237
|
-
case "number"
|
|
261
|
+
case "number" /* Number */:
|
|
238
262
|
return "DOUBLE PRECISION";
|
|
239
|
-
case "boolean"
|
|
263
|
+
case "boolean" /* Boolean */:
|
|
240
264
|
return "BOOLEAN";
|
|
241
|
-
case "date"
|
|
265
|
+
case "date" /* Date */:
|
|
242
266
|
return "TIMESTAMPTZ";
|
|
243
|
-
case "json"
|
|
244
|
-
case "array"
|
|
267
|
+
case "json" /* Json */:
|
|
268
|
+
case "array" /* Array */:
|
|
245
269
|
return "JSONB";
|
|
246
270
|
default:
|
|
247
271
|
return "TEXT";
|
|
@@ -354,6 +378,9 @@ var PostgresAdapter = class {
|
|
|
354
378
|
};
|
|
355
379
|
|
|
356
380
|
export {
|
|
381
|
+
FieldType,
|
|
382
|
+
RelationType,
|
|
383
|
+
AdapterType,
|
|
357
384
|
SeedORMError,
|
|
358
385
|
ValidationError,
|
|
359
386
|
AdapterError,
|
|
@@ -364,4 +391,4 @@ export {
|
|
|
364
391
|
isOperatorObject,
|
|
365
392
|
PostgresAdapter
|
|
366
393
|
};
|
|
367
|
-
//# sourceMappingURL=chunk-
|
|
394
|
+
//# sourceMappingURL=chunk-3BYE5BOC.js.map
|