@atscript/db 0.1.150 → 0.1.152

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/index.d.cts CHANGED
@@ -1,11 +1,11 @@
1
- import { $ as TDbReferentialAction, $t as TDbSpaceOptions, A as TCrudOp, An as andFilters, At as TRowResolveOptions, B as TDbDefaultValue, Bn as TReadControls, Bt as TypedWithRelation, C as NavPropsOf, Cn as REL_FILTER_MAX_DEPTH, Ct as TIdentification, D as TCascadeResolver, Dn as TRelationFilterJunction, Dt as TPrimaryKeyChange, E as PrimaryKeyOf, En as TRelGuardState, Et as TMetadataOverrides, F as TDbActionProcessor, Fn as relationStaticFilter, Ft as TTouchManyOptions, G as TDbIndexField, Gn as TRowOrderKey, Gt as isGeoIndexableType, H as TDbFieldMeta, Hn as TGenericLogger, Ht as UniqueryControls, I as TDbActionTargetSummary, In as DbEncryption, It as TValueFormatterPair, J as TDbInsertIgnoreSlot, Jt as ALL_VIEW_CAPABILITIES, K as TDbIndexType, Kn as UniquSelect, Kt as isGeoPointType, L as TDbAvailableActions, Ln as TDbEncryptionOptions, Lt as TViewJsonType, M as TDbActionInfo, Mn as forEachResolvedRelation, Mt as TSyncColumnResult, N as TDbActionIntent, Nn as hasRelationOp, Nt as TTableOptionDiff, O as TCascadeTarget, On as TRelationFilterTable, Ot as TReferencingForeignKey, P as TDbActionLevel, Pn as isResolvedRelationFilter, Pt as TTableResolver, Q as TDbObjectKind, Qt as TAdapterFactory, R as TDbCollation, Rn as DocumentFieldMapper, Rt as TWriteOptions, S as FlatOf, Sn as resolveDesignType, St as TIdResolveOptions, T as OwnPropsOf, Tn as ResolvedRelationFilter, Tt as TMetaResponse, U as TDbForeignKey, Un as TExprAggregate, Ut as WithRelation, V as TDbDeleteResult, Vn as NoopLogger, Vt as Uniquery, W as TDbIndex, Wn as TFirstLast, Wt as TableMetadata, X as TDbInsertManyResult, Xt as TViewCapability, Y as TDbInsertManyIgnoreResult, Yt as BaseDbAdapter, Z as TDbInsertResult, Zt as DbSpace, _ as DbPatch, _n as IntegrityStrategy, _t as TExistingTableOption, a as isJsonValueField, an as AtscriptExprNode, at as TDbUpdateResult, b as FieldOpsFor, bn as AtscriptDbReadable, bt as TFkLookupTarget, c as resolveCalendarBuckets, cn as AtscriptQueryFieldRef, ct as TDbWriteCheckContext, d as AggregateFn, dn as TViewJoin, dt as TDeleteOptions, en as AtscriptDbView, et as TDbRelation, f as AggregateQuery, fn as TViewPlan, ft as TDerivedChangeReason, g as DbControls, gn as AtscriptDbTable, gt as TExistingForeignKey, h as AtscriptDbWritable, hn as translateQueryTree, ht as TExistingColumn, i as isBucketableField, in as aliasTargetOf, it as TDbUpdateOptions, j as TCrudPermissions, jn as containsRelationFilter, jt as TSearchIndexInfo, k as TColumnDiff, kn as TRelationStaticFilter, kt as TRelationInfo, l as AggregateControls, ln as AtscriptQueryNode, lt as TDbWriteGuard, mn as isFieldRef, mt as TEnsureTableOptions, n as TComputedSelect, nn as isAtscriptDbView, nt as TDbRemoveGuardContext, o as jsonValueAncestor, on as AtscriptOrderItem, ot as TDbWriteAction, p as AggregateResult, pn as evaluateExpr, pt as TDerivedColumn, q as TDbInsertIgnoreResult, qt as ALL_BUCKET_UNITS, r as TResolvedBucket, rn as isViewType, rt as TDbStorageType, s as normalizeComputedSelect, sn as AtscriptQueryComparison, st as TDbWriteCheck, t as TBucketFieldSource, tn as TViewColumnMapping, tt as TDbRemoveGuard, u as AggregateExpr, un as AtscriptRef, ut as TDbWriteGuardContext, v as DbQuery, vn as NativeIntegrity, vt as TFieldMeta, w as NullableOptional, wn as REL_FILTER_MAX_NODES, wt as TInsertOptions, x as FilterExpr, xn as DbResponse, xt as TIdDescriptor, y as DbRow, yn as TCascadePin, yt as TFkLookupResolver, z as TDbDefaultFn, zn as FieldMappingStrategy, zt as TWriteTableResolver } from "./buckets-24Ac4sIS.cjs";
1
+ import { $ as TDbReferentialAction, $t as TDbSpaceOptions, A as TCrudOp, An as andFilters, At as TRowResolveOptions, B as TDbDefaultValue, Bn as TReadControls, Bt as TypedWithRelation, C as NavPropsOf, Cn as REL_FILTER_MAX_DEPTH, Ct as TIdentification, D as TCascadeResolver, Dn as TRelationFilterJunction, Dt as TPrimaryKeyChange, E as PrimaryKeyOf, En as TRelGuardState, Et as TMetadataOverrides, F as TDbActionProcessor, Fn as relationStaticFilter, Ft as TTouchManyOptions, G as TDbIndexField, Gn as TRowOrderKey, Gt as isGeoIndexableType, H as TDbFieldMeta, Hn as TGenericLogger, Ht as UniqueryControls, I as TDbActionTargetSummary, In as DbEncryption, It as TValueFormatterPair, J as TDbInsertIgnoreSlot, Jt as ALL_VIEW_CAPABILITIES, K as TDbIndexType, Kn as UniquSelect, Kt as isGeoPointType, L as TDbAvailableActions, Ln as TDbEncryptionOptions, Lt as TViewJsonType, M as TDbActionInfo, Mn as forEachResolvedRelation, Mt as TSyncColumnResult, N as TDbActionIntent, Nn as hasRelationOp, Nt as TTableOptionDiff, O as TCascadeTarget, On as TRelationFilterTable, Ot as TReferencingForeignKey, P as TDbActionLevel, Pn as isResolvedRelationFilter, Pt as TTableResolver, Q as TDbObjectKind, Qt as TAdapterFactory, R as TDbCollation, Rn as DocumentFieldMapper, Rt as TWriteOptions, S as FlatOf, Sn as resolveDesignType, St as TIdResolveOptions, T as OwnPropsOf, Tn as ResolvedRelationFilter, Tt as TMetaResponse, U as TDbForeignKey, Un as TExprAggregate, Ut as WithRelation, V as TDbDeleteResult, Vn as NoopLogger, Vt as Uniquery, W as TDbIndex, Wn as TFirstLast, Wt as TableMetadata, X as TDbInsertManyResult, Xt as TViewCapability, Y as TDbInsertManyIgnoreResult, Yt as BaseDbAdapter, Z as TDbInsertResult, Zt as DbSpace, _ as DbPatch, _n as IntegrityStrategy, _t as TExistingTableOption, a as isJsonValueField, an as AtscriptExprNode, at as TDbUpdateResult, b as FieldOpsFor, bn as AtscriptDbReadable, bt as TFkLookupTarget, c as resolveCalendarBuckets, cn as AtscriptQueryFieldRef, ct as TDbWriteCheckContext, d as AggregateFn, dn as TViewJoin, dt as TDeleteOptions, en as AtscriptDbView, et as TDbRelation, f as AggregateQuery, fn as TViewPlan, ft as TDerivedChangeReason, g as DbControls, gn as AtscriptDbTable, gt as TExistingForeignKey, h as AtscriptDbWritable, hn as translateQueryTree, ht as TExistingColumn, i as isBucketableField, in as aliasTargetOf, it as TDbUpdateOptions, j as TCrudPermissions, jn as containsRelationFilter, jt as TSearchIndexInfo, k as TColumnDiff, kn as TRelationStaticFilter, kt as TRelationInfo, l as AggregateControls, ln as AtscriptQueryNode, lt as TDbWriteGuard, mn as isFieldRef, mt as TEnsureTableOptions, n as TComputedSelect, nn as isAtscriptDbView, nt as TDbRemoveGuardContext, o as jsonValueAncestor, on as AtscriptOrderItem, ot as TDbWriteAction, p as AggregateResult, pn as evaluateExpr, pt as TDerivedColumn, q as TDbInsertIgnoreResult, qt as ALL_BUCKET_UNITS, r as TResolvedBucket, rn as isViewType, rt as TDbStorageType, s as normalizeComputedSelect, sn as AtscriptQueryComparison, st as TDbWriteCheck, t as TBucketFieldSource, tn as TViewColumnMapping, tt as TDbRemoveGuard, u as AggregateExpr, un as AtscriptRef, ut as TDbWriteGuardContext, v as DbQuery, vn as NativeIntegrity, vt as TFieldMeta, w as NullableOptional, wn as REL_FILTER_MAX_NODES, wt as TInsertOptions, x as FilterExpr, xn as DbResponse, xt as TIdDescriptor, y as DbRow, yn as TCascadePin, yt as TFkLookupResolver, z as TDbDefaultFn, zn as FieldMappingStrategy, zt as TWriteTableResolver } from "./buckets-tzd8iIM2.cjs";
2
2
  import { _ as separateFieldOps, a as $mul, c as $update, d as TDbFieldOp, f as TFieldOps, g as separateCas, h as reconcileCas, i as $insert, l as $upsert, m as isDbFieldOp, n as $dec, o as $remove, p as getDbFieldOp, r as $inc, s as $replace, t as $cas, u as TDbCas } from "./ops-AqhV7s9o.cjs";
