@vibeorm/runtime 2.0.0-alpha.4 → 2.0.0-alpha.5
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/db-now.d.ts +1 -1
- package/dist/db-now.d.ts.map +1 -1
- package/dist/index.js +75 -30
- package/dist/index.js.map +5 -5
- package/dist/model-meta.d.ts +20 -1
- package/dist/model-meta.d.ts.map +1 -1
- package/dist/query-builder.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/db-now.d.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
/** The database-clock sentinel produced by {@link dbNow}. */
|
|
19
19
|
export type DbNow = {
|
|
20
20
|
readonly __vibeDbNow: true;
|
|
21
|
-
/**
|
|
21
|
+
/** Seconds added to the database's now() (fractional OK; negative = past). */
|
|
22
22
|
readonly offsetSeconds: number;
|
|
23
23
|
};
|
|
24
24
|
/**
|
package/dist/db-now.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-now.d.ts","sourceRoot":"","sources":["../src/db-now.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,6DAA6D;AAC7D,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,
|
|
1
|
+
{"version":3,"file":"db-now.d.ts","sourceRoot":"","sources":["../src/db-now.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,6DAA6D;AAC7D,MAAM,MAAM,KAAK,GAAG;IAClB,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,CAAC,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAUxE;AAED,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAYtD"}
|
package/dist/index.js
CHANGED
|
@@ -14,10 +14,10 @@ function toSqlExecutor(params) {
|
|
|
14
14
|
import { VibeError } from "@vibeorm/schema";
|
|
15
15
|
function dbNow(options) {
|
|
16
16
|
const offset = options?.plusSeconds ?? 0;
|
|
17
|
-
if (typeof offset !== "number" || !Number.
|
|
17
|
+
if (typeof offset !== "number" || !Number.isFinite(offset)) {
|
|
18
18
|
throw new VibeError({
|
|
19
19
|
code: "VIBE_VALIDATION",
|
|
20
|
-
message: `dbNow plusSeconds must be a
|
|
20
|
+
message: `dbNow plusSeconds must be a finite number of seconds (fractional is fine), got ${String(offset)}`,
|
|
21
21
|
meta: { plusSeconds: offset }
|
|
22
22
|
});
|
|
23
23
|
}
|
|
@@ -167,26 +167,44 @@ function buildModelMeta(params) {
|
|
|
167
167
|
primaryKey.push(...fields.filter((field) => field.isId));
|
|
168
168
|
}
|
|
169
169
|
const compoundUniques = new Map;
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
return;
|
|
173
|
-
const key = names.join("_");
|
|
174
|
-
if (compoundUniques.has(key))
|
|
175
|
-
return;
|
|
170
|
+
const singleIndexUniques = [];
|
|
171
|
+
const resolveFields = (names) => {
|
|
176
172
|
const resolved = [];
|
|
177
173
|
for (const name of names) {
|
|
178
174
|
const field = fieldByName.get(name);
|
|
179
175
|
if (field === undefined)
|
|
180
|
-
return;
|
|
176
|
+
return null;
|
|
181
177
|
resolved.push(field);
|
|
182
178
|
}
|
|
183
|
-
|
|
179
|
+
return resolved;
|
|
180
|
+
};
|
|
181
|
+
const addCompound = (names, nullsNotDistinct) => {
|
|
182
|
+
if (names.length < 2)
|
|
183
|
+
return;
|
|
184
|
+
const key = names.join("_");
|
|
185
|
+
if (compoundUniques.has(key))
|
|
186
|
+
return;
|
|
187
|
+
const resolved = resolveFields(names);
|
|
188
|
+
if (resolved === null)
|
|
189
|
+
return;
|
|
190
|
+
compoundUniques.set(key, { fields: resolved, nullsNotDistinct });
|
|
184
191
|
};
|
|
185
192
|
if (primaryKey.length > 1)
|
|
186
|
-
addCompound(primaryKey.map((field) => field.name));
|
|
193
|
+
addCompound(primaryKey.map((field) => field.name), false);
|
|
187
194
|
for (const index of model.indexes) {
|
|
188
|
-
if (index.kind
|
|
189
|
-
|
|
195
|
+
if (index.kind !== "unique" || index.where !== undefined)
|
|
196
|
+
continue;
|
|
197
|
+
const names = index.fields.map((entry) => entry.name);
|
|
198
|
+
const nullsNotDistinct = index.nullsNotDistinct === true;
|
|
199
|
+
if (names.length === 1) {
|
|
200
|
+
const field = resolveFields(names)?.[0];
|
|
201
|
+
if (field === undefined || field.isId || field.isUnique)
|
|
202
|
+
continue;
|
|
203
|
+
if (singleIndexUniques.some((entry) => entry.fields[0]?.name === field.name))
|
|
204
|
+
continue;
|
|
205
|
+
singleIndexUniques.push({ fields: [field], nullsNotDistinct });
|
|
206
|
+
} else {
|
|
207
|
+
addCompound(names, nullsNotDistinct);
|
|
190
208
|
}
|
|
191
209
|
}
|
|
192
210
|
const computedSpecs = params.computed;
|
|
@@ -204,6 +222,7 @@ function buildModelMeta(params) {
|
|
|
204
222
|
primaryKey,
|
|
205
223
|
uniqueFields: fields.filter((field) => field.isId || field.isUnique),
|
|
206
224
|
compoundUniques,
|
|
225
|
+
singleIndexUniques,
|
|
207
226
|
relations: model.relations,
|
|
208
227
|
relationByName: new Map(model.relations.map((relation) => [relation.name, relation])),
|
|
209
228
|
isView: model.isView,
|
|
@@ -2096,7 +2115,7 @@ function expandCompoundWhere(params) {
|
|
|
2096
2115
|
for (const [key, value] of Object.entries(where)) {
|
|
2097
2116
|
if (value === undefined)
|
|
2098
2117
|
continue;
|
|
2099
|
-
const fields = model.compoundUniques.get(key);
|
|
2118
|
+
const fields = model.compoundUniques.get(key)?.fields;
|
|
2100
2119
|
if (fields === undefined)
|
|
2101
2120
|
continue;
|
|
2102
2121
|
const spec = requireObject({ value, what: `${what}.${key}`, model });
|
|
@@ -2115,33 +2134,52 @@ function expandCompoundWhere(params) {
|
|
|
2115
2134
|
}
|
|
2116
2135
|
return expanded ?? where;
|
|
2117
2136
|
}
|
|
2137
|
+
function isAddressableMember(params) {
|
|
2138
|
+
return isDirectValue(params.value) || params.nullsNotDistinct && params.value === null;
|
|
2139
|
+
}
|
|
2118
2140
|
function assertUniqueWhere(params) {
|
|
2119
2141
|
const { model, where, method } = params;
|
|
2120
2142
|
for (const field of model.uniqueFields) {
|
|
2121
2143
|
if (isDirectValue(where[field.name]))
|
|
2122
2144
|
return;
|
|
2123
2145
|
}
|
|
2124
|
-
for (const
|
|
2125
|
-
|
|
2146
|
+
for (const single of model.singleIndexUniques) {
|
|
2147
|
+
const field = single.fields[0];
|
|
2148
|
+
if (isAddressableMember({ value: where[field.name], nullsNotDistinct: single.nullsNotDistinct }))
|
|
2126
2149
|
return;
|
|
2127
2150
|
}
|
|
2128
|
-
const
|
|
2151
|
+
for (const group of model.compoundUniques.values()) {
|
|
2152
|
+
if (group.fields.every((field) => isAddressableMember({ value: where[field.name], nullsNotDistinct: group.nullsNotDistinct })))
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
const candidates = [
|
|
2156
|
+
...model.uniqueFields.map((field) => field.name),
|
|
2157
|
+
...model.singleIndexUniques.map((single) => single.fields[0].name),
|
|
2158
|
+
...model.compoundUniques.keys()
|
|
2159
|
+
];
|
|
2129
2160
|
throw validationError2({
|
|
2130
2161
|
message: `${model.clientName}.${method}() needs a unique where \u2014 one of: ${candidates.join(", ")}`,
|
|
2131
2162
|
meta: { model: model.name, method, candidates }
|
|
2132
2163
|
});
|
|
2133
2164
|
}
|
|
2134
|
-
function
|
|
2165
|
+
function conflictTargetGroup(params) {
|
|
2135
2166
|
const { model, where } = params;
|
|
2136
2167
|
for (const field of model.uniqueFields) {
|
|
2137
2168
|
if (isDirectValue(where[field.name]))
|
|
2138
|
-
return [field];
|
|
2169
|
+
return { fields: [field], nullsNotDistinct: false };
|
|
2170
|
+
}
|
|
2171
|
+
for (const single of model.singleIndexUniques) {
|
|
2172
|
+
const field = single.fields[0];
|
|
2173
|
+
if (isAddressableMember({ value: where[field.name], nullsNotDistinct: single.nullsNotDistinct })) {
|
|
2174
|
+
return single;
|
|
2175
|
+
}
|
|
2139
2176
|
}
|
|
2140
|
-
for (const
|
|
2141
|
-
if (fields.every((field) =>
|
|
2142
|
-
return
|
|
2177
|
+
for (const group of model.compoundUniques.values()) {
|
|
2178
|
+
if (group.fields.every((field) => isAddressableMember({ value: where[field.name], nullsNotDistinct: group.nullsNotDistinct }))) {
|
|
2179
|
+
return group;
|
|
2180
|
+
}
|
|
2143
2181
|
}
|
|
2144
|
-
return model.primaryKey;
|
|
2182
|
+
return { fields: model.primaryKey, nullsNotDistinct: false };
|
|
2145
2183
|
}
|
|
2146
2184
|
function sameDirectValue(a, b) {
|
|
2147
2185
|
if (a === b)
|
|
@@ -2161,15 +2199,21 @@ function upsertUsesNativeStatement(params) {
|
|
|
2161
2199
|
});
|
|
2162
2200
|
assertUniqueWhere({ model, where, method: "upsert" });
|
|
2163
2201
|
const create = requireObject({ value: args.create, what: "create", model });
|
|
2164
|
-
const
|
|
2165
|
-
const targetNames = new Set(
|
|
2202
|
+
const target = conflictTargetGroup({ model, where });
|
|
2203
|
+
const targetNames = new Set(target.fields.map((field) => field.name));
|
|
2166
2204
|
for (const [key, value] of Object.entries(where)) {
|
|
2167
2205
|
if (value === undefined)
|
|
2168
2206
|
continue;
|
|
2169
2207
|
if (!targetNames.has(key))
|
|
2170
2208
|
return false;
|
|
2171
2209
|
}
|
|
2172
|
-
return
|
|
2210
|
+
return target.fields.every((field) => {
|
|
2211
|
+
const createValue = create[field.name];
|
|
2212
|
+
const whereValue = where[field.name];
|
|
2213
|
+
if (target.nullsNotDistinct && createValue === null && whereValue === null)
|
|
2214
|
+
return true;
|
|
2215
|
+
return isDirectValue(createValue) && sameDirectValue(createValue, whereValue);
|
|
2216
|
+
});
|
|
2173
2217
|
}
|
|
2174
2218
|
var writePlanCache = new WeakMap;
|
|
2175
2219
|
function getWritePlan(params) {
|
|
@@ -2451,7 +2495,8 @@ function parseCursor(params) {
|
|
|
2451
2495
|
}
|
|
2452
2496
|
if (fields.length === 1) {
|
|
2453
2497
|
const field = fields[0];
|
|
2454
|
-
|
|
2498
|
+
const isSingleIndexUnique = model.singleIndexUniques.some((single) => single.fields[0].name === field.name);
|
|
2499
|
+
if (!field.isId && !field.isUnique && !isSingleIndexUnique) {
|
|
2455
2500
|
throw validationError2({
|
|
2456
2501
|
message: `cursor field ${model.name}.${field.name} must be @id or @unique \u2014 a non-unique cursor cannot identify a position`,
|
|
2457
2502
|
meta: { model: model.name, field: field.name }
|
|
@@ -2460,7 +2505,7 @@ function parseCursor(params) {
|
|
|
2460
2505
|
} else {
|
|
2461
2506
|
const names = new Set(fields.map((field) => field.name));
|
|
2462
2507
|
const matchesGroup = (group) => group.length === names.size && group.every((field) => names.has(field.name));
|
|
2463
|
-
const matches = matchesGroup(model.primaryKey) || [...model.compoundUniques.values()].some(matchesGroup);
|
|
2508
|
+
const matches = matchesGroup(model.primaryKey) || [...model.compoundUniques.values()].some((group) => matchesGroup(group.fields));
|
|
2464
2509
|
if (!matches) {
|
|
2465
2510
|
const candidates = [
|
|
2466
2511
|
model.primaryKey.map((field) => field.name).join(" + ") || "none",
|
|
@@ -3662,7 +3707,7 @@ function buildQuery(params) {
|
|
|
3662
3707
|
assertUniqueWhere({ model, where, method });
|
|
3663
3708
|
const row = compileCreateRow({ dialect, model, data: args.create });
|
|
3664
3709
|
const columns = orderedColumns({ model, values: row.values });
|
|
3665
|
-
const targetFields =
|
|
3710
|
+
const targetFields = conflictTargetGroup({ model, where }).fields;
|
|
3666
3711
|
for (const field of targetFields) {
|
|
3667
3712
|
if (!(field.columnName in row.values)) {
|
|
3668
3713
|
throw new VibeError5({
|
|
@@ -6984,4 +7029,4 @@ export {
|
|
|
6984
7029
|
COUNT_ALIAS
|
|
6985
7030
|
};
|
|
6986
7031
|
|
|
6987
|
-
//# debugId=
|
|
7032
|
+
//# debugId=0C003BB2F334C66D64756E2164756E21
|