@atscript/db 0.1.105 → 0.1.107

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.
@@ -936,8 +936,15 @@ declare abstract class BaseDbAdapter {
936
936
  * ```
937
937
  */
938
938
  protected syncIndexesWithDiff(opts: {
939
+ /**
940
+ * Existing indexes. When `columns` is provided (ordered column names),
941
+ * plain/unique indexes whose definition no longer matches the model are
942
+ * dropped and recreated — a name match alone is not enough (e.g. a
943
+ * composite index that gained or lost a member keeps its name).
944
+ */
939
945
  listExisting(): Promise<Array<{
940
946
  name: string;
947
+ columns?: string[];
941
948
  }>>;
942
949
  createIndex(index: TDbIndex): Promise<void>;
943
950
  dropIndex(name: string): Promise<void>;
@@ -1007,9 +1014,18 @@ declare abstract class BaseDbAdapter {
1007
1014
  }>;
1008
1015
  /**
1009
1016
  * Whether this adapter supports geospatial search (`geoSearch()` and the
1010
- * `$geoWithin` filter operator). Override in adapters that do (MongoDB in v1).
1017
+ * `$geoWithin` filter operator). Override in adapters that do
1018
+ * (MongoDB, PostgreSQL with PostGIS, MySQL, SQLite).
1011
1019
  */
1012
1020
  isGeoSearchable(): boolean;
1021
+ /**
1022
+ * Resolves the targeted geo index (or the table's only one) and returns the
1023
+ * physical column it covers. Shared by SQL adapters; MongoDB resolves field
1024
+ * paths separately.
1025
+ *
1026
+ * @param indexName - Optional geo index name (multi-geo tables).
1027
+ */
1028
+ protected _resolveGeoColumn(indexName?: string): string;
1013
1029
  /**
1014
1030
  * Distance-ranked geospatial search. Results sorted by distance ascending;
1015
1031
  * each row carries a `$distance` field (meters from the query point).
@@ -1157,6 +1173,15 @@ declare abstract class BaseDbAdapter {
1157
1173
  * Optional — only relational adapters implement this.
1158
1174
  */
1159
1175
  dropColumns?(columns: string[]): Promise<void>;
1176
+ /**
1177
+ * Drops managed (`atscript__`-prefixed) indexes that reference any of the
1178
+ * given columns. Called by schema sync BEFORE {@link dropColumns} — engines
1179
+ * like SQLite refuse to drop a column while an index still references it,
1180
+ * and a composite index that survives by name must be rebuilt without the
1181
+ * removed column (recreated later by {@link syncIndexes}).
1182
+ * Optional — only relational adapters implement this.
1183
+ */
1184
+ dropIndexesForColumns?(columns: string[]): Promise<void>;
1160
1185
  /**
1161
1186
  * Drops a table by name (without needing a registered readable).
1162
1187
  * Used by schema sync to remove tables no longer in the schema.
@@ -1189,6 +1214,13 @@ declare abstract class BaseDbAdapter {
1189
1214
  * Optional — adapters that don't implement this skip type change detection.
1190
1215
  */
1191
1216
  typeMapper?(field: TDbFieldMeta): string;
1217
+ /**
1218
+ * Resolves any runtime state {@link typeMapper} depends on (e.g. detecting
1219
+ * pgvector/VECTOR support). Schema sync calls this BEFORE hashing the
1220
+ * desired schema — a type mapping that changes between hash time and DDL
1221
+ * time would make the stored hash never match and re-sync on every run.
1222
+ */
1223
+ prepareTypeMapper?(): Promise<void>;
1192
1224
  /**
1193
1225
  * Returns a value formatter for a field, or undefined if no formatting is needed.
1194
1226
  * Called once per field during build. The returned formatter(s) are cached and
@@ -936,8 +936,15 @@ declare abstract class BaseDbAdapter {
936
936
  * ```
937
937
  */
938
938
  protected syncIndexesWithDiff(opts: {
939
+ /**
940
+ * Existing indexes. When `columns` is provided (ordered column names),
941
+ * plain/unique indexes whose definition no longer matches the model are
942
+ * dropped and recreated — a name match alone is not enough (e.g. a
943
+ * composite index that gained or lost a member keeps its name).
944
+ */
939
945
  listExisting(): Promise<Array<{
940
946
  name: string;
947
+ columns?: string[];
941
948
  }>>;
942
949
  createIndex(index: TDbIndex): Promise<void>;
943
950
  dropIndex(name: string): Promise<void>;
@@ -1007,9 +1014,18 @@ declare abstract class BaseDbAdapter {
1007
1014
  }>;
1008
1015
  /**
1009
1016
  * Whether this adapter supports geospatial search (`geoSearch()` and the
1010
- * `$geoWithin` filter operator). Override in adapters that do (MongoDB in v1).
1017
+ * `$geoWithin` filter operator). Override in adapters that do
1018
+ * (MongoDB, PostgreSQL with PostGIS, MySQL, SQLite).
1011
1019
  */
1012
1020
  isGeoSearchable(): boolean;
1021
+ /**
1022
+ * Resolves the targeted geo index (or the table's only one) and returns the
1023
+ * physical column it covers. Shared by SQL adapters; MongoDB resolves field
1024
+ * paths separately.
1025
+ *
1026
+ * @param indexName - Optional geo index name (multi-geo tables).
1027
+ */
1028
+ protected _resolveGeoColumn(indexName?: string): string;
1013
1029
  /**
1014
1030
  * Distance-ranked geospatial search. Results sorted by distance ascending;
1015
1031
  * each row carries a `$distance` field (meters from the query point).
@@ -1157,6 +1173,15 @@ declare abstract class BaseDbAdapter {
1157
1173
  * Optional — only relational adapters implement this.
1158
1174
  */
1159
1175
  dropColumns?(columns: string[]): Promise<void>;
1176
+ /**
1177
+ * Drops managed (`atscript__`-prefixed) indexes that reference any of the
1178
+ * given columns. Called by schema sync BEFORE {@link dropColumns} — engines
1179
+ * like SQLite refuse to drop a column while an index still references it,
1180
+ * and a composite index that survives by name must be rebuilt without the
1181
+ * removed column (recreated later by {@link syncIndexes}).
1182
+ * Optional — only relational adapters implement this.
1183
+ */
1184
+ dropIndexesForColumns?(columns: string[]): Promise<void>;
1160
1185
  /**
1161
1186
  * Drops a table by name (without needing a registered readable).
1162
1187
  * Used by schema sync to remove tables no longer in the schema.
@@ -1189,6 +1214,13 @@ declare abstract class BaseDbAdapter {
1189
1214
  * Optional — adapters that don't implement this skip type change detection.
1190
1215
  */
1191
1216
  typeMapper?(field: TDbFieldMeta): string;
1217
+ /**
1218
+ * Resolves any runtime state {@link typeMapper} depends on (e.g. detecting
1219
+ * pgvector/VECTOR support). Schema sync calls this BEFORE hashing the
1220
+ * desired schema — a type mapping that changes between hash time and DDL
1221
+ * time would make the stored hash never match and re-sync on every run.
1222
+ */
1223
+ prepareTypeMapper?(): Promise<void>;
1192
1224
  /**
1193
1225
  * Returns a value formatter for a field, or undefined if no formatting is needed.
1194
1226
  * Called once per field during build. The returned formatter(s) are cached and
@@ -1,4 +1,4 @@
1
- import { C as TCascadeResolver, F as TDbDeleteResult, H as TDbInsertResult, K as TDbUpdateResult, V as TDbInsertManyResult, X as TFkLookupResolver, _t as TGenericLogger, a as TDbEncryptionOptions, c as BaseDbAdapter, ct as TWriteTableResolver, i as DbEncryption, ot as TTableResolver, pt as TableMetadata, t as AtscriptDbReadable } from "./db-readable-Ds01ezjj.mjs";
1
+ import { C as TCascadeResolver, F as TDbDeleteResult, H as TDbInsertResult, K as TDbUpdateResult, V as TDbInsertManyResult, X as TFkLookupResolver, _t as TGenericLogger, a as TDbEncryptionOptions, c as BaseDbAdapter, ct as TWriteTableResolver, i as DbEncryption, ot as TTableResolver, pt as TableMetadata, t as AtscriptDbReadable } from "./db-readable-Cd8DJCP0.mjs";
2
2
  import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
3
3
  import { FilterExpr } from "@uniqu/core";
4
4
 
@@ -1,4 +1,4 @@
1
- import { C as TCascadeResolver, F as TDbDeleteResult, H as TDbInsertResult, K as TDbUpdateResult, V as TDbInsertManyResult, X as TFkLookupResolver, _t as TGenericLogger, a as TDbEncryptionOptions, c as BaseDbAdapter, ct as TWriteTableResolver, i as DbEncryption, ot as TTableResolver, pt as TableMetadata, t as AtscriptDbReadable } from "./db-readable-BepVc21V.cjs";
1
+ import { C as TCascadeResolver, F as TDbDeleteResult, H as TDbInsertResult, K as TDbUpdateResult, V as TDbInsertManyResult, X as TFkLookupResolver, _t as TGenericLogger, a as TDbEncryptionOptions, c as BaseDbAdapter, ct as TWriteTableResolver, i as DbEncryption, ot as TTableResolver, pt as TableMetadata, t as AtscriptDbReadable } from "./db-readable-kouK9DON.cjs";
2
2
  import { FilterExpr } from "@uniqu/core";
3
3
  import { AtscriptQueryComparison, AtscriptQueryFieldRef, AtscriptQueryFieldRef as AtscriptQueryFieldRef$1, AtscriptQueryNode, AtscriptQueryNode as AtscriptQueryNode$1, AtscriptRef, FlatOf, NavPropsOf, OwnPropsOf, PrimaryKeyOf, TAtscriptAnnotatedType, TAtscriptDataType, Validator } from "@atscript/typescript/utils";
4
4
 
@@ -2383,8 +2383,9 @@ var BaseDbAdapter = class {
2383
2383
  */
2384
2384
  async syncIndexesWithDiff(opts) {
2385
2385
  const prefix = opts.prefix ?? "atscript__";
2386
- const existing = await opts.listExisting();
2387
- const existingNames = new Set(existing.filter((i) => i.name.startsWith(prefix)).map((i) => i.name));
2386
+ const managed = (await opts.listExisting()).filter((i) => i.name.startsWith(prefix));
2387
+ const existingNames = new Set(managed.map((i) => i.name));
2388
+ const existingColumns = new Map(managed.filter((i) => i.columns).map((i) => [i.name, i.columns]));
2388
2389
  const desiredNames = /* @__PURE__ */ new Set();
2389
2390
  for (const index of this._table.indexes.values()) {
2390
2391
  if (opts.warnUnsupportedTypes?.types.includes(index.type)) {
@@ -2393,7 +2394,18 @@ var BaseDbAdapter = class {
2393
2394
  }
2394
2395
  if (opts.shouldSkipType?.(index.type)) continue;
2395
2396
  desiredNames.add(index.key);
2396
- if (!existingNames.has(index.key)) await opts.createIndex(index);
2397
+ if (!existingNames.has(index.key)) {
2398
+ await opts.createIndex(index);
2399
+ continue;
2400
+ }
2401
+ if (index.type === "plain" || index.type === "unique") {
2402
+ const liveColumns = existingColumns.get(index.key);
2403
+ const desiredColumns = index.fields.map((f) => f.name);
2404
+ if (liveColumns && (liveColumns.length !== desiredColumns.length || liveColumns.some((c, i) => c !== desiredColumns[i]))) {
2405
+ await opts.dropIndex(index.key);
2406
+ await opts.createIndex(index);
2407
+ }
2408
+ }
2397
2409
  }
2398
2410
  for (const name of existingNames) if (!desiredNames.has(name)) await opts.dropIndex(name);
2399
2411
  }
@@ -2460,12 +2472,29 @@ var BaseDbAdapter = class {
2460
2472
  }
2461
2473
  /**
2462
2474
  * Whether this adapter supports geospatial search (`geoSearch()` and the
2463
- * `$geoWithin` filter operator). Override in adapters that do (MongoDB in v1).
2475
+ * `$geoWithin` filter operator). Override in adapters that do
2476
+ * (MongoDB, PostgreSQL with PostGIS, MySQL, SQLite).
2464
2477
  */
2465
2478
  isGeoSearchable() {
2466
2479
  return false;
2467
2480
  }
2468
2481
  /**
2482
+ * Resolves the targeted geo index (or the table's only one) and returns the
2483
+ * physical column it covers. Shared by SQL adapters; MongoDB resolves field
2484
+ * paths separately.
2485
+ *
2486
+ * @param indexName - Optional geo index name (multi-geo tables).
2487
+ */
2488
+ _resolveGeoColumn(indexName) {
2489
+ const geoIndexes = [...this._table.indexes.values()].filter((index) => index.type === "geo");
2490
+ const column = (indexName ? geoIndexes.find((candidate) => candidate.name === indexName) : geoIndexes[0])?.fields[0]?.name;
2491
+ if (!column) throw new DbError("GEO_INDEX_MISSING", [{
2492
+ path: indexName ?? "",
2493
+ message: `No geo index${indexName ? ` "${indexName}"` : ""} on "${this._table.tableName}"`
2494
+ }]);
2495
+ return column;
2496
+ }
2497
+ /**
2469
2498
  * Distance-ranked geospatial search. Results sorted by distance ascending;
2470
2499
  * each row carries a `$distance` field (meters from the query point).
2471
2500
  * `$maxDistance` / `$minDistance` (meters) ride in `query.controls`.
@@ -2383,8 +2383,9 @@ var BaseDbAdapter = class {
2383
2383
  */
2384
2384
  async syncIndexesWithDiff(opts) {
2385
2385
  const prefix = opts.prefix ?? "atscript__";
2386
- const existing = await opts.listExisting();
2387
- const existingNames = new Set(existing.filter((i) => i.name.startsWith(prefix)).map((i) => i.name));
2386
+ const managed = (await opts.listExisting()).filter((i) => i.name.startsWith(prefix));
2387
+ const existingNames = new Set(managed.map((i) => i.name));
2388
+ const existingColumns = new Map(managed.filter((i) => i.columns).map((i) => [i.name, i.columns]));
2388
2389
  const desiredNames = /* @__PURE__ */ new Set();
2389
2390
  for (const index of this._table.indexes.values()) {
2390
2391
  if (opts.warnUnsupportedTypes?.types.includes(index.type)) {
@@ -2393,7 +2394,18 @@ var BaseDbAdapter = class {
2393
2394
  }
2394
2395
  if (opts.shouldSkipType?.(index.type)) continue;
2395
2396
  desiredNames.add(index.key);
2396
- if (!existingNames.has(index.key)) await opts.createIndex(index);
2397
+ if (!existingNames.has(index.key)) {
2398
+ await opts.createIndex(index);
2399
+ continue;
2400
+ }
2401
+ if (index.type === "plain" || index.type === "unique") {
2402
+ const liveColumns = existingColumns.get(index.key);
2403
+ const desiredColumns = index.fields.map((f) => f.name);
2404
+ if (liveColumns && (liveColumns.length !== desiredColumns.length || liveColumns.some((c, i) => c !== desiredColumns[i]))) {
2405
+ await opts.dropIndex(index.key);
2406
+ await opts.createIndex(index);
2407
+ }
2408
+ }
2397
2409
  }
2398
2410
  for (const name of existingNames) if (!desiredNames.has(name)) await opts.dropIndex(name);
2399
2411
  }
@@ -2460,12 +2472,29 @@ var BaseDbAdapter = class {
2460
2472
  }
2461
2473
  /**
2462
2474
  * Whether this adapter supports geospatial search (`geoSearch()` and the
2463
- * `$geoWithin` filter operator). Override in adapters that do (MongoDB in v1).
2475
+ * `$geoWithin` filter operator). Override in adapters that do
2476
+ * (MongoDB, PostgreSQL with PostGIS, MySQL, SQLite).
2464
2477
  */
2465
2478
  isGeoSearchable() {
2466
2479
  return false;
2467
2480
  }
2468
2481
  /**
2482
+ * Resolves the targeted geo index (or the table's only one) and returns the
2483
+ * physical column it covers. Shared by SQL adapters; MongoDB resolves field
2484
+ * paths separately.
2485
+ *
2486
+ * @param indexName - Optional geo index name (multi-geo tables).
2487
+ */
2488
+ _resolveGeoColumn(indexName) {
2489
+ const geoIndexes = [...this._table.indexes.values()].filter((index) => index.type === "geo");
2490
+ const column = (indexName ? geoIndexes.find((candidate) => candidate.name === indexName) : geoIndexes[0])?.fields[0]?.name;
2491
+ if (!column) throw new require_db_error.DbError("GEO_INDEX_MISSING", [{
2492
+ path: indexName ?? "",
2493
+ message: `No geo index${indexName ? ` "${indexName}"` : ""} on "${this._table.tableName}"`
2494
+ }]);
2495
+ return column;
2496
+ }
2497
+ /**
2469
2498
  * Distance-ranked geospatial search. Results sorted by distance ascending;
2470
2499
  * each row carries a `$distance` field (meters from the query point).
2471
2500
  * `$maxDistance` / `$minDistance` (meters) ride in `query.controls`.
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_db_error = require("./db-error-DXwEzmYJ.cjs");
3
- const require_db_view = require("./db-view-Bw_hlrWw.cjs");
3
+ const require_db_view = require("./db-view-DICbqrN-.cjs");
4
4
  const require_ops = require("./ops.cjs");
5
5
  const require_validator = require("./validator-BSk8lPH1.cjs");
6
6
  let node_crypto = require("node:crypto");
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as TIdentification, A as TDbActionLevel, B as TDbIndexType, C as TCascadeResolver, D as TCrudPermissions, E as TCrudOp, F as TDbDeleteResult, G as TDbStorageType, H as TDbInsertResult, I as TDbFieldMeta, J as TExistingTableOption, K as TDbUpdateResult, L as TDbForeignKey, M as TDbCollation, N as TDbDefaultFn, O as TDbActionInfo, P as TDbDefaultValue, Q as TIdDescriptor, R as TDbIndex, S as PrimaryKeyOf, T as TColumnDiff, U as TDbReferentialAction, V as TDbInsertManyResult, W as TDbRelation, X as TFkLookupResolver, Y as TFieldMeta, Z as TFkLookupTarget, _ as FieldOpsFor, _t as TGenericLogger, a as TDbEncryptionOptions, at as TTableOptionDiff, b as NavPropsOf, c as BaseDbAdapter, ct as TWriteTableResolver, d as AggregateFn, dt as UniqueryControls, et as TMetaResponse, f as AggregateQuery, ft as WithRelation, g as DbQuery, gt as NoopLogger, h as DbControls, ht as isGeoPointType, i as DbEncryption, it as TSyncColumnResult, j as TDbActionProcessor, k as TDbActionIntent, l as AggregateControls, lt as TypedWithRelation, m as AtscriptDbWritable, mt as isGeoIndexableType, n as DbResponse, nt as TRelationInfo, o as DocumentFieldMapper, ot as TTableResolver, p as AggregateResult, pt as TableMetadata, q as TExistingColumn, r as resolveDesignType, rt as TSearchIndexInfo, s as FieldMappingStrategy, st as TValueFormatterPair, t as AtscriptDbReadable, tt as TMetadataOverrides, u as AggregateExpr, ut as Uniquery, v as FilterExpr, vt as UniquSelect, w as TCascadeTarget, x as OwnPropsOf, y as FlatOf, z as TDbIndexField } from "./db-readable-BepVc21V.cjs";
1
+ import { $ as TIdentification, A as TDbActionLevel, B as TDbIndexType, C as TCascadeResolver, D as TCrudPermissions, E as TCrudOp, F as TDbDeleteResult, G as TDbStorageType, H as TDbInsertResult, I as TDbFieldMeta, J as TExistingTableOption, K as TDbUpdateResult, L as TDbForeignKey, M as TDbCollation, N as TDbDefaultFn, O as TDbActionInfo, P as TDbDefaultValue, Q as TIdDescriptor, R as TDbIndex, S as PrimaryKeyOf, T as TColumnDiff, U as TDbReferentialAction, V as TDbInsertManyResult, W as TDbRelation, X as TFkLookupResolver, Y as TFieldMeta, Z as TFkLookupTarget, _ as FieldOpsFor, _t as TGenericLogger, a as TDbEncryptionOptions, at as TTableOptionDiff, b as NavPropsOf, c as BaseDbAdapter, ct as TWriteTableResolver, d as AggregateFn, dt as UniqueryControls, et as TMetaResponse, f as AggregateQuery, ft as WithRelation, g as DbQuery, gt as NoopLogger, h as DbControls, ht as isGeoPointType, i as DbEncryption, it as TSyncColumnResult, j as TDbActionProcessor, k as TDbActionIntent, l as AggregateControls, lt as TypedWithRelation, m as AtscriptDbWritable, mt as isGeoIndexableType, n as DbResponse, nt as TRelationInfo, o as DocumentFieldMapper, ot as TTableResolver, p as AggregateResult, pt as TableMetadata, q as TExistingColumn, r as resolveDesignType, rt as TSearchIndexInfo, s as FieldMappingStrategy, st as TValueFormatterPair, t as AtscriptDbReadable, tt as TMetadataOverrides, u as AggregateExpr, ut as Uniquery, v as FilterExpr, vt as UniquSelect, w as TCascadeTarget, x as OwnPropsOf, y as FlatOf, z as TDbIndexField } from "./db-readable-kouK9DON.cjs";
2
2
  import { a as $mul, c as $update, d as TDbFieldOp, f as TFieldOps, g as separateFieldOps, h as separateCas, 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-DJRnNTVo.cjs";
3
- import { a as TViewColumnMapping, c as AtscriptQueryNode, d as TViewPlan, f as translateQueryTree, h as NativeIntegrity, i as AtscriptDbView, l as AtscriptRef, m as IntegrityStrategy, n as TAdapterFactory, o as AtscriptQueryComparison, p as AtscriptDbTable, r as TDbSpaceOptions, s as AtscriptQueryFieldRef, t as DbSpace, u as TViewJoin } from "./db-space-CWhIEC7E.cjs";
3
+ import { a as TViewColumnMapping, c as AtscriptQueryNode, d as TViewPlan, f as translateQueryTree, h as NativeIntegrity, i as AtscriptDbView, l as AtscriptRef, m as IntegrityStrategy, n as TAdapterFactory, o as AtscriptQueryComparison, p as AtscriptDbTable, r as TDbSpaceOptions, s as AtscriptQueryFieldRef, t as DbSpace, u as TViewJoin } from "./db-space-x9FjYQlZ.cjs";
4
4
  import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.cjs";
5
5
  import { c as TArrayPatch, i as buildValidationContext, l as TDbPatch, n as ValidatorMode, o as forceNavNonOptional, r as buildDbValidator, s as isNavRelation, t as ValidationContext, u as getKeyProps } from "./validator-Crqe6vRW.cjs";
6
6
  import { AggregateQuery as AggregateQuery$1, FilterExpr as FilterExpr$1, FilterVisitor, Uniquery as Uniquery$1, computeInsights, isPrimitive, walkFilter } from "@uniqu/core";
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as TIdentification, A as TDbActionLevel, B as TDbIndexType, C as TCascadeResolver, D as TCrudPermissions, E as TCrudOp, F as TDbDeleteResult, G as TDbStorageType, H as TDbInsertResult, I as TDbFieldMeta, J as TExistingTableOption, K as TDbUpdateResult, L as TDbForeignKey, M as TDbCollation, N as TDbDefaultFn, O as TDbActionInfo, P as TDbDefaultValue, Q as TIdDescriptor, R as TDbIndex, S as PrimaryKeyOf, T as TColumnDiff, U as TDbReferentialAction, V as TDbInsertManyResult, W as TDbRelation, X as TFkLookupResolver, Y as TFieldMeta, Z as TFkLookupTarget, _ as FieldOpsFor, _t as TGenericLogger, a as TDbEncryptionOptions, at as TTableOptionDiff, b as NavPropsOf, c as BaseDbAdapter, ct as TWriteTableResolver, d as AggregateFn, dt as UniqueryControls, et as TMetaResponse, f as AggregateQuery, ft as WithRelation, g as DbQuery, gt as NoopLogger, h as DbControls, ht as isGeoPointType, i as DbEncryption, it as TSyncColumnResult, j as TDbActionProcessor, k as TDbActionIntent, l as AggregateControls, lt as TypedWithRelation, m as AtscriptDbWritable, mt as isGeoIndexableType, n as DbResponse, nt as TRelationInfo, o as DocumentFieldMapper, ot as TTableResolver, p as AggregateResult, pt as TableMetadata, q as TExistingColumn, r as resolveDesignType, rt as TSearchIndexInfo, s as FieldMappingStrategy, st as TValueFormatterPair, t as AtscriptDbReadable, tt as TMetadataOverrides, u as AggregateExpr, ut as Uniquery, v as FilterExpr, vt as UniquSelect, w as TCascadeTarget, x as OwnPropsOf, y as FlatOf, z as TDbIndexField } from "./db-readable-Ds01ezjj.mjs";
1
+ import { $ as TIdentification, A as TDbActionLevel, B as TDbIndexType, C as TCascadeResolver, D as TCrudPermissions, E as TCrudOp, F as TDbDeleteResult, G as TDbStorageType, H as TDbInsertResult, I as TDbFieldMeta, J as TExistingTableOption, K as TDbUpdateResult, L as TDbForeignKey, M as TDbCollation, N as TDbDefaultFn, O as TDbActionInfo, P as TDbDefaultValue, Q as TIdDescriptor, R as TDbIndex, S as PrimaryKeyOf, T as TColumnDiff, U as TDbReferentialAction, V as TDbInsertManyResult, W as TDbRelation, X as TFkLookupResolver, Y as TFieldMeta, Z as TFkLookupTarget, _ as FieldOpsFor, _t as TGenericLogger, a as TDbEncryptionOptions, at as TTableOptionDiff, b as NavPropsOf, c as BaseDbAdapter, ct as TWriteTableResolver, d as AggregateFn, dt as UniqueryControls, et as TMetaResponse, f as AggregateQuery, ft as WithRelation, g as DbQuery, gt as NoopLogger, h as DbControls, ht as isGeoPointType, i as DbEncryption, it as TSyncColumnResult, j as TDbActionProcessor, k as TDbActionIntent, l as AggregateControls, lt as TypedWithRelation, m as AtscriptDbWritable, mt as isGeoIndexableType, n as DbResponse, nt as TRelationInfo, o as DocumentFieldMapper, ot as TTableResolver, p as AggregateResult, pt as TableMetadata, q as TExistingColumn, r as resolveDesignType, rt as TSearchIndexInfo, s as FieldMappingStrategy, st as TValueFormatterPair, t as AtscriptDbReadable, tt as TMetadataOverrides, u as AggregateExpr, ut as Uniquery, v as FilterExpr, vt as UniquSelect, w as TCascadeTarget, x as OwnPropsOf, y as FlatOf, z as TDbIndexField } from "./db-readable-Cd8DJCP0.mjs";
2
2
  import { a as $mul, c as $update, d as TDbFieldOp, f as TFieldOps, g as separateFieldOps, h as separateCas, 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-DJRnNTVo.mjs";
3
- import { a as TViewColumnMapping, c as AtscriptQueryNode, d as TViewPlan, f as translateQueryTree, h as NativeIntegrity, i as AtscriptDbView, l as AtscriptRef, m as IntegrityStrategy, n as TAdapterFactory, o as AtscriptQueryComparison, p as AtscriptDbTable, r as TDbSpaceOptions, s as AtscriptQueryFieldRef, t as DbSpace, u as TViewJoin } from "./db-space-0U_4PiNS.mjs";
3
+ import { a as TViewColumnMapping, c as AtscriptQueryNode, d as TViewPlan, f as translateQueryTree, h as NativeIntegrity, i as AtscriptDbView, l as AtscriptRef, m as IntegrityStrategy, n as TAdapterFactory, o as AtscriptQueryComparison, p as AtscriptDbTable, r as TDbSpaceOptions, s as AtscriptQueryFieldRef, t as DbSpace, u as TViewJoin } from "./db-space-Cisga1hJ.mjs";
4
4
  import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.mjs";
5
5
  import { c as TArrayPatch, i as buildValidationContext, l as TDbPatch, n as ValidatorMode, o as forceNavNonOptional, r as buildDbValidator, s as isNavRelation, t as ValidationContext, u as getKeyProps } from "./validator-Crqe6vRW.mjs";
6
6
  import { AggregateQuery as AggregateQuery$1, FilterExpr as FilterExpr$1, FilterVisitor, Uniquery as Uniquery$1, computeInsights, isPrimitive, walkFilter } from "@uniqu/core";
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as DbError, t as CasExhaustedError } from "./db-error-BHPXOKzc.mjs";
2
- import { S as NoopLogger, _ as FieldMappingStrategy, a as ApplicationIntegrity, b as isGeoIndexableType, c as NativeIntegrity, d as assertGeoPoint, f as guardAggregate, g as DocumentFieldMapper, h as RelationalFieldMapper, i as decomposePatch, l as AtscriptDbReadable, m as guardQuery, n as AtscriptDbTable, o as BaseDbAdapter, p as guardFilter, r as assertNoVersionWrites, s as IntegrityStrategy, t as AtscriptDbView, u as resolveDesignType, v as UniquSelect, x as isGeoPointType, y as TableMetadata } from "./db-view-TyzB5VFb.mjs";
2
+ import { S as NoopLogger, _ as FieldMappingStrategy, a as ApplicationIntegrity, b as isGeoIndexableType, c as NativeIntegrity, d as assertGeoPoint, f as guardAggregate, g as DocumentFieldMapper, h as RelationalFieldMapper, i as decomposePatch, l as AtscriptDbReadable, m as guardQuery, n as AtscriptDbTable, o as BaseDbAdapter, p as guardFilter, r as assertNoVersionWrites, s as IntegrityStrategy, t as AtscriptDbView, u as resolveDesignType, v as UniquSelect, x as isGeoPointType, y as TableMetadata } from "./db-view-CNmNZRhb.mjs";
3
3
  import { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, getDbFieldOp, isDbFieldOp, separateCas, separateFieldOps } from "./ops.mjs";
4
4
  import { a as isNavRelation, i as forceNavNonOptional, n as buildValidationContext, o as createDbValidatorPlugin, s as getKeyProps, t as buildDbValidator } from "./validator-C7plt6Kf.mjs";
5
5
  import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
package/dist/plugin.cjs CHANGED
@@ -1308,7 +1308,7 @@ const dbPlugin = () => ({
1308
1308
  kind: "array",
1309
1309
  of: "number"
1310
1310
  },
1311
- documentation: "Represents a **geographic point** as a `[longitude, latitude]` tuple (GeoJSON coordinate order).\n\n- Equivalent to `number[]` of length 2, but explicitly marks the field as a geo point.\n- **Coordinate order is GeoJSON order: longitude first.**\n- Each adapter maps this to its native storage:\n - **MongoDB** → GeoJSON `{ type: 'Point', coordinates: [lng, lat] }`\n - **PostgreSQL** → PostGIS `geography(Point,4326)` (Phase 2)\n - **MySQL** → `POINT` SRID 4326 (Phase 2)\n - **SQLite** → JSON `TEXT` (Phase 2)\n\n**Example:**\n```atscript\n@db.index.geo\ngeo: db.geoPoint\n```\n"
1311
+ documentation: "Represents a **geographic point** as a `[longitude, latitude]` tuple (GeoJSON coordinate order).\n\n- Equivalent to `number[]` of length 2, but explicitly marks the field as a geo point.\n- **Coordinate order is GeoJSON order: longitude first.**\n- Each adapter maps this to its native storage:\n - **MongoDB** → GeoJSON `{ type: 'Point', coordinates: [lng, lat] }`\n - **PostgreSQL** → PostGIS `geography(Point,4326)` (JSONB without PostGIS)\n - **MySQL** → `POINT SRID 4326`\n - **SQLite** → JSON `TEXT` (haversine-based search)\n\n**Example:**\n```atscript\n@db.index.geo\ngeo: db.geoPoint\n```\n"
1312
1312
  },
1313
1313
  currencyCode: {
1314
1314
  type: "string",
package/dist/plugin.mjs CHANGED
@@ -1304,7 +1304,7 @@ const dbPlugin = () => ({
1304
1304
  kind: "array",
1305
1305
  of: "number"
1306
1306
  },
1307
- documentation: "Represents a **geographic point** as a `[longitude, latitude]` tuple (GeoJSON coordinate order).\n\n- Equivalent to `number[]` of length 2, but explicitly marks the field as a geo point.\n- **Coordinate order is GeoJSON order: longitude first.**\n- Each adapter maps this to its native storage:\n - **MongoDB** → GeoJSON `{ type: 'Point', coordinates: [lng, lat] }`\n - **PostgreSQL** → PostGIS `geography(Point,4326)` (Phase 2)\n - **MySQL** → `POINT` SRID 4326 (Phase 2)\n - **SQLite** → JSON `TEXT` (Phase 2)\n\n**Example:**\n```atscript\n@db.index.geo\ngeo: db.geoPoint\n```\n"
1307
+ documentation: "Represents a **geographic point** as a `[longitude, latitude]` tuple (GeoJSON coordinate order).\n\n- Equivalent to `number[]` of length 2, but explicitly marks the field as a geo point.\n- **Coordinate order is GeoJSON order: longitude first.**\n- Each adapter maps this to its native storage:\n - **MongoDB** → GeoJSON `{ type: 'Point', coordinates: [lng, lat] }`\n - **PostgreSQL** → PostGIS `geography(Point,4326)` (JSONB without PostGIS)\n - **MySQL** → `POINT SRID 4326`\n - **SQLite** → JSON `TEXT` (haversine-based search)\n\n**Example:**\n```atscript\n@db.index.geo\ngeo: db.geoPoint\n```\n"
1308
1308
  },
1309
1309
  currencyCode: {
1310
1310
  type: "string",
package/dist/rel.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { L as TDbForeignKey, W as TDbRelation, _t as TGenericLogger, c as BaseDbAdapter, ct as TWriteTableResolver, ot as TTableResolver, pt as TableMetadata } from "./db-readable-BepVc21V.cjs";
1
+ import { L as TDbForeignKey, W as TDbRelation, _t as TGenericLogger, c as BaseDbAdapter, ct as TWriteTableResolver, ot as TTableResolver, pt as TableMetadata } from "./db-readable-kouK9DON.cjs";
2
2
  import { t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.cjs";
3
3
  import { FilterExpr, WithRelation } from "@uniqu/core";
4
4
  import { Validator } from "@atscript/typescript/utils";
package/dist/rel.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { L as TDbForeignKey, W as TDbRelation, _t as TGenericLogger, c as BaseDbAdapter, ct as TWriteTableResolver, ot as TTableResolver, pt as TableMetadata } from "./db-readable-Ds01ezjj.mjs";
1
+ import { L as TDbForeignKey, W as TDbRelation, _t as TGenericLogger, c as BaseDbAdapter, ct as TWriteTableResolver, ot as TTableResolver, pt as TableMetadata } from "./db-readable-Cd8DJCP0.mjs";
2
2
  import { t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.mjs";
3
3
  import { Validator } from "@atscript/typescript/utils";
4
4
  import { FilterExpr, WithRelation } from "@uniqu/core";
package/dist/sync.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_db_view = require("./db-view-Bw_hlrWw.cjs");
2
+ const require_db_view = require("./db-view-DICbqrN-.cjs");
3
3
  //#region src/schema/schema-hash.ts
4
4
  /** Extracts sorted field snapshots from a readable's field descriptors. */
5
5
  function extractFieldSnapshots(fields, typeMapper) {
@@ -671,20 +671,37 @@ async function executeSyncTable(readable, safe, trackedNames, deps) {
671
671
  }
672
672
  }
673
673
  }
674
- await adapter.syncIndexes();
675
- if (adapter.syncForeignKeys) await adapter.syncForeignKeys();
676
- if (adapter.afterSyncTable) await adapter.afterSyncTable();
674
+ try {
675
+ await adapter.syncIndexes();
676
+ if (adapter.syncForeignKeys) await adapter.syncForeignKeys();
677
+ if (adapter.afterSyncTable) await adapter.afterSyncTable();
678
+ } catch (error) {
679
+ const msg = `Index/FK sync failed on ${name}: ${error.message}`;
680
+ deps.logger.error?.(`[schema-sync] ${msg}`);
681
+ init.errors = [...init.errors ?? [], msg];
682
+ init.status = "error";
683
+ }
677
684
  return new SyncEntry(init);
678
685
  }
679
- async function executeSyncView(view, trackedNames, deps) {
680
- const renamedFrom = view.renamedFrom;
681
- const isRenamed = renamedFrom && trackedNames.has(renamedFrom);
682
- if (isRenamed) await deps.space.dropViewByName(renamedFrom);
683
- let definitionChanged = false;
684
- if (!isRenamed && trackedNames.has(view.tableName)) {
685
- definitionChanged = await viewDefinitionChanged(view, deps.store);
686
- if (definitionChanged) await deps.space.dropViewByName(view.tableName);
686
+ /** Determines whether a view's predecessor (on rename) or stale definition must be dropped. */
687
+ async function planViewSync(view, trackedNames, store) {
688
+ const isRenamed = !!(view.renamedFrom && trackedNames.has(view.renamedFrom));
689
+ return {
690
+ isRenamed,
691
+ definitionChanged: !isRenamed && trackedNames.has(view.tableName) && await viewDefinitionChanged(view, store)
692
+ };
693
+ }
694
+ /** Drops the stale view a plan identified — views don't support ALTER VIEW. */
695
+ async function dropOutdatedView(view, plan, space) {
696
+ if (plan.isRenamed) await space.dropViewByName(view.renamedFrom);
697
+ else if (plan.definitionChanged) await space.dropViewByName(view.tableName);
698
+ }
699
+ async function executeSyncView(view, trackedNames, deps, plan) {
700
+ if (!plan) {
701
+ plan = await planViewSync(view, trackedNames, deps.store);
702
+ await dropOutdatedView(view, plan, deps.space);
687
703
  }
704
+ const { isRenamed, definitionChanged } = plan;
688
705
  await view.dbAdapter.ensureTable();
689
706
  const viewType = view.viewPlan.materialized ? "M" : "V";
690
707
  let status;
@@ -695,7 +712,7 @@ async function executeSyncView(view, trackedNames, deps) {
695
712
  name: view.tableName,
696
713
  status,
697
714
  viewType,
698
- renamedFrom: isRenamed ? renamedFrom : void 0,
715
+ renamedFrom: isRenamed ? view.renamedFrom : void 0,
699
716
  recreated: definitionChanged || void 0
700
717
  });
701
718
  }
@@ -750,6 +767,7 @@ async function applyColumnDiff(adapter, readable, diff, init, safe, logger) {
750
767
  }
751
768
  if (!safe && !init.recreated && init.status !== "error" && diff.removed.length > 0 && adapter.dropColumns) {
752
769
  const colNames = diff.removed.map((c) => c.name);
770
+ if (adapter.dropIndexesForColumns) await adapter.dropIndexesForColumns(colNames);
753
771
  await adapter.dropColumns(colNames);
754
772
  init.columnsDropped = colNames;
755
773
  init.status = "alter";
@@ -791,19 +809,28 @@ var SchemaSync = class {
791
809
  else views.push(readable);
792
810
  } else tables.push(readable);
793
811
  }
812
+ const allReadables = [
813
+ ...tables,
814
+ ...views,
815
+ ...externalViews
816
+ ];
817
+ const snapshots = [];
818
+ for (const r of allReadables) {
819
+ if (r.isView) {
820
+ snapshots.push(computeViewSnapshot(r));
821
+ continue;
822
+ }
823
+ r.fieldDescriptors;
824
+ await r.dbAdapter.prepareTypeMapper?.();
825
+ const tm = r.dbAdapter.typeMapper?.bind(r.dbAdapter);
826
+ const opts = r.dbAdapter.getDesiredTableOptions?.();
827
+ snapshots.push(computeTableSnapshot(r, tm, opts));
828
+ }
794
829
  return {
795
830
  tables,
796
831
  views,
797
832
  externalViews,
798
- hash: computeSchemaHash([
799
- ...tables,
800
- ...views,
801
- ...externalViews
802
- ].map((r) => {
803
- if (r.isView) return computeViewSnapshot(r);
804
- const tm = r.dbAdapter.typeMapper?.bind(r.dbAdapter);
805
- return computeTableSnapshot(r, tm, (r.fieldDescriptors, r.dbAdapter.getDesiredTableOptions?.()));
806
- }))
833
+ hash: computeSchemaHash(snapshots)
807
834
  };
808
835
  }
809
836
  /**
@@ -944,6 +971,13 @@ var SchemaSync = class {
944
971
  const previouslyTracked = await this.store.readTrackedList();
945
972
  const trackedNames = new Set(previouslyTracked.map((e) => e.name));
946
973
  const deps = this.buildExecutorDeps();
974
+ const viewPlans = /* @__PURE__ */ new Map();
975
+ for (const readable of views) {
976
+ const view = readable;
977
+ const plan = await planViewSync(view, trackedNames, this.store);
978
+ viewPlans.set(view.tableName, plan);
979
+ await dropOutdatedView(view, plan, this.space);
980
+ }
947
981
  const entries = [];
948
982
  for (const readable of tables) {
949
983
  this.assertLockHeld(heartbeat.getAbortReason);
@@ -953,7 +987,7 @@ var SchemaSync = class {
953
987
  const removed = await this.detectRemoved(allReadables, previouslyTracked);
954
988
  for (const readable of views) {
955
989
  this.assertLockHeld(heartbeat.getAbortReason);
956
- entries.push(await executeSyncView(readable, trackedNames, deps));
990
+ entries.push(await executeSyncView(readable, trackedNames, deps, viewPlans.get(readable.tableName)));
957
991
  }
958
992
  const externalEntries = await Promise.all(externalViews.map((v) => this.checkExternalView(v)));
959
993
  entries.push(...externalEntries);
@@ -967,7 +1001,9 @@ var SchemaSync = class {
967
1001
  entries.push(...removed.filter((e) => e.viewType !== "E"));
968
1002
  }
969
1003
  this.assertLockHeld(heartbeat.getAbortReason);
1004
+ const erroredNames = new Set(entries.filter((e) => e.status === "error" && e.viewType !== "E").map((e) => e.name));
970
1005
  for (const readable of allReadables) {
1006
+ if (erroredNames.has(readable.tableName)) continue;
971
1007
  const adapter = readable.dbAdapter;
972
1008
  const tm = adapter.typeMapper?.bind(adapter);
973
1009
  const opts = adapter.getDesiredTableOptions?.();
@@ -980,7 +1016,7 @@ var SchemaSync = class {
980
1016
  }
981
1017
  for (const readable of allReadables) if (readable.renamedFrom) await this.store.deleteTableSnapshot(readable.renamedFrom);
982
1018
  await this.store.writeTrackedList(allReadables);
983
- await this.store.writeHash(hash);
1019
+ if (erroredNames.size === 0) await this.store.writeHash(hash);
984
1020
  return {
985
1021
  status: "synced",
986
1022
  schemaHash: hash,
package/dist/sync.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { G as TDbStorageType, I as TDbFieldMeta, J as TExistingTableOption, L as TDbForeignKey, P as TDbDefaultValue, T as TColumnDiff, _t as TGenericLogger, at as TTableOptionDiff, q as TExistingColumn, t as AtscriptDbReadable } from "./db-readable-BepVc21V.cjs";
2
- import { i as AtscriptDbView, t as DbSpace } from "./db-space-CWhIEC7E.cjs";
1
+ import { G as TDbStorageType, I as TDbFieldMeta, J as TExistingTableOption, L as TDbForeignKey, P as TDbDefaultValue, T as TColumnDiff, _t as TGenericLogger, at as TTableOptionDiff, q as TExistingColumn, t as AtscriptDbReadable } from "./db-readable-kouK9DON.cjs";
2
+ import { i as AtscriptDbView, t as DbSpace } from "./db-space-x9FjYQlZ.cjs";
3
3
  import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
4
4
 
5
5
  //#region src/schema/sync-entry.d.ts
package/dist/sync.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { G as TDbStorageType, I as TDbFieldMeta, J as TExistingTableOption, L as TDbForeignKey, P as TDbDefaultValue, T as TColumnDiff, _t as TGenericLogger, at as TTableOptionDiff, q as TExistingColumn, t as AtscriptDbReadable } from "./db-readable-Ds01ezjj.mjs";
2
- import { i as AtscriptDbView, t as DbSpace } from "./db-space-0U_4PiNS.mjs";
1
+ import { G as TDbStorageType, I as TDbFieldMeta, J as TExistingTableOption, L as TDbForeignKey, P as TDbDefaultValue, T as TColumnDiff, _t as TGenericLogger, at as TTableOptionDiff, q as TExistingColumn, t as AtscriptDbReadable } from "./db-readable-Cd8DJCP0.mjs";
2
+ import { i as AtscriptDbView, t as DbSpace } from "./db-space-Cisga1hJ.mjs";
3
3
  import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
4
4
 
5
5
  //#region src/schema/sync-entry.d.ts
package/dist/sync.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { S as NoopLogger } from "./db-view-TyzB5VFb.mjs";
1
+ import { S as NoopLogger } from "./db-view-CNmNZRhb.mjs";
2
2
  //#region src/schema/schema-hash.ts
3
3
  /** Extracts sorted field snapshots from a readable's field descriptors. */
4
4
  function extractFieldSnapshots(fields, typeMapper) {
@@ -670,20 +670,37 @@ async function executeSyncTable(readable, safe, trackedNames, deps) {
670
670
  }
671
671
  }
672
672
  }
673
- await adapter.syncIndexes();
674
- if (adapter.syncForeignKeys) await adapter.syncForeignKeys();
675
- if (adapter.afterSyncTable) await adapter.afterSyncTable();
673
+ try {
674
+ await adapter.syncIndexes();
675
+ if (adapter.syncForeignKeys) await adapter.syncForeignKeys();
676
+ if (adapter.afterSyncTable) await adapter.afterSyncTable();
677
+ } catch (error) {
678
+ const msg = `Index/FK sync failed on ${name}: ${error.message}`;
679
+ deps.logger.error?.(`[schema-sync] ${msg}`);
680
+ init.errors = [...init.errors ?? [], msg];
681
+ init.status = "error";
682
+ }
676
683
  return new SyncEntry(init);
677
684
  }
678
- async function executeSyncView(view, trackedNames, deps) {
679
- const renamedFrom = view.renamedFrom;
680
- const isRenamed = renamedFrom && trackedNames.has(renamedFrom);
681
- if (isRenamed) await deps.space.dropViewByName(renamedFrom);
682
- let definitionChanged = false;
683
- if (!isRenamed && trackedNames.has(view.tableName)) {
684
- definitionChanged = await viewDefinitionChanged(view, deps.store);
685
- if (definitionChanged) await deps.space.dropViewByName(view.tableName);
685
+ /** Determines whether a view's predecessor (on rename) or stale definition must be dropped. */
686
+ async function planViewSync(view, trackedNames, store) {
687
+ const isRenamed = !!(view.renamedFrom && trackedNames.has(view.renamedFrom));
688
+ return {
689
+ isRenamed,
690
+ definitionChanged: !isRenamed && trackedNames.has(view.tableName) && await viewDefinitionChanged(view, store)
691
+ };
692
+ }
693
+ /** Drops the stale view a plan identified — views don't support ALTER VIEW. */
694
+ async function dropOutdatedView(view, plan, space) {
695
+ if (plan.isRenamed) await space.dropViewByName(view.renamedFrom);
696
+ else if (plan.definitionChanged) await space.dropViewByName(view.tableName);
697
+ }
698
+ async function executeSyncView(view, trackedNames, deps, plan) {
699
+ if (!plan) {
700
+ plan = await planViewSync(view, trackedNames, deps.store);
701
+ await dropOutdatedView(view, plan, deps.space);
686
702
  }
703
+ const { isRenamed, definitionChanged } = plan;
687
704
  await view.dbAdapter.ensureTable();
688
705
  const viewType = view.viewPlan.materialized ? "M" : "V";
689
706
  let status;
@@ -694,7 +711,7 @@ async function executeSyncView(view, trackedNames, deps) {
694
711
  name: view.tableName,
695
712
  status,
696
713
  viewType,
697
- renamedFrom: isRenamed ? renamedFrom : void 0,
714
+ renamedFrom: isRenamed ? view.renamedFrom : void 0,
698
715
  recreated: definitionChanged || void 0
699
716
  });
700
717
  }
@@ -749,6 +766,7 @@ async function applyColumnDiff(adapter, readable, diff, init, safe, logger) {
749
766
  }
750
767
  if (!safe && !init.recreated && init.status !== "error" && diff.removed.length > 0 && adapter.dropColumns) {
751
768
  const colNames = diff.removed.map((c) => c.name);
769
+ if (adapter.dropIndexesForColumns) await adapter.dropIndexesForColumns(colNames);
752
770
  await adapter.dropColumns(colNames);
753
771
  init.columnsDropped = colNames;
754
772
  init.status = "alter";
@@ -790,19 +808,28 @@ var SchemaSync = class {
790
808
  else views.push(readable);
791
809
  } else tables.push(readable);
792
810
  }
811
+ const allReadables = [
812
+ ...tables,
813
+ ...views,
814
+ ...externalViews
815
+ ];
816
+ const snapshots = [];
817
+ for (const r of allReadables) {
818
+ if (r.isView) {
819
+ snapshots.push(computeViewSnapshot(r));
820
+ continue;
821
+ }
822
+ r.fieldDescriptors;
823
+ await r.dbAdapter.prepareTypeMapper?.();
824
+ const tm = r.dbAdapter.typeMapper?.bind(r.dbAdapter);
825
+ const opts = r.dbAdapter.getDesiredTableOptions?.();
826
+ snapshots.push(computeTableSnapshot(r, tm, opts));
827
+ }
793
828
  return {
794
829
  tables,
795
830
  views,
796
831
  externalViews,
797
- hash: computeSchemaHash([
798
- ...tables,
799
- ...views,
800
- ...externalViews
801
- ].map((r) => {
802
- if (r.isView) return computeViewSnapshot(r);
803
- const tm = r.dbAdapter.typeMapper?.bind(r.dbAdapter);
804
- return computeTableSnapshot(r, tm, (r.fieldDescriptors, r.dbAdapter.getDesiredTableOptions?.()));
805
- }))
832
+ hash: computeSchemaHash(snapshots)
806
833
  };
807
834
  }
808
835
  /**
@@ -943,6 +970,13 @@ var SchemaSync = class {
943
970
  const previouslyTracked = await this.store.readTrackedList();
944
971
  const trackedNames = new Set(previouslyTracked.map((e) => e.name));
945
972
  const deps = this.buildExecutorDeps();
973
+ const viewPlans = /* @__PURE__ */ new Map();
974
+ for (const readable of views) {
975
+ const view = readable;
976
+ const plan = await planViewSync(view, trackedNames, this.store);
977
+ viewPlans.set(view.tableName, plan);
978
+ await dropOutdatedView(view, plan, this.space);
979
+ }
946
980
  const entries = [];
947
981
  for (const readable of tables) {
948
982
  this.assertLockHeld(heartbeat.getAbortReason);
@@ -952,7 +986,7 @@ var SchemaSync = class {
952
986
  const removed = await this.detectRemoved(allReadables, previouslyTracked);
953
987
  for (const readable of views) {
954
988
  this.assertLockHeld(heartbeat.getAbortReason);
955
- entries.push(await executeSyncView(readable, trackedNames, deps));
989
+ entries.push(await executeSyncView(readable, trackedNames, deps, viewPlans.get(readable.tableName)));
956
990
  }
957
991
  const externalEntries = await Promise.all(externalViews.map((v) => this.checkExternalView(v)));
958
992
  entries.push(...externalEntries);
@@ -966,7 +1000,9 @@ var SchemaSync = class {
966
1000
  entries.push(...removed.filter((e) => e.viewType !== "E"));
967
1001
  }
968
1002
  this.assertLockHeld(heartbeat.getAbortReason);
1003
+ const erroredNames = new Set(entries.filter((e) => e.status === "error" && e.viewType !== "E").map((e) => e.name));
969
1004
  for (const readable of allReadables) {
1005
+ if (erroredNames.has(readable.tableName)) continue;
970
1006
  const adapter = readable.dbAdapter;
971
1007
  const tm = adapter.typeMapper?.bind(adapter);
972
1008
  const opts = adapter.getDesiredTableOptions?.();
@@ -979,7 +1015,7 @@ var SchemaSync = class {
979
1015
  }
980
1016
  for (const readable of allReadables) if (readable.renamedFrom) await this.store.deleteTableSnapshot(readable.renamedFrom);
981
1017
  await this.store.writeTrackedList(allReadables);
982
- await this.store.writeHash(hash);
1018
+ if (erroredNames.size === 0) await this.store.writeHash(hash);
983
1019
  return {
984
1020
  status: "synced",
985
1021
  schemaHash: hash,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db",
3
- "version": "0.1.105",
3
+ "version": "0.1.107",
4
4
  "description": "Database adapter utilities for atscript.",
5
5
  "keywords": [
6
6
  "atscript",
@@ -71,14 +71,14 @@
71
71
  "access": "public"
72
72
  },
73
73
  "devDependencies": {
74
- "@atscript/core": "^0.1.76",
75
- "@atscript/typescript": "^0.1.76",
74
+ "@atscript/core": "^0.1.77",
75
+ "@atscript/typescript": "^0.1.77",
76
76
  "@uniqu/core": "^0.1.6",
77
- "unplugin-atscript": "^0.1.76"
77
+ "unplugin-atscript": "^0.1.77"
78
78
  },
79
79
  "peerDependencies": {
80
- "@atscript/core": "^0.1.76",
81
- "@atscript/typescript": "^0.1.76",
80
+ "@atscript/core": "^0.1.77",
81
+ "@atscript/typescript": "^0.1.77",
82
82
  "@uniqu/core": "^0.1.6"
83
83
  },
84
84
  "scripts": {