3
3
  import { a as isPlainObject, i as isEmptyObject, n as findAncestorInSet, o as selfOrAncestor, r as getPath, t as deletePath } from "./object-CtPYTIvy.cjs";
4
4
  import { c as CalendarBucketLabel, d as WeekStart, g as ALL_AGGREGATE_FNS, l as ComputedExpr, o as BucketExpr, s as BucketUnit, u as ResolvedBucket } from "./agg-42CSpGDR.cjs";
5
5
  import { _ as aggregateFailure, a as buildValidationContext, c as isNavRelation, d as getKeyProps, f as CasExhaustedError, g as aggregateExpressionsNotSupported, h as DbErrorCode, l as TArrayPatch, m as DbError, n as ValidatorMode, p as CasMismatchError, r as buildDbValidator, s as forceNavNonOptional, t as ValidationContext, u as TDbPatch, v as bucketTimeZoneUnavailable, y as isConflict } from "./validator-tBNvM1qc.cjs";
6
6
  import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.cjs";
7
- import { r as fkColumns, s as isColumnTypeChanged } from "./fk-diff-CCLiJ9sJ.cjs";
8
- import { i as tableNameOf } from "./relation-helpers-DjVJN2ng.cjs";
7
+ import { r as fkColumns, s as isColumnTypeChanged } from "./fk-diff-Bw26lwSc.cjs";
8
+ import { i as tableNameOf } from "./relation-helpers-DxIZKvnJ.cjs";
9
9
  import { AggregateQuery as AggregateQuery$1, FilterExpr as FilterExpr$1, FilterVisitor, RELATION_OPS, RelationOp, RelationOp as RelationOp$1, ResolvedRowOrderKey, ResolvedSelectExpr, Uniquery as Uniquery$1, computeInsights, isPrimitive, isRelationOp, walkFilter } from "@uniqu/core";
10
10
  import { AtscriptExprNode as AtscriptExprNode$1, TAtscriptAnnotatedType } from "@atscript/typescript/utils";
11
11
 
@@ -50,19 +50,13 @@ declare class RelationalFieldMapper extends FieldMappingStrategy {
50
50
  */
51
51
  private translateControls;
52
52
  /**
53
- * Flattens nested object fields into __-separated keys and
54
- * JSON-stringifies @db.json / array fields.
53
+ * Flattens nested objects into __-separated keys and JSON-stringifies
54
+ * @db.json / array fields — with `prepareCommon` folded in: primary-key
55
+ * values prepared (`adapter.prepareId`), top-level ignored fields (skipped
56
+ * by {@link writeFlattenedField}) and derived fields left out. `payload`
57
+ * itself is not modified.
55
58
  */
56
- private flattenPayload;
57
- /**
58
- * Classifies and writes a single field to the result object.
59
- * Recurses into nested objects that should be flattened.
60
- */
61
- private writeFlattenedField;
62
- /**
63
- * When a parent object is null/undefined, set all its flattened children to null.
64
- */
65
- private setFlattenedChildrenNull;
59
+ private flattenWritePayload;
66
60
  }
67
61
  //#endregion
68
62
  //#region src/strategies/application-integrity.d.ts
@@ -93,7 +87,12 @@ declare class ApplicationIntegrity extends IntegrityStrategy {
93
87
  * leave the parent behind). Any other filter → `undefined`.
94
88
  */
95
89
  cascadeBeforeDelete(filter: FilterExpr$1, tableName: string, meta: TableMetadata, cascadeResolver: TCascadeResolver, translateFilter: (f: FilterExpr$1) => FilterExpr$1, adapter: BaseDbAdapter): Promise<TCascadePin>;
96
- needsCascade(cascadeResolver: TCascadeResolver | undefined): boolean;
90
+ /**
91
+ * A resolver is set and — when `tableName` is given (since 0.1.151) — some
92
+ * table references `tableName` with an `onDelete` action: a delete from a
93
+ * table nothing references needs no cascade pass (nor its transaction).
94
+ */
95
+ needsCascade(cascadeResolver: TCascadeResolver | undefined, tableName?: string): boolean;
97
96
  private recordKey;
