uql-orm 0.47.1 → 0.49.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.
@@ -182,9 +182,10 @@ export type UpdatePayload<E> = {
182
182
  */
183
183
  export type FieldValue<E> = E[FieldKey<E>];
184
184
  /**
185
- * Infers the name of the key identifier on an entity
185
+ * The key's name where the entity states it: the `idKey` brand first, then the conventional names.
186
+ * `never` when nothing does, which is the case {@link IdKey} falls back on and `@Id` refuses.
186
187
  */
187
- export type IdKey<E> = (E extends {
188
+ export type NamedIdKey<E> = E extends {
188
189
  [idKey]?: infer K;
189
190
  } ? K & FieldKey<E> : E extends {
190
191
  _id?: unknown;
@@ -192,7 +193,11 @@ export type IdKey<E> = (E extends {
192
193
  id?: unknown;
193
194
  } ? 'id' & FieldKey<E> : E extends {
194
195
  uuid?: unknown;
195
- } ? 'uuid' & FieldKey<E> : FieldKey<E>) & string;
196
+ } ? 'uuid' & FieldKey<E> : never;
197
+ /**
198
+ * Infers the name of the key identifier on an entity
199
+ */
200
+ export type IdKey<E> = ([NamedIdKey<E>] extends [never] ? FieldKey<E> : NamedIdKey<E>) & string;
196
201
  /**
197
202
  * Infers the value of the key identifier on an entity.
198
203
  *
@@ -570,7 +575,7 @@ export type RelationManyToManyOptions<E> = RelationOptionsThroughOwner<E> | Rela
570
575
  /**
571
576
  * Lifecycle hook event names.
572
577
  */
573
- export type HookEvent = 'beforeInsert' | 'afterInsert' | 'beforeUpdate' | 'afterUpdate' | 'beforeDelete' | 'afterDelete' | 'afterLoad';
578
+ export type HookEvent = 'beforeInsert' | 'afterInsert' | 'beforeUpdate' | 'afterUpdate' | 'beforeUpsert' | 'afterUpsert' | 'beforeDelete' | 'afterDelete' | 'afterLoad';
574
579
  /**
575
580
  * A registered hook: the method name on the entity class to call.
576
581
  */
@@ -25,7 +25,7 @@ export type QueryRawFnOptions = {
25
25
  /**
26
26
  * A `raw` callback: write into `ctx`, or return a string or number to have it appended. Anything else
27
27
  * it returns is ignored, which is why the return type is `unknown` rather than `void | Scalar` - the
28
- * latter rejected `({ ctx }) => ctx.append(...)`, the form every virtual field is written in, because
28
+ * latter rejected `({ ctx }) => ctx.append(...)`, the form every computed field is written in, because
29
29
  * TypeScript's "returning a value where void is expected" allowance does not apply to a union.
30
30
  *
31
31
  * `Required`, and the parameter not optional, because the one place that calls it (`getRawValue`)
@@ -153,14 +153,14 @@ export interface UniversalQuerier extends SharedQuerier<'server', QueryOptions>
153
153
  * @param payload the data to be persisted
154
154
  * @return the ID
155
155
  */
156
- saveOne<E extends object>(entity: Type<E>, payload: EntityData<E>): Promise<IdValue<E> | undefined>;
156
+ saveOne<E extends object>(entity: Type<E>, payload: EntityData<E>): Promise<EntityId<E> | undefined>;
157
157
  /**
158
158
  * Insert or update records.
159
159
  * @param entity the entity to persist on
160
160
  * @param payload the data to be persisted
161
161
  * @return the IDs
162
162
  */
163
- saveMany<E extends object>(entity: Type<E>, payload: EntityData<E>[]): Promise<(IdValue<E> | undefined)[]>;
163
+ saveMany<E extends object>(entity: Type<E>, payload: EntityData<E>[]): Promise<(EntityId<E> | undefined)[]>;
164
164
  /**
165
165
  * Restore soft-deleted records (sets the soft-delete field back to `null`). Throws if the
166
166
  * entity has no soft-delete field.
@@ -1,6 +1,13 @@
1
1
  import { type CascadeType, type EntityData, type EntityIndexMeta, type EntityMeta, type FieldKey, type FieldOptions, type JsonUpdateOp, type OnFieldCallback, type Query, type QueryAggMap, type QueryAggregateOp, type QueryExclude, type QueryGroupMap, type QueryOptions, QueryRaw, type QuerySearch, type QuerySelect, type QuerySelectValue, type QuerySizeComparisonOps, type QuerySortMap, type QueryVectorSearch, type QueryWhere, type QueryWhereMap, type RelationKey } from '../type/index.js';
2
2
  export type CallbackKey = keyof Pick<FieldOptions, 'onInsert' | 'onUpdate'>;
3
3
  export declare function filterFieldKeys<E>(meta: EntityMeta<E>, payload: EntityData<E>, callbackKey: CallbackKey): FieldKey<E>[];
4
+ /** Appends `record`'s not-yet-`seen` insertable keys (real, caller-written, defined value) to `keys`. */
5
+ /**
6
+ * The insertable keys `record` itself carries, as a string, for grouping rows by the statement they
7
+ * can share. Only the row's own keys: the `onInsert` columns {@link getInsertFieldKeys} appends are a
8
+ * property of the entity, identical for every row, so they cannot tell two rows apart.
9
+ */
10
+ export declare function insertShapeOf<E>(meta: EntityMeta<E>, record: EntityData<E>): string;
4
11
  /**
5
12
  * Resolves the columns of an INSERT statement: the union of the persistable fields provided by
6
13
  * any record (in first-seen order), plus every `onInsert` field. Records missing one of these
@@ -16,6 +16,20 @@ function isInsertableField(meta, record, key) {
16
16
  return !!field && !isDatabaseWritten(field) && record[key] !== undefined;
17
17
  }
18
18
  /** Appends `record`'s not-yet-`seen` insertable keys (real, caller-written, defined value) to `keys`. */
19
+ /**
20
+ * The insertable keys `record` itself carries, as a string, for grouping rows by the statement they
21
+ * can share. Only the row's own keys: the `onInsert` columns {@link getInsertFieldKeys} appends are a
22
+ * property of the entity, identical for every row, so they cannot tell two rows apart.
23
+ */
24
+ export function insertShapeOf(meta, record) {
25
+ let shape = '';
26
+ for (const key of getKeys(record)) {
27
+ if (isInsertableField(meta, record, key)) {
28
+ shape += `${key},`;
29
+ }
30
+ }
31
+ return shape;
32
+ }
19
33
  function addInsertFieldKeys(meta, record, seen, keys) {
20
34
  for (const key of getKeys(record)) {
21
35
  if (!seen.has(key) && isInsertableField(meta, record, key)) {
@@ -34,7 +34,7 @@ declare const FIELD_OPTION_FAMILY: {
34
34
  readonly comment: '*';
35
35
  };
36
36
  /**
37
- * The only options a `virtual` field reaches: it is skipped in DDL and dropped from every insert and
37
+ * The only options an inlined computed field reaches: it is skipped in DDL and dropped from every insert and
38
38
  * update, so the whole persistence half of the options above is dead on one. Stated as what survives
39
39
  * rather than on each option that dies, because it is one fact rather than nineteen - and because an
40
40
  * option added without a thought then lands on the safe side of it.
@@ -34,7 +34,7 @@ const FIELD_OPTION_FAMILY = {
34
34
  comment: '*',
35
35
  };
36
36
  /**
37
- * The only options a `virtual` field reaches: it is skipped in DDL and dropped from every insert and
37
+ * The only options an inlined computed field reaches: it is skipped in DDL and dropped from every insert and
38
38
  * update, so the whole persistence half of the options above is dead on one. Stated as what survives
39
39
  * rather than on each option that dies, because it is one fact rather than nineteen - and because an
40
40
  * option added without a thought then lands on the safe side of it.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "homepage": "https://uql-orm.dev",
4
4
  "description": "The JSON-native TypeScript ORM for Bun, Browsers, Edge, Deno, Node, Workers. Supports PostgreSQL, PGlite, MySQL, MariaDB, SQLite, CockroachDB, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
5
5
  "license": "MIT",
6
- "version": "0.47.1",
6
+ "version": "0.49.0",
7
7
  "type": "module",
8
8
  "engines": {
9
9
  "node": ">=24"