@atscript/db 0.1.155 → 0.1.157

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.
Files changed (33) hide show
  1. package/dist/agg.d.cts +1 -1
  2. package/dist/agg.d.mts +1 -1
  3. package/dist/{buckets-goCoky8v.d.mts → buckets-8mG67Mtw.d.mts} +49 -7
  4. package/dist/{buckets-DcWm3SLt.d.cts → buckets-B4T_alUe.d.cts} +49 -7
  5. package/dist/{column-diff-C68V_eYM.mjs → column-diff-DR1glykD.mjs} +74 -11
  6. package/dist/{column-diff-DMkGYAGM.cjs → column-diff-DpeUbhEI.cjs} +74 -11
  7. package/dist/{fk-diff-BYLNWoF9.d.mts → fk-diff-5gX42hBt.d.mts} +1 -1
  8. package/dist/{fk-diff-DblOPBLS.d.cts → fk-diff-BWdQ7kRZ.d.cts} +1 -1
  9. package/dist/index.cjs +3 -3
  10. package/dist/index.d.cts +9 -8
  11. package/dist/index.d.mts +9 -8
  12. package/dist/index.mjs +3 -3
  13. package/dist/plugin.cjs +19 -2
  14. package/dist/plugin.mjs +19 -2
  15. package/dist/rel.d.cts +2 -2
  16. package/dist/rel.d.mts +2 -2
  17. package/dist/{relation-helpers-B9MVBrtx.d.mts → relation-helpers-17_2f5hX.d.mts} +1 -1
  18. package/dist/{relation-helpers-D9kMFfum.d.cts → relation-helpers-BLBrLQYi.d.cts} +1 -1
  19. package/dist/{search-fields-DBIZKwyb.mjs → search-fields-CE8cmj9X.mjs} +1 -0
  20. package/dist/{search-fields-DwrsJUC1.cjs → search-fields-CT_UZ9fg.cjs} +1 -0
  21. package/dist/shared.cjs +1 -1
  22. package/dist/shared.mjs +1 -1
  23. package/dist/sync.cjs +35 -9
  24. package/dist/sync.d.cts +2 -2
  25. package/dist/sync.d.mts +2 -2
  26. package/dist/sync.mjs +35 -9
  27. package/dist/{validation-utils-DxHIqa36.mjs → validation-utils-BGjqc2jU.mjs} +1 -1
  28. package/dist/{validation-utils-_PA7iSLm.cjs → validation-utils-CtLNlBtB.cjs} +1 -1
  29. package/dist/{validator-BC3nwFi4.mjs → validator-B3fPJjS1.mjs} +2 -2
  30. package/dist/{validator-DHwSyBej.cjs → validator-B4YDLUkj.cjs} +2 -2
  31. package/dist/validator.cjs +1 -1
  32. package/dist/validator.mjs +1 -1
  33. package/package.json +6 -6
package/dist/agg.d.cts CHANGED
@@ -1,3 +1,3 @@
1
- import { r as TResolvedBucket } from "./buckets-DcWm3SLt.cjs";
1
+ import { r as TResolvedBucket } from "./buckets-B4T_alUe.cjs";
2
2
  import { _ as TDbAggregateFn, a as AggregateResult, c as CalendarBucketLabel, d as WeekStart, f as isAggregateExpr, h as resolveAlias, i as AggregateQuery, l as ComputedExpr, m as resolveAggregateSearch, n as AggregateExpr, o as BucketExpr, p as isBucketExpr, r as AggregateFn, s as BucketUnit, t as AggregateControls, u as ResolvedBucket, v as assertAggregateFn } from "./agg-42CSpGDR.cjs";
3
3
  export { type AggregateControls, type AggregateExpr, type AggregateFn, type AggregateQuery, type AggregateResult, type BucketExpr, type BucketUnit, type CalendarBucketLabel, type ComputedExpr, type ResolvedBucket, type TDbAggregateFn, type TResolvedBucket, type WeekStart, assertAggregateFn, isAggregateExpr, isBucketExpr, resolveAggregateSearch, resolveAlias };
package/dist/agg.d.mts CHANGED
@@ -1,3 +1,3 @@
1
- import { r as TResolvedBucket } from "./buckets-goCoky8v.mjs";
1
+ import { r as TResolvedBucket } from "./buckets-8mG67Mtw.mjs";
2
2
  import { _ as TDbAggregateFn, a as AggregateResult, c as CalendarBucketLabel, d as WeekStart, f as isAggregateExpr, h as resolveAlias, i as AggregateQuery, l as ComputedExpr, m as resolveAggregateSearch, n as AggregateExpr, o as BucketExpr, p as isBucketExpr, r as AggregateFn, s as BucketUnit, t as AggregateControls, u as ResolvedBucket, v as assertAggregateFn } from "./agg-Wo-smrYV.mjs";
3
3
  export { type AggregateControls, type AggregateExpr, type AggregateFn, type AggregateQuery, type AggregateResult, type BucketExpr, type BucketUnit, type CalendarBucketLabel, type ComputedExpr, type ResolvedBucket, type TDbAggregateFn, type TResolvedBucket, type WeekStart, assertAggregateFn, isAggregateExpr, isBucketExpr, resolveAggregateSearch, resolveAlias };
@@ -1551,8 +1551,23 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
1551
1551
  * that object is present (an absent or `null` embedded object stays so), and
1552
1552
  * in every object of an array on the way; a field inside a JSON value or an
1553
1553
  * array has no column default, so the SDK fills its function default too.
1554
- */
1555
- protected _applyDefaults(data: Record<string, unknown>): Record<string, unknown>;
1554
+ * Every `now` default the SDK fills gets the one time `now` (one per write
1555
+ * call, so `createdAt` / `updatedAt` of a new row are equal).
1556
+ */
1557
+ protected _applyDefaults(data: Record<string, unknown>, now?: number): Record<string, unknown>;
1558
+ /**
1559
+ * `@db.onUpdate.now` (since 0.1.156): sets each such field of a replace /
1560
+ * patch payload to `now`, overriding the caller's value. A nested field
1561
+ * (`audit.updatedAt`) is set inside its object only when the payload
1562
+ * carries that object — in every item of an array on the way and of the
1563
+ * items an array patch operator adds or updates — like
1564
+ * {@link _applyDefaults}. `topLevel: false` (a patch) instead drops the
1565
+ * caller's values of the table's own columns: {@link _stampTopLevel} sets
1566
+ * them once the patch is known to write something.
1567
+ */
1568
+ private _stampOnUpdate;
1569
+ /** Sets the top-level `@db.onUpdate.now` columns of a non-empty patch to `now`. */
1570
+ private _stampTopLevel;
1556
1571
  /** {@link _nestedDefaultFillable} per nested default path. */