98
97
  /**
99
98
  * Builds a filter for child records whose FK matches the deleted parent's PK values.
package/dist/index.d.mts CHANGED
@@ -1,11 +1,11 @@
1
- import { $ as TDbReferentialAction, $t as TDbSpaceOptions, A as TCrudOp, An as andFilters, At as TRowResolveOptions, B as TDbDefaultValue, Bn as TReadControls, Bt as TypedWithRelation, C as NavPropsOf, Cn as REL_FILTER_MAX_DEPTH, Ct as TIdentification, D as TCascadeResolver, Dn as TRelationFilterJunction, Dt as TPrimaryKeyChange, E as PrimaryKeyOf, En as TRelGuardState, Et as TMetadataOverrides, F as TDbActionProcessor, Fn as relationStaticFilter, Ft as TTouchManyOptions, G as TDbIndexField, Gn as TRowOrderKey, Gt as isGeoIndexableType, H as TDbFieldMeta, Hn as TGenericLogger, Ht as UniqueryControls, I as TDbActionTargetSummary, In as DbEncryption, It as TValueFormatterPair, J as TDbInsertIgnoreSlot, Jt as ALL_VIEW_CAPABILITIES, K as TDbIndexType, Kn as UniquSelect, Kt as isGeoPointType, L as TDbAvailableActions, Ln as TDbEncryptionOptions, Lt as TViewJsonType, M as TDbActionInfo, Mn as forEachResolvedRelation, Mt as TSyncColumnResult, N as TDbActionIntent, Nn as hasRelationOp, Nt as TTableOptionDiff, O as TCascadeTarget, On as TRelationFilterTable, Ot as TReferencingForeignKey, P as TDbActionLevel, Pn as isResolvedRelationFilter, Pt as TTableResolver, Q as TDbObjectKind, Qt as TAdapterFactory, R as TDbCollation, Rn as DocumentFieldMapper, Rt as TWriteOptions, S as FlatOf, Sn as resolveDesignType, St as TIdResolveOptions, T as OwnPropsOf, Tn as ResolvedRelationFilter, Tt as TMetaResponse, U as TDbForeignKey, Un as TExprAggregate, Ut as WithRelation, V as TDbDeleteResult, Vn as NoopLogger, Vt as Uniquery, W as TDbIndex, Wn as TFirstLast, Wt as TableMetadata, X as TDbInsertManyResult, Xt as TViewCapability, Y as TDbInsertManyIgnoreResult, Yt as BaseDbAdapter, Z as TDbInsertResult, Zt as DbSpace, _ as DbPatch, _n as IntegrityStrategy, _t as TExistingTableOption, a as isJsonValueField, an as AtscriptExprNode, at as TDbUpdateResult, b as FieldOpsFor, bn as AtscriptDbReadable, bt as TFkLookupTarget, c as resolveCalendarBuckets, cn as AtscriptQueryFieldRef, ct as TDbWriteCheckContext, d as AggregateFn, dn as TViewJoin, dt as TDeleteOptions, en as AtscriptDbView, et as TDbRelation, f as AggregateQuery, fn as TViewPlan, ft as TDerivedChangeReason, g as DbControls, gn as AtscriptDbTable, gt as TExistingForeignKey, h as AtscriptDbWritable, hn as translateQueryTree, ht as TExistingColumn, i as isBucketableField, in as aliasTargetOf, it as TDbUpdateOptions, j as TCrudPermissions, jn as containsRelationFilter, jt as TSearchIndexInfo, k as TColumnDiff, kn as TRelationStaticFilter, kt as TRelationInfo, l as AggregateControls, ln as AtscriptQueryNode, lt as TDbWriteGuard, mn as isFieldRef, mt as TEnsureTableOptions, n as TComputedSelect, nn as isAtscriptDbView, nt as TDbRemoveGuardContext, o as jsonValueAncestor, on as AtscriptOrderItem, ot as TDbWriteAction, p as AggregateResult, pn as evaluateExpr, pt as TDerivedColumn, q as TDbInsertIgnoreResult, qt as ALL_BUCKET_UNITS, r as TResolvedBucket, rn as isViewType, rt as TDbStorageType, s as normalizeComputedSelect, sn as AtscriptQueryComparison, st as TDbWriteCheck, t as TBucketFieldSource, tn as TViewColumnMapping, tt as TDbRemoveGuard, u as AggregateExpr, un as AtscriptRef, ut as TDbWriteGuardContext, v as DbQuery, vn as NativeIntegrity, vt as TFieldMeta, w as NullableOptional, wn as REL_FILTER_MAX_NODES, wt as TInsertOptions, x as FilterExpr, xn as DbResponse, xt as TIdDescriptor, y as DbRow, yn as TCascadePin, yt as TFkLookupResolver, z as TDbDefaultFn, zn as FieldMappingStrategy, zt as TWriteTableResolver } from "./buckets-XOz0Bw5x.mjs";
1
+ import { $ as TDbReferentialAction, $t as TDbSpaceOptions, A as TCrudOp, An as andFilters, At as TRowResolveOptions, B as TDbDefaultValue, Bn as TReadControls, Bt as TypedWithRelation, C as NavPropsOf, Cn as REL_FILTER_MAX_DEPTH, Ct as TIdentification, D as TCascadeResolver, Dn as TRelationFilterJunction, Dt as TPrimaryKeyChange, E as PrimaryKeyOf, En as TRelGuardState, Et as TMetadataOverrides, F as TDbActionProcessor, Fn as relationStaticFilter, Ft as TTouchManyOptions, G as TDbIndexField, Gn as TRowOrderKey, Gt as isGeoIndexableType, H as TDbFieldMeta, Hn as TGenericLogger, Ht as UniqueryControls, I as TDbActionTargetSummary, In as DbEncryption, It as TValueFormatterPair, J as TDbInsertIgnoreSlot, Jt as ALL_VIEW_CAPABILITIES, K as TDbIndexType, Kn as UniquSelect, Kt as isGeoPointType, L as TDbAvailableActions, Ln as TDbEncryptionOptions, Lt as TViewJsonType, M as TDbActionInfo, Mn as forEachResolvedRelation, Mt as TSyncColumnResult, N as TDbActionIntent, Nn as hasRelationOp, Nt as TTableOptionDiff, O as TCascadeTarget, On as TRelationFilterTable, Ot as TReferencingForeignKey, P as TDbActionLevel, Pn as isResolvedRelationFilter, Pt as TTableResolver, Q as TDbObjectKind, Qt as TAdapterFactory, R as TDbCollation, Rn as DocumentFieldMapper, Rt as TWriteOptions, S as FlatOf, Sn as resolveDesignType, St as TIdResolveOptions, T as OwnPropsOf, Tn as ResolvedRelationFilter, Tt as TMetaResponse, U as TDbForeignKey, Un as TExprAggregate, Ut as WithRelation, V as TDbDeleteResult, Vn as NoopLogger, Vt as Uniquery, W as TDbIndex, Wn as TFirstLast, Wt as TableMetadata, X as TDbInsertManyResult, Xt as TViewCapability, Y as TDbInsertManyIgnoreResult, Yt as BaseDbAdapter, Z as TDbInsertResult, Zt as DbSpace, _ as DbPatch, _n as IntegrityStrategy, _t as TExistingTableOption, a as isJsonValueField, an as AtscriptExprNode, at as TDbUpdateResult, b as FieldOpsFor, bn as AtscriptDbReadable, bt as TFkLookupTarget, c as resolveCalendarBuckets, cn as AtscriptQueryFieldRef, ct as TDbWriteCheckContext, d as AggregateFn, dn as TViewJoin, dt as TDeleteOptions, en as AtscriptDbView, et as TDbRelation, f as AggregateQuery, fn as TViewPlan, ft as TDerivedChangeReason, g as DbControls, gn as AtscriptDbTable, gt as TExistingForeignKey, h as AtscriptDbWritable, hn as translateQueryTree, ht as TExistingColumn, i as isBucketableField, in as aliasTargetOf, it as TDbUpdateOptions, j as TCrudPermissions, jn as containsRelationFilter, jt as TSearchIndexInfo, k as TColumnDiff, kn as TRelationStaticFilter, kt as TRelationInfo, l as AggregateControls, ln as AtscriptQueryNode, lt as TDbWriteGuard, mn as isFieldRef, mt as TEnsureTableOptions, n as TComputedSelect, nn as isAtscriptDbView, nt as TDbRemoveGuardContext, o as jsonValueAncestor, on as AtscriptOrderItem, ot as TDbWriteAction, p as AggregateResult, pn as evaluateExpr, pt as TDerivedColumn, q as TDbInsertIgnoreResult, qt as ALL_BUCKET_UNITS, r as TResolvedBucket, rn as isViewType, rt as TDbStorageType, s as normalizeComputedSelect, sn as AtscriptQueryComparison, st as TDbWriteCheck, t as TBucketFieldSource, tn as TViewColumnMapping, tt as TDbRemoveGuard, u as AggregateExpr, un as AtscriptRef, ut as TDbWriteGuardContext, v as DbQuery, vn as NativeIntegrity, vt as TFieldMeta, w as NullableOptional, wn as REL_FILTER_MAX_NODES, wt as TInsertOptions, x as FilterExpr, xn as DbResponse, xt as TIdDescriptor, y as DbRow, yn as TCascadePin, yt as TFkLookupResolver, z as TDbDefaultFn, zn as FieldMappingStrategy, zt as TWriteTableResolver } from "./buckets-Cb0zmhDc.mjs";
2
2
  import { _ as separateFieldOps, a as $mul, c as $update, d as TDbFieldOp, f as TFieldOps, g as separateCas, h as reconcileCas, i as $insert, l as $upsert, m as isDbFieldOp, n as $dec, o as $remove, p as getDbFieldOp, r as $inc, s as $replace, t as $cas, u as TDbCas } from "./ops-AqhV7s9o.mjs";
3
3
  import { a as isPlainObject, i as isEmptyObject, n as findAncestorInSet, o as selfOrAncestor, r as getPath, t as deletePath } from "./object-CtPYTIvy.mjs";
4
4
  import { c as CalendarBucketLabel, d as WeekStart, g as ALL_AGGREGATE_FNS, l as ComputedExpr, o as BucketExpr, s as BucketUnit, u as ResolvedBucket } from "./agg-Wo-smrYV.mjs";
5
5
  import { _ as aggregateFailure, a as buildValidationContext, c as isNavRelation, d as getKeyProps, f as CasExhaustedError, g as aggregateExpressionsNotSupported, h as DbErrorCode, l as TArrayPatch, m as DbError, n as ValidatorMode, p as CasMismatchError, r as buildDbValidator, s as forceNavNonOptional, t as ValidationContext, u as TDbPatch, v as bucketTimeZoneUnavailable, y as isConflict } from "./validator-tBNvM1qc.mjs";
6
6
  import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.mjs";
7
- import { r as fkColumns, s as isColumnTypeChanged } from "./fk-diff-R8eU7gq-.mjs";
8
- import { i as tableNameOf } from "./relation-helpers-H7UEVkHs.mjs";
7
+ import { r as fkColumns, s as isColumnTypeChanged } from "./fk-diff-lcAnwvm9.mjs";
8
+ import { i as tableNameOf } from "./relation-helpers-Bdo4NHPC.mjs";
9
9
  import { AtscriptExprNode as AtscriptExprNode$1, TAtscriptAnnotatedType } from "@atscript/typescript/utils";
10
10
  import { AggregateQuery as AggregateQuery$1, FilterExpr as FilterExpr$1, FilterVisitor, RELATION_OPS, RelationOp, RelationOp as RelationOp$1, ResolvedRowOrderKey, ResolvedSelectExpr, Uniquery as Uniquery$1, computeInsights, isPrimitive, isRelationOp, walkFilter } from "@uniqu/core";
11
11
 
@@ -50,19 +50,13 @@ declare class RelationalFieldMapper extends FieldMappingStrategy {
50
50
  */
