uql-orm 0.81.0 → 0.83.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 +3 -3
- package/dist/browser/querier/httpQuerier.d.ts +2 -2
- package/dist/browser/querier/httpQuerier.js +2 -1
- package/dist/browser/type/clientQuerier.d.ts +2 -2
- package/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +9 -8
- package/dist/bunSql/bunSql.util.js +2 -1
- package/dist/cockroachdb/crdbQuerierPool.js +2 -2
- package/dist/dialect/abstractSqlDialect.d.ts +16 -6
- package/dist/dialect/abstractSqlDialect.js +110 -41
- package/dist/dialect/hydrateColumn.js +2 -12
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +2 -0
- package/dist/dialect/mysqlLikeSqlDialect.js +5 -0
- package/dist/dialect/operators.d.ts +7 -1
- package/dist/dialect/operators.js +13 -1
- package/dist/dialect/pgLikeSqlDialect.d.ts +1 -0
- package/dist/dialect/pgLikeSqlDialect.js +13 -4
- package/dist/entity/metadata/definition.d.ts +1 -2
- package/dist/entity/metadata/definition.js +37 -39
- package/dist/http/handler.js +5 -4
- package/dist/http/query.d.ts +1 -1
- package/dist/http/query.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/maria/mariadbQuerierPool.js +4 -2
- package/dist/migrate/acquireQuerierForMigrations.js +2 -1
- package/dist/migrate/assertCliConfig.js +7 -6
- package/dist/migrate/bin.js +0 -0
- package/dist/migrate/builder/expressions.d.ts +2 -0
- package/dist/migrate/builder/expressions.js +20 -10
- package/dist/migrate/builder/tableBuilder.js +1 -1
- package/dist/migrate/cli-config.js +5 -4
- package/dist/migrate/ddl/indexDdl.js +4 -3
- package/dist/migrate/ddl/mysqlIndexDdl.js +5 -4
- package/dist/migrate/ddl/pgIndexDdl.js +2 -1
- package/dist/migrate/ddl/sqliteIndexDdl.js +2 -1
- package/dist/migrate/ddl/tableDdl.js +2 -1
- package/dist/migrate/generator/mongoCommand.js +2 -1
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +1 -1
- package/dist/migrate/generator/mongoSchemaGenerator.js +7 -6
- package/dist/migrate/indexPredicate.js +2 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +2 -1
- package/dist/migrate/introspection/mongoIntrospector.d.ts +1 -1
- package/dist/migrate/introspection/mongoIntrospector.js +3 -2
- package/dist/migrate/introspection/mssqlIntrospector.js +13 -1
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +2 -0
- package/dist/migrate/introspection/mysqlIntrospector.js +6 -2
- package/dist/migrate/introspection/postgresIntrospector.js +1 -1
- package/dist/migrate/migrationTarget.js +2 -1
- package/dist/migrate/migrator.js +2 -1
- package/dist/migrate/schemaGenerator.js +5 -4
- package/dist/migrate/storage/databaseStorage.js +1 -1
- package/dist/migrate/triggerSql.d.ts +1 -1
- package/dist/migrate/triggerSql.js +77 -61
- package/dist/mongo/mongoDialect.d.ts +1 -3
- package/dist/mongo/mongoDialect.js +9 -14
- package/dist/mongo/mongodbQuerier.js +7 -10
- package/dist/mssql/mssqlQuerier.d.ts +2 -0
- package/dist/mssql/mssqlQuerier.js +8 -5
- package/dist/mysql/mysql2QuerierPool.d.ts +1 -0
- package/dist/mysql/mysql2QuerierPool.js +20 -2
- package/dist/neon/neonQuerierPool.js +2 -2
- package/dist/pglite/pgliteQuerierPool.js +10 -4
- package/dist/postgres/pgQuerierPool.js +2 -2
- package/dist/postgres/{pgNumericTypes.d.ts → pgWireTypes.d.ts} +4 -3
- package/dist/postgres/{pgNumericTypes.js → pgWireTypes.js} +7 -3
- package/dist/querier/abstractQuerier.d.ts +9 -4
- package/dist/querier/abstractQuerier.js +26 -19
- package/dist/querier/abstractQuerierPool.d.ts +3 -3
- package/dist/querier/abstractSqlQuerier.d.ts +2 -2
- package/dist/querier/abstractSqlQuerier.js +1 -1
- package/dist/querier/abstractSqlQuerierPool.d.ts +2 -2
- package/dist/querier/queryError.d.ts +2 -2
- package/dist/schema/canonicalType.d.ts +3 -0
- package/dist/schema/canonicalType.js +31 -9
- package/dist/schema/schemaASTBuilder.js +2 -1
- package/dist/schema/schemaASTDiffer.js +4 -2
- package/dist/sqlite/sqliteDialect.d.ts +1 -3
- package/dist/sqlite/sqliteDialect.js +3 -6
- package/dist/type/dialect.d.ts +23 -1
- package/dist/type/entity.d.ts +16 -12
- package/dist/type/logger.d.ts +2 -2
- package/dist/type/querier.d.ts +3 -3
- package/dist/type/query.d.ts +3 -13
- package/dist/type/queryAggregate.d.ts +4 -10
- package/dist/type/queryRaw.d.ts +17 -3
- package/dist/type/queryRaw.js +2 -1
- package/dist/type/queryWhere.d.ts +7 -7
- package/dist/type/universalQuerier.d.ts +3 -3
- package/dist/type/vector.d.ts +2 -1
- package/dist/type/vector.js +2 -1
- package/dist/util/date.d.ts +11 -0
- package/dist/util/date.js +19 -0
- package/dist/util/dialect.util.d.ts +13 -5
- package/dist/util/dialect.util.js +28 -20
- package/dist/util/field.util.d.ts +4 -4
- package/dist/util/field.util.js +10 -2
- package/dist/util/fieldOption.util.d.ts +5 -3
- package/dist/util/fieldOption.util.js +6 -5
- package/dist/util/hook.util.d.ts +1 -1
- package/dist/util/hook.util.js +8 -1
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.js +1 -0
- package/dist/util/logger.d.ts +3 -3
- package/dist/util/object.util.js +3 -2
- package/dist/util/raw.d.ts +6 -7
- package/dist/util/raw.js +10 -12
- package/dist/util/sqlLiteral.d.ts +8 -1
- package/dist/util/sqlLiteral.js +14 -9
- package/dist/util/triggerWrite.d.ts +15 -0
- package/dist/util/triggerWrite.js +20 -0
- package/package.json +1 -1
- package/skills/uql-orm/SKILL.md +4 -4
|
@@ -120,7 +120,8 @@ const MYSQL_SCALAR_MAP = {
|
|
|
120
120
|
boolean: 'TINYINT(1)',
|
|
121
121
|
date: 'DATE',
|
|
122
122
|
time: 'TIME',
|
|
123
|
-
|
|
123
|
+
// The milliseconds a `Date` holds, which a bare `DATETIME` rounds away.
|
|
124
|
+
timestamp: 'DATETIME(3)',
|
|
124
125
|
json: 'JSON',
|
|
125
126
|
uuid: 'CHAR(36)',
|
|
126
127
|
blob: 'BLOB',
|
|
@@ -193,25 +194,28 @@ const ENGINE_TYPES = {
|
|
|
193
194
|
postgres: {
|
|
194
195
|
scalars: { ...PG_SCALAR_MAP, vector: 'VECTOR', halfvec: 'HALFVEC', sparsevec: 'SPARSEVEC' },
|
|
195
196
|
sizes: PG_SIZES,
|
|
197
|
+
timestampPrecision: 6,
|
|
196
198
|
},
|
|
197
199
|
// CockroachDB's VECTOR is native, no extension needed.
|
|
198
|
-
cockroachdb: { scalars: withVectorType(PG_SCALAR_MAP, 'VECTOR'), sizes: PG_SIZES },
|
|
200
|
+
cockroachdb: { scalars: withVectorType(PG_SCALAR_MAP, 'VECTOR'), sizes: PG_SIZES, timestampPrecision: 6 },
|
|
199
201
|
// MySQL does have a `VECTOR` type (26.7), but no distance function outside HeatWave and no vector
|
|
200
202
|
// index, so JSON keeps the column queryable with the JSON operators and needs no conversion.
|
|
201
203
|
mysql: {
|
|
202
204
|
scalars: withVectorType(MYSQL_SCALAR_MAP, 'JSON'),
|
|
203
205
|
sizes: MYSQL_SIZES,
|
|
204
206
|
decimal: { precision: 10, scale: 2 },
|
|
207
|
+
timestampPrecision: 0,
|
|
205
208
|
},
|
|
206
209
|
mariadb: {
|
|
207
210
|
scalars: withVectorType(MYSQL_SCALAR_MAP, 'VECTOR'),
|
|
208
211
|
sizes: MYSQL_SIZES,
|
|
209
212
|
decimal: { precision: 10, scale: 2 },
|
|
213
|
+
timestampPrecision: 0,
|
|
210
214
|
},
|
|
211
215
|
// SQLite uses affinity, so no size variants. `F32_BLOB` is libSQL's vector type; elsewhere just a name of BLOB affinity.
|
|
212
216
|
sqlite: { scalars: withVectorType(SQLITE_SCALAR_MAP, 'F32_BLOB') },
|
|
213
217
|
// 2025 and up; below that the server refuses the type rather than storing it as text.
|
|
214
|
-
mssql: { scalars: withVectorType(MSSQL_SCALAR_MAP, 'VECTOR'), sizes: MSSQL_SIZES },
|
|
218
|
+
mssql: { scalars: withVectorType(MSSQL_SCALAR_MAP, 'VECTOR'), sizes: MSSQL_SIZES, timestampPrecision: 7 },
|
|
215
219
|
mongodb: { scalars: withVectorType(MONGO_SCALAR_MAP, 'array') },
|
|
216
220
|
};
|
|
217
221
|
/**
|
|
@@ -241,8 +245,11 @@ export function sqlToCanonical(sqlType) {
|
|
|
241
245
|
const normalized = sqlType.toLowerCase().trim();
|
|
242
246
|
const unsigned = normalized.includes('unsigned');
|
|
243
247
|
const withoutUnsigned = normalized.replace(/\s*unsigned\s*/i, ' ').trim();
|
|
244
|
-
// Extract base type and parameters: "VARCHAR(255)" -> ["varchar", "255"]
|
|
245
|
-
|
|
248
|
+
// Extract base type and parameters: "VARCHAR(255)" -> ["varchar", "255"], and Postgres's
|
|
249
|
+
// "timestamp(3) with time zone", whose parameter sits inside the name, as "timestamp with time zone(3)".
|
|
250
|
+
const match = withoutUnsigned
|
|
251
|
+
.replace(/^(\w+)\((\d+)\)\s+(.+)$/, '$1 $3($2)')
|
|
252
|
+
.match(/^([a-z][a-z0-9_ ]*?)(?:\(([^)]+)\))?$/);
|
|
246
253
|
const base = match ? SQL_TO_CANONICAL[match[1]] : undefined;
|
|
247
254
|
if (!match || !base) {
|
|
248
255
|
return { category: 'string', raw: sqlType };
|
|
@@ -260,7 +267,7 @@ export function sqlToCanonical(sqlType) {
|
|
|
260
267
|
size: measured && params[0] === 'max' ? 'small' : base.size,
|
|
261
268
|
withTimezone: base.withTimezone,
|
|
262
269
|
length: measured ? first : undefined,
|
|
263
|
-
precision: decimal ? first : undefined,
|
|
270
|
+
precision: decimal || base.category === 'timestamp' ? first : undefined,
|
|
264
271
|
scale: decimal ? second : undefined,
|
|
265
272
|
unsigned: unsigned || undefined,
|
|
266
273
|
};
|
|
@@ -317,6 +324,9 @@ export function canonicalToSql(type, dialect) {
|
|
|
317
324
|
if (type.category === 'timestamp' && type.withTimezone && features.supportsTimestamptz) {
|
|
318
325
|
sqlType = 'TIMESTAMPTZ';
|
|
319
326
|
}
|
|
327
|
+
if (type.category === 'timestamp' && type.precision !== undefined && engine.timestampPrecision !== undefined) {
|
|
328
|
+
sqlType = `${sqlType.replace(/\(\d+\)$/, '')}(${type.precision})`;
|
|
329
|
+
}
|
|
320
330
|
return type.unsigned && features.supportsUnsigned ? `${sqlType} UNSIGNED` : sqlType;
|
|
321
331
|
}
|
|
322
332
|
/** See {@link DialectFeatures.stringSizing} for what each mode means. */
|
|
@@ -343,12 +353,22 @@ function formatDecimalSqlType(type, fallback, baseType) {
|
|
|
343
353
|
export function canonicalToTypeScript(type) {
|
|
344
354
|
return CANONICAL_TO_TS[type.category];
|
|
345
355
|
}
|
|
356
|
+
/** The fractional-second digits an engine's timestamp holds when its type states none; `undefined` where it counts none. */
|
|
357
|
+
export function defaultTimestampPrecision(dialectName) {
|
|
358
|
+
return ENGINE_TYPES[dialectName].timestampPrecision;
|
|
359
|
+
}
|
|
346
360
|
/**
|
|
347
361
|
* A type as `dialect` stores it, rendered and read back: several types share one storage type, and only
|
|
348
362
|
* the engine settles an unstated bound. Migrations and drift both compare through it.
|
|
349
363
|
*/
|
|
350
364
|
export function engineType(dialect) {
|
|
351
|
-
|
|
365
|
+
const timestampPrecision = defaultTimestampPrecision(dialect.dialectName);
|
|
366
|
+
return (type) => {
|
|
367
|
+
const stored = sqlToCanonical(canonicalToSql(type, dialect));
|
|
368
|
+
return stored.category === 'timestamp' && stored.precision === undefined
|
|
369
|
+
? { ...stored, precision: timestampPrecision }
|
|
370
|
+
: stored;
|
|
371
|
+
};
|
|
352
372
|
}
|
|
353
373
|
/**
|
|
354
374
|
* Convert UQL FieldOptions to a canonical type.
|
|
@@ -363,14 +383,16 @@ export function fieldOptionsToCanonical(options) {
|
|
|
363
383
|
switch (columnFamily(options.type)) {
|
|
364
384
|
case 'numeric':
|
|
365
385
|
// BIGINT for every `Number` without a scale, key or not: a 32-bit column is a migration waiting
|
|
366
|
-
// to happen, and the pools decode it back to a JS number at the wire (see `
|
|
386
|
+
// to happen, and the pools decode it back to a JS number at the wire (see `pgWireTypes`).
|
|
367
387
|
return isIntegerColumn(options)
|
|
368
388
|
? { category: 'integer', size: 'big' }
|
|
369
389
|
: { category: 'decimal', precision: options.precision, scale: options.scale };
|
|
370
390
|
case 'boolean':
|
|
371
391
|
return { category: 'boolean' };
|
|
392
|
+
// An instant: uql reads a zoneless timestamp as UTC, but the database's own clock and every other
|
|
393
|
+
// client read one in the session's zone.
|
|
372
394
|
case 'date':
|
|
373
|
-
return { category: 'timestamp' };
|
|
395
|
+
return { category: 'timestamp', withTimezone: true, precision: options.precision };
|
|
374
396
|
// `String`, and anything a reflected type left unrecognised.
|
|
375
397
|
default:
|
|
376
398
|
return { category: 'string', length: options.length };
|
|
@@ -4,6 +4,7 @@ import { fulltextWeights, textWeightSteps } from '../util/dialect.util.js';
|
|
|
4
4
|
import { isAutoIncrement, isInlinedExpression, isSoleIdField } from '../util/field.util.js';
|
|
5
5
|
import { definedEntries } from '../util/object.util.js';
|
|
6
6
|
import { derivedForeignKeyName, derivedIndexName, qualifyName } from '../util/sql.util.js';
|
|
7
|
+
import { UqlUsageError } from '../util/uqlError.js';
|
|
7
8
|
import { resolveColumnCanonicalType } from './canonicalType.js';
|
|
8
9
|
import { createTableNode, keyOfColumns, SchemaAST } from './schemaAST.js';
|
|
9
10
|
import { DEFAULT_FOREIGN_KEY_ACTION } from './types.js';
|
|
@@ -37,7 +38,7 @@ export function buildSchemaAST(entities, options = {}) {
|
|
|
37
38
|
}
|
|
38
39
|
/** The `compileDdl` of a build given no dialect, which has nothing to render an entity's SQL with. */
|
|
39
40
|
function refuseDdl() {
|
|
40
|
-
throw new
|
|
41
|
+
throw new UqlUsageError('building the schema of an entity that declares SQL (a check, a stored computed column, an index expression or predicate) needs a dialect to render it: pass `compileDdl`, as `buildEntityAST` does');
|
|
41
42
|
}
|
|
42
43
|
/** The entries a vector index of `meta` covers: the members it names, and any expression. */
|
|
43
44
|
function vectorIndexedEntries(meta) {
|
|
@@ -124,7 +124,9 @@ function diffColumn(tableName, source, target, opts) {
|
|
|
124
124
|
// inconsistently (`BIGINT(20)`, SQLite's `notnull: 0` rowid), so neither is compared.
|
|
125
125
|
const generatedType = source.isAutoIncrement && target.isAutoIncrement;
|
|
126
126
|
const impliedNotNull = source.isPrimaryKey && target.isPrimaryKey;
|
|
127
|
-
const
|
|
127
|
+
const expectedType = opts.normalizeType(source.type);
|
|
128
|
+
const actualType = opts.normalizeType(target.type);
|
|
129
|
+
const typeChanged = !generatedType && !areTypesEqual(expectedType, actualType);
|
|
128
130
|
if (typeChanged) {
|
|
129
131
|
differences.push(`type: ${formatType(source.type)} -> ${formatType(target.type)}`);
|
|
130
132
|
}
|
|
@@ -158,7 +160,7 @@ function diffColumn(tableName, source, target, opts) {
|
|
|
158
160
|
// Only the type this diff actually reports: a column altered for its default carries no data loss,
|
|
159
161
|
// and a generated key's type - never compared above - reads as unsigned against an entity that
|
|
160
162
|
// cannot say so.
|
|
161
|
-
isBreaking: (typeChanged || signednessChanged) && isBreakingTypeChange(
|
|
163
|
+
isBreaking: (typeChanged || signednessChanged) && isBreakingTypeChange(actualType, expectedType),
|
|
162
164
|
description: differences.join(', '),
|
|
163
165
|
};
|
|
164
166
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbstractSqlDialect, type DerivedRelation, type
|
|
1
|
+
import { AbstractSqlDialect, type DerivedRelation, type RelationRows } from '../dialect/abstractSqlDialect.js';
|
|
2
2
|
import { type JsonAccessMode, type JsonSlot } from '../dialect/jsonSql.js';
|
|
3
3
|
import { type EntityMeta, type FieldOptions, type Query, type QueryContext, type QueryPager, type QueryTextSearchOptions, type QueryWhere, type SqlDialectFeatures, type VectorDistance, type VectorMetric } from '../type/index.js';
|
|
4
4
|
/** What SQLite and the engines derived from it have. */
|
|
@@ -60,8 +60,6 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
60
60
|
vector: (expr: string) => string;
|
|
61
61
|
};
|
|
62
62
|
private bytesAsText;
|
|
63
|
-
/** A date reads back as SQLite stored it, a number or text, which JSON carries unchanged. */
|
|
64
|
-
protected hydrateKind(field: FieldOptions | undefined): HydrateKind | undefined;
|
|
65
63
|
/**
|
|
66
64
|
* FTS5 matches the table itself, so this works only where the table *is* an FTS5 virtual table (UQL does
|
|
67
65
|
* not create those; declare it outside your entities). The whole query is bound, column filter and all.
|
|
@@ -3,9 +3,10 @@ import { BYTES_PREFIX } from '../dialect/hydrateColumn.js';
|
|
|
3
3
|
import { chainedCall, groupsPerCall, jsonSetCall, jsonPath, jsonArraySlotArgs, jsonSlotArgs, jsonRemoveCall, jsonSetTarget, } from '../dialect/jsonSql.js';
|
|
4
4
|
import { QueryRaw, } from '../type/index.js';
|
|
5
5
|
import { indexDistance, isVectorIndexType } from '../type/vector.js';
|
|
6
|
+
import { utcTimestamp } from '../util/date.js';
|
|
6
7
|
import { declaredIndexName } from '../util/ddlExpression.util.js';
|
|
7
8
|
import { findVectorIndex, findVectorSort, textSearchFields, vectorCandidates } from '../util/dialect.util.js';
|
|
8
|
-
import {
|
|
9
|
+
import { isIntegerColumn } from '../util/field.util.js';
|
|
9
10
|
/**
|
|
10
11
|
* An FTS5 query over `columns` for what a person typed: each word a quoted string, which FTS5 reads as a
|
|
11
12
|
* term to match and never as syntax, and every one required, as the other engines read plain words.
|
|
@@ -128,7 +129,7 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
128
129
|
}
|
|
129
130
|
normalizeValue(value) {
|
|
130
131
|
if (value instanceof Date)
|
|
131
|
-
return value
|
|
132
|
+
return utcTimestamp(value);
|
|
132
133
|
return super.normalizeValue(value);
|
|
133
134
|
}
|
|
134
135
|
/**
|
|
@@ -169,10 +170,6 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
169
170
|
bytesAsText(expr) {
|
|
170
171
|
return `${this.escape(BYTES_PREFIX)} || hex(${expr})`;
|
|
171
172
|
}
|
|
172
|
-
/** A date reads back as SQLite stored it, a number or text, which JSON carries unchanged. */
|
|
173
|
-
hydrateKind(field) {
|
|
174
|
-
return columnFamily(field?.type) === 'date' ? undefined : super.hydrateKind(field);
|
|
175
|
-
}
|
|
176
173
|
/**
|
|
177
174
|
* FTS5 matches the table itself, so this works only where the table *is* an FTS5 virtual table (UQL does
|
|
178
175
|
* not create those; declare it outside your entities). The whole query is bound, column filter and all.
|
package/dist/type/dialect.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { EntityMeta, UpdatePayload } from './entity.js';
|
|
1
|
+
import type { EntityMeta, EntityPredicate, UpdatePayload } from './entity.js';
|
|
2
2
|
import type { Query, QueryConflictPaths, QueryPage, QueryRenderOptions, QuerySearch, RelationQuery } from './query.js';
|
|
3
3
|
import type { QueryAggMap, QueryAggregate, QueryAggregateOp, QueryGroupMap } from './queryAggregate.js';
|
|
4
|
+
import type { QueryRawRenderOptions } from './queryRaw.js';
|
|
4
5
|
import type { QueryWhere } from './queryWhere.js';
|
|
5
6
|
import type { Type } from './utility.js';
|
|
6
7
|
import type { QueryVectorQuery } from './vector.js';
|
|
@@ -231,6 +232,25 @@ export interface TriggerFeatures {
|
|
|
231
232
|
*/
|
|
232
233
|
readonly before: boolean;
|
|
233
234
|
}
|
|
235
|
+
/** Where DDL's SQL sits: the row a trigger's predicate reads, as its prefix, and a set-based body's rows. */
|
|
236
|
+
export type DdlRenderOptions = Pick<QueryComparisonOptions, 'escapedPrefix' | 'operand'> & Pick<QueryRawRenderOptions, 'rows'>;
|
|
237
|
+
/**
|
|
238
|
+
* A write a trigger's body runs, as `insertInto`, `updateTable` and `deleteFrom` state it. Held untyped
|
|
239
|
+
* here, past those helpers' typing, since the dialect renders it by the entity's metadata alone.
|
|
240
|
+
*/
|
|
241
|
+
export type TriggerWrite = {
|
|
242
|
+
readonly entity: Type<object>;
|
|
243
|
+
} & ({
|
|
244
|
+
readonly kind: 'insert';
|
|
245
|
+
readonly row: Readonly<Record<string, unknown>>;
|
|
246
|
+
} | {
|
|
247
|
+
readonly kind: 'update';
|
|
248
|
+
readonly set: Readonly<Record<string, unknown>>;
|
|
249
|
+
readonly where: EntityPredicate<object>;
|
|
250
|
+
} | {
|
|
251
|
+
readonly kind: 'delete';
|
|
252
|
+
readonly where: EntityPredicate<object>;
|
|
253
|
+
});
|
|
234
254
|
/**
|
|
235
255
|
* What a SQL statement is rendered through, as a `raw` callback and a query context see it:
|
|
236
256
|
* `AbstractSqlDialect` is the one implementation.
|
|
@@ -262,6 +282,8 @@ export interface SqlQueryDialect {
|
|
|
262
282
|
update<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, payload: UpdatePayload<E>, opts?: QueryRenderOptions): void;
|
|
263
283
|
/** An upsert of one record or many by their conflict paths. */
|
|
264
284
|
upsert<E>(ctx: QueryContext, entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E | E[]): void;
|
|
285
|
+
/** A write in a trigger's body; `rows` is where a set-based engine's body reads its rows from. */
|
|
286
|
+
triggerWrite(ctx: QueryContext, write: TriggerWrite, rows?: string): void;
|
|
265
287
|
/** A delete of the records the query matches, a soft delete where the entity has one. */
|
|
266
288
|
delete<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, opts?: QueryRenderOptions): void;
|
|
267
289
|
/**
|
package/dist/type/entity.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { EnumValues, ForeignKeyAction, IndexType } from '../schema/types.js';
|
|
2
2
|
import type { SqlDialectName } from './dialect.js';
|
|
3
3
|
import type { FilterOptions, RelationQuery } from './query.js';
|
|
4
|
-
import type { ColumnRef, QueryRaw, RelationAggregate } from './queryRaw.js';
|
|
4
|
+
import type { ColumnRef, QueryRaw, RawFor, RelationAggregate } from './queryRaw.js';
|
|
5
5
|
import type { QueryWhere } from './queryWhere.js';
|
|
6
6
|
import type { AtLeastOne, Except, ExactlyOne, IsEqual, IsMany, Json, Scalar, Type, Unpacked, Writable } from './utility.js';
|
|
7
7
|
import type { VectorDistance, VectorIndexOptions, VectorIndexType } from './vector.js';
|
|
@@ -37,6 +37,10 @@ export type WritableKey<E> = {
|
|
|
37
37
|
}[FieldKey<E>];
|
|
38
38
|
/** A whole-record write as a caller supplies one: {@link EntityData} without the fields it cannot write. */
|
|
39
39
|
export type EntityWrite<E> = EntityData<E, WritableKey<E>>;
|
|
40
|
+
/** A row a trigger's body writes: each writable field its value, or SQL - a row's ref most often. */
|
|
41
|
+
export type WriteRow<E, F extends keyof E = WritableKey<E>> = {
|
|
42
|
+
readonly [K in F]?: E[K] | RawFor<QueryRaw, E[K]>;
|
|
43
|
+
};
|
|
40
44
|
/**
|
|
41
45
|
* The property an entity brands with {@link versionKey} as its optimistic lock, `never` where it
|
|
42
46
|
* brands none. The brand is what carries `@Field({ version: true })` to the type level, since a
|
|
@@ -114,7 +118,7 @@ export type JsonArrayFields<T> = {
|
|
|
114
118
|
*/
|
|
115
119
|
export type JsonUpdateOp<T = unknown> = {
|
|
116
120
|
readonly $set?: Partial<T>;
|
|
117
|
-
readonly $unset?: unknown extends T ? string[] : (keyof T & string)[];
|
|
121
|
+
readonly $unset?: unknown extends T ? readonly string[] : readonly (keyof T & string)[];
|
|
118
122
|
readonly $push?: JsonArrayFields<T>;
|
|
119
123
|
readonly $pull?: JsonArrayFields<T>;
|
|
120
124
|
};
|
|
@@ -133,7 +137,7 @@ export type FieldUpdateOp<T extends number | bigint = number | bigint> = Exactly
|
|
|
133
137
|
/** The {@link FieldUpdateOp} a field takes: `never` on one it has no operator for, which is any but a number. */
|
|
134
138
|
type FieldUpdateOpFor<V> = [NonNullable<V>] extends [number] ? FieldUpdateOp<number> : [NonNullable<V>] extends [bigint] ? FieldUpdateOp<bigint> : never;
|
|
135
139
|
/** What an update takes beyond the value: `null` to clear an optional member, `raw` SQL, and update operators. */
|
|
136
|
-
type UpdateExtra<V, Raw> = (undefined extends V ? null : never) | Raw | JsonUpdateOpFor<V> | FieldUpdateOpFor<V>;
|
|
140
|
+
type UpdateExtra<V, Raw> = (undefined extends V ? null : never) | RawFor<Raw, V> | JsonUpdateOpFor<V> | FieldUpdateOpFor<V>;
|
|
137
141
|
/**
|
|
138
142
|
* What a whole-record write persists: the fields and relations with their declared optionality, a
|
|
139
143
|
* related row's alike, and no methods. Two mapped types, since asking each key costs a conditional.
|
|
@@ -284,7 +288,7 @@ export type FieldOptions<V = TsTypeOf<FieldType>, E = unknown> = {
|
|
|
284
288
|
readonly columnType?: ColumnType | QueryRaw;
|
|
285
289
|
/** A string column's length. */
|
|
286
290
|
readonly length?: number;
|
|
287
|
-
/** A decimal column's
|
|
291
|
+
/** A decimal column's digits, or a timestamp's fractional-second digits. */
|
|
288
292
|
readonly precision?: number;
|
|
289
293
|
/** A decimal column's scale. */
|
|
290
294
|
readonly scale?: number;
|
|
@@ -411,6 +415,10 @@ export type RelationReference<O, E> = {
|
|
|
411
415
|
readonly foreign: F;
|
|
412
416
|
};
|
|
413
417
|
}[FieldKey<E>];
|
|
418
|
+
/** The fields of `E` whose value is a `T`, however optional: `FieldKeyOf<E, number | bigint>` are the ones a sum adds up. */
|
|
419
|
+
export type FieldKeyOf<E, T> = {
|
|
420
|
+
readonly [K in FieldKey<E>]-?: [NonNullable<E[K]>] extends [T] ? K : never;
|
|
421
|
+
}[FieldKey<E>];
|
|
414
422
|
/** The fields of `O` that can hold any value `V` takes. */
|
|
415
423
|
type FieldKeyHolding<O, V> = {
|
|
416
424
|
readonly [K in keyof O]-?: [NonNullable<V>] extends [NonNullable<O[K]>] ? K : never;
|
|
@@ -446,16 +454,12 @@ export type KeyMap<E> = {
|
|
|
446
454
|
};
|
|
447
455
|
/** The fields of `E` as {@link ColumnRef}s, for SQL that names them: `refs(User)`, or a definition's callback. */
|
|
448
456
|
export type RefMap<E, F extends keyof E = FieldKey<E>> = {
|
|
449
|
-
readonly [K in F]-?: ColumnRef<K & string>;
|
|
457
|
+
readonly [K in F]-?: ColumnRef<K & string, E[K]>;
|
|
450
458
|
};
|
|
451
459
|
/** SQL a definition writes: `raw`, or a callback reading the fields off its refs, bivariant so the registry can hold it. */
|
|
452
460
|
export type EntitySql<E> = QueryRaw | {
|
|
453
461
|
sql(refs: RefMap<E>): QueryRaw;
|
|
454
462
|
}['sql'];
|
|
455
|
-
/** The fields of `C` a `sum` or an `avg` can add up. */
|
|
456
|
-
type NumericKey<C> = {
|
|
457
|
-
readonly [K in FieldKey<C>]-?: [NonNullable<C[K]>] extends [number | bigint] ? K : never;
|
|
458
|
-
}[FieldKey<C>];
|
|
459
463
|
/** One field of `C`, read off its refs: `(item) => item.amount`. */
|
|
460
464
|
type PickRef<C, K extends keyof C> = (refs: RefMap<C>) => ColumnRef<K & string>;
|
|
461
465
|
/**
|
|
@@ -466,11 +470,11 @@ type PickRef<C, K extends keyof C> = (refs: RefMap<C>) => ColumnRef<K & string>;
|
|
|
466
470
|
export type RelationRef<C> = {
|
|
467
471
|
count(q?: AggregateFilter<C>): RelationAggregate<number, true>;
|
|
468
472
|
count(q: AggregatePage<C>): RelationAggregate<number, false>;
|
|
469
|
-
sum<K extends
|
|
470
|
-
sum<K extends
|
|
473
|
+
sum<K extends FieldKeyOf<C, number | bigint>>(pick: PickRef<C, K>, q?: AggregateFilter<C>): RelationAggregate<NonNullable<C[K]>, true>;
|
|
474
|
+
sum<K extends FieldKeyOf<C, number | bigint>>(pick: PickRef<C, K>, q: AggregateTopRows<C>): RelationAggregate<NonNullable<C[K]>, false>;
|
|
471
475
|
min<K extends FieldKey<C>>(pick: PickRef<C, K>, q?: AggregateRows<C>): RelationAggregate<NonNullable<C[K]> | null, false>;
|
|
472
476
|
max<K extends FieldKey<C>>(pick: PickRef<C, K>, q?: AggregateRows<C>): RelationAggregate<NonNullable<C[K]> | null, false>;
|
|
473
|
-
avg<K extends
|
|
477
|
+
avg<K extends FieldKeyOf<C, number | bigint>>(pick: PickRef<C, K>, q?: AggregateRows<C>): RelationAggregate<number | null, false>;
|
|
474
478
|
};
|
|
475
479
|
/**
|
|
476
480
|
* What an aggregate reads of the related rows. The predicate is an {@link EntityPredicate} rather than a
|
package/dist/type/logger.d.ts
CHANGED
|
@@ -12,9 +12,9 @@ export interface Logger {
|
|
|
12
12
|
* @param values - The parameters passed to the query.
|
|
13
13
|
* @param duration - The time it took to execute the query in milliseconds.
|
|
14
14
|
*/
|
|
15
|
-
logQuery?(query: string, values?: unknown[], duration?: number): void;
|
|
15
|
+
logQuery?(query: string, values?: readonly unknown[], duration?: number): void;
|
|
16
16
|
/** Logs a query that took longer than the threshold, its values `undefined` unless `logValues` is on. */
|
|
17
|
-
logSlowQuery?(query: string, values?: unknown[], duration?: number): void;
|
|
17
|
+
logSlowQuery?(query: string, values?: readonly unknown[], duration?: number): void;
|
|
18
18
|
/**
|
|
19
19
|
* Logs a warning.
|
|
20
20
|
*/
|
package/dist/type/querier.d.ts
CHANGED
|
@@ -106,11 +106,11 @@ export interface SqlQuerier extends Querier {
|
|
|
106
106
|
/**
|
|
107
107
|
* Execute a raw SQL query and return results
|
|
108
108
|
*/
|
|
109
|
-
all<T>(query: string, values?: unknown[]): Promise<T[]>;
|
|
109
|
+
all<T>(query: string, values?: readonly unknown[]): Promise<T[]>;
|
|
110
110
|
/**
|
|
111
111
|
* Execute a raw SQL command (INSERT, UPDATE, DELETE, DDL)
|
|
112
112
|
*/
|
|
113
|
-
run(query: string, values?: unknown[]): Promise<QueryUpdateResult>;
|
|
113
|
+
run(query: string, values?: readonly unknown[]): Promise<QueryUpdateResult>;
|
|
114
114
|
}
|
|
115
115
|
/**
|
|
116
116
|
* Type guard to check if a querier supports raw SQL execution
|
|
@@ -136,7 +136,7 @@ export declare function isMongoQuerier(querier: Querier): querier is MongoQuerie
|
|
|
136
136
|
export type ListenerContext<E extends object = object> = {
|
|
137
137
|
readonly entity: Type<E>;
|
|
138
138
|
readonly querier: Querier;
|
|
139
|
-
readonly payloads: E[];
|
|
139
|
+
readonly payloads: readonly E[];
|
|
140
140
|
readonly event: HookEvent;
|
|
141
141
|
};
|
|
142
142
|
/**
|
package/dist/type/query.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FieldKey, JsonFieldPaths, RelationKey, RelationTarget, ToManyRelationKey, WrittenId } from './entity.js';
|
|
1
|
+
import type { FieldKey, FieldKeyOf, JsonFieldPaths, RelationKey, RelationTarget, ToManyRelationKey, WrittenId } from './entity.js';
|
|
2
2
|
import type { QueryLock } from './queryLock.js';
|
|
3
3
|
import type { QueryRaw } from './queryRaw.js';
|
|
4
4
|
import type { QueryWhere } from './queryWhere.js';
|
|
@@ -145,17 +145,13 @@ export type QuerySortValue = QuerySortDirection | QueryVectorSearch;
|
|
|
145
145
|
export type QuerySortByCount = {
|
|
146
146
|
$count: QuerySortDirection;
|
|
147
147
|
};
|
|
148
|
-
/** The fields of `E` a vector search can rank by. */
|
|
149
|
-
type VectorFieldKey<E> = {
|
|
150
|
-
[P in FieldKey<E>]: NonNullable<E[P]> extends readonly number[] ? P : never;
|
|
151
|
-
}[FieldKey<E>];
|
|
152
148
|
/**
|
|
153
149
|
* Ordering parents by the row of a to-many nearest a vector, per vector field: its distance is the
|
|
154
150
|
* smallest of theirs. Nothing to `$project`, since no one row of the parent's answers under it. Never
|
|
155
151
|
* where the target has no vector, since an empty map would admit any value at all.
|
|
156
152
|
*/
|
|
157
|
-
export type QuerySortByNearest<E> = [
|
|
158
|
-
[P in
|
|
153
|
+
export type QuerySortByNearest<E> = [FieldKeyOf<E, readonly number[]>] extends [never] ? never : {
|
|
154
|
+
[P in FieldKeyOf<E, readonly number[]>]?: QueryVectorQuery;
|
|
159
155
|
};
|
|
160
156
|
/**
|
|
161
157
|
* Ordering by relevance to the `$text` at the root of `$where`, in either direction as any key sorts. The
|
|
@@ -371,12 +367,6 @@ Pick<E, Exclude<ProjectedKeys<E, S, V, X, P>, PopulatedToMany<E, P>> & keyof E>
|
|
|
371
367
|
} : E;
|
|
372
368
|
/** The to-many relations a query populated, which come back as lists rather than as optional ones. */
|
|
373
369
|
type PopulatedToMany<E, P> = Extract<P, ToManyRelationKey<E>>;
|
|
374
|
-
/**
|
|
375
|
-
* stringified query.
|
|
376
|
-
*/
|
|
377
|
-
export type QueryStringified = {
|
|
378
|
-
[K in keyof Query<unknown>]?: string;
|
|
379
|
-
};
|
|
380
370
|
/** What upserting one row reports. `created` is only knowable for a single statement, so a batch has none. */
|
|
381
371
|
export type QueryUpsertOneResult<E> = {
|
|
382
372
|
readonly id?: WrittenId<E>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FieldKey, RelationKey, RelationTarget } from './entity.js';
|
|
1
|
+
import type { FieldKey, FieldKeyOf, RelationKey, RelationTarget } from './entity.js';
|
|
2
2
|
import type { QueryPager, QuerySelect, QuerySortDirection } from './query.js';
|
|
3
3
|
import type { QueryRaw } from './queryRaw.js';
|
|
4
4
|
import type { QueryWhere, QueryWhereFieldValue } from './queryWhere.js';
|
|
@@ -44,13 +44,6 @@ export declare function resolveAggregateOp(key: string): {
|
|
|
44
44
|
export type QueryFieldRef<E, F extends keyof E = FieldKey<E>> = ExactlyOne<Required<QuerySelect<E, F, true>>>;
|
|
45
45
|
/** The argument of an aggregate function: a field, or `'*'` (only meaningful for `COUNT(*)`). */
|
|
46
46
|
export type QueryAggregateArg<E> = QueryFieldRef<E> | '*';
|
|
47
|
-
/**
|
|
48
|
-
* Fields `SUM`/`AVG` can total. Restricted to numeric columns because totalling a text or date one is
|
|
49
|
-
* either an engine error or a coercion, and neither produces the value the signature promises.
|
|
50
|
-
*/
|
|
51
|
-
type NumericFieldKey<E> = {
|
|
52
|
-
readonly [K in FieldKey<E>]: [NonNullable<E[K]>] extends [number | bigint] ? K : never;
|
|
53
|
-
}[FieldKey<E>];
|
|
54
47
|
/** Every aggregate op, plain and DISTINCT-qualified. */
|
|
55
48
|
type AggregateOp = QueryAggregateOp | QueryAggregateDistinctOp;
|
|
56
49
|
/**
|
|
@@ -67,9 +60,10 @@ type AveragingOp = OpsOf<'$avg'>;
|
|
|
67
60
|
type TotallingOp = SummingOp | AveragingOp;
|
|
68
61
|
/**
|
|
69
62
|
* Every aggregate op mapped to the argument it accepts: `$count` a field or `'*'` (`COUNT(*)`),
|
|
70
|
-
* the totalling ops a numeric field,
|
|
63
|
+
* the totalling ops a numeric field, since totalling any other is an engine error or a coercion,
|
|
64
|
+
* and `$min`/`$max`/`$countDistinct` any field.
|
|
71
65
|
*/
|
|
72
|
-
type QueryAggregateArgMap<E> = Record<'$count', QueryAggregateArg<E>> & Record<TotallingOp, QueryFieldRef<E,
|
|
66
|
+
type QueryAggregateArgMap<E> = Record<'$count', QueryAggregateArg<E>> & Record<TotallingOp, QueryFieldRef<E, FieldKeyOf<E, number | bigint>>> & Record<Exclude<AggregateOp, '$count' | TotallingOp>, QueryFieldRef<E>>;
|
|
73
67
|
/**
|
|
74
68
|
* An aggregate over one field, exactly one op per entry: `{ $sum: { amount: true } }` is `SUM("amount")`,
|
|
75
69
|
* `{ $countDistinct: { id: true } }` is `COUNT(DISTINCT "id")`, and only `$count` takes `'*'`. Its own
|
package/dist/type/queryRaw.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export type QueryRawRenderOptions = {
|
|
|
15
15
|
* computed field's own, or the one whose schema is built. Absent where a statement renders SQL.
|
|
16
16
|
*/
|
|
17
17
|
entity?: Type<unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* The `FROM` a set-based trigger's body reads its rows through, `FROM inserted` and the like, which a
|
|
20
|
+
* write in it names. Absent where the body reads `NEW` and `OLD` bare, and outside a trigger.
|
|
21
|
+
*/
|
|
22
|
+
rows?: string;
|
|
18
23
|
};
|
|
19
24
|
/** {@link QueryRawRenderOptions} as the callers along the way fill them in, every one still optional. */
|
|
20
25
|
export type QueryRawFnOptions = Partial<QueryRawRenderOptions>;
|
|
@@ -45,12 +50,21 @@ export declare class QueryRaw {
|
|
|
45
50
|
}
|
|
46
51
|
/**
|
|
47
52
|
* A field of an entity as SQL, read off `refs(Entity)` or a definition's refs: interpolated into `raw`, it
|
|
48
|
-
* renders as the field's column. Its `key` is how an index tells a column from an expression
|
|
53
|
+
* renders as the field's column. Its `key` is how an index tells a column from an expression, and `V`,
|
|
54
|
+
* the field's type, is what a value slot checks it against: see {@link RawFor}.
|
|
49
55
|
*/
|
|
50
|
-
export declare class ColumnRef<K extends string = string> extends QueryRaw {
|
|
56
|
+
export declare class ColumnRef<K extends string = string, V = unknown> extends QueryRaw {
|
|
51
57
|
readonly key: K;
|
|
58
|
+
readonly __value?: V;
|
|
52
59
|
constructor(key: K, value: QueryRawFn);
|
|
53
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* SQL where a value of type `V` goes: bare SQL, whose type is its author's to know, or a ref to a column
|
|
63
|
+
* holding one, nullability aside. `Raw` is what the transport carries, so the wire's `never` stays one.
|
|
64
|
+
*/
|
|
65
|
+
export type RawFor<Raw, V> = Raw & {
|
|
66
|
+
readonly __value?: V | null;
|
|
67
|
+
};
|
|
54
68
|
/**
|
|
55
69
|
* A relation aggregate as SQL, read off a `computed` field's refs: `(user) => user.resources.count()`.
|
|
56
70
|
* It renders as the correlated subquery a `$count` reads, so a field holding one is read, filtered and
|
|
@@ -63,7 +77,7 @@ export declare class ColumnRef<K extends string = string> extends QueryRaw {
|
|
|
63
77
|
export declare class RelationAggregate<V = unknown, Storable extends boolean = boolean> extends QueryRaw {
|
|
64
78
|
/** What it reads, kept beside the SQL so a read decodes the value the way the target's field does. */
|
|
65
79
|
readonly spec: RelationAggregateSpec;
|
|
66
|
-
|
|
80
|
+
readonly __value?: V;
|
|
67
81
|
private readonly __storable;
|
|
68
82
|
constructor(
|
|
69
83
|
/** What it reads, kept beside the SQL so a read decodes the value the way the target's field does. */
|
package/dist/type/queryRaw.js
CHANGED
|
@@ -29,7 +29,8 @@ export class QueryRaw {
|
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* A field of an entity as SQL, read off `refs(Entity)` or a definition's refs: interpolated into `raw`, it
|
|
32
|
-
* renders as the field's column. Its `key` is how an index tells a column from an expression
|
|
32
|
+
* renders as the field's column. Its `key` is how an index tells a column from an expression, and `V`,
|
|
33
|
+
* the field's type, is what a value slot checks it against: see {@link RawFor}.
|
|
33
34
|
*/
|
|
34
35
|
export class ColumnRef extends QueryRaw {
|
|
35
36
|
key;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FieldKey, JsonFieldPaths, JsonFieldPathValue, RelationKey, RelationTarget } from './entity.js';
|
|
2
2
|
import type { QuerySelect } from './query.js';
|
|
3
|
-
import type { QueryRaw } from './queryRaw.js';
|
|
3
|
+
import type { QueryRaw, RawFor } from './queryRaw.js';
|
|
4
4
|
import type { AtLeastOne, ExpandScalar, IsMany, QueryComparableScalar, Scalar } from './utility.js';
|
|
5
5
|
import type { QueryVectorQuery } from './vector.js';
|
|
6
6
|
/**
|
|
@@ -136,7 +136,7 @@ export type QueryWhereFieldOperatorMap<T, Raw = QueryRaw> = {
|
|
|
136
136
|
* whether a value is between two values (inclusive). Shorthand for $gte + $lte.
|
|
137
137
|
* @example { age: { $between: [18, 65] } }
|
|
138
138
|
*/
|
|
139
|
-
$between?: [ExpandScalar<T>, ExpandScalar<T>];
|
|
139
|
+
$between?: readonly [ExpandScalar<T>, ExpandScalar<T>];
|
|
140
140
|
/**
|
|
141
141
|
* whether a string begins with the given string (case sensitive).
|
|
142
142
|
*/
|
|
@@ -176,11 +176,11 @@ export type QueryWhereFieldOperatorMap<T, Raw = QueryRaw> = {
|
|
|
176
176
|
/**
|
|
177
177
|
* whether a value matches any of the given values.
|
|
178
178
|
*/
|
|
179
|
-
$in?: ExpandScalar<T>[];
|
|
179
|
+
$in?: readonly ExpandScalar<T>[];
|
|
180
180
|
/**
|
|
181
181
|
* whether a value does not match any of the given values.
|
|
182
182
|
*/
|
|
183
|
-
$nin?: ExpandScalar<T>[];
|
|
183
|
+
$nin?: readonly ExpandScalar<T>[];
|
|
184
184
|
/**
|
|
185
185
|
* whether a value is null.
|
|
186
186
|
* @example { deletedAt: { $isNull: true } }
|
|
@@ -195,7 +195,7 @@ export type QueryWhereFieldOperatorMap<T, Raw = QueryRaw> = {
|
|
|
195
195
|
* whether an array contains all the specified values.
|
|
196
196
|
* @example { tags: { $all: ['typescript', 'orm'] } }
|
|
197
197
|
*/
|
|
198
|
-
$all?: unknown extends T ? unknown[] : NonNullable<T> extends readonly (infer U)[] ? ExpandScalar<U>[] : never;
|
|
198
|
+
$all?: unknown extends T ? readonly unknown[] : NonNullable<T> extends readonly (infer U)[] ? readonly ExpandScalar<U>[] : never;
|
|
199
199
|
/** whether an array has the given length, or one in range: `{ roles: { $size: { $gte: 2 } } }`. */
|
|
200
200
|
$size?: number | QuerySizeComparisonOps;
|
|
201
201
|
/**
|
|
@@ -284,9 +284,9 @@ type IsUntypedColumn<T> = [Scalar] extends [NonNullable<T>] ? true : false;
|
|
|
284
284
|
* A field's filter value: the value, `null` where it is optional, a list as an implicit `$in` (not on
|
|
285
285
|
* an array field, where it would be ambiguous), or an operator map.
|
|
286
286
|
*/
|
|
287
|
-
export type QueryWhereFieldValue<T, Raw = QueryRaw> = T | (undefined extends T ? null : never) | (IsMany<T> extends true ? never : T[]) | QueryWhereFieldOperators<T, Raw> | Raw
|
|
287
|
+
export type QueryWhereFieldValue<T, Raw = QueryRaw> = T | (undefined extends T ? null : never) | (IsMany<T> extends true ? never : readonly T[]) | QueryWhereFieldOperators<T, Raw> | RawFor<Raw, T>;
|
|
288
288
|
/**
|
|
289
289
|
* query filter array - the value every {@link QueryGroupOp} takes.
|
|
290
290
|
*/
|
|
291
|
-
export type QueryWhereArray<E, Raw = QueryRaw> = (QueryWhere<E, Raw> | Raw)[];
|
|
291
|
+
export type QueryWhereArray<E, Raw = QueryRaw> = readonly (QueryWhere<E, Raw> | Raw)[];
|
|
292
292
|
export {};
|
|
@@ -65,11 +65,11 @@ export interface UniversalQuerier extends SharedQuerier<'server', QueryOptions>
|
|
|
65
65
|
* Ids are exact everywhere but MySQL, which infers them from its header and reports `undefined` rather
|
|
66
66
|
* than a guess where it cannot: a batch naming some keys, or a key that is not `AUTO_INCREMENT`.
|
|
67
67
|
*/
|
|
68
|
-
insertMany<E extends object>(entity: Type<E>, payload: EntityWrite<E>[]): Promise<(WrittenId<E> | undefined)[]>;
|
|
68
|
+
insertMany<E extends object>(entity: Type<E>, payload: readonly EntityWrite<E>[]): Promise<(WrittenId<E> | undefined)[]>;
|
|
69
69
|
/** Insert or update a record by its conflict paths; resolves to its id and whether it was created. */
|
|
70
70
|
upsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityWrite<E>): Promise<QueryUpsertOneResult<E>>;
|
|
71
71
|
/** Insert or update records by their conflict paths; resolves to their ids in payload order. */
|
|
72
|
-
upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityWrite<E>[]): Promise<QueryUpsertManyResult<E>>;
|
|
72
|
+
upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: readonly EntityWrite<E>[]): Promise<QueryUpsertManyResult<E>>;
|
|
73
73
|
/**
|
|
74
74
|
* insert or update a record.
|
|
75
75
|
* @param entity the entity to persist on
|
|
@@ -83,7 +83,7 @@ export interface UniversalQuerier extends SharedQuerier<'server', QueryOptions>
|
|
|
83
83
|
* @param payload the data to be persisted
|
|
84
84
|
* @return the IDs
|
|
85
85
|
*/
|
|
86
|
-
saveMany<E extends object>(entity: Type<E>, payload: EntityWrite<E>[]): Promise<(WrittenId<E> | undefined)[]>;
|
|
86
|
+
saveMany<E extends object>(entity: Type<E>, payload: readonly EntityWrite<E>[]): Promise<(WrittenId<E> | undefined)[]>;
|
|
87
87
|
/**
|
|
88
88
|
* Restore soft-deleted records (sets the soft-delete field back to `null`). Throws if the
|
|
89
89
|
* entity has no soft-delete field.
|
package/dist/type/vector.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { VectorCast } from '../dialect/vectorCast.js';
|
|
2
2
|
import type { IndexType } from '../schema/types.js';
|
|
3
|
+
import { UqlUsageError } from '../util/uqlError.js';
|
|
3
4
|
/**
|
|
4
5
|
* A vector search's metric: `cosine` (the default), `l2`, `inner` product or `l1`. No hamming: every
|
|
5
6
|
* engine's takes a bit vector, which no field type maps to.
|
|
@@ -37,7 +38,7 @@ export type VectorMetric = {
|
|
|
37
38
|
readonly index?: string;
|
|
38
39
|
};
|
|
39
40
|
/** The error every dialect throws for a metric it lacks. */
|
|
40
|
-
export declare function unsupportedVectorMetric(dialectName: string, distance: VectorDistance, indexName?: string):
|
|
41
|
+
export declare function unsupportedVectorMetric(dialectName: string, distance: VectorDistance, indexName?: string): UqlUsageError;
|
|
41
42
|
/**
|
|
42
43
|
* Vector-specific tuning options shared by `@Index` decorator, entity metadata, and migration schema.
|
|
43
44
|
*/
|
package/dist/type/vector.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { UqlUsageError } from '../util/uqlError.js';
|
|
1
2
|
/** The keys that describe the search rather than bound it, so `$near`'s bounds are what is left. */
|
|
2
3
|
export const VECTOR_QUERY_KEYS = ['$vector', '$distance'];
|
|
3
4
|
/** The error every dialect throws for a metric it lacks. */
|
|
4
5
|
export function unsupportedVectorMetric(dialectName, distance, indexName) {
|
|
5
6
|
const where = indexName === undefined ? '' : ` (index "${indexName}")`;
|
|
6
|
-
return new
|
|
7
|
+
return new UqlUsageError(`${dialectName} does not support vector distance metric: ${distance}${where}`);
|
|
7
8
|
}
|
|
8
9
|
/** The metric a search or an index measures by where nothing names one: every engine with vectors has it. */
|
|
9
10
|
export const DEFAULT_VECTOR_DISTANCE = 'cosine';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `YYYY-MM-DD HH:mm:ss.SSS` in UTC, then `zone`: how a date is written, whichever machine writes it. Not
|
|
3
|
+
* `toISOString` as it is, whose `T` and `Z` MySQL rejects outright ("Invalid default value").
|
|
4
|
+
*/
|
|
5
|
+
export declare function utcTimestamp(date: Date, zone?: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* A timestamp's text as the `Date` it names, the one rule every driver and hydration read dates by: UTC
|
|
8
|
+
* where it names no zone, its own offset where it does, a bare day at UTC midnight, and the fraction cut
|
|
9
|
+
* to the milliseconds a `Date` holds. Text that is none of these, such as `infinity`, stays text.
|
|
10
|
+
*/
|
|
11
|
+
export declare function decodeDate(text: string): Date | string;
|