1557
1572
  private _fillableDefaults?;
1558
1573
  /**
@@ -2788,9 +2803,12 @@ declare abstract class BaseDbAdapter {
2788
2803
  abstract deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
2789
2804
  /**
2790
2805
  * Synchronizes indexes between the Atscript definitions and the database.
2791
- * Uses `this._table.indexes` for the full index definitions.
2806
+ * Uses `this._table.indexes` for the full index definitions. Schema sync
2807
+ * passes `opts` when safe mode skipped work the indexes depend on (see
2808
+ * {@link TSyncIndexesOptions}); an adapter whose indexes do not depend on it
2809
+ * ignores them.
2792
2810
  */
2793
- abstract syncIndexes(): Promise<void>;
2811
+ abstract syncIndexes(opts?: TSyncIndexesOptions): Promise<void>;
2794
2812
  /**
2795
2813
  * Ensures the table exists in the database, creating it if needed.
2796
2814
  * Uses `this._table.tableName`, `this._table.schema`, etc.
@@ -2922,8 +2940,9 @@ declare abstract class BaseDbAdapter {
2922
2940
  * given columns. Called by schema sync BEFORE {@link dropColumns} — engines
2923
2941
  * like SQLite refuse to drop a column while an index still references it,
2924
2942
  * and a composite index that survives by name must be rebuilt without the
2925
- * removed column (recreated later by {@link syncIndexes}).
2926
- * Optional — only relational adapters implement this.
2943
+ * removed column (recreated later by {@link syncIndexes}). MongoDB drops
2944
+ * them because a unique index would reject the `$unset` (`null` twice).
2945
+ * Optional — the SQL adapters and MongoDB implement this.
2927
2946
  */
2928
2947
  dropIndexesForColumns?(columns: string[]): Promise<void>;
2929
2948
  /**
@@ -3115,6 +3134,12 @@ declare class TableMetadata {
3115
3134
  /** Adapter-contributed multi-field unique keys (logical paths) — see `TMetadataOverrides.addUniqueKeys`. */
3116
3135
  uniqueKeys: string[][];
3117
3136
  defaults: Map<string, TDbDefaultValue>;
3137
+ /**
3138
+ * Logical paths annotated with `@db.onUpdate.now` (`number.timestamp.updated`
3139
+ * applies it): set to the current time by every update and replace.
3140
+ * @since 0.1.156
3141
+ */
3142
+ onUpdateNow: Set<string>;
3118
3143
  /** Logical path → `@db.column` override (top-level keys only on document storage). */
3119
3144
  columnMap: Map<string, string>;
3120
3145
  dimensions: string[];
@@ -4183,6 +4208,23 @@ interface TEnsureTableOptions {
4183
4208
  */
4184
4209
  deferForeignKeysTo?: ReadonlySet<string>;
4185
4210
  }
4211
+ /**
4212
+ * Options accepted by `BaseDbAdapter.syncIndexes` (since 0.1.156): indexes
4213
+ * that stay as they are because safe mode skipped the change they depend on
4214
+ * (`entry.skipped`) — it is pending, applied by the next run without `safe`.
4215
+ */
4216
+ interface TSyncIndexesOptions {
4217
+ /**
4218
+ * The primary-key rebuild is skipped (`'pk-rebuild'`): an adapter whose
4219
+ * key is an index (MongoDB's `__pk` unique index) keeps the live one.
4220
+ */
4221
+ keepPrimaryKey?: boolean;
4222
+ /**
4223
+ * Physical columns whose type change is skipped (`'recreate'`): indexes
4224
+ * over them are neither dropped, replaced nor created.
4225
+ */
4226
+ keepColumns?: readonly string[];
4227
+ }
4186
4228
  /** Result of applying column diff to the database. */