51
51
  private translateControls;
52
52
  /**
53
- * Flattens nested object fields into __-separated keys and
54
- * JSON-stringifies @db.json / array fields.
53
+ * Flattens nested objects into __-separated keys and JSON-stringifies
54
+ * @db.json / array fields — with `prepareCommon` folded in: primary-key
55
+ * values prepared (`adapter.prepareId`), top-level ignored fields (skipped
56
+ * by {@link writeFlattenedField}) and derived fields left out. `payload`
57
+ * itself is not modified.
55
58
  */
56
- private flattenPayload;
57
- /**
58
- * Classifies and writes a single field to the result object.
59
- * Recurses into nested objects that should be flattened.
60
- */
61
- private writeFlattenedField;
62
- /**
63
- * When a parent object is null/undefined, set all its flattened children to null.
64
- */
65
- private setFlattenedChildrenNull;
59
+ private flattenWritePayload;
66
60
  }
67
61
  //#endregion
68
62
  //#region src/strategies/application-integrity.d.ts
@@ -93,7 +87,12 @@ declare class ApplicationIntegrity extends IntegrityStrategy {
93
87
  * leave the parent behind). Any other filter → `undefined`.
94
88
  */
95
89
  cascadeBeforeDelete(filter: FilterExpr$1, tableName: string, meta: TableMetadata, cascadeResolver: TCascadeResolver, translateFilter: (f: FilterExpr$1) => FilterExpr$1, adapter: BaseDbAdapter): Promise<TCascadePin>;
96
- needsCascade(cascadeResolver: TCascadeResolver | undefined): boolean;
90
+ /**
91
+ * A resolver is set and — when `tableName` is given (since 0.1.151) — some
92
+ * table references `tableName` with an `onDelete` action: a delete from a
93
+ * table nothing references needs no cascade pass (nor its transaction).
94
+ */
95
+ needsCascade(cascadeResolver: TCascadeResolver | undefined, tableName?: string): boolean;
97
96
  private recordKey;
98
97
  /**
99
98
  * Builds a filter for child records whose FK matches the deleted parent's PK values.
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { a as aggregateExpressionsNotSupported, c as isConflict, l as spaceClosedError, n as CasMismatchError, o as aggregateFailure, r as DbError, s as bucketTimeZoneUnavailable, t as CasExhaustedError } from "./db-error-Az85UhTX.mjs";
2
- import { $ as DocumentFieldMapper, A as resolveDesignType, B as checkHavingKeys, C as ALL_BUCKET_UNITS, D as IntegrityStrategy, E as createFailureCollector, F as ENCRYPTED_REASON, G as guardFilter, H as collectQueryPaths, I as acceptedOperatorsHint, J as guardQuery, K as guardPath, L as assertGeoPoint, M as searchIndexNotFoundMessage, N as vectorIndexNotFoundMessage, O as NativeIntegrity, P as ADAPTER_FILTER_REASON, Q as RelationalFieldMapper, R as bucketSourceVerdict, S as ApplicationIntegrity, T as BaseDbAdapter, U as groupSourceVerdict, V as classifyQueryPath, W as guardAggregate, X as sortFieldNames, Y as narrowerFilterOps, Z as unsupportedOperatorMessage, _ as isViewType, _t as NoopLogger, at as describeFulltext, b as assertNoVersionWrites, ct as TableMetadata, dt as aliasTargetOf, et as FieldMappingStrategy, f as fkColumns, ft as isBucketableField, g as isAtscriptDbView, gt as resolveCalendarBuckets, h as AtscriptDbView, ht as normalizeComputedSelect, it as defaultFulltextIndex, j as geoIndexNotFoundMessage, k as AtscriptDbReadable, lt as isGeoIndexableType, mt as jsonValueAncestor, n as isColumnTypeChanged, nt as numericOperandProblem, ot as searchTermInteger, pt as isJsonValueField, q as guardPaths, rt as INTEGER_REGEX_OP, st as splitFulltextFields, tt as UniquSelect, ut as isGeoPointType, v as AtscriptDbTable, w as ALL_VIEW_CAPABILITIES, x as decomposePatch, y as isVersionExemptPatch, z as canFilterLeaf } from "./column-diff-CKLDWBbo.mjs";
2
+ import { $ as DocumentFieldMapper, A as resolveDesignType, B as checkHavingKeys, C as ALL_BUCKET_UNITS, D as IntegrityStrategy, E as createFailureCollector, F as ENCRYPTED_REASON, G as guardFilter, H as collectQueryPaths, I as acceptedOperatorsHint, J as guardQuery, K as guardPath, L as assertGeoPoint, M as searchIndexNotFoundMessage, N as vectorIndexNotFoundMessage, O as NativeIntegrity, P as ADAPTER_FILTER_REASON, Q as RelationalFieldMapper, R as bucketSourceVerdict, S as ApplicationIntegrity, T as BaseDbAdapter, U as groupSourceVerdict, V as classifyQueryPath, W as guardAggregate, X as sortFieldNames, Y as narrowerFilterOps, Z as unsupportedOperatorMessage, _ as isViewType, _t as NoopLogger, at as describeFulltext, b as assertNoVersionWrites, ct as TableMetadata, dt as aliasTargetOf, et as FieldMappingStrategy, f as fkColumns, ft as isBucketableField, g as isAtscriptDbView, gt as resolveCalendarBuckets, h as AtscriptDbView, ht as normalizeComputedSelect, it as defaultFulltextIndex, j as geoIndexNotFoundMessage, k as AtscriptDbReadable, lt as isGeoIndexableType, mt as jsonValueAncestor, n as isColumnTypeChanged, nt as numericOperandProblem, ot as searchTermInteger, pt as isJsonValueField, q as guardPaths, rt as INTEGER_REGEX_OP, st as splitFulltextFields, tt as UniquSelect, ut as isGeoPointType, v as AtscriptDbTable, w as ALL_VIEW_CAPABILITIES, x as decomposePatch, y as isVersionExemptPatch, z as canFilterLeaf } from "./column-diff-CnyLjbQ8.mjs";
3
3
  import { n as ALL_AGGREGATE_FNS } from "./aggregate-fns-CyaZyb9I.mjs";
4
- import { A as isResolvedRelationFilter, C as REL_FILTER_MAX_NODES, E as containsRelationFilter, G as isFieldRef, K as translateQueryTree, N as relationStaticFilter, O as forEachResolvedRelation, S as REL_FILTER_MAX_DEPTH, T as andFilters, V as tableNameOf, W as evaluateExpr, k as hasRelationOp, w as ResolvedRelationFilter, x as uniqueKeyTuple } from "./nested-writer-CUBoq1ZO.mjs";
4
+ import { A as hasRelationOp, C as REL_FILTER_MAX_DEPTH, D as containsRelationFilter, E as andFilters, G as evaluateExpr, H as tableNameOf, K as isFieldRef, P as relationStaticFilter, S as uniqueKeyTuple, T as ResolvedRelationFilter, j as isResolvedRelationFilter, k as forEachResolvedRelation, q as translateQueryTree, w as REL_FILTER_MAX_NODES } from "./nested-writer-cvbVAkNo.mjs";
5
5
  import { i as DB_ENTITY_ANNOTATIONS, n as searchMemberKind } from "./search-fields-DBIZKwyb.mjs";
6
6
  import { a as isPlainObject, i as isEmptyObject, n as findAncestorInSet, o as selfOrAncestor, r as getPath, t as deletePath } from "./object-TkiJQ-Dp.mjs";
7
7
  import { t as DEFAULT_DB_SPACE } from "./consts-C_-5_pFq.mjs";
@@ -235,6 +235,12 @@ var DbSpace = class {
235
235
  _readables = /* @__PURE__ */ new WeakMap();
