@vibeorm/runtime 2.0.0-alpha.4 → 2.0.0-alpha.6
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 +78 -32
- package/dist/index.js.map +5 -5
- package/dist/model-meta.d.ts +25 -4
- package/dist/model-meta.d.ts.map +1 -1
- package/dist/query-builder.d.ts.map +1 -1
- package/package.json +3 -3
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
|
}
|
|
@@ -31,7 +31,7 @@ function isDbNow(value) {
|
|
|
31
31
|
return false;
|
|
32
32
|
}
|
|
33
33
|
const candidate = value;
|
|
34
|
-
return candidate.__vibeDbNow === true && typeof candidate.offsetSeconds === "number" && Number.
|
|
34
|
+
return candidate.__vibeDbNow === true && typeof candidate.offsetSeconds === "number" && Number.isFinite(candidate.offsetSeconds);
|
|
35
35
|
}
|
|
36
36
|
// packages/runtime/src/model-meta.ts
|
|
37
37
|
import { VibeError as VibeError2, isEnumType, validateComputedConfig } from "@vibeorm/schema";
|
|
@@ -167,26 +167,45 @@ 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;
|
|
184
180
|
};
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
const addCompound = (names, nullsNotDistinct, clientName) => {
|
|
182
|
+
if (names.length < 2)
|
|
183
|
+
return;
|
|
184
|
+
const key = clientName ?? 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 });
|
|
191
|
+
};
|
|
192
|
+
if (primaryKey.length > 1) {
|
|
193
|
+
addCompound(primaryKey.map((field) => field.name), false, model.primaryKey?.clientName);
|
|
194
|
+
}
|
|
187
195
|
for (const index of model.indexes) {
|
|
188
|
-
if (index.kind
|
|
189
|
-
|
|
196
|
+
if (index.kind !== "unique" || index.where !== undefined)
|
|
197
|
+
continue;
|
|
198
|
+
const names = index.fields.map((entry) => entry.name);
|
|
199
|
+
const nullsNotDistinct = index.nullsNotDistinct === true;
|
|
200
|
+
if (names.length === 1) {
|
|
201
|
+
const field = resolveFields(names)?.[0];
|
|
202
|
+
if (field === undefined || field.isId || field.isUnique)
|
|
203
|
+
continue;
|
|
204
|
+
if (singleIndexUniques.some((entry) => entry.fields[0]?.name === field.name))
|
|
205
|
+
continue;
|
|
206
|
+
singleIndexUniques.push({ fields: [field], nullsNotDistinct });
|
|
207
|
+
} else {
|
|
208
|
+
addCompound(names, nullsNotDistinct, index.clientName);
|
|
190
209
|
}
|
|
191
210
|
}
|
|
192
211
|
const computedSpecs = params.computed;
|
|
@@ -204,6 +223,7 @@ function buildModelMeta(params) {
|
|
|
204
223
|
primaryKey,
|
|
205
224
|
uniqueFields: fields.filter((field) => field.isId || field.isUnique),
|
|
206
225
|
compoundUniques,
|
|
226
|
+
singleIndexUniques,
|
|
207
227
|
relations: model.relations,
|
|
208
228
|
relationByName: new Map(model.relations.map((relation) => [relation.name, relation])),
|
|
209
229
|
isView: model.isView,
|
|
@@ -2096,7 +2116,7 @@ function expandCompoundWhere(params) {
|
|
|
2096
2116
|
for (const [key, value] of Object.entries(where)) {
|
|
2097
2117
|
if (value === undefined)
|
|
2098
2118
|
continue;
|
|
2099
|
-
const fields = model.compoundUniques.get(key);
|
|
2119
|
+
const fields = model.compoundUniques.get(key)?.fields;
|
|
2100
2120
|
if (fields === undefined)
|
|
2101
2121
|
continue;
|
|
2102
2122
|
const spec = requireObject({ value, what: `${what}.${key}`, model });
|
|
@@ -2115,33 +2135,52 @@ function expandCompoundWhere(params) {
|
|
|
2115
2135
|
}
|
|
2116
2136
|
return expanded ?? where;
|
|
2117
2137
|
}
|
|
2138
|
+
function isAddressableMember(params) {
|
|
2139
|
+
return isDirectValue(params.value) || params.nullsNotDistinct && params.value === null;
|
|
2140
|
+
}
|
|
2118
2141
|
function assertUniqueWhere(params) {
|
|
2119
2142
|
const { model, where, method } = params;
|
|
2120
2143
|
for (const field of model.uniqueFields) {
|
|
2121
2144
|
if (isDirectValue(where[field.name]))
|
|
2122
2145
|
return;
|
|
2123
2146
|
}
|
|
2124
|
-
for (const
|
|
2125
|
-
|
|
2147
|
+
for (const single of model.singleIndexUniques) {
|
|
2148
|
+
const field = single.fields[0];
|
|
2149
|
+
if (isAddressableMember({ value: where[field.name], nullsNotDistinct: single.nullsNotDistinct }))
|
|
2150
|
+
return;
|
|
2151
|
+
}
|
|
2152
|
+
for (const group of model.compoundUniques.values()) {
|
|
2153
|
+
if (group.fields.every((field) => isAddressableMember({ value: where[field.name], nullsNotDistinct: group.nullsNotDistinct })))
|
|
2126
2154
|
return;
|
|
2127
2155
|
}
|
|
2128
|
-
const candidates = [
|
|
2156
|
+
const candidates = [
|
|
2157
|
+
...model.uniqueFields.map((field) => field.name),
|
|
2158
|
+
...model.singleIndexUniques.map((single) => single.fields[0].name),
|
|
2159
|
+
...model.compoundUniques.keys()
|
|
2160
|
+
];
|
|
2129
2161
|
throw validationError2({
|
|
2130
2162
|
message: `${model.clientName}.${method}() needs a unique where \u2014 one of: ${candidates.join(", ")}`,
|
|
2131
2163
|
meta: { model: model.name, method, candidates }
|
|
2132
2164
|
});
|
|
2133
2165
|
}
|
|
2134
|
-
function
|
|
2166
|
+
function conflictTargetGroup(params) {
|
|
2135
2167
|
const { model, where } = params;
|
|
2136
2168
|
for (const field of model.uniqueFields) {
|
|
2137
2169
|
if (isDirectValue(where[field.name]))
|
|
2138
|
-
return [field];
|
|
2170
|
+
return { fields: [field], nullsNotDistinct: false };
|
|
2171
|
+
}
|
|
2172
|
+
for (const single of model.singleIndexUniques) {
|
|
2173
|
+
const field = single.fields[0];
|
|
2174
|
+
if (isAddressableMember({ value: where[field.name], nullsNotDistinct: single.nullsNotDistinct })) {
|
|
2175
|
+
return single;
|
|
2176
|
+
}
|
|
2139
2177
|
}
|
|
2140
|
-
for (const
|
|
2141
|
-
if (fields.every((field) =>
|
|
2142
|
-
return
|
|
2178
|
+
for (const group of model.compoundUniques.values()) {
|
|
2179
|
+
if (group.fields.every((field) => isAddressableMember({ value: where[field.name], nullsNotDistinct: group.nullsNotDistinct }))) {
|
|
2180
|
+
return group;
|
|
2181
|
+
}
|
|
2143
2182
|
}
|
|
2144
|
-
return model.primaryKey;
|
|
2183
|
+
return { fields: model.primaryKey, nullsNotDistinct: false };
|
|
2145
2184
|
}
|
|
2146
2185
|
function sameDirectValue(a, b) {
|
|
2147
2186
|
if (a === b)
|
|
@@ -2161,15 +2200,21 @@ function upsertUsesNativeStatement(params) {
|
|
|
2161
2200
|
});
|
|
2162
2201
|
assertUniqueWhere({ model, where, method: "upsert" });
|
|
2163
2202
|
const create = requireObject({ value: args.create, what: "create", model });
|
|
2164
|
-
const
|
|
2165
|
-
const targetNames = new Set(
|
|
2203
|
+
const target = conflictTargetGroup({ model, where });
|
|
2204
|
+
const targetNames = new Set(target.fields.map((field) => field.name));
|
|
2166
2205
|
for (const [key, value] of Object.entries(where)) {
|
|
2167
2206
|
if (value === undefined)
|
|
2168
2207
|
continue;
|
|
2169
2208
|
if (!targetNames.has(key))
|
|
2170
2209
|
return false;
|
|
2171
2210
|
}
|
|
2172
|
-
return
|
|
2211
|
+
return target.fields.every((field) => {
|
|
2212
|
+
const createValue = create[field.name];
|
|
2213
|
+
const whereValue = where[field.name];
|
|
2214
|
+
if (target.nullsNotDistinct && createValue === null && whereValue === null)
|
|
2215
|
+
return true;
|
|
2216
|
+
return isDirectValue(createValue) && sameDirectValue(createValue, whereValue);
|
|
2217
|
+
});
|
|
2173
2218
|
}
|
|
2174
2219
|
var writePlanCache = new WeakMap;
|
|
2175
2220
|
function getWritePlan(params) {
|
|
@@ -2451,7 +2496,8 @@ function parseCursor(params) {
|
|
|
2451
2496
|
}
|
|
2452
2497
|
if (fields.length === 1) {
|
|
2453
2498
|
const field = fields[0];
|
|
2454
|
-
|
|
2499
|
+
const isSingleIndexUnique = model.singleIndexUniques.some((single) => single.fields[0].name === field.name);
|
|
2500
|
+
if (!field.isId && !field.isUnique && !isSingleIndexUnique) {
|
|
2455
2501
|
throw validationError2({
|
|
2456
2502
|
message: `cursor field ${model.name}.${field.name} must be @id or @unique \u2014 a non-unique cursor cannot identify a position`,
|
|
2457
2503
|
meta: { model: model.name, field: field.name }
|
|
@@ -2460,7 +2506,7 @@ function parseCursor(params) {
|
|
|
2460
2506
|
} else {
|
|
2461
2507
|
const names = new Set(fields.map((field) => field.name));
|
|
2462
2508
|
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);
|
|
2509
|
+
const matches = matchesGroup(model.primaryKey) || [...model.compoundUniques.values()].some((group) => matchesGroup(group.fields));
|
|
2464
2510
|
if (!matches) {
|
|
2465
2511
|
const candidates = [
|
|
2466
2512
|
model.primaryKey.map((field) => field.name).join(" + ") || "none",
|
|
@@ -3662,7 +3708,7 @@ function buildQuery(params) {
|
|
|
3662
3708
|
assertUniqueWhere({ model, where, method });
|
|
3663
3709
|
const row = compileCreateRow({ dialect, model, data: args.create });
|
|
3664
3710
|
const columns = orderedColumns({ model, values: row.values });
|
|
3665
|
-
const targetFields =
|
|
3711
|
+
const targetFields = conflictTargetGroup({ model, where }).fields;
|
|
3666
3712
|
for (const field of targetFields) {
|
|
3667
3713
|
if (!(field.columnName in row.values)) {
|
|
3668
3714
|
throw new VibeError5({
|
|
@@ -6984,4 +7030,4 @@ export {
|
|
|
6984
7030
|
COUNT_ALIAS
|
|
6985
7031
|
};
|
|
6986
7032
|
|
|
6987
|
-
//# debugId=
|
|
7033
|
+
//# debugId=4B6B11C516D4A81A64756E2164756E21
|