4187
4229
  interface TSyncColumnResult {
4188
4230
  added: string[];
@@ -4683,4 +4725,4 @@ declare function isJsonValueField(fd: TDbFieldMeta): boolean;
4683
4725
  */
4684
4726
  declare function jsonValueAncestor(path: string, jsonValueParents: ReadonlySet<string>): string | undefined;
4685
4727
  //#endregion
4686
- export { TDbInsertResult as $, BaseDbAdapter as $t, TColumnDiff as A, ResolvedRelationFilter as An, TPrimaryKeyChange as At, TDbDefaultFn as B, relationStaticFilter as Bn, TViewJsonType as Bt, NavPropsOf$1 as C, NativeIntegrity as Cn, TIdDescriptor as Ct, PrimaryKeyOf$1 as D, resolveDesignType as Dn, TJsonCopyTarget as Dt, OwnPropsOf$1 as E, DbResponse as En, TInsertOptions as Et, TDbActionLevel as F, andFilters as Fn, TSyncColumnResult as Ft, TDbIndex as G, TReadControls as Gn, UniqueryControls$1 as Gt, TDbDeleteResult as H, TDbEncryptionOptions as Hn, TWriteTableResolver as Ht, TDbActionProcessor as I, containsRelationFilter as In, TTableOptionDiff as It, TDbInsertIgnoreOptions as J, TExprAggregate as Jn, isGeoIndexableType as Jt, TDbIndexField as K, NoopLogger as Kn, WithRelation$1 as Kt, TDbActionTargetSummary as L, forEachResolvedRelation as Ln, TTableResolver as Lt, TCrudPermissions as M, TRelationFilterJunction as Mn, TRelationInfo as Mt, TDbActionInfo as N, TRelationFilterTable as Nn, TRowResolveOptions as Nt, TCascadeResolver as O, REL_FILTER_MAX_DEPTH as On, TMetaResponse as Ot, TDbActionIntent as P, TRelationStaticFilter as Pn, TSearchIndexInfo as Pt, TDbInsertManyResult as Q, ALL_VIEW_CAPABILITIES as Qt, TDbAvailableActions as R, hasRelationOp as Rn, TTouchManyOptions as Rt, FlatOf$1 as S, IntegrityStrategy as Sn, TFkLookupTarget as St, NullsPlacement$1 as T, AtscriptDbReadable as Tn, TIdentification as Tt, TDbFieldMeta as U, DocumentFieldMapper as Un, TypedWithRelation as Ut, TDbDefaultValue as V, DbEncryption as Vn, TWriteOptions as Vt, TDbForeignKey as W, FieldMappingStrategy as Wn, Uniquery$1 as Wt, TDbInsertIgnoreSlot as X, TRowOrderKey as Xn, TPathPresence as Xt, TDbInsertIgnoreResult as Y, TFirstLast as Yn, isGeoPointType as Yt, TDbInsertManyIgnoreResult as Z, UniquSelect as Zn, ALL_BUCKET_UNITS as Zt, DbPatch as _, TViewPlan as _n, TExistingColumn as _t, isJsonValueField as a, TViewColumnMapping as an, TDbStorageType as at, FieldOpsFor as b, translateQueryTree as bn, TFieldMeta as bt, resolveCalendarBuckets as c, TViewReadPlan as cn, TDbWriteAction as ct, AggregateFn$1 as d, AtscriptOrderItem as dn, TDbWriteGuard as dt, TViewCapability as en, TDbObjectKind as et, AggregateQuery$1 as f, AtscriptQueryComparison as fn, TDbWriteGuardContext as ft, DbControls as g, TViewJoin as gn, TEnsureTableOptions as gt, AtscriptDbWritable as h, AtscriptRef as hn, TDerivedColumn as ht, isBucketableField as i, AtscriptDbView as in, TDbRemoveGuardContext as it, TCrudOp as j, TRelGuardState as jn, TReferencingForeignKey as jt, TCascadeTarget as k, REL_FILTER_MAX_NODES as kn, TMetadataOverrides as kt, AggregateControls as l, aliasTargetOf as ln, TDbWriteCheck as lt, AtscriptDbTableLike as m, AtscriptQueryNode$1 as mn, TDerivedChangeReason as mt, TComputedSelect as n, TAdapterFactory as nn, TDbRelation as nt, jsonValueAncestor as o, isAtscriptDbView as on, TDbUpdateOptions as ot, AggregateResult as p, AtscriptQueryFieldRef$1 as pn, TDeleteOptions as pt, TDbIndexType as q, TGenericLogger as qn, TableMetadata as qt, TResolvedBucket as r, TDbSpaceOptions as rn, TDbRemoveGuard as rt, normalizeComputedSelect as s, isViewType as sn, TDbUpdateResult as st, TBucketFieldSource as t, DbSpace as tn, TDbReferentialAction as tt, AggregateExpr$1 as u, AtscriptExprNode$1 as un, TDbWriteCheckContext as ut, DbQuery as v, evaluateExpr as vn, TExistingForeignKey as vt, NullableOptional as w, TCascadePin as wn, TIdResolveOptions as wt, FilterExpr$1 as x, AtscriptDbTable as xn, TFkLookupResolver as xt, DbRow as y, isFieldRef as yn, TExistingTableOption as yt, TDbCollation as z, isResolvedRelationFilter as zn, TValueFormatterPair as zt };
4728
+ export { TDbInsertResult as $, ALL_VIEW_CAPABILITIES as $t, TColumnDiff as A, REL_FILTER_MAX_NODES as An, TPrimaryKeyChange as At, TDbDefaultFn as B, isResolvedRelationFilter as Bn, TValueFormatterPair as Bt, NavPropsOf$1 as C, IntegrityStrategy as Cn, TIdDescriptor as Ct, PrimaryKeyOf$1 as D, DbResponse as Dn, TJsonCopyTarget as Dt, OwnPropsOf$1 as E, AtscriptDbReadable as En, TInsertOptions as Et, TDbActionLevel as F, TRelationStaticFilter as Fn, TSyncColumnResult as Ft, TDbIndex as G, FieldMappingStrategy as Gn, Uniquery$1 as Gt, TDbDeleteResult as H, DbEncryption as Hn, TWriteOptions as Ht, TDbActionProcessor as I, andFilters as In, TSyncIndexesOptions as It, TDbInsertIgnoreOptions as J, TGenericLogger as Jn, TableMetadata as Jt, TDbIndexField as K, TReadControls as Kn, UniqueryControls$1 as Kt, TDbActionTargetSummary as L, containsRelationFilter as Ln, TTableOptionDiff as Lt, TCrudPermissions as M, TRelGuardState as Mn, TRelationInfo as Mt, TDbActionInfo as N, TRelationFilterJunction as Nn, TRowResolveOptions as Nt, TCascadeResolver as O, resolveDesignType as On, TMetaResponse as Ot, TDbActionIntent as P, TRelationFilterTable as Pn, TSearchIndexInfo as Pt, TDbInsertManyResult as Q, UniquSelect as Qn, ALL_BUCKET_UNITS as Qt, TDbAvailableActions as R, forEachResolvedRelation as Rn, TTableResolver as Rt, FlatOf$1 as S, AtscriptDbTable as Sn, TFkLookupTarget as St, NullsPlacement$1 as T, TCascadePin as Tn, TIdentification as Tt, TDbFieldMeta as U, TDbEncryptionOptions as Un, TWriteTableResolver as Ut, TDbDefaultValue as V, relationStaticFilter as Vn, TViewJsonType as Vt, TDbForeignKey as W, DocumentFieldMapper as Wn, TypedWithRelation as Wt, TDbInsertIgnoreSlot as X, TFirstLast as Xn, isGeoPointType as Xt, TDbInsertIgnoreResult as Y, TExprAggregate as Yn, isGeoIndexableType as Yt, TDbInsertManyIgnoreResult as Z, TRowOrderKey as Zn, TPathPresence as Zt, DbPatch as _, TViewJoin as _n, TExistingColumn as _t, isJsonValueField as a, AtscriptDbView as an, TDbStorageType as at, FieldOpsFor as b, isFieldRef as bn, TFieldMeta as bt, resolveCalendarBuckets as c, isViewType as cn, TDbWriteAction as ct, AggregateFn$1 as d, AtscriptExprNode$1 as dn, TDbWriteGuard as dt, BaseDbAdapter as en, TDbObjectKind as et, AggregateQuery$1 as f, AtscriptOrderItem as fn, TDbWriteGuardContext as ft, DbControls as g, AtscriptRef as gn, TEnsureTableOptions as gt, AtscriptDbWritable as h, AtscriptQueryNode$1 as hn, TDerivedColumn as ht, isBucketableField as i, TDbSpaceOptions as in, TDbRemoveGuardContext as it, TCrudOp as j, ResolvedRelationFilter as jn, TReferencingForeignKey as jt, TCascadeTarget as k, REL_FILTER_MAX_DEPTH as kn, TMetadataOverrides as kt, AggregateControls as l, TViewReadPlan as ln, TDbWriteCheck as lt, AtscriptDbTableLike as m, AtscriptQueryFieldRef$1 as mn, TDerivedChangeReason as mt, TComputedSelect as n, DbSpace as nn, TDbRelation as nt, jsonValueAncestor as o, TViewColumnMapping as on, TDbUpdateOptions as ot, AggregateResult as p, AtscriptQueryComparison as pn, TDeleteOptions as pt, TDbIndexType as q, NoopLogger as qn, WithRelation$1 as qt, TResolvedBucket as r, TAdapterFactory as rn, TDbRemoveGuard as rt, normalizeComputedSelect as s, isAtscriptDbView as sn, TDbUpdateResult as st, TBucketFieldSource as t, TViewCapability as tn, TDbReferentialAction as tt, AggregateExpr$1 as u, aliasTargetOf as un, TDbWriteCheckContext as ut, DbQuery as v, TViewPlan as vn, TExistingForeignKey as vt, NullableOptional as w, NativeIntegrity as wn, TIdResolveOptions as wt, FilterExpr$1 as x, translateQueryTree as xn, TFkLookupResolver as xt, DbRow as y, evaluateExpr as yn, TExistingTableOption as yt, TDbCollation as z, hasRelationOp as zn, TTouchManyOptions as zt };
@@ -1551,8 +1551,23 @@ declare class AtscriptDbTable<T extends TAtscriptAnnotatedType = TAtscriptAnnota
1551
1551
  * that object is present (an absent or `null` embedded object stays so), and
1552
1552
  * in every object of an array on the way; a field inside a JSON value or an
1553
1553
  * array has no column default, so the SDK fills its function default too.
1554
- */
1555
- protected _applyDefaults(data: Record<string, unknown>): Record<string, unknown>;
1554
+ * Every `now` default the SDK fills gets the one time `now` (one per write
1555
+ * call, so `createdAt` / `updatedAt` of a new row are equal).
1556
+ */
1557
+ protected _applyDefaults(data: Record<string, unknown>, now?: number): Record<string, unknown>;
1558
+ /**
1559
+ * `@db.onUpdate.now` (since 0.1.156): sets each such field of a replace /
1560
+ * patch payload to `now`, overriding the caller's value. A nested field
1561
+ * (`audit.updatedAt`) is set inside its object only when the payload
1562
+ * carries that object — in every item of an array on the way and of the
1563
+ * items an array patch operator adds or updates — like
1564
+ * {@link _applyDefaults}. `topLevel: false` (a patch) instead drops the
1565
+ * caller's values of the table's own columns: {@link _stampTopLevel} sets
1566
+ * them once the patch is known to write something.
1567
+ */
1568
+ private _stampOnUpdate;
1569
+ /** Sets the top-level `@db.onUpdate.now` columns of a non-empty patch to `now`. */
1570
+ private _stampTopLevel;
1556
1571
  /** {@link _nestedDefaultFillable} per nested default path. */
1557
1572
  private _fillableDefaults?;
1558
1573
  /**
@@ -2788,9 +2803,12 @@ declare abstract class BaseDbAdapter {
2788
2803
  abstract deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
2789
2804
  /**
2790
2805
  * Synchronizes indexes between the Atscript definitions and the database.
2791
- * Uses `this._table.indexes` for the full index definitions.
2806
+ * Uses `this._table.indexes` for the full index definitions. Schema sync
2807
+ * passes `opts` when safe mode skipped work the indexes depend on (see
2808
+ * {@link TSyncIndexesOptions}); an adapter whose indexes do not depend on it
2809
+ * ignores them.
2792
2810
  */
2793
- abstract syncIndexes(): Promise<void>;
2811
+ abstract syncIndexes(opts?: TSyncIndexesOptions): Promise<void>;
2794
2812
  /**
2795
2813
  * Ensures the table exists in the database, creating it if needed.
2796
2814
  * Uses `this._table.tableName`, `this._table.schema`, etc.
@@ -2922,8 +2940,9 @@ declare abstract class BaseDbAdapter {
2922
2940
  * given columns. Called by schema sync BEFORE {@link dropColumns} — engines
2923
2941
  * like SQLite refuse to drop a column while an index still references it,
2924
2942
  * and a composite index that survives by name must be rebuilt without the
2925
- * removed column (recreated later by {@link syncIndexes}).
2926
- * Optional — only relational adapters implement this.
2943
+ * removed column (recreated later by {@link syncIndexes}). MongoDB drops
2944
+ * them because a unique index would reject the `$unset` (`null` twice).
2945
+ * Optional — the SQL adapters and MongoDB implement this.
2927
2946
  */
2928
2947
  dropIndexesForColumns?(columns: string[]): Promise<void>;
2929
2948
  /**
@@ -3115,6 +3134,12 @@ declare class TableMetadata {
3115
3134
  /** Adapter-contributed multi-field unique keys (logical paths) — see `TMetadataOverrides.addUniqueKeys`. */
3116
3135
  uniqueKeys: string[][];
3117
3136
  defaults: Map<string, TDbDefaultValue>;
3137
+ /**
3138
+ * Logical paths annotated with `@db.onUpdate.now` (`number.timestamp.updated`
3139
+ * applies it): set to the current time by every update and replace.
3140
+ * @since 0.1.156
3141
+ */
3142
+ onUpdateNow: Set<string>;
3118
3143
  /** Logical path → `@db.column` override (top-level keys only on document storage). */
3119
3144
  columnMap: Map<string, string>;
3120
3145
  dimensions: string[];
@@ -4183,6 +4208,23 @@ interface TEnsureTableOptions {
4183
4208
  */
4184
4209
  deferForeignKeysTo?: ReadonlySet<string>;
4185
4210
  }
4211
+ /**
4212
+ * Options accepted by `BaseDbAdapter.syncIndexes` (since 0.1.156): indexes
4213
+ * that stay as they are because safe mode skipped the change they depend on
4214
+ * (`entry.skipped`) — it is pending, applied by the next run without `safe`.
4215
+ */
4216
+ interface TSyncIndexesOptions {
4217
+ /**
4218
+ * The primary-key rebuild is skipped (`'pk-rebuild'`): an adapter whose
4219
+ * key is an index (MongoDB's `__pk` unique index) keeps the live one.
4220
+ */
4221
+ keepPrimaryKey?: boolean;
4222
+ /**
4223
+ * Physical columns whose type change is skipped (`'recreate'`): indexes
4224
+ * over them are neither dropped, replaced nor created.
4225
+ */
4226
+ keepColumns?: readonly string[];
4227
+ }
4186
4228
  /** Result of applying column diff to the database. */
4187
4229
  interface TSyncColumnResult {
4188
4230
  added: string[];
@@ -4683,4 +4725,4 @@ declare function isJsonValueField(fd: TDbFieldMeta): boolean;
4683
4725
  */
4684
4726
  declare function jsonValueAncestor(path: string, jsonValueParents: ReadonlySet<string>): string | undefined;
4685
4727
  //#endregion
4686
- export { TDbInsertResult as $, BaseDbAdapter as $t, TColumnDiff as A, ResolvedRelationFilter as An, TPrimaryKeyChange as At, TDbDefaultFn as B, relationStaticFilter as Bn, TViewJsonType as Bt, NavPropsOf$1 as C, NativeIntegrity as Cn, TIdDescriptor as Ct, PrimaryKeyOf$1 as D, resolveDesignType as Dn, TJsonCopyTarget as Dt, OwnPropsOf$1 as E, DbResponse as En, TInsertOptions as Et, TDbActionLevel as F, andFilters as Fn, TSyncColumnResult as Ft, TDbIndex as G, TReadControls as Gn, UniqueryControls$1 as Gt, TDbDeleteResult as H, TDbEncryptionOptions as Hn, TWriteTableResolver as Ht, TDbActionProcessor as I, containsRelationFilter as In, TTableOptionDiff as It, TDbInsertIgnoreOptions as J, TExprAggregate as Jn, isGeoIndexableType as Jt, TDbIndexField as K, NoopLogger as Kn, WithRelation$1 as Kt, TDbActionTargetSummary as L, forEachResolvedRelation as Ln, TTableResolver as Lt, TCrudPermissions as M, TRelationFilterJunction as Mn, TRelationInfo as Mt, TDbActionInfo as N, TRelationFilterTable as Nn, TRowResolveOptions as Nt, TCascadeResolver as O, REL_FILTER_MAX_DEPTH as On, TMetaResponse as Ot, TDbActionIntent as P, TRelationStaticFilter as Pn, TSearchIndexInfo as Pt, TDbInsertManyResult as Q, ALL_VIEW_CAPABILITIES as Qt, TDbAvailableActions as R, hasRelationOp as Rn, TTouchManyOptions as Rt, FlatOf$1 as S, IntegrityStrategy as Sn, TFkLookupTarget as St, NullsPlacement$1 as T, AtscriptDbReadable as Tn, TIdentification as Tt, TDbFieldMeta as U, DocumentFieldMapper as Un, TypedWithRelation as Ut, TDbDefaultValue as V, DbEncryption as Vn, TWriteOptions as Vt, TDbForeignKey as W, FieldMappingStrategy as Wn, Uniquery$1 as Wt, TDbInsertIgnoreSlot as X, TRowOrderKey as Xn, TPathPresence as Xt, TDbInsertIgnoreResult as Y, TFirstLast as Yn, isGeoPointType as Yt, TDbInsertManyIgnoreResult as Z, UniquSelect as Zn, ALL_BUCKET_UNITS as Zt, DbPatch as _, TViewPlan as _n, TExistingColumn as _t, isJsonValueField as a, TViewColumnMapping as an, TDbStorageType as at, FieldOpsFor as b, translateQueryTree as bn, TFieldMeta as bt, resolveCalendarBuckets as c, TViewReadPlan as cn, TDbWriteAction as ct, AggregateFn$1 as d, AtscriptOrderItem as dn, TDbWriteGuard as dt, TViewCapability as en, TDbObjectKind as et, AggregateQuery$1 as f, AtscriptQueryComparison as fn, TDbWriteGuardContext as ft, DbControls as g, TViewJoin as gn, TEnsureTableOptions as gt, AtscriptDbWritable as h, AtscriptRef as hn, TDerivedColumn as ht, isBucketableField as i, AtscriptDbView as in, TDbRemoveGuardContext as it, TCrudOp as j, TRelGuardState as jn, TReferencingForeignKey as jt, TCascadeTarget as k, REL_FILTER_MAX_NODES as kn, TMetadataOverrides as kt, AggregateControls as l, aliasTargetOf as ln, TDbWriteCheck as lt, AtscriptDbTableLike as m, AtscriptQueryNode$1 as mn, TDerivedChangeReason as mt, TComputedSelect as n, TAdapterFactory as nn, TDbRelation as nt, jsonValueAncestor as o, isAtscriptDbView as on, TDbUpdateOptions as ot, AggregateResult as p, AtscriptQueryFieldRef$1 as pn, TDeleteOptions as pt, TDbIndexType as q, TGenericLogger as qn, TableMetadata as qt, TResolvedBucket as r, TDbSpaceOptions as rn, TDbRemoveGuard as rt, normalizeComputedSelect as s, isViewType as sn, TDbUpdateResult as st, TBucketFieldSource as t, DbSpace as tn, TDbReferentialAction as tt, AggregateExpr$1 as u, AtscriptExprNode$1 as un, TDbWriteCheckContext as ut, DbQuery as v, evaluateExpr as vn, TExistingForeignKey as vt, NullableOptional as w, TCascadePin as wn, TIdResolveOptions as wt, FilterExpr$1 as x, AtscriptDbTable as xn, TFkLookupResolver as xt, DbRow as y, isFieldRef as yn, TExistingTableOption as yt, TDbCollation as z, isResolvedRelationFilter as zn, TValueFormatterPair as zt };
4728
+ export { TDbInsertResult as $, ALL_VIEW_CAPABILITIES as $t, TColumnDiff as A, REL_FILTER_MAX_NODES as An, TPrimaryKeyChange as At, TDbDefaultFn as B, isResolvedRelationFilter as Bn, TValueFormatterPair as Bt, NavPropsOf$1 as C, IntegrityStrategy as Cn, TIdDescriptor as Ct, PrimaryKeyOf$1 as D, DbResponse as Dn, TJsonCopyTarget as Dt, OwnPropsOf$1 as E, AtscriptDbReadable as En, TInsertOptions as Et, TDbActionLevel as F, TRelationStaticFilter as Fn, TSyncColumnResult as Ft, TDbIndex as G, FieldMappingStrategy as Gn, Uniquery$1 as Gt, TDbDeleteResult as H, DbEncryption as Hn, TWriteOptions as Ht, TDbActionProcessor as I, andFilters as In, TSyncIndexesOptions as It, TDbInsertIgnoreOptions as J, TGenericLogger as Jn, TableMetadata as Jt, TDbIndexField as K, TReadControls as Kn, UniqueryControls$1 as Kt, TDbActionTargetSummary as L, containsRelationFilter as Ln, TTableOptionDiff as Lt, TCrudPermissions as M, TRelGuardState as Mn, TRelationInfo as Mt, TDbActionInfo as N, TRelationFilterJunction as Nn, TRowResolveOptions as Nt, TCascadeResolver as O, resolveDesignType as On, TMetaResponse as Ot, TDbActionIntent as P, TRelationFilterTable as Pn, TSearchIndexInfo as Pt, TDbInsertManyResult as Q, UniquSelect as Qn, ALL_BUCKET_UNITS as Qt, TDbAvailableActions as R, forEachResolvedRelation as Rn, TTableResolver as Rt, FlatOf$1 as S, AtscriptDbTable as Sn, TFkLookupTarget as St, NullsPlacement$1 as T, TCascadePin as Tn, TIdentification as Tt, TDbFieldMeta as U, TDbEncryptionOptions as Un, TWriteTableResolver as Ut, TDbDefaultValue as V, relationStaticFilter as Vn, TViewJsonType as Vt, TDbForeignKey as W, DocumentFieldMapper as Wn, TypedWithRelation as Wt, TDbInsertIgnoreSlot as X, TFirstLast as Xn, isGeoPointType as Xt, TDbInsertIgnoreResult as Y, TExprAggregate as Yn, isGeoIndexableType as Yt, TDbInsertManyIgnoreResult as Z, TRowOrderKey as Zn, TPathPresence as Zt, DbPatch as _, TViewJoin as _n, TExistingColumn as _t, isJsonValueField as a, AtscriptDbView as an, TDbStorageType as at, FieldOpsFor as b, isFieldRef as bn, TFieldMeta as bt, resolveCalendarBuckets as c, isViewType as cn, TDbWriteAction as ct, AggregateFn$1 as d, AtscriptExprNode$1 as dn, TDbWriteGuard as dt, BaseDbAdapter as en, TDbObjectKind as et, AggregateQuery$1 as f, AtscriptOrderItem as fn, TDbWriteGuardContext as ft, DbControls as g, AtscriptRef as gn, TEnsureTableOptions as gt, AtscriptDbWritable as h, AtscriptQueryNode$1 as hn, TDerivedColumn as ht, isBucketableField as i, TDbSpaceOptions as in, TDbRemoveGuardContext as it, TCrudOp as j, ResolvedRelationFilter as jn, TReferencingForeignKey as jt, TCascadeTarget as k, REL_FILTER_MAX_DEPTH as kn, TMetadataOverrides as kt, AggregateControls as l, TViewReadPlan as ln, TDbWriteCheck as lt, AtscriptDbTableLike as m, AtscriptQueryFieldRef$1 as mn, TDerivedChangeReason as mt, TComputedSelect as n, DbSpace as nn, TDbRelation as nt, jsonValueAncestor as o, TViewColumnMapping as on, TDbUpdateOptions as ot, AggregateResult as p, AtscriptQueryComparison as pn, TDeleteOptions as pt, TDbIndexType as q, NoopLogger as qn, WithRelation$1 as qt, TResolvedBucket as r, TAdapterFactory as rn, TDbRemoveGuard as rt, normalizeComputedSelect as s, isAtscriptDbView as sn, TDbUpdateResult as st, TBucketFieldSource as t, TViewCapability as tn, TDbReferentialAction as tt, AggregateExpr$1 as u, aliasTargetOf as un, TDbWriteCheckContext as ut, DbQuery as v, TViewPlan as vn, TExistingForeignKey as vt, NullableOptional as w, NativeIntegrity as wn, TIdResolveOptions as wt, FilterExpr$1 as x, translateQueryTree as xn, TFkLookupResolver as xt, DbRow as y, evaluateExpr as yn, TExistingTableOption as yt, TDbCollation as z, hasRelationOp as zn, TTouchManyOptions as zt };
@@ -1,8 +1,8 @@
1
1
  import { a as aggregateExpressionsNotSupported, n as CasMismatchError, r as DbError, u as spaceClosedError } from "./db-error-eSsQX7rt.mjs";
2
2
  import { a as SOURCE_VALUE_FNS, c as isFirstLast, o as SUPPORTED_AGGREGATE_FNS, r as BASE_AGGREGATE_FNS, t as AGG_ANNOTATIONS } from "./aggregate-fns-B_sgl4mB.mjs";
3
3
  import { A as hasRelationOp, D as containsRelationFilter, F as resolveRelationFilterTree, H as tableNameOf, I as compositeKey, J as viewExprNullable, K as isFieldRef, L as findFKEntryForRelation, M as noteRelationFilter, N as relGuardState, O as createRelationFilterHost, R as findFKForRelation, S as uniqueKeyTuple, U as computeOf, V as slicePerGroup, W as computedOperands, Y as walkViewExpr, _ as remapDeleteFkViolation, a as batchPatchNestedFrom, b as rowMatchesKey, c as batchReplaceNestedFrom, d as checkDepthOverflow, f as planNestedFromVia, g as enrichFkViolation, h as validateBatch, i as batchInsertNestedVia, l as batchReplaceNestedTo, m as preValidateNestedFrom, n as batchInsertNestedFrom, p as planPatchNestedTo, r as batchInsertNestedTo, s as batchPatchNestedVia, t as applyPatchNestedTo, u as batchReplaceNestedVia, v as findRowsByKeys, x as sameKey, y as pkTupleKey, z as findRemoteFK } from "./nested-writer-BOj_c_lI.mjs";
4
- import { a as forceNavNonOptional, c as getKeyProps, d as soleUnionMember, f as unionValueMembers, i as dbPlugin, l as columnUnionBase, n as buildPatchPartial, t as buildDbValidator, u as pathPresence } from "./validator-BC3nwFi4.mjs";
5
- import { a as DERIVED_INCOMPATIBLE, n as searchMemberKind, r as numericTypeProblem, s as isJsonLeafType } from "./search-fields-DBIZKwyb.mjs";
4
+ import { a as forceNavNonOptional, c as getKeyProps, d as soleUnionMember, f as unionValueMembers, i as dbPlugin, l as columnUnionBase, n as buildPatchPartial, t as buildDbValidator, u as pathPresence } from "./validator-B3fPJjS1.mjs";
5
+ import { a as DERIVED_INCOMPATIBLE, n as searchMemberKind, r as numericTypeProblem, s as isJsonLeafType } from "./search-fields-CE8cmj9X.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 { resolveAlias as resolveAlias$1 } from "./agg.mjs";
8
8
  import { t as NOT_DIMENSION_REASON } from "./aggregate-rules-jdPrxqWa.mjs";
@@ -503,6 +503,12 @@ var TableMetadata = class {
503
503
  /** Adapter-contributed multi-field unique keys (logical paths) — see `TMetadataOverrides.addUniqueKeys`. */
504
504
  uniqueKeys = [];
505
505
  defaults = /* @__PURE__ */ new Map();
506
+ /**
507
+ * Logical paths annotated with `@db.onUpdate.now` (`number.timestamp.updated`
508
+ * applies it): set to the current time by every update and replace.
509
+ * @since 0.1.156
510
+ */
511
+ onUpdateNow = /* @__PURE__ */ new Set();
506
512
  /** Logical path → `@db.column` override (top-level keys only on document storage). */
507
513
  columnMap = /* @__PURE__ */ new Map();
508
514
  dimensions = [];
@@ -827,6 +833,7 @@ var TableMetadata = class {
827
833
  if (columnFrom && renamable) this._columnFromMap.set(fieldName, columnFrom);
828
834
  const resolvedDefault = resolveDefaultFromMetadata(metadata);
829
835
  if (resolvedDefault) this.defaults.set(fieldName, resolvedDefault);
836
+ if (metadata.has("db.onUpdate.now")) this.onUpdateNow.add(fieldName);
830
837
  if (metadata.has("db.ignore")) this.ignoredFields.add(fieldName);
831
838
  if (isNavRelation(metadata)) {
832
839
  this.navFields.add(fieldName);
@@ -996,6 +1003,7 @@ var TableMetadata = class {
996
1003
  if (metadata.has("db.search.vector") || metadata.has("db.search.filter")) reject("@db.search.*", "search over ciphertext is impossible");
997
1004
  if (metadata.has("db.mongo.search.text") || metadata.has("db.mongo.search.autocomplete")) reject("@db.mongo.search.*", "Atlas Search over ciphertext is impossible");
998
1005
  if (metadata.has("db.column.version")) reject("@db.column.version", "the OCC filter needs cleartext equality");
1006
+ if (metadata.has("db.onUpdate.now")) reject("@db.onUpdate.now", "the update time is set after the encryption transform");
999
1007
  if (metadata.has("db.default.increment") || metadata.has("db.default.now")) reject("@db.default.increment / @db.default.now", "engine-side defaults bypass the encryption transform");
1000
1008
  if (metadata.get("db.patch.strategy") === "merge") reject("@db.patch.strategy \"merge\"", "ciphertext is opaque — partial merges would silently drop omitted keys");
1001
1009
  }
@@ -6809,10 +6817,11 @@ function assertNoVersionWrites(data, versionColumn) {
6809
6817
  * Whether a logical (pre-decomposition) patch writes ONLY version-exempt
6810
6818
  * fields (`@db.column.version.exempt`). `data` has nav fields, filter keys and
6811
6819
  * derived fields removed and `$cas` separated. Mirrors `flattenPatchPayload`
6812
- * (keep the two side by side): PK keys are skipped; an exempt path (or one
6813
- * under an exempt ancestor) is covered whatever its value (scalar, field op,
6814
- * array ops, object); a non-exempt merge-strategy object recurses (only the
6815
- * supplied children are written); anything else is a non-exempt write.
6820
+ * (keep the two side by side): PK keys and `@db.onUpdate.now` fields are
6821
+ * skipped; an exempt path (or one under an exempt ancestor) is covered
6822
+ * whatever its value (scalar, field op, array ops, object); a non-exempt
6823
+ * merge-strategy object recurses (only the supplied children are written);
6824
+ * anything else is a non-exempt write.
6816
6825
  * True iff at least one key is covered and none is not.
6817
6826
  * @since 0.1.150
6818
6827
  */
@@ -6821,11 +6830,12 @@ function isVersionExemptPatch(data, table) {
6821
6830
  if (meta.versionExemptPaths.size === 0) return false;
6822
6831
  const primaryKeys = meta.primaryKeys;
6823
6832
  const flatMap = meta.flatMap;
6833
+ const stamps = meta.onUpdateNow;
6824
6834
  const walk = (obj, prefix) => {
6825
6835
  let result = "none";
6826
6836
  for (const [k, value] of Object.entries(obj)) {
6827
6837
  const key = prefix ? `${prefix}.${k}` : k;
6828
- if (primaryKeys.includes(key)) continue;
6838
+ if (primaryKeys.includes(key) || stamps.has(key)) continue;
6829
6839
  if (meta.isVersionExemptPath(key)) {
6830
6840
  result = "exempt";
6831
6841
  continue;
@@ -7138,7 +7148,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
7138
7148
  const singleStatement = !ignore && this._isSingleStatementWrite(payloads, depth, guard, check);
7139
7149
  return enrichFkViolation(this._meta, () => this._inWriteTransaction(singleStatement, async () => {
7140
7150
  const clone = depth === 0 ? _cloneWritePayload : _shallowPrunedClone;
7141
- let items = payloads.map((p) => this._applyDefaults(clone(p)));
7151
+ const now = Date.now();
7152
+ let items = payloads.map((p) => this._applyDefaults(clone(p), now));
7142
7153
  if (ignore) this._linkToByKeyInIgnoreMode(items);
7143
7154
  let originals = canNest ? items.map((item) => ({ ...item })) : [];
7144
7155
  const validator = this.getValidator("insert");
@@ -7214,10 +7225,12 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
7214
7225
  const versionColumn = this.versionColumn;
7215
7226
  const expectedVersions = Array.from({ length: payloads.length });
7216
7227
  const clone = depth === 0 ? _cloneWritePayload : _shallowPrunedClone;
7228
+ const now = Date.now();
7217
7229
  const items = payloads.map((p, i) => {
7218
7230
  const c = clone(p);
7219
7231
  expectedVersions[i] = separateCas(c, versionColumn);
7220
- return this._applyDefaults(c);
7232
+ this._stampOnUpdate(c, now, true);
7233
+ return this._applyDefaults(c, now);
7221
7234
  });
7222
7235
  const originals = canNest ? items.map((item) => ({ ...item })) : [];
7223
7236
  const validator = this.getValidator("bulkReplace");
@@ -7301,9 +7314,11 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
7301
7314
  const versionColumn = this.versionColumn;
7302
7315
  const expectedVersions = Array.from({ length: payloads.length });
7303
7316
  const clone = depth === 0 ? _cloneWritePayload : _shallowPrunedClone;
7317
+ const now = Date.now();
7304
7318
  const cloned = payloads.map((p, i) => {
7305
7319
  const c = clone(p);
7306
7320
  expectedVersions[i] = separateCas(c, versionColumn);
7321
+ this._stampOnUpdate(c, now, false);
7307
7322
  return c;
7308
7323
  });
7309
7324
  const validator = this.getValidator("bulkUpdate");
@@ -7350,6 +7365,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
7350
7365
  continue;
7351
7366
  }
7352
7367
  const updateOpts = expectedVersion === void 0 && versionColumn !== void 0 && isVersionExemptPatch(data, this) ? [KEEP_VERSION] : [];
7368
+ this._stampTopLevel(data, now);
7353
7369
  let result;
7354
7370
  if (this.adapter.supportsNativePatch()) {
7355
7371
  const ops = separateFieldOps(data);
@@ -7509,6 +7525,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
7509
7525
  await this._integrity.validateForeignKeys([data], this._meta, this._fkLookupResolver, this._writeTableResolver, true);
7510
7526
  const dataCopy = _cloneWritePayload(data);
7511
7527
  this._meta.stripDerived(dataCopy);
7528
+ const now = Date.now();
7529
+ this._stampOnUpdate(dataCopy, now, false);
7512
7530
  const versionColumn = this.versionColumn;
7513
7531
  if ("$cas" in dataCopy) throw new DbError("INVALID_QUERY", [{
7514
7532
  path: "$cas",
@@ -7516,6 +7534,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
7516
7534
  }]);
7517
7535
  if (versionColumn !== void 0) assertNoVersionWrites(dataCopy, versionColumn);
7518
7536
  const updateOpts = versionColumn !== void 0 && isVersionExemptPatch(dataCopy, this) ? [KEEP_VERSION] : [];
7537
+ this._stampTopLevel(dataCopy, now);
7519
7538
  await this._encryptItems([dataCopy], "patch");
7520
7539
  const update = decomposePatch(dataCopy, this);
7521
7540
  const ops = separateFieldOps(update);
@@ -7536,6 +7555,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
7536
7555
  this._guardMutationFilter(filter);
7537
7556
  await this._integrity.validateForeignKeys([data], this._meta, this._fkLookupResolver, this._writeTableResolver);
7538
7557
  const dataCopy = _cloneWritePayload(data);
7558
+ this._stampOnUpdate(dataCopy, Date.now(), true);
7539
7559
  await this._encryptItems([dataCopy], "write");
7540
7560
  return enrichFkViolation(this._meta, () => this.adapter.replaceMany(this._fieldMapper.translateFilter(filter, this._meta), this._fieldMapper.prepareForWrite(dataCopy, this._meta, this.adapter)));
7541
7561
  }
@@ -7910,8 +7930,10 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
7910
7930
  * that object is present (an absent or `null` embedded object stays so), and
7911
7931
  * in every object of an array on the way; a field inside a JSON value or an
7912
7932
  * array has no column default, so the SDK fills its function default too.
7933
+ * Every `now` default the SDK fills gets the one time `now` (one per write
7934
+ * call, so `createdAt` / `updatedAt` of a new row are equal).
7913
7935
  */
7914
- _applyDefaults(data) {
7936
+ _applyDefaults(data, now = Date.now()) {
7915
7937
  const nativeFns = this.adapter.nativeDefaultFns();
7916
7938
  const versionField = this._meta.versionField;
7917
7939
  for (const [field, def] of this._meta.defaults.entries()) {
@@ -7919,7 +7941,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
7919
7941
  let make;
7920
7942
  if (def.kind === "value") make = () => this._valueDefault(field, def.value);
7921
7943
  else if (def.kind === "fn" && (!nativeFns.has(def.fn) || findAncestorInSet(field, this._meta.jsonParents) !== void 0)) {
7922
- if (def.fn === "now") make = Date.now;
7944
+ if (def.fn === "now") make = () => now;
7923
7945
  else if (def.fn === "uuid") make = () => crypto.randomUUID();
7924
7946
  }
7925
7947
  if (!make) continue;
@@ -7929,6 +7951,26 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
7929
7951
  }
7930
7952
  return data;
7931
7953
  }
7954
+ /**
7955
+ * `@db.onUpdate.now` (since 0.1.156): sets each such field of a replace /
7956
+ * patch payload to `now`, overriding the caller's value. A nested field
7957
+ * (`audit.updatedAt`) is set inside its object only when the payload
7958
+ * carries that object — in every item of an array on the way and of the
7959
+ * items an array patch operator adds or updates — like
7960
+ * {@link _applyDefaults}. `topLevel: false` (a patch) instead drops the
7961
+ * caller's values of the table's own columns: {@link _stampTopLevel} sets
7962
+ * them once the patch is known to write something.
7963
+ */
7964
+ _stampOnUpdate(data, now, topLevel) {
7965
+ for (const field of this._meta.onUpdateNow) if (!field.includes(".")) if (topLevel) data[field] = now;
7966
+ else delete data[field];
7967
+ else if (this._nestedDefaultFillable(field)) stampNested(data, field.split("."), 0, now);
7968
+ }
7969
+ /** Sets the top-level `@db.onUpdate.now` columns of a non-empty patch to `now`. */
7970
+ _stampTopLevel(data, now) {
7971
+ if (isEmptyObject(data)) return;
7972
+ for (const field of this._meta.onUpdateNow) if (!field.includes(".")) data[field] = now;
7973
+ }
7932
7974
  /** {@link _nestedDefaultFillable} per nested default path. */
7933
7975
  _fillableDefaults;
7934
7976
  /**
@@ -8122,6 +8164,27 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
8122
8164
  }
8123
8165
  };
8124
8166
  /**
8167
+ * Sets `segs` below `value` to `now` (see `_stampOnUpdate`), through
8168
+ * arrays and the item lists of array patch operators (`$insert`, `$upsert`,
8169
+ * `$replace`, `$update` — not `$remove`).
8170
+ */
8171
+ function stampNested(value, segs, i, now) {
8172
+ if (Array.isArray(value)) {
8173
+ for (const item of value) stampNested(item, segs, i, now);
8174
+ return;
8175
+ }
8176
+ if (!isPlainObject(value)) return;
8177
+ const key = segs[i];
8178
+ if (i === segs.length - 1) {
8179
+ value[key] = now;
8180
+ return;
8181
+ }
8182
+ const next = value[key];
8183
+ if (isPlainObject(next) && Object.keys(next).some((k) => k.startsWith("$"))) {
8184
+ for (const [op, items] of Object.entries(next)) if (op !== "$remove") stampNested(items, segs, i + 1, now);
8185
+ } else stampNested(next, segs, i + 1, now);
8186
+ }
8187
+ /**
8125
8188
  * Sets `segs[i..]` to `make()` where it is `undefined`, inside present plain
8126
8189
  * objects only — fanned out over the objects of every array on the way.
8127
8190
  */