236
236
  /** All tables created in this space — used for reverse FK lookup during cascade. */
237
237
  _allTables = /* @__PURE__ */ new Set();
238
+ /**
239
+ * Per-table-name memo of {@link _getCascadeTargets} / {@link _getFkLookupTarget}
240
+ * (since 0.1.151) — both scan every table; cleared whenever a table is added.
241
+ */
242
+ _cascadeTargets = /* @__PURE__ */ new Map();
243
+ _fkLookupTargets = /* @__PURE__ */ new Map();
238
244
  /** Every table / view handle the space created — flagged closed on {@link close}. */
239
245
  _handles = /* @__PURE__ */ new Set();
240
246
  /** Lazily created adapter for administrative ops (drop table/view) that don't need a registered readable. */
@@ -284,6 +290,8 @@ var DbSpace = class {
284
290
  return resolved instanceof AtscriptDbTable ? resolved : void 0;
285
291
  });
286
292
  this._allTables.add(readable);
293
+ this._cascadeTargets.clear();
294
+ this._fkLookupTargets.clear();
287
295
  readable.setCascadeResolver((tableName) => this._getCascadeTargets(tableName));
288
296
  readable.setFkLookupResolver((tableName) => this._getFkLookupTarget(tableName));
289
297
  readable.setEncryption(this._encryption);
