turbine-orm 0.41.0 → 0.42.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.
@@ -844,14 +844,28 @@ function prismaPropertyAlias(model) {
844
844
  */
845
845
  function makeDelegate(ctx, mm, getQI) {
846
846
  const pe = ctx.options.prismaErrorCodes;
847
- const lift = (run, batchable) => new CompatPromise(async () => {
848
- try {
849
- return await run();
850
- }
851
- catch (err) {
852
- throw decorate(err, pe);
853
- }
854
- }, batchable);
847
+ // Build a lazy Prisma-style promise for one delegate call. Crucially, the
848
+ // Prisma-arg `translate` step runs INSIDE the deferred paths (the run closure
849
+ // and the batchable build closure), never eagerly at call time: a
850
+ // translation/validation error (unknown relation in `include`, unknown
851
+ // compound selector, negative take, ...) must surface as a REJECTED promise
852
+ // so a Prisma-shaped `.catch()` fires, not as a synchronous throw. The
853
+ // async run wrapper also converts any synchronous throw from the underlying
854
+ // `qi.*` build into a rejection; the array `$transaction([...])` batch path
855
+ // catches the same throw from `batch.build`.
856
+ const defer = (translate, run, batch) => {
857
+ const batchable = batch
858
+ ? { build: () => batch.build(getQI(), translate()), reshape: batch.reshape }
859
+ : undefined;
860
+ return new CompatPromise(async () => {
861
+ try {
862
+ return await run(getQI(), translate());
863
+ }
864
+ catch (err) {
865
+ throw decorate(err, pe);
866
+ }
867
+ }, batchable);
868
+ };
855
869
  const requireWhere = (args, op) => {
856
870
  if (!args || args.where === undefined) {
857
871
  throw new ValidationError(`[turbine] prisma-compat: ${op} on "${modelName(ctx, mm)}" requires a \`where\`.`);
@@ -859,125 +873,68 @@ function makeDelegate(ctx, mm, getQI) {
859
873
  return args;
860
874
  };
861
875
  return {
862
- findMany: (args = {}) => {
863
- const t = translateReadArgs(ctx, mm, args);
864
- return lift(() => getQI()
865
- .findMany(t)
866
- .then((r) => reshapeRows(ctx, mm, r)), { build: () => getQI().buildFindMany(t), reshape: (raw) => reshapeRows(ctx, mm, raw) });
867
- },
868
- findFirst: (args = {}) => {
869
- const t = translateReadArgs(ctx, mm, args);
870
- return lift(() => getQI()
871
- .findFirst(t)
872
- .then((r) => reshapeRowOrNull(ctx, mm, r)), { build: () => getQI().buildFindFirst(t), reshape: (raw) => reshapeRowOrNull(ctx, mm, raw) });
873
- },
874
- findUnique: (args) => {
875
- const t = translateReadArgs(ctx, mm, requireWhere(args, 'findUnique'));
876
- return lift(() => getQI()
877
- .findUnique(t)
878
- .then((r) => reshapeRowOrNull(ctx, mm, r)), { build: () => getQI().buildFindUnique(t), reshape: (raw) => reshapeRowOrNull(ctx, mm, raw) });
879
- },
880
- findFirstOrThrow: (args = {}) => {
881
- const t = translateReadArgs(ctx, mm, args);
882
- return lift(() => getQI()
883
- .findFirstOrThrow(t)
884
- .then((r) => reshapeRow(ctx, mm, r)), { build: () => getQI().buildFindFirstOrThrow(t), reshape: (raw) => reshapeRow(ctx, mm, raw) });
885
- },
886
- findUniqueOrThrow: (args) => {
887
- const t = translateReadArgs(ctx, mm, requireWhere(args, 'findUniqueOrThrow'));
888
- return lift(() => getQI()
889
- .findUniqueOrThrow(t)
890
- .then((r) => reshapeRow(ctx, mm, r)), { build: () => getQI().buildFindUniqueOrThrow(t), reshape: (raw) => reshapeRow(ctx, mm, raw) });
891
- },
892
- create: (args) => {
876
+ findMany: (args = {}) => defer(() => translateReadArgs(ctx, mm, args), (qi, t) => qi.findMany(t).then((r) => reshapeRows(ctx, mm, r)), { build: (qi, t) => qi.buildFindMany(t), reshape: (raw) => reshapeRows(ctx, mm, raw) }),
877
+ findFirst: (args = {}) => defer(() => translateReadArgs(ctx, mm, args), (qi, t) => qi.findFirst(t).then((r) => reshapeRowOrNull(ctx, mm, r)), { build: (qi, t) => qi.buildFindFirst(t), reshape: (raw) => reshapeRowOrNull(ctx, mm, raw) }),
878
+ findUnique: (args) => defer(() => translateReadArgs(ctx, mm, requireWhere(args, 'findUnique')), (qi, t) => qi.findUnique(t).then((r) => reshapeRowOrNull(ctx, mm, r)), { build: (qi, t) => qi.buildFindUnique(t), reshape: (raw) => reshapeRowOrNull(ctx, mm, raw) }),
879
+ findFirstOrThrow: (args = {}) => defer(() => translateReadArgs(ctx, mm, args), (qi, t) => qi.findFirstOrThrow(t).then((r) => reshapeRow(ctx, mm, r)), { build: (qi, t) => qi.buildFindFirstOrThrow(t), reshape: (raw) => reshapeRow(ctx, mm, raw) }),
880
+ findUniqueOrThrow: (args) => defer(() => translateReadArgs(ctx, mm, requireWhere(args, 'findUniqueOrThrow')), (qi, t) => qi.findUniqueOrThrow(t).then((r) => reshapeRow(ctx, mm, r)), { build: (qi, t) => qi.buildFindUniqueOrThrow(t), reshape: (raw) => reshapeRow(ctx, mm, raw) }),
881
+ create: (args) => defer(() => {
893
882
  const t = { data: translateWriteData(ctx, mm, args.data) };
894
883
  if (typeof args.timeout === 'number')
895
884
  t.timeout = args.timeout;
896
- return lift(() => getQI()
897
- .create(t)
898
- .then((r) => reshapeRow(ctx, mm, r)), { build: () => getQI().buildCreate(t), reshape: (raw) => reshapeRow(ctx, mm, raw) });
899
- },
900
- createMany: (args) => {
885
+ return t;
886
+ }, (qi, t) => qi.create(t).then((r) => reshapeRow(ctx, mm, r)), { build: (qi, t) => qi.buildCreate(t), reshape: (raw) => reshapeRow(ctx, mm, raw) }),
887
+ createMany: (args) => defer(() => {
901
888
  const data = args.data;
902
889
  const rows = Array.isArray(data) ? data.map((d) => translateWriteData(ctx, mm, d)) : [];
903
890
  const t = { data: rows };
904
891
  if (args.skipDuplicates)
905
892
  t.skipDuplicates = true;
906
- return lift(() => getQI()
907
- .createMany(t)
908
- .then((r) => ({ count: r.length })), { build: () => getQI().buildCreateMany(t), reshape: (raw) => ({ count: raw.length }) });
909
- },
910
- update: (args) => {
893
+ return t;
894
+ }, (qi, t) => qi.createMany(t).then((r) => ({ count: r.length })), { build: (qi, t) => qi.buildCreateMany(t), reshape: (raw) => ({ count: raw.length }) }),
895
+ update: (args) => defer(() => {
911
896
  const a = requireWhere(args, 'update');
912
- const t = { where: translateWhere(ctx, mm, a.where), data: translateWriteData(ctx, mm, a.data) };
913
- return lift(() => getQI()
914
- .update(t)
915
- .then((r) => reshapeRow(ctx, mm, r)), { build: () => getQI().buildUpdate(t), reshape: (raw) => reshapeRow(ctx, mm, raw) });
916
- },
917
- updateMany: (args) => {
897
+ return { where: translateWhere(ctx, mm, a.where), data: translateWriteData(ctx, mm, a.data) };
898
+ }, (qi, t) => qi.update(t).then((r) => reshapeRow(ctx, mm, r)), { build: (qi, t) => qi.buildUpdate(t), reshape: (raw) => reshapeRow(ctx, mm, raw) }),
899
+ updateMany: (args) => defer(() => {
918
900
  const a = args;
919
901
  const t = { where: translateWhere(ctx, mm, a.where ?? {}), data: translateWriteData(ctx, mm, a.data) };
920
902
  if (a.where === undefined)
921
903
  t.allowFullTableScan = true;
922
- return lift(() => getQI().updateMany(t), {
923
- build: () => getQI().buildUpdateMany(t),
924
- reshape: (raw) => raw,
925
- });
926
- },
927
- delete: (args) => {
904
+ return t;
905
+ }, (qi, t) => qi.updateMany(t), { build: (qi, t) => qi.buildUpdateMany(t), reshape: (raw) => raw }),
906
+ delete: (args) => defer(() => {
928
907
  const a = requireWhere(args, 'delete');
929
- const t = { where: translateWhere(ctx, mm, a.where) };
930
- return lift(() => getQI()
931
- .delete(t)
932
- .then((r) => reshapeRow(ctx, mm, r)), { build: () => getQI().buildDelete(t), reshape: (raw) => reshapeRow(ctx, mm, raw) });
933
- },
934
- deleteMany: (args = {}) => {
908
+ return { where: translateWhere(ctx, mm, a.where) };
909
+ }, (qi, t) => qi.delete(t).then((r) => reshapeRow(ctx, mm, r)), { build: (qi, t) => qi.buildDelete(t), reshape: (raw) => reshapeRow(ctx, mm, raw) }),
910
+ deleteMany: (args = {}) => defer(() => {
935
911
  const a = args;
936
912
  const t = { where: translateWhere(ctx, mm, a.where ?? {}) };
937
913
  if (a.where === undefined)
938
914
  t.allowFullTableScan = true;
939
- return lift(() => getQI().deleteMany(t), {
940
- build: () => getQI().buildDeleteMany(t),
941
- reshape: (raw) => raw,
942
- });
943
- },
944
- upsert: (args) => {
915
+ return t;
916
+ }, (qi, t) => qi.deleteMany(t), { build: (qi, t) => qi.buildDeleteMany(t), reshape: (raw) => raw }),
917
+ upsert: (args) => defer(() => {
945
918
  const a = requireWhere(args, 'upsert');
946
- const t = {
919
+ return {
947
920
  where: translateWhere(ctx, mm, a.where),
948
921
  create: translateWriteData(ctx, mm, a.create),
949
922
  update: translateWriteData(ctx, mm, a.update),
950
923
  };
951
- return lift(() => getQI()
952
- .upsert(t)
953
- .then((r) => reshapeRow(ctx, mm, r)), { build: () => getQI().buildUpsert(t), reshape: (raw) => reshapeRow(ctx, mm, raw) });
954
- },
955
- count: (args = {}) => {
924
+ }, (qi, t) => qi.upsert(t).then((r) => reshapeRow(ctx, mm, r)), { build: (qi, t) => qi.buildUpsert(t), reshape: (raw) => reshapeRow(ctx, mm, raw) }),
925
+ count: (args = {}) => defer(() => {
956
926
  const t = {};
957
927
  if (args.where !== undefined)
958
928
  t.where = translateWhere(ctx, mm, args.where);
959
929
  if (typeof args.timeout === 'number')
960
930
  t.timeout = args.timeout;
961
- return lift(() => getQI().count(t), {
962
- build: () => getQI().buildCount(t),
963
- reshape: (raw) => raw,
964
- });
965
- },
966
- aggregate: (args) => {
967
- const t = translateAggregateArgs(ctx, mm, args, false);
968
- return lift(() => getQI()
969
- .aggregate(t)
970
- .then((r) => reshapeAggregate(ctx, mm, r)), { build: () => getQI().buildAggregate(t), reshape: (raw) => reshapeAggregate(ctx, mm, raw) });
971
- },
972
- groupBy: (args) => {
973
- const t = translateAggregateArgs(ctx, mm, args, true);
974
- return lift(() => getQI()
975
- .groupBy(t)
976
- .then((rows) => rows.map((r) => reshapeGroupRow(ctx, mm, r))), {
977
- build: () => getQI().buildGroupBy(t),
978
- reshape: (raw) => raw.map((r) => reshapeGroupRow(ctx, mm, r)),
979
- });
980
- },
931
+ return t;
932
+ }, (qi, t) => qi.count(t), { build: (qi, t) => qi.buildCount(t), reshape: (raw) => raw }),
933
+ aggregate: (args) => defer(() => translateAggregateArgs(ctx, mm, args, false), (qi, t) => qi.aggregate(t).then((r) => reshapeAggregate(ctx, mm, r)), { build: (qi, t) => qi.buildAggregate(t), reshape: (raw) => reshapeAggregate(ctx, mm, raw) }),
934
+ groupBy: (args) => defer(() => translateAggregateArgs(ctx, mm, args, true), (qi, t) => qi.groupBy(t).then((rows) => rows.map((r) => reshapeGroupRow(ctx, mm, r))), {
935
+ build: (qi, t) => qi.buildGroupBy(t),
936
+ reshape: (raw) => raw.map((r) => reshapeGroupRow(ctx, mm, r)),
937
+ }),
981
938
  };
982
939
  }
983
940
  function poolOf(db) {
@@ -18,10 +18,12 @@
18
18
  * 1. a composite primary key (length ≥ 2);
19
19
  * 2. each composite `uniqueColumns` entry (introspected composite UNIQUE
20
20
  * constraints);
21
- * 3. each composite UNIQUE index in `indexes` (`unique && !docPath`, the ONLY
22
- * composite-unique source a `defineSchema` code-first client has, since
23
- * `defineSchema` records single-column uniques in `uniqueColumns` and
24
- * composite uniques as declared unique indexes).
21
+ * 3. each composite UNIQUE index in `indexes` (`unique && !docPath &&
22
+ * !partial`, the ONLY composite-unique source a `defineSchema` code-first
23
+ * client has, since `defineSchema` records single-column uniques in
24
+ * `uniqueColumns` and composite uniques as declared unique indexes). A
25
+ * PARTIAL unique index is skipped: it only guarantees uniqueness over its
26
+ * predicate's rows, not table-wide, so it cannot address a single row.
25
27
  *
26
28
  * For every column set two lookup names are registered (both mapping to the same
27
29
  * ordered FIELD list): the underscore join of the camelCase FIELD names
Binary file
package/dist/schema.d.ts CHANGED
@@ -180,6 +180,16 @@ export interface IndexMetadata {
180
180
  columns: string[];
181
181
  unique: boolean;
182
182
  definition: string;
183
+ /**
184
+ * True when the index carries a top-level `WHERE` predicate (a Postgres
185
+ * PARTIAL index). A partial UNIQUE index only guarantees uniqueness over the
186
+ * rows matching its predicate, NOT over the whole table, so it must be
187
+ * EXCLUDED from compound-unique selector derivation (both the runtime
188
+ * `where` expansion and the generated `*WhereUnique` selector branches):
189
+ * addressing a row by it could match zero or many rows. Introspection sets
190
+ * it from the `indexdef`; absent / `false` means a full (table-wide) index.
191
+ */
192
+ partial?: boolean;
183
193
  /**
184
194
  * Set only for a PowDB doc-field expression index: the JSON path (string keys
185
195
  * and integer array indexes) into the single json document column named by
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "turbine-orm",
3
- "version": "0.41.0",
3
+ "version": "0.42.0",
4
4
  "description": "Postgres-native TypeScript ORM — runs on Neon, Vercel Postgres, Cloudflare, Supabase. Streaming cursors, typed errors, single-query nested relations. One dependency, no WASM engine",
5
5
  "type": "module",
6
6
  "exports": {