arkormx 0.1.11 → 0.2.1
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/dist/index.cjs +203 -3
- package/dist/index.d.cts +76 -1
- package/dist/index.d.mts +76 -1
- package/dist/index.mjs +203 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2203,9 +2203,14 @@ const defineFactory = (model, definition) => {
|
|
|
2203
2203
|
* @since 0.1.0
|
|
2204
2204
|
*/
|
|
2205
2205
|
var ModelNotFoundException = class extends ArkormException {
|
|
2206
|
-
|
|
2206
|
+
modelName;
|
|
2207
|
+
constructor(modelName, message = "No query results for the given model.") {
|
|
2207
2208
|
super(message);
|
|
2208
2209
|
this.name = "ModelNotFoundException";
|
|
2210
|
+
this.modelName = modelName;
|
|
2211
|
+
}
|
|
2212
|
+
getModelName() {
|
|
2213
|
+
return this.modelName;
|
|
2209
2214
|
}
|
|
2210
2215
|
};
|
|
2211
2216
|
|
|
@@ -3605,7 +3610,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
3605
3610
|
*/
|
|
3606
3611
|
async firstOrFail() {
|
|
3607
3612
|
const model = await this.first();
|
|
3608
|
-
if (!model) throw new ModelNotFoundException("Record not found.");
|
|
3613
|
+
if (!model) throw new ModelNotFoundException(this.model.name, "Record not found.");
|
|
3609
3614
|
return model;
|
|
3610
3615
|
}
|
|
3611
3616
|
async find(value, key = "id") {
|
|
@@ -3638,7 +3643,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
3638
3643
|
*/
|
|
3639
3644
|
async valueOrFail(column) {
|
|
3640
3645
|
const result = await this.value(column);
|
|
3641
|
-
if (result == null) throw new ModelNotFoundException("Record not found.");
|
|
3646
|
+
if (result == null) throw new ModelNotFoundException(this.model.name, "Record not found.");
|
|
3642
3647
|
return result;
|
|
3643
3648
|
}
|
|
3644
3649
|
/**
|
|
@@ -3664,6 +3669,99 @@ var QueryBuilder = class QueryBuilder {
|
|
|
3664
3669
|
return this.model.hydrate(created);
|
|
3665
3670
|
}
|
|
3666
3671
|
/**
|
|
3672
|
+
* Creates multiple records and returns hydrated model instances.
|
|
3673
|
+
*
|
|
3674
|
+
* @param values
|
|
3675
|
+
* @returns
|
|
3676
|
+
*/
|
|
3677
|
+
async createMany(values) {
|
|
3678
|
+
if (values.length === 0) return [];
|
|
3679
|
+
return await Promise.all(values.map(async (value) => await this.create(value)));
|
|
3680
|
+
}
|
|
3681
|
+
/**
|
|
3682
|
+
* Insert one or more records.
|
|
3683
|
+
*
|
|
3684
|
+
* @param values
|
|
3685
|
+
* @returns
|
|
3686
|
+
*/
|
|
3687
|
+
async insert(values) {
|
|
3688
|
+
const payloads = this.normalizeInsertPayloads(values);
|
|
3689
|
+
if (payloads.length === 0) return true;
|
|
3690
|
+
const delegate = this.delegate;
|
|
3691
|
+
if (typeof delegate.createMany === "function") {
|
|
3692
|
+
await delegate.createMany({ data: payloads });
|
|
3693
|
+
return true;
|
|
3694
|
+
}
|
|
3695
|
+
await Promise.all(payloads.map(async (payload) => {
|
|
3696
|
+
await this.delegate.create({ data: payload });
|
|
3697
|
+
}));
|
|
3698
|
+
return true;
|
|
3699
|
+
}
|
|
3700
|
+
/**
|
|
3701
|
+
* Insert one or more records while ignoring insertion errors.
|
|
3702
|
+
*
|
|
3703
|
+
* @param values
|
|
3704
|
+
* @returns
|
|
3705
|
+
*/
|
|
3706
|
+
async insertOrIgnore(values) {
|
|
3707
|
+
const payloads = this.normalizeInsertPayloads(values);
|
|
3708
|
+
if (payloads.length === 0) return 0;
|
|
3709
|
+
const delegate = this.delegate;
|
|
3710
|
+
if (typeof delegate.createMany === "function") {
|
|
3711
|
+
const result = await delegate.createMany({
|
|
3712
|
+
data: payloads,
|
|
3713
|
+
skipDuplicates: true
|
|
3714
|
+
});
|
|
3715
|
+
return this.resolveAffectedCount(result, payloads.length);
|
|
3716
|
+
}
|
|
3717
|
+
let inserted = 0;
|
|
3718
|
+
for (const payload of payloads) try {
|
|
3719
|
+
await this.delegate.create({ data: payload });
|
|
3720
|
+
inserted += 1;
|
|
3721
|
+
} catch {
|
|
3722
|
+
continue;
|
|
3723
|
+
}
|
|
3724
|
+
return inserted;
|
|
3725
|
+
}
|
|
3726
|
+
/**
|
|
3727
|
+
* Insert a record and return its primary key value.
|
|
3728
|
+
*
|
|
3729
|
+
* @param values
|
|
3730
|
+
* @param sequence
|
|
3731
|
+
* @returns
|
|
3732
|
+
*/
|
|
3733
|
+
async insertGetId(values, sequence) {
|
|
3734
|
+
const created = await this.delegate.create({ data: values });
|
|
3735
|
+
const key = sequence ?? "id";
|
|
3736
|
+
if (!(key in created)) throw new ArkormException(`Inserted record does not contain key [${key}].`);
|
|
3737
|
+
return created[key];
|
|
3738
|
+
}
|
|
3739
|
+
/**
|
|
3740
|
+
* Insert records using values produced by another query/source.
|
|
3741
|
+
*
|
|
3742
|
+
* @param columns
|
|
3743
|
+
* @param query
|
|
3744
|
+
* @returns
|
|
3745
|
+
*/
|
|
3746
|
+
async insertUsing(columns, query) {
|
|
3747
|
+
const rows = await this.resolveInsertUsingRows(columns, query);
|
|
3748
|
+
if (rows.length === 0) return 0;
|
|
3749
|
+
await this.insert(rows);
|
|
3750
|
+
return rows.length;
|
|
3751
|
+
}
|
|
3752
|
+
/**
|
|
3753
|
+
* Insert records using values produced by another query/source while ignoring insertion errors.
|
|
3754
|
+
*
|
|
3755
|
+
* @param columns
|
|
3756
|
+
* @param query
|
|
3757
|
+
* @returns
|
|
3758
|
+
*/
|
|
3759
|
+
async insertOrIgnoreUsing(columns, query) {
|
|
3760
|
+
const rows = await this.resolveInsertUsingRows(columns, query);
|
|
3761
|
+
if (rows.length === 0) return 0;
|
|
3762
|
+
return this.insertOrIgnore(rows);
|
|
3763
|
+
}
|
|
3764
|
+
/**
|
|
3667
3765
|
* Updates records matching the current query constraints with the
|
|
3668
3766
|
* specified data and returns the updated record(s) as model instance(s).
|
|
3669
3767
|
*
|
|
@@ -3681,6 +3779,71 @@ var QueryBuilder = class QueryBuilder {
|
|
|
3681
3779
|
return this.model.hydrate(updated);
|
|
3682
3780
|
}
|
|
3683
3781
|
/**
|
|
3782
|
+
* Update records using update-many semantics when available.
|
|
3783
|
+
*
|
|
3784
|
+
* @param data
|
|
3785
|
+
* @returns
|
|
3786
|
+
*/
|
|
3787
|
+
async updateFrom(data) {
|
|
3788
|
+
const where = this.buildWhere();
|
|
3789
|
+
if (!where) throw new ArkormException("Update requires a where clause.");
|
|
3790
|
+
const delegate = this.delegate;
|
|
3791
|
+
if (typeof delegate.updateMany === "function") {
|
|
3792
|
+
const result = await delegate.updateMany({
|
|
3793
|
+
where,
|
|
3794
|
+
data
|
|
3795
|
+
});
|
|
3796
|
+
return this.resolveAffectedCount(result, 0);
|
|
3797
|
+
}
|
|
3798
|
+
await this.update(data);
|
|
3799
|
+
return 1;
|
|
3800
|
+
}
|
|
3801
|
+
/**
|
|
3802
|
+
* Insert a record when no match exists, otherwise update the matching record.
|
|
3803
|
+
*
|
|
3804
|
+
* @param attributes
|
|
3805
|
+
* @param values
|
|
3806
|
+
* @returns
|
|
3807
|
+
*/
|
|
3808
|
+
async updateOrInsert(attributes, values = {}) {
|
|
3809
|
+
const exists = await this.delegate.findFirst({ where: attributes }) != null;
|
|
3810
|
+
const resolvedValues = typeof values === "function" ? await values(exists) : values;
|
|
3811
|
+
if (!exists) {
|
|
3812
|
+
await this.delegate.create({ data: {
|
|
3813
|
+
...attributes,
|
|
3814
|
+
...resolvedValues
|
|
3815
|
+
} });
|
|
3816
|
+
return true;
|
|
3817
|
+
}
|
|
3818
|
+
return await this.clone().where(attributes).update(resolvedValues) != null;
|
|
3819
|
+
}
|
|
3820
|
+
/**
|
|
3821
|
+
* Insert new records or update existing records by one or more unique keys.
|
|
3822
|
+
*
|
|
3823
|
+
* @param values
|
|
3824
|
+
* @param uniqueBy
|
|
3825
|
+
* @param update
|
|
3826
|
+
* @returns
|
|
3827
|
+
*/
|
|
3828
|
+
async upsert(values, uniqueBy, update = null) {
|
|
3829
|
+
if (values.length === 0) return 0;
|
|
3830
|
+
const uniqueKeys = Array.isArray(uniqueBy) ? uniqueBy : [uniqueBy];
|
|
3831
|
+
let affected = 0;
|
|
3832
|
+
for (const row of values) {
|
|
3833
|
+
const attributes = uniqueKeys.reduce((all, key) => {
|
|
3834
|
+
all[key] = row[key];
|
|
3835
|
+
return all;
|
|
3836
|
+
}, {});
|
|
3837
|
+
const updatePayload = (update ?? Object.keys(row).filter((key) => !uniqueKeys.includes(key))).reduce((all, key) => {
|
|
3838
|
+
if (key in row) all[key] = row[key];
|
|
3839
|
+
return all;
|
|
3840
|
+
}, {});
|
|
3841
|
+
await this.updateOrInsert(attributes, updatePayload);
|
|
3842
|
+
affected += 1;
|
|
3843
|
+
}
|
|
3844
|
+
return affected;
|
|
3845
|
+
}
|
|
3846
|
+
/**
|
|
3684
3847
|
* Deletes records matching the current query constraints and returns
|
|
3685
3848
|
* the deleted record(s) as model instance(s).
|
|
3686
3849
|
*
|
|
@@ -3719,6 +3882,43 @@ var QueryBuilder = class QueryBuilder {
|
|
|
3719
3882
|
async doesntExist() {
|
|
3720
3883
|
return !await this.exists();
|
|
3721
3884
|
}
|
|
3885
|
+
normalizeInsertPayloads(values) {
|
|
3886
|
+
if (Array.isArray(values)) return values;
|
|
3887
|
+
return [values];
|
|
3888
|
+
}
|
|
3889
|
+
resolveAffectedCount(result, fallback) {
|
|
3890
|
+
if (typeof result === "number") return result;
|
|
3891
|
+
if (result && typeof result === "object" && "count" in result) {
|
|
3892
|
+
const candidate = result.count;
|
|
3893
|
+
if (typeof candidate === "number") return candidate;
|
|
3894
|
+
}
|
|
3895
|
+
return fallback;
|
|
3896
|
+
}
|
|
3897
|
+
async resolveInsertUsingRows(columns, query) {
|
|
3898
|
+
const resolvedQuery = typeof query === "function" ? await query() : query;
|
|
3899
|
+
return (await this.resolveInsertUsingSource(resolvedQuery)).map((row) => {
|
|
3900
|
+
return columns.reduce((record, column) => {
|
|
3901
|
+
record[column] = row[column];
|
|
3902
|
+
return record;
|
|
3903
|
+
}, {});
|
|
3904
|
+
});
|
|
3905
|
+
}
|
|
3906
|
+
async resolveInsertUsingSource(source) {
|
|
3907
|
+
if (source && typeof source === "object") {
|
|
3908
|
+
const asBuilder = source;
|
|
3909
|
+
if (typeof asBuilder.get === "function") {
|
|
3910
|
+
const collection = await asBuilder.get();
|
|
3911
|
+
if (typeof collection.all === "function") return collection.all().map((item) => {
|
|
3912
|
+
const asModel = item;
|
|
3913
|
+
if (typeof asModel.getRawAttributes === "function") return asModel.getRawAttributes();
|
|
3914
|
+
return item;
|
|
3915
|
+
});
|
|
3916
|
+
}
|
|
3917
|
+
if (Array.isArray(source)) return source;
|
|
3918
|
+
}
|
|
3919
|
+
if (Array.isArray(source)) return source;
|
|
3920
|
+
throw new ArkormException("insertUsing expects a query builder, array of records, or async resolver.");
|
|
3921
|
+
}
|
|
3722
3922
|
/**
|
|
3723
3923
|
* Execute callback when no records exist.
|
|
3724
3924
|
*
|
package/dist/index.d.cts
CHANGED
|
@@ -1477,6 +1477,51 @@ declare class QueryBuilder<TModel, TDelegate extends PrismaDelegateLike = Prisma
|
|
|
1477
1477
|
* @returns
|
|
1478
1478
|
*/
|
|
1479
1479
|
create(data: DelegateCreateData<TDelegate>): Promise<TModel>;
|
|
1480
|
+
/**
|
|
1481
|
+
* Creates multiple records and returns hydrated model instances.
|
|
1482
|
+
*
|
|
1483
|
+
* @param values
|
|
1484
|
+
* @returns
|
|
1485
|
+
*/
|
|
1486
|
+
createMany(values: DelegateCreateData<TDelegate>[]): Promise<TModel[]>;
|
|
1487
|
+
/**
|
|
1488
|
+
* Insert one or more records.
|
|
1489
|
+
*
|
|
1490
|
+
* @param values
|
|
1491
|
+
* @returns
|
|
1492
|
+
*/
|
|
1493
|
+
insert(values: DelegateCreateData<TDelegate> | DelegateCreateData<TDelegate>[]): Promise<boolean>;
|
|
1494
|
+
/**
|
|
1495
|
+
* Insert one or more records while ignoring insertion errors.
|
|
1496
|
+
*
|
|
1497
|
+
* @param values
|
|
1498
|
+
* @returns
|
|
1499
|
+
*/
|
|
1500
|
+
insertOrIgnore(values: DelegateCreateData<TDelegate> | DelegateCreateData<TDelegate>[]): Promise<number>;
|
|
1501
|
+
/**
|
|
1502
|
+
* Insert a record and return its primary key value.
|
|
1503
|
+
*
|
|
1504
|
+
* @param values
|
|
1505
|
+
* @param sequence
|
|
1506
|
+
* @returns
|
|
1507
|
+
*/
|
|
1508
|
+
insertGetId(values: DelegateCreateData<TDelegate>, sequence?: string | null): Promise<unknown>;
|
|
1509
|
+
/**
|
|
1510
|
+
* Insert records using values produced by another query/source.
|
|
1511
|
+
*
|
|
1512
|
+
* @param columns
|
|
1513
|
+
* @param query
|
|
1514
|
+
* @returns
|
|
1515
|
+
*/
|
|
1516
|
+
insertUsing(columns: string[], query: unknown): Promise<number>;
|
|
1517
|
+
/**
|
|
1518
|
+
* Insert records using values produced by another query/source while ignoring insertion errors.
|
|
1519
|
+
*
|
|
1520
|
+
* @param columns
|
|
1521
|
+
* @param query
|
|
1522
|
+
* @returns
|
|
1523
|
+
*/
|
|
1524
|
+
insertOrIgnoreUsing(columns: string[], query: unknown): Promise<number>;
|
|
1480
1525
|
/**
|
|
1481
1526
|
* Updates records matching the current query constraints with the
|
|
1482
1527
|
* specified data and returns the updated record(s) as model instance(s).
|
|
@@ -1485,6 +1530,30 @@ declare class QueryBuilder<TModel, TDelegate extends PrismaDelegateLike = Prisma
|
|
|
1485
1530
|
* @returns
|
|
1486
1531
|
*/
|
|
1487
1532
|
update(data: DelegateUpdateData<TDelegate>): Promise<TModel>;
|
|
1533
|
+
/**
|
|
1534
|
+
* Update records using update-many semantics when available.
|
|
1535
|
+
*
|
|
1536
|
+
* @param data
|
|
1537
|
+
* @returns
|
|
1538
|
+
*/
|
|
1539
|
+
updateFrom(data: DelegateUpdateData<TDelegate>): Promise<number>;
|
|
1540
|
+
/**
|
|
1541
|
+
* Insert a record when no match exists, otherwise update the matching record.
|
|
1542
|
+
*
|
|
1543
|
+
* @param attributes
|
|
1544
|
+
* @param values
|
|
1545
|
+
* @returns
|
|
1546
|
+
*/
|
|
1547
|
+
updateOrInsert(attributes: Record<string, unknown>, values?: Record<string, unknown> | ((exists: boolean) => Record<string, unknown> | Promise<Record<string, unknown>>)): Promise<boolean>;
|
|
1548
|
+
/**
|
|
1549
|
+
* Insert new records or update existing records by one or more unique keys.
|
|
1550
|
+
*
|
|
1551
|
+
* @param values
|
|
1552
|
+
* @param uniqueBy
|
|
1553
|
+
* @param update
|
|
1554
|
+
* @returns
|
|
1555
|
+
*/
|
|
1556
|
+
upsert(values: Array<Record<string, unknown>>, uniqueBy: string | string[], update?: string[] | null): Promise<number>;
|
|
1488
1557
|
/**
|
|
1489
1558
|
* Deletes records matching the current query constraints and returns
|
|
1490
1559
|
* the deleted record(s) as model instance(s).
|
|
@@ -1510,6 +1579,10 @@ declare class QueryBuilder<TModel, TDelegate extends PrismaDelegateLike = Prisma
|
|
|
1510
1579
|
* @returns
|
|
1511
1580
|
*/
|
|
1512
1581
|
doesntExist(): Promise<boolean>;
|
|
1582
|
+
private normalizeInsertPayloads;
|
|
1583
|
+
private resolveAffectedCount;
|
|
1584
|
+
private resolveInsertUsingRows;
|
|
1585
|
+
private resolveInsertUsingSource;
|
|
1513
1586
|
/**
|
|
1514
1587
|
* Execute callback when no records exist.
|
|
1515
1588
|
*
|
|
@@ -2557,7 +2630,9 @@ declare class ArkormException extends Error {
|
|
|
2557
2630
|
* @since 0.1.0
|
|
2558
2631
|
*/
|
|
2559
2632
|
declare class ModelNotFoundException extends ArkormException {
|
|
2560
|
-
|
|
2633
|
+
private modelName;
|
|
2634
|
+
constructor(modelName: string, message?: string);
|
|
2635
|
+
getModelName(): string;
|
|
2561
2636
|
}
|
|
2562
2637
|
//#endregion
|
|
2563
2638
|
//#region src/helpers/migrations.d.ts
|
package/dist/index.d.mts
CHANGED
|
@@ -1477,6 +1477,51 @@ declare class QueryBuilder<TModel, TDelegate extends PrismaDelegateLike = Prisma
|
|
|
1477
1477
|
* @returns
|
|
1478
1478
|
*/
|
|
1479
1479
|
create(data: DelegateCreateData<TDelegate>): Promise<TModel>;
|
|
1480
|
+
/**
|
|
1481
|
+
* Creates multiple records and returns hydrated model instances.
|
|
1482
|
+
*
|
|
1483
|
+
* @param values
|
|
1484
|
+
* @returns
|
|
1485
|
+
*/
|
|
1486
|
+
createMany(values: DelegateCreateData<TDelegate>[]): Promise<TModel[]>;
|
|
1487
|
+
/**
|
|
1488
|
+
* Insert one or more records.
|
|
1489
|
+
*
|
|
1490
|
+
* @param values
|
|
1491
|
+
* @returns
|
|
1492
|
+
*/
|
|
1493
|
+
insert(values: DelegateCreateData<TDelegate> | DelegateCreateData<TDelegate>[]): Promise<boolean>;
|
|
1494
|
+
/**
|
|
1495
|
+
* Insert one or more records while ignoring insertion errors.
|
|
1496
|
+
*
|
|
1497
|
+
* @param values
|
|
1498
|
+
* @returns
|
|
1499
|
+
*/
|
|
1500
|
+
insertOrIgnore(values: DelegateCreateData<TDelegate> | DelegateCreateData<TDelegate>[]): Promise<number>;
|
|
1501
|
+
/**
|
|
1502
|
+
* Insert a record and return its primary key value.
|
|
1503
|
+
*
|
|
1504
|
+
* @param values
|
|
1505
|
+
* @param sequence
|
|
1506
|
+
* @returns
|
|
1507
|
+
*/
|
|
1508
|
+
insertGetId(values: DelegateCreateData<TDelegate>, sequence?: string | null): Promise<unknown>;
|
|
1509
|
+
/**
|
|
1510
|
+
* Insert records using values produced by another query/source.
|
|
1511
|
+
*
|
|
1512
|
+
* @param columns
|
|
1513
|
+
* @param query
|
|
1514
|
+
* @returns
|
|
1515
|
+
*/
|
|
1516
|
+
insertUsing(columns: string[], query: unknown): Promise<number>;
|
|
1517
|
+
/**
|
|
1518
|
+
* Insert records using values produced by another query/source while ignoring insertion errors.
|
|
1519
|
+
*
|
|
1520
|
+
* @param columns
|
|
1521
|
+
* @param query
|
|
1522
|
+
* @returns
|
|
1523
|
+
*/
|
|
1524
|
+
insertOrIgnoreUsing(columns: string[], query: unknown): Promise<number>;
|
|
1480
1525
|
/**
|
|
1481
1526
|
* Updates records matching the current query constraints with the
|
|
1482
1527
|
* specified data and returns the updated record(s) as model instance(s).
|
|
@@ -1485,6 +1530,30 @@ declare class QueryBuilder<TModel, TDelegate extends PrismaDelegateLike = Prisma
|
|
|
1485
1530
|
* @returns
|
|
1486
1531
|
*/
|
|
1487
1532
|
update(data: DelegateUpdateData<TDelegate>): Promise<TModel>;
|
|
1533
|
+
/**
|
|
1534
|
+
* Update records using update-many semantics when available.
|
|
1535
|
+
*
|
|
1536
|
+
* @param data
|
|
1537
|
+
* @returns
|
|
1538
|
+
*/
|
|
1539
|
+
updateFrom(data: DelegateUpdateData<TDelegate>): Promise<number>;
|
|
1540
|
+
/**
|
|
1541
|
+
* Insert a record when no match exists, otherwise update the matching record.
|
|
1542
|
+
*
|
|
1543
|
+
* @param attributes
|
|
1544
|
+
* @param values
|
|
1545
|
+
* @returns
|
|
1546
|
+
*/
|
|
1547
|
+
updateOrInsert(attributes: Record<string, unknown>, values?: Record<string, unknown> | ((exists: boolean) => Record<string, unknown> | Promise<Record<string, unknown>>)): Promise<boolean>;
|
|
1548
|
+
/**
|
|
1549
|
+
* Insert new records or update existing records by one or more unique keys.
|
|
1550
|
+
*
|
|
1551
|
+
* @param values
|
|
1552
|
+
* @param uniqueBy
|
|
1553
|
+
* @param update
|
|
1554
|
+
* @returns
|
|
1555
|
+
*/
|
|
1556
|
+
upsert(values: Array<Record<string, unknown>>, uniqueBy: string | string[], update?: string[] | null): Promise<number>;
|
|
1488
1557
|
/**
|
|
1489
1558
|
* Deletes records matching the current query constraints and returns
|
|
1490
1559
|
* the deleted record(s) as model instance(s).
|
|
@@ -1510,6 +1579,10 @@ declare class QueryBuilder<TModel, TDelegate extends PrismaDelegateLike = Prisma
|
|
|
1510
1579
|
* @returns
|
|
1511
1580
|
*/
|
|
1512
1581
|
doesntExist(): Promise<boolean>;
|
|
1582
|
+
private normalizeInsertPayloads;
|
|
1583
|
+
private resolveAffectedCount;
|
|
1584
|
+
private resolveInsertUsingRows;
|
|
1585
|
+
private resolveInsertUsingSource;
|
|
1513
1586
|
/**
|
|
1514
1587
|
* Execute callback when no records exist.
|
|
1515
1588
|
*
|
|
@@ -2557,7 +2630,9 @@ declare class ArkormException extends Error {
|
|
|
2557
2630
|
* @since 0.1.0
|
|
2558
2631
|
*/
|
|
2559
2632
|
declare class ModelNotFoundException extends ArkormException {
|
|
2560
|
-
|
|
2633
|
+
private modelName;
|
|
2634
|
+
constructor(modelName: string, message?: string);
|
|
2635
|
+
getModelName(): string;
|
|
2561
2636
|
}
|
|
2562
2637
|
//#endregion
|
|
2563
2638
|
//#region src/helpers/migrations.d.ts
|
package/dist/index.mjs
CHANGED
|
@@ -2174,9 +2174,14 @@ const defineFactory = (model, definition) => {
|
|
|
2174
2174
|
* @since 0.1.0
|
|
2175
2175
|
*/
|
|
2176
2176
|
var ModelNotFoundException = class extends ArkormException {
|
|
2177
|
-
|
|
2177
|
+
modelName;
|
|
2178
|
+
constructor(modelName, message = "No query results for the given model.") {
|
|
2178
2179
|
super(message);
|
|
2179
2180
|
this.name = "ModelNotFoundException";
|
|
2181
|
+
this.modelName = modelName;
|
|
2182
|
+
}
|
|
2183
|
+
getModelName() {
|
|
2184
|
+
return this.modelName;
|
|
2180
2185
|
}
|
|
2181
2186
|
};
|
|
2182
2187
|
|
|
@@ -3576,7 +3581,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
3576
3581
|
*/
|
|
3577
3582
|
async firstOrFail() {
|
|
3578
3583
|
const model = await this.first();
|
|
3579
|
-
if (!model) throw new ModelNotFoundException("Record not found.");
|
|
3584
|
+
if (!model) throw new ModelNotFoundException(this.model.name, "Record not found.");
|
|
3580
3585
|
return model;
|
|
3581
3586
|
}
|
|
3582
3587
|
async find(value, key = "id") {
|
|
@@ -3609,7 +3614,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
3609
3614
|
*/
|
|
3610
3615
|
async valueOrFail(column) {
|
|
3611
3616
|
const result = await this.value(column);
|
|
3612
|
-
if (result == null) throw new ModelNotFoundException("Record not found.");
|
|
3617
|
+
if (result == null) throw new ModelNotFoundException(this.model.name, "Record not found.");
|
|
3613
3618
|
return result;
|
|
3614
3619
|
}
|
|
3615
3620
|
/**
|
|
@@ -3635,6 +3640,99 @@ var QueryBuilder = class QueryBuilder {
|
|
|
3635
3640
|
return this.model.hydrate(created);
|
|
3636
3641
|
}
|
|
3637
3642
|
/**
|
|
3643
|
+
* Creates multiple records and returns hydrated model instances.
|
|
3644
|
+
*
|
|
3645
|
+
* @param values
|
|
3646
|
+
* @returns
|
|
3647
|
+
*/
|
|
3648
|
+
async createMany(values) {
|
|
3649
|
+
if (values.length === 0) return [];
|
|
3650
|
+
return await Promise.all(values.map(async (value) => await this.create(value)));
|
|
3651
|
+
}
|
|
3652
|
+
/**
|
|
3653
|
+
* Insert one or more records.
|
|
3654
|
+
*
|
|
3655
|
+
* @param values
|
|
3656
|
+
* @returns
|
|
3657
|
+
*/
|
|
3658
|
+
async insert(values) {
|
|
3659
|
+
const payloads = this.normalizeInsertPayloads(values);
|
|
3660
|
+
if (payloads.length === 0) return true;
|
|
3661
|
+
const delegate = this.delegate;
|
|
3662
|
+
if (typeof delegate.createMany === "function") {
|
|
3663
|
+
await delegate.createMany({ data: payloads });
|
|
3664
|
+
return true;
|
|
3665
|
+
}
|
|
3666
|
+
await Promise.all(payloads.map(async (payload) => {
|
|
3667
|
+
await this.delegate.create({ data: payload });
|
|
3668
|
+
}));
|
|
3669
|
+
return true;
|
|
3670
|
+
}
|
|
3671
|
+
/**
|
|
3672
|
+
* Insert one or more records while ignoring insertion errors.
|
|
3673
|
+
*
|
|
3674
|
+
* @param values
|
|
3675
|
+
* @returns
|
|
3676
|
+
*/
|
|
3677
|
+
async insertOrIgnore(values) {
|
|
3678
|
+
const payloads = this.normalizeInsertPayloads(values);
|
|
3679
|
+
if (payloads.length === 0) return 0;
|
|
3680
|
+
const delegate = this.delegate;
|
|
3681
|
+
if (typeof delegate.createMany === "function") {
|
|
3682
|
+
const result = await delegate.createMany({
|
|
3683
|
+
data: payloads,
|
|
3684
|
+
skipDuplicates: true
|
|
3685
|
+
});
|
|
3686
|
+
return this.resolveAffectedCount(result, payloads.length);
|
|
3687
|
+
}
|
|
3688
|
+
let inserted = 0;
|
|
3689
|
+
for (const payload of payloads) try {
|
|
3690
|
+
await this.delegate.create({ data: payload });
|
|
3691
|
+
inserted += 1;
|
|
3692
|
+
} catch {
|
|
3693
|
+
continue;
|
|
3694
|
+
}
|
|
3695
|
+
return inserted;
|
|
3696
|
+
}
|
|
3697
|
+
/**
|
|
3698
|
+
* Insert a record and return its primary key value.
|
|
3699
|
+
*
|
|
3700
|
+
* @param values
|
|
3701
|
+
* @param sequence
|
|
3702
|
+
* @returns
|
|
3703
|
+
*/
|
|
3704
|
+
async insertGetId(values, sequence) {
|
|
3705
|
+
const created = await this.delegate.create({ data: values });
|
|
3706
|
+
const key = sequence ?? "id";
|
|
3707
|
+
if (!(key in created)) throw new ArkormException(`Inserted record does not contain key [${key}].`);
|
|
3708
|
+
return created[key];
|
|
3709
|
+
}
|
|
3710
|
+
/**
|
|
3711
|
+
* Insert records using values produced by another query/source.
|
|
3712
|
+
*
|
|
3713
|
+
* @param columns
|
|
3714
|
+
* @param query
|
|
3715
|
+
* @returns
|
|
3716
|
+
*/
|
|
3717
|
+
async insertUsing(columns, query) {
|
|
3718
|
+
const rows = await this.resolveInsertUsingRows(columns, query);
|
|
3719
|
+
if (rows.length === 0) return 0;
|
|
3720
|
+
await this.insert(rows);
|
|
3721
|
+
return rows.length;
|
|
3722
|
+
}
|
|
3723
|
+
/**
|
|
3724
|
+
* Insert records using values produced by another query/source while ignoring insertion errors.
|
|
3725
|
+
*
|
|
3726
|
+
* @param columns
|
|
3727
|
+
* @param query
|
|
3728
|
+
* @returns
|
|
3729
|
+
*/
|
|
3730
|
+
async insertOrIgnoreUsing(columns, query) {
|
|
3731
|
+
const rows = await this.resolveInsertUsingRows(columns, query);
|
|
3732
|
+
if (rows.length === 0) return 0;
|
|
3733
|
+
return this.insertOrIgnore(rows);
|
|
3734
|
+
}
|
|
3735
|
+
/**
|
|
3638
3736
|
* Updates records matching the current query constraints with the
|
|
3639
3737
|
* specified data and returns the updated record(s) as model instance(s).
|
|
3640
3738
|
*
|
|
@@ -3652,6 +3750,71 @@ var QueryBuilder = class QueryBuilder {
|
|
|
3652
3750
|
return this.model.hydrate(updated);
|
|
3653
3751
|
}
|
|
3654
3752
|
/**
|
|
3753
|
+
* Update records using update-many semantics when available.
|
|
3754
|
+
*
|
|
3755
|
+
* @param data
|
|
3756
|
+
* @returns
|
|
3757
|
+
*/
|
|
3758
|
+
async updateFrom(data) {
|
|
3759
|
+
const where = this.buildWhere();
|
|
3760
|
+
if (!where) throw new ArkormException("Update requires a where clause.");
|
|
3761
|
+
const delegate = this.delegate;
|
|
3762
|
+
if (typeof delegate.updateMany === "function") {
|
|
3763
|
+
const result = await delegate.updateMany({
|
|
3764
|
+
where,
|
|
3765
|
+
data
|
|
3766
|
+
});
|
|
3767
|
+
return this.resolveAffectedCount(result, 0);
|
|
3768
|
+
}
|
|
3769
|
+
await this.update(data);
|
|
3770
|
+
return 1;
|
|
3771
|
+
}
|
|
3772
|
+
/**
|
|
3773
|
+
* Insert a record when no match exists, otherwise update the matching record.
|
|
3774
|
+
*
|
|
3775
|
+
* @param attributes
|
|
3776
|
+
* @param values
|
|
3777
|
+
* @returns
|
|
3778
|
+
*/
|
|
3779
|
+
async updateOrInsert(attributes, values = {}) {
|
|
3780
|
+
const exists = await this.delegate.findFirst({ where: attributes }) != null;
|
|
3781
|
+
const resolvedValues = typeof values === "function" ? await values(exists) : values;
|
|
3782
|
+
if (!exists) {
|
|
3783
|
+
await this.delegate.create({ data: {
|
|
3784
|
+
...attributes,
|
|
3785
|
+
...resolvedValues
|
|
3786
|
+
} });
|
|
3787
|
+
return true;
|
|
3788
|
+
}
|
|
3789
|
+
return await this.clone().where(attributes).update(resolvedValues) != null;
|
|
3790
|
+
}
|
|
3791
|
+
/**
|
|
3792
|
+
* Insert new records or update existing records by one or more unique keys.
|
|
3793
|
+
*
|
|
3794
|
+
* @param values
|
|
3795
|
+
* @param uniqueBy
|
|
3796
|
+
* @param update
|
|
3797
|
+
* @returns
|
|
3798
|
+
*/
|
|
3799
|
+
async upsert(values, uniqueBy, update = null) {
|
|
3800
|
+
if (values.length === 0) return 0;
|
|
3801
|
+
const uniqueKeys = Array.isArray(uniqueBy) ? uniqueBy : [uniqueBy];
|
|
3802
|
+
let affected = 0;
|
|
3803
|
+
for (const row of values) {
|
|
3804
|
+
const attributes = uniqueKeys.reduce((all, key) => {
|
|
3805
|
+
all[key] = row[key];
|
|
3806
|
+
return all;
|
|
3807
|
+
}, {});
|
|
3808
|
+
const updatePayload = (update ?? Object.keys(row).filter((key) => !uniqueKeys.includes(key))).reduce((all, key) => {
|
|
3809
|
+
if (key in row) all[key] = row[key];
|
|
3810
|
+
return all;
|
|
3811
|
+
}, {});
|
|
3812
|
+
await this.updateOrInsert(attributes, updatePayload);
|
|
3813
|
+
affected += 1;
|
|
3814
|
+
}
|
|
3815
|
+
return affected;
|
|
3816
|
+
}
|
|
3817
|
+
/**
|
|
3655
3818
|
* Deletes records matching the current query constraints and returns
|
|
3656
3819
|
* the deleted record(s) as model instance(s).
|
|
3657
3820
|
*
|
|
@@ -3690,6 +3853,43 @@ var QueryBuilder = class QueryBuilder {
|
|
|
3690
3853
|
async doesntExist() {
|
|
3691
3854
|
return !await this.exists();
|
|
3692
3855
|
}
|
|
3856
|
+
normalizeInsertPayloads(values) {
|
|
3857
|
+
if (Array.isArray(values)) return values;
|
|
3858
|
+
return [values];
|
|
3859
|
+
}
|
|
3860
|
+
resolveAffectedCount(result, fallback) {
|
|
3861
|
+
if (typeof result === "number") return result;
|
|
3862
|
+
if (result && typeof result === "object" && "count" in result) {
|
|
3863
|
+
const candidate = result.count;
|
|
3864
|
+
if (typeof candidate === "number") return candidate;
|
|
3865
|
+
}
|
|
3866
|
+
return fallback;
|
|
3867
|
+
}
|
|
3868
|
+
async resolveInsertUsingRows(columns, query) {
|
|
3869
|
+
const resolvedQuery = typeof query === "function" ? await query() : query;
|
|
3870
|
+
return (await this.resolveInsertUsingSource(resolvedQuery)).map((row) => {
|
|
3871
|
+
return columns.reduce((record, column) => {
|
|
3872
|
+
record[column] = row[column];
|
|
3873
|
+
return record;
|
|
3874
|
+
}, {});
|
|
3875
|
+
});
|
|
3876
|
+
}
|
|
3877
|
+
async resolveInsertUsingSource(source) {
|
|
3878
|
+
if (source && typeof source === "object") {
|
|
3879
|
+
const asBuilder = source;
|
|
3880
|
+
if (typeof asBuilder.get === "function") {
|
|
3881
|
+
const collection = await asBuilder.get();
|
|
3882
|
+
if (typeof collection.all === "function") return collection.all().map((item) => {
|
|
3883
|
+
const asModel = item;
|
|
3884
|
+
if (typeof asModel.getRawAttributes === "function") return asModel.getRawAttributes();
|
|
3885
|
+
return item;
|
|
3886
|
+
});
|
|
3887
|
+
}
|
|
3888
|
+
if (Array.isArray(source)) return source;
|
|
3889
|
+
}
|
|
3890
|
+
if (Array.isArray(source)) return source;
|
|
3891
|
+
throw new ArkormException("insertUsing expects a query builder, array of records, or async resolver.");
|
|
3892
|
+
}
|
|
3693
3893
|
/**
|
|
3694
3894
|
* Execute callback when no records exist.
|
|
3695
3895
|
*
|