@@ -412,6 +420,14 @@ var DbSpace = class {
412
420
  * Accesses `table.foreignKeys` which triggers `_flatten()` if needed.
413
421
  */
414
422
  _getCascadeTargets(tableName) {
423
+ let targets = this._cascadeTargets.get(tableName);
424
+ if (targets === void 0) {
425
+ targets = this._scanCascadeTargets(tableName);
426
+ this._cascadeTargets.set(tableName, targets);
427
+ }
428
+ return targets;
429
+ }
430
+ _scanCascadeTargets(tableName) {
415
431
  const targets = [];
416
432
  for (const table of this._allTables) for (const fk of table.foreignKeys.values()) if (fk.targetTable === tableName && fk.onDelete) targets.push({
417
433
  fk,
@@ -427,6 +443,12 @@ var DbSpace = class {
427
443
  * Searches all registered tables for one with the matching table name.
428
444
  */
429
445
  _getFkLookupTarget(tableName) {
446
+ if (this._fkLookupTargets.has(tableName)) return this._fkLookupTargets.get(tableName);
447
+ const target = this._scanFkLookupTarget(tableName);
448
+ this._fkLookupTargets.set(tableName, target);
449
+ return target;
450
+ }
451
+ _scanFkLookupTarget(tableName) {
430
452
  for (const table of this._allTables) if (table.tableName === tableName) return { count: (filter) => table.count({ filter }) };
431
453
  }
432
454
  };
@@ -676,6 +676,68 @@ function uniqueKeyTuple(row, fields) {
676
676
  }
677
677
  return JSON.stringify(parts);
678
678
  }
679
+ /**
680
+ * For every equality `filters[i]`, the FIRST row of `rows` that
681
+ * {@link rowMatchesKey} matches it — `rows.find(…)` per filter, answered from
682
+ * one hash index per distinct filter key set instead of a scan per filter.
683
+ * A filter holding a `null` / `undefined` value (where {@link sameKey}
684
+ * differs from string identity) is matched by the scan. `undefined` = no row.
685
+ * @since 0.1.151
686
+ */
687
+ function findRowsByKeys(rows, filters) {
688
+ const out = Array.from({ length: filters.length });
689
+ if (rows.length * filters.length <= 64) {
690
+ for (let i = 0; i < filters.length; i++) out[i] = rows.find((r) => rowMatchesKey(r, filters[i]));
691
+ return out;
692
+ }
693
+ const indexes = /* @__PURE__ */ new Map();
694
+ for (let i = 0; i < filters.length; i++) {
695
+ const filter = filters[i];
696
+ const fields = Object.keys(filter);
697
+ let indexable = fields.length > 0;
698
+ for (const field of fields) {
699
+ const value = filter[field];
700
+ if (value === null || value === void 0) {
701
+ indexable = false;
702
+ break;
703
+ }
704
+ }
705
+ if (!indexable) {
706
+ out[i] = rows.find((r) => rowMatchesKey(r, filter));
707
+ continue;
708
+ }
709
+ const signature = JSON.stringify(fields);
710
+ let index = indexes.get(signature);
711
+ if (index === void 0) {
712
+ index = /* @__PURE__ */ new Map();
713
+ for (const row of rows) {
714
+ const key = keyTupleString(row, fields);
715
+ if (key !== void 0 && !index.has(key)) index.set(key, row);
716
+ }
717
+ indexes.set(signature, index);
718
+ }
719
+ out[i] = index.get(keyTupleString(filter, fields, true));
720
+ }
721
+ return out;
722
+ }
723
+ /**
724
+ * The {@link keyString} tuple of `fields` of `source` (dot-paths read nested
725
+ * for a row, plain keys for a filter); `undefined` when a component is null /
726
+ * missing (it can only equal a null filter value, which is never indexed).
727
+ */
728
+ function keyTupleString(source, fields, plain = false) {
729
+ if (fields.length === 1) {
730
+ const value = plain ? source[fields[0]] : require_object.getPath(source, fields[0]);
731
+ return value === null || value === void 0 ? void 0 : keyString(value);
732
+ }
733
+ const parts = [];
734
+ for (const field of fields) {
735
+ const value = plain ? source[field] : require_object.getPath(source, field);
736
+ if (value === null || value === void 0) return void 0;
737
+ parts.push(keyString(value));
738
+ }
739
+ return JSON.stringify(parts);
740
+ }
679
741
  //#endregion
680
742
  //#region src/table/error-utils.ts
681
743
  /**
@@ -944,10 +1006,11 @@ async function batchReplaceNestedFrom(host, originals, maxDepth, depth, plan) {
944
1006
  if (relation.direction !== "from" || !hasNavValue(originals, navField)) continue;
945
1007
  const target = fromWriteTarget(host, relation, navField, plan, maxDepth, depth);
946
1008
  if (!target) continue;
947
- for (const original of originals) {
948
- const children = original[navField];
1009
+ const replacing = originals.filter((original) => Array.isArray(original[navField]) && original[parentPKField] !== void 0);
1010
+ const current = await currentChildrenOf(target, replacing.map((original) => original[parentPKField]), replacing.flatMap((original) => original[navField]));
1011
+ for (const original of replacing) {
949
1012
  const parentPK = original[parentPKField];
950
- if (Array.isArray(children) && parentPK !== void 0) await fromReplace(target, children, parentPK);
1013
+ await fromReplace(target, original[navField], parentPK, current?.get(keyString(parentPK)));
951
1014
  }
952
1015
  }
953
1016
  }
@@ -1105,12 +1168,24 @@ async function batchPatchNestedFrom(host, originals, maxDepth, depth, plan) {
1105
1168
  const target = fromWriteTarget(host, relation, navField, plan, maxDepth, depth);
1106
1169
  if (!target) continue;
1107
1170
  const { targetTable, fkField, childPKs } = target;
1171
+ const patching = [];
1108
1172
  for (const original of originals) {
1109
1173
  const navValue = original[navField];
1110
1174
  const parentPK = original[parentPKField];
1111
1175
  if (navValue === void 0 || navValue === null || parentPK === void 0) continue;
1112
- const ops = extractNavPatchOps(navValue);
1113
- if (ops.replace) await fromReplace(target, ops.replace, parentPK);
1176
+ patching.push({
1177
+ parentPK,
1178
+ ops: extractNavPatchOps(navValue)
1179
+ });
1180
+ }
1181
+ const current = new Set(patching.map(({ parentPK }) => keyString(parentPK))).size < patching.length ? void 0 : await currentChildrenOf(target, patching.filter(({ ops }) => ops.replace).map(({ parentPK }) => parentPK), patching.flatMap(({ ops }) => [
1182
+ ...ops.replace ?? [],
1183
+ ...ops.update ?? [],
1184
+ ...ops.upsert ?? [],
1185
+ ...ops.insert ?? []
1186
+ ]));
1187
+ for (const { parentPK, ops } of patching) {
1188
+ if (ops.replace) await fromReplace(target, ops.replace, parentPK, current?.get(keyString(parentPK)));
1114
1189
  if (ops.remove && ops.remove.length > 0) {
1115
1190
  const removeFilters = ops.remove.map((child) => {
1116
1191
  const rec = child;
@@ -1395,7 +1470,7 @@ async function fromUpdate(target, items) {
1395
1470
  * FROM $replace helper: delete the parent's orphans, replace its keyed
1396
1471
  * children, insert the rest.
1397
1472
  */
1398
- async function fromReplace(target, children, parentPK) {
1473
+ async function fromReplace(target, children, parentPK, current) {
1399
1474
  const { targetTable, navField, fkField, childPKs, maxDepth, depth } = target;
1400
1475
  const toReplace = [];
1401
1476
  const toInsert = [];
@@ -1410,7 +1485,7 @@ async function fromReplace(target, children, parentPK) {
1410
1485
  toReplace.push(childData);
1411
1486
  } else toInsert.push(childData);
1412
1487
  }
1413
- const existing = await targetTable.findMany({
1488
+ const existing = current ?? await targetTable.findMany({
1414
1489
  filter: { [fkField]: parentPK },
1415
1490
  controls: childPKs.length > 0 ? { $select: [...childPKs] } : {}
1416
1491
  });
@@ -1427,6 +1502,47 @@ async function fromReplace(target, children, parentPK) {
1427
1502
  }));
1428
1503
  }
1429
1504
  /**
1505
+ * The current (keyed) children of several parents in ONE read — what
1506
+ * {@link fromReplace} reads per parent, grouped by parent key (since 0.1.151).
1507
+ * The writes stay per parent and in order; only this read is shared, which is
1508
+ * safe because each parent's writes are pinned to its own children (the
1509
+ * foreign key is filtered on and never SET) and no child entry of the batch
1510
+ * (`entries`) carries nested navigation data (no nested write of a child can
1511
+ * reach another parent's children); a row removed meanwhile by a delete cascade is a no-op orphan
1512
+ * delete or the same `CONFLICT` either way. Done only where grouping cannot
1513
+ * differ from the store's own matching: two or more DISTINCT integer parent
1514
+ * keys (no collation can merge two of them) and a child table with a primary
1515
+ * key; a fetched row that maps to no parent key (a representation the
1516
+ * grouping does not know) also falls back. `undefined` → read per parent.
1517
+ */
1518
+ async function currentChildrenOf(target, parentPKs, entries) {
1519
+ const { targetTable, fkField, childPKs } = target;
1520
+ if (parentPKs.length < 2 || childPKs.length === 0) return void 0;
1521
+ const childNav = targetTable.getMetadata().navFields;
1522
+ if (childNav.size > 0) for (const entry of entries) for (const navField of childNav) {
1523
+ const value = entry?.[navField];
1524
+ if (value !== void 0 && value !== null) return void 0;
1525
+ }
1526
+ const groups = /* @__PURE__ */ new Map();
1527
+ for (const pk of parentPKs) {
1528
+ if (!(typeof pk === "number" && Number.isInteger(pk)) && typeof pk !== "bigint") return;
1529
+ const key = keyString(pk);
1530
+ if (groups.has(key)) return void 0;
1531
+ groups.set(key, []);
1532
+ }
1533
+ const rows = await targetTable.findMany({
1534
+ filter: { [fkField]: { $in: parentPKs } },
1535
+ controls: { $select: [...new Set([...childPKs, fkField])] }
1536
+ });
1537
+ for (const row of rows) {
1538
+ const value = row[fkField];
1539
+ const group = value === null || value === void 0 ? void 0 : groups.get(keyString(value));
1540
+ if (!group) return void 0;
1541
+ group.push(row);
1542
+ }
1543
+ return groups;
1544
+ }
1545
+ /**
1430
1546
  * Links `targets` to the parent: keyed entries by their key, unkeyed ones
1431
1547
  * inserted first — then one junction insert.
1432
1548
  */
@@ -1621,6 +1737,12 @@ Object.defineProperty(exports, "findRemoteFK", {
1621
1737
  return findRemoteFK;
1622
1738
  }
1623
1739
  });
1740
+ Object.defineProperty(exports, "findRowsByKeys", {
1741
+ enumerable: true,
1742
+ get: function() {
1743
+ return findRowsByKeys;
1744
+ }
1745
+ });
1624
1746
  Object.defineProperty(exports, "forEachResolvedRelation", {
1625
1747
  enumerable: true,
1626
1748
  get: function() {
@@ -676,6 +676,68 @@ function uniqueKeyTuple(row, fields) {
676
676
  }
677
677
  return JSON.stringify(parts);
678
678
  }
679
+ /**
680
+ * For every equality `filters[i]`, the FIRST row of `rows` that
681
+ * {@link rowMatchesKey} matches it — `rows.find(…)` per filter, answered from
682
+ * one hash index per distinct filter key set instead of a scan per filter.
683
+ * A filter holding a `null` / `undefined` value (where {@link sameKey}
684
+ * differs from string identity) is matched by the scan. `undefined` = no row.
685
+ * @since 0.1.151
686
+ */
687
+ function findRowsByKeys(rows, filters) {
688
+ const out = Array.from({ length: filters.length });
689
+ if (rows.length * filters.length <= 64) {
690
+ for (let i = 0; i < filters.length; i++) out[i] = rows.find((r) => rowMatchesKey(r, filters[i]));
691
+ return out;
692
+ }
693
+ const indexes = /* @__PURE__ */ new Map();
694
+ for (let i = 0; i < filters.length; i++) {
695
+ const filter = filters[i];
696
+ const fields = Object.keys(filter);
697
+ let indexable = fields.length > 0;
698
+ for (const field of fields) {
699
+ const value = filter[field];
700
+ if (value === null || value === void 0) {
701
+ indexable = false;
702
+ break;
703
+ }
704
+ }
705
+ if (!indexable) {
706
+ out[i] = rows.find((r) => rowMatchesKey(r, filter));
707
+ continue;
708
+ }
709
+ const signature = JSON.stringify(fields);
710
+ let index = indexes.get(signature);
711
+ if (index === void 0) {
712
+ index = /* @__PURE__ */ new Map();
713
+ for (const row of rows) {
714
+ const key = keyTupleString(row, fields);
715
+ if (key !== void 0 && !index.has(key)) index.set(key, row);
716
+ }
717
+ indexes.set(signature, index);
718
+ }
719
+ out[i] = index.get(keyTupleString(filter, fields, true));
720
+ }
721
+ return out;
722
+ }
723
+ /**
724
+ * The {@link keyString} tuple of `fields` of `source` (dot-paths read nested
725
+ * for a row, plain keys for a filter); `undefined` when a component is null /
726
+ * missing (it can only equal a null filter value, which is never indexed).
727
+ */
728
+ function keyTupleString(source, fields, plain = false) {
729
+ if (fields.length === 1) {
730
+ const value = plain ? source[fields[0]] : getPath(source, fields[0]);
731
+ return value === null || value === void 0 ? void 0 : keyString(value);
732
+ }
733
+ const parts = [];
734
+ for (const field of fields) {
735
+ const value = plain ? source[field] : getPath(source, field);
736
+ if (value === null || value === void 0) return void 0;
737
+ parts.push(keyString(value));
738
+ }
739
+ return JSON.stringify(parts);
740
+ }
679
741
  //#endregion
680
742
  //#region src/table/error-utils.ts
681
743
  /**
@@ -944,10 +1006,11 @@ async function batchReplaceNestedFrom(host, originals, maxDepth, depth, plan) {
944
1006
  if (relation.direction !== "from" || !hasNavValue(originals, navField)) continue;
945
1007
  const target = fromWriteTarget(host, relation, navField, plan, maxDepth, depth);
946
1008
  if (!target) continue;
947
- for (const original of originals) {
948
- const children = original[navField];
1009
+ const replacing = originals.filter((original) => Array.isArray(original[navField]) && original[parentPKField] !== void 0);
1010
+ const current = await currentChildrenOf(target, replacing.map((original) => original[parentPKField]), replacing.flatMap((original) => original[navField]));
1011
+ for (const original of replacing) {
949
1012
  const parentPK = original[parentPKField];
950
- if (Array.isArray(children) && parentPK !== void 0) await fromReplace(target, children, parentPK);
1013
+ await fromReplace(target, original[navField], parentPK, current?.get(keyString(parentPK)));
951
1014
  }
952
1015
  }
953
1016
  }
@@ -1105,12 +1168,24 @@ async function batchPatchNestedFrom(host, originals, maxDepth, depth, plan) {
1105
1168
  const target = fromWriteTarget(host, relation, navField, plan, maxDepth, depth);
1106
1169
  if (!target) continue;
1107
1170
  const { targetTable, fkField, childPKs } = target;
1171
+ const patching = [];
1108
1172
  for (const original of originals) {
1109
1173
  const navValue = original[navField];
1110
1174
  const parentPK = original[parentPKField];
1111
1175
  if (navValue === void 0 || navValue === null || parentPK === void 0) continue;
1112
- const ops = extractNavPatchOps(navValue);
1113
- if (ops.replace) await fromReplace(target, ops.replace, parentPK);
1176
+ patching.push({
1177
+ parentPK,
1178
+ ops: extractNavPatchOps(navValue)
1179
+ });
1180
+ }
1181
+ const current = new Set(patching.map(({ parentPK }) => keyString(parentPK))).size < patching.length ? void 0 : await currentChildrenOf(target, patching.filter(({ ops }) => ops.replace).map(({ parentPK }) => parentPK), patching.flatMap(({ ops }) => [
1182
+ ...ops.replace ?? [],
1183
+ ...ops.update ?? [],
1184
+ ...ops.upsert ?? [],
1185
+ ...ops.insert ?? []
1186
+ ]));
1187
+ for (const { parentPK, ops } of patching) {
1188
+ if (ops.replace) await fromReplace(target, ops.replace, parentPK, current?.get(keyString(parentPK)));
1114
1189
  if (ops.remove && ops.remove.length > 0) {
1115
1190
  const removeFilters = ops.remove.map((child) => {
1116
1191
  const rec = child;
@@ -1395,7 +1470,7 @@ async function fromUpdate(target, items) {
1395
1470
  * FROM $replace helper: delete the parent's orphans, replace its keyed
1396
1471
  * children, insert the rest.
1397
1472
  */
1398
- async function fromReplace(target, children, parentPK) {
1473
+ async function fromReplace(target, children, parentPK, current) {
1399
1474
  const { targetTable, navField, fkField, childPKs, maxDepth, depth } = target;
1400
1475
  const toReplace = [];
1401
1476
  const toInsert = [];
@@ -1410,7 +1485,7 @@ async function fromReplace(target, children, parentPK) {
1410
1485
  toReplace.push(childData);
1411
1486
  } else toInsert.push(childData);
1412
1487
  }
1413
- const existing = await targetTable.findMany({
1488
+ const existing = current ?? await targetTable.findMany({
1414
1489
  filter: { [fkField]: parentPK },
1415
1490
  controls: childPKs.length > 0 ? { $select: [...childPKs] } : {}
1416
1491
  });
@@ -1427,6 +1502,47 @@ async function fromReplace(target, children, parentPK) {
1427
1502
  }));
1428
1503
  }
1429
1504
  /**
1505
+ * The current (keyed) children of several parents in ONE read — what
1506
+ * {@link fromReplace} reads per parent, grouped by parent key (since 0.1.151).
1507
+ * The writes stay per parent and in order; only this read is shared, which is
1508
+ * safe because each parent's writes are pinned to its own children (the
1509
+ * foreign key is filtered on and never SET) and no child entry of the batch
1510
+ * (`entries`) carries nested navigation data (no nested write of a child can
1511
+ * reach another parent's children); a row removed meanwhile by a delete cascade is a no-op orphan
1512
+ * delete or the same `CONFLICT` either way. Done only where grouping cannot
1513
+ * differ from the store's own matching: two or more DISTINCT integer parent
1514
+ * keys (no collation can merge two of them) and a child table with a primary
1515
+ * key; a fetched row that maps to no parent key (a representation the
1516
+ * grouping does not know) also falls back. `undefined` → read per parent.
1517
+ */
1518
+ async function currentChildrenOf(target, parentPKs, entries) {
1519
+ const { targetTable, fkField, childPKs } = target;
1520
+ if (parentPKs.length < 2 || childPKs.length === 0) return void 0;
1521
+ const childNav = targetTable.getMetadata().navFields;
1522
+ if (childNav.size > 0) for (const entry of entries) for (const navField of childNav) {
1523
+ const value = entry?.[navField];
1524
+ if (value !== void 0 && value !== null) return void 0;
1525
+ }
1526
+ const groups = /* @__PURE__ */ new Map();
1527
+ for (const pk of parentPKs) {
1528
+ if (!(typeof pk === "number" && Number.isInteger(pk)) && typeof pk !== "bigint") return;
1529
+ const key = keyString(pk);
1530
+ if (groups.has(key)) return void 0;
1531
+ groups.set(key, []);
1532
+ }
1533
+ const rows = await targetTable.findMany({
1534
+ filter: { [fkField]: { $in: parentPKs } },
1535
+ controls: { $select: [...new Set([...childPKs, fkField])] }
1536
+ });
1537
+ for (const row of rows) {
1538
+ const value = row[fkField];
1539
+ const group = value === null || value === void 0 ? void 0 : groups.get(keyString(value));
1540
+ if (!group) return void 0;
1541
+ group.push(row);
1542
+ }
1543
+ return groups;
1544
+ }
1545
+ /**
1430
1546
  * Links `targets` to the parent: keyed entries by their key, unkeyed ones
1431
1547
  * inserted first — then one junction insert.
1432
1548
  */
@@ -1471,4 +1587,4 @@ async function viaReplace(via, targets, parentPK, maxDepth, depth) {
1471
1587
  await viaLinkTargets(via, targets, parentPK, maxDepth, depth);
1472
1588
  }
1473
1589
  //#endregion
1474
- export { isResolvedRelationFilter as A, slicePerGroup as B, REL_FILTER_MAX_NODES as C, createRelationFilterHost as D, containsRelationFilter as E, compositeKey as F, isFieldRef as G, computeOf as H, findFKEntryForRelation as I, walkViewExpr as J, translateQueryTree as K, findFKForRelation as L, relGuardState as M, relationStaticFilter as N, forEachResolvedRelation as O, resolveRelationFilterTree as P, findRemoteFK as R, REL_FILTER_MAX_DEPTH as S, andFilters as T, computedOperands as U, tableNameOf as V, evaluateExpr as W, remapDeleteFkViolation as _, batchPatchNestedFrom as a, sameKey as b, batchReplaceNestedFrom as c, checkDepthOverflow as d, planNestedFromVia as f, enrichFkViolation as g, validateBatch as h, batchInsertNestedVia as i, noteRelationFilter as j, hasRelationOp as k, batchReplaceNestedTo as l, preValidateNestedFrom as m, batchInsertNestedFrom as n, batchPatchNestedTo as o, planPatchNestedTo as p, viewExprNullable as q, batchInsertNestedTo as r, batchPatchNestedVia as s, applyPatchNestedTo as t, batchReplaceNestedVia as u, pkTupleKey as v, ResolvedRelationFilter as w, uniqueKeyTuple as x, rowMatchesKey as y, resolveRelationTargetTable as z };
1590
+ export { hasRelationOp as A, resolveRelationTargetTable as B, REL_FILTER_MAX_DEPTH as C, containsRelationFilter as D, andFilters as E, resolveRelationFilterTree as F, evaluateExpr as G, tableNameOf as H, compositeKey as I, viewExprNullable as J, isFieldRef as K, findFKEntryForRelation as L, noteRelationFilter as M, relGuardState as N, createRelationFilterHost as O, relationStaticFilter as P, findFKForRelation as R, uniqueKeyTuple as S, ResolvedRelationFilter as T, computeOf as U, slicePerGroup as V, computedOperands as W, walkViewExpr as Y, remapDeleteFkViolation as _, batchPatchNestedFrom as a, rowMatchesKey as b, batchReplaceNestedFrom as c, checkDepthOverflow as d, planNestedFromVia as f, enrichFkViolation as g, validateBatch as h, batchInsertNestedVia as i, isResolvedRelationFilter as j, forEachResolvedRelation as k, batchReplaceNestedTo as l, preValidateNestedFrom as m, batchInsertNestedFrom as n, batchPatchNestedTo as o, planPatchNestedTo as p, translateQueryTree as q, batchInsertNestedTo as r, batchPatchNestedVia as s, applyPatchNestedTo as t, batchReplaceNestedVia as u, findRowsByKeys as v, REL_FILTER_MAX_NODES as w, sameKey as x, pkTupleKey as y, findRemoteFK as z };
package/dist/rel.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_nested_writer = require("./nested-writer-BUQvk6QT.cjs");
3
- const require_relation_loader = require("./relation-loader-D8OrdH-r.cjs");
2
+ const require_nested_writer = require("./nested-writer-BzbOPSUp.cjs");
3
+ const require_relation_loader = require("./relation-loader-fNjHgBMs.cjs");
4
4
  exports.applyPatchNestedTo = require_nested_writer.applyPatchNestedTo;
5
5
  exports.batchInsertNestedFrom = require_nested_writer.batchInsertNestedFrom;
6
6
  exports.batchInsertNestedTo = require_nested_writer.batchInsertNestedTo;
package/dist/rel.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { Hn as TGenericLogger, Pt as TTableResolver, U as TDbForeignKey, Wt as TableMetadata, Yt as BaseDbAdapter, et as TDbRelation, h as AtscriptDbWritable, m as AtscriptDbTableLike, zt as TWriteTableResolver } from "./buckets-24Ac4sIS.cjs";
1
+ import { Hn as TGenericLogger, Pt as TTableResolver, U as TDbForeignKey, Wt as TableMetadata, Yt as BaseDbAdapter, et as TDbRelation, h as AtscriptDbWritable, m as AtscriptDbTableLike, zt as TWriteTableResolver } from "./buckets-tzd8iIM2.cjs";
2
2
  import { t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.cjs";
3
- import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-DjVJN2ng.cjs";
3
+ import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-DxIZKvnJ.cjs";
4
4
  import { FilterExpr, WithRelation } from "@uniqu/core";
5
5
  import { Validator } from "@atscript/typescript/utils";
6
6
 
package/dist/rel.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { Hn as TGenericLogger, Pt as TTableResolver, U as TDbForeignKey, Wt as TableMetadata, Yt as BaseDbAdapter, et as TDbRelation, h as AtscriptDbWritable, m as AtscriptDbTableLike, zt as TWriteTableResolver } from "./buckets-XOz0Bw5x.mjs";
1
+ import { Hn as TGenericLogger, Pt as TTableResolver, U as TDbForeignKey, Wt as TableMetadata, Yt as BaseDbAdapter, et as TDbRelation, h as AtscriptDbWritable, m as AtscriptDbTableLike, zt as TWriteTableResolver } from "./buckets-Cb0zmhDc.mjs";
2
2
  import { t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.mjs";
3
- import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-H7UEVkHs.mjs";
3
+ import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-Bdo4NHPC.mjs";
4
4
  import { Validator } from "@atscript/typescript/utils";
5
5
  import { FilterExpr, WithRelation } from "@uniqu/core";
6
6
 
package/dist/rel.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { t as loadRelationsImpl } from "./relation-loader-ByY1Byrl.mjs";
2
- import { L as findFKForRelation, R as findRemoteFK, a as batchPatchNestedFrom, c as batchReplaceNestedFrom, d as checkDepthOverflow, f as planNestedFromVia, h as validateBatch, i as batchInsertNestedVia, l as batchReplaceNestedTo, m as preValidateNestedFrom, n as batchInsertNestedFrom, o as batchPatchNestedTo, p as planPatchNestedTo, r as batchInsertNestedTo, s as batchPatchNestedVia, t as applyPatchNestedTo, u as batchReplaceNestedVia, z as resolveRelationTargetTable } from "./nested-writer-CUBoq1ZO.mjs";
1
+ import { t as loadRelationsImpl } from "./relation-loader-DYab07lw.mjs";
2
+ import { B as resolveRelationTargetTable, R as findFKForRelation, a as batchPatchNestedFrom, c as batchReplaceNestedFrom, d as checkDepthOverflow, f as planNestedFromVia, h as validateBatch, i as batchInsertNestedVia, l as batchReplaceNestedTo, m as preValidateNestedFrom, n as batchInsertNestedFrom, o as batchPatchNestedTo, p as planPatchNestedTo, r as batchInsertNestedTo, s as batchPatchNestedVia, t as applyPatchNestedTo, u as batchReplaceNestedVia, z as findRemoteFK } from "./nested-writer-cvbVAkNo.mjs";
3
3
  export { applyPatchNestedTo, batchInsertNestedFrom, batchInsertNestedTo, batchInsertNestedVia, batchPatchNestedFrom, batchPatchNestedTo, batchPatchNestedVia, batchReplaceNestedFrom, batchReplaceNestedTo, batchReplaceNestedVia, checkDepthOverflow, findFKForRelation, findRemoteFK, loadRelationsImpl, planNestedFromVia, planPatchNestedTo, preValidateNestedFrom, resolveRelationTargetTable, validateBatch };
@@ -1,4 +1,4 @@
1
- import { U as TDbForeignKey, et as TDbRelation } from "./buckets-24Ac4sIS.cjs";
1
+ import { U as TDbForeignKey, et as TDbRelation } from "./buckets-Cb0zmhDc.mjs";
2
2
  import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
3
3
 
4
4
  //#region src/rel/relation-helpers.d.ts
@@ -1,4 +1,4 @@
1
- import { U as TDbForeignKey, et as TDbRelation } from "./buckets-XOz0Bw5x.mjs";
1
+ import { U as TDbForeignKey, et as TDbRelation } from "./buckets-tzd8iIM2.cjs";
2
2
  import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
3
3
 
4
4
  //#region src/rel/relation-helpers.d.ts
@@ -1,4 +1,4 @@
1
- import { B as slicePerGroup, F as compositeKey, L as findFKForRelation, N as relationStaticFilter, R as findRemoteFK, T as andFilters, z as resolveRelationTargetTable } from "./nested-writer-CUBoq1ZO.mjs";
1
+ import { B as resolveRelationTargetTable, E as andFilters, I as compositeKey, P as relationStaticFilter, R as findFKForRelation, V as slicePerGroup, z as findRemoteFK } from "./nested-writer-cvbVAkNo.mjs";
2
2
  //#region \0rolldown/runtime.js
3
3
  var __defProp = Object.defineProperty;
4
4
  var __exportAll = (all, no_symbols) => {