@atscript/db 0.1.127 → 0.1.128
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{db-readable-BkAGccv9.d.mts → db-readable-BTl4NzNN.d.mts} +289 -16
- package/dist/{db-readable-C0nDKX8A.d.cts → db-readable-CkbZn_z-.d.cts} +289 -16
- package/dist/{db-space-B_ASuDaR.d.mts → db-space-BEB5Jl8M.d.mts} +81 -29
- package/dist/{db-space-CSntT6yS.d.cts → db-space-BevOyVrC.d.cts} +81 -29
- package/dist/{db-view-BP0Qbeux.cjs → db-view-C8-MsjND.cjs} +696 -97
- package/dist/{db-view-C8rZM5_N.mjs → db-view-Ck0I-PMI.mjs} +643 -98
- package/dist/index.cjs +46 -2
- package/dist/index.d.cts +162 -37
- package/dist/index.d.mts +162 -37
- package/dist/index.mjs +36 -4
- package/dist/{ops-DJRnNTVo.d.cts → ops-AqhV7s9o.d.cts} +24 -1
- package/dist/{ops-DJRnNTVo.d.mts → ops-AqhV7s9o.d.mts} +24 -1
- package/dist/ops.cjs +43 -0
- package/dist/ops.d.cts +2 -2
- package/dist/ops.d.mts +2 -2
- package/dist/ops.mjs +43 -1
- package/dist/plugin.cjs +12 -5
- package/dist/plugin.mjs +12 -5
- package/dist/rel.d.cts +1 -1
- package/dist/rel.d.mts +1 -1
- package/dist/sync.cjs +1096 -361
- package/dist/sync.d.cts +234 -15
- package/dist/sync.d.mts +234 -15
- package/dist/sync.mjs +1095 -362
- package/dist/{validator-CSGug4vg.cjs → validator-BNUHCXIE.cjs} +31 -2
- package/dist/{validator-BcBtg8yW.d.cts → validator-DuAmWc8L.d.cts} +38 -1
- package/dist/{validator-BcBtg8yW.d.mts → validator-DuAmWc8L.d.mts} +38 -1
- package/dist/{validator-0vRXN51D.mjs → validator-eKYWf3xf.mjs} +20 -3
- package/dist/validator.cjs +7 -1
- package/dist/validator.d.cts +3 -3
- package/dist/validator.d.mts +3 -3
- package/dist/validator.mjs +4 -3
- package/package.json +8 -8
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { n as DbError, t as CasExhaustedError } from "./db-error-BHPXOKzc.mjs";
|
|
2
|
-
import { C as NoopLogger, S as
|
|
2
|
+
import { A as isGeoIndexableType, C as unsupportedOperatorMessage, D as UniquSelect, E as FieldMappingStrategy, M as NoopLogger, O as TableMetadata, S as sortFieldNames, T as DocumentFieldMapper, _ as guardAggregate, a as decomposePatch, b as guardPaths, c as createFailureCollector, d as AtscriptDbReadable, f as resolveDesignType, g as collectQueryPaths, h as classifyQueryPath, i as assertNoVersionWrites, j as isGeoPointType, k as findAncestorInSet, l as IntegrityStrategy, m as checkHavingKeys, n as isAtscriptDbView, o as ApplicationIntegrity, p as assertGeoPoint, r as AtscriptDbTable, s as BaseDbAdapter, t as AtscriptDbView, u as NativeIntegrity, v as guardFilter, w as RelationalFieldMapper, x as guardQuery, y as guardPath } from "./db-view-Ck0I-PMI.mjs";
|
|
3
|
+
import { a as forceNavNonOptional, c as getKeyProps, l as isEmptyObject, o as isNavRelation, r as buildValidationContext, s as createDbValidatorPlugin, t as buildDbValidator, u as isPlainObject } from "./validator-eKYWf3xf.mjs";
|
|
3
4
|
import { t as DEFAULT_DB_SPACE } from "./consts-C_-5_pFq.mjs";
|
|
4
|
-
import { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, getDbFieldOp, isDbFieldOp, separateCas, separateFieldOps } from "./ops.mjs";
|
|
5
|
-
import { a as forceNavNonOptional, c as getKeyProps, o as isNavRelation, r as buildValidationContext, s as createDbValidatorPlugin, t as buildDbValidator } from "./validator-0vRXN51D.mjs";
|
|
5
|
+
import { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, getDbFieldOp, isDbFieldOp, reconcileCas, separateCas, separateFieldOps } from "./ops.mjs";
|
|
6
6
|
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
7
7
|
import { computeInsights, isPrimitive, walkFilter } from "@uniqu/core";
|
|
8
8
|
//#region src/encryption.ts
|
|
@@ -149,6 +149,11 @@ var DbEncryption = class {
|
|
|
149
149
|
* a version conflict (`matchedCount === 0`) it re-reads the row, calls the
|
|
150
150
|
* mutator with the fresh state, and retries — up to `maxAttempts` times.
|
|
151
151
|
*
|
|
152
|
+
* A mutator that returns `undefined` or an empty object `{}` aborts without
|
|
153
|
+
* writing: the helper resolves `{ matchedCount: 1, modifiedCount: 0 }` and the
|
|
154
|
+
* version does not move (since 0.1.128 — previously `{}` reported a fabricated
|
|
155
|
+
* match as well, but through the empty-patch short-circuit).
|
|
156
|
+
*
|
|
152
157
|
* The filter (typically the primary key) is threaded into the update payload
|
|
153
158
|
* so the table layer can extract the row identity. If `mutator` returns
|
|
154
159
|
* fields that overlap with the filter, the patch wins (last-write semantics
|
|
@@ -178,6 +183,10 @@ async function withOptimisticRetry(table, filter, mutator, opts) {
|
|
|
178
183
|
}]);
|
|
179
184
|
lastSeenVersion = row[versionColumn];
|
|
180
185
|
const patch = await mutator(row);
|
|
186
|
+
if (patch === void 0 || isEmptyObject(patch)) return {
|
|
187
|
+
matchedCount: 1,
|
|
188
|
+
modifiedCount: 0
|
|
189
|
+
};
|
|
181
190
|
const result = await table.updateOne({
|
|
182
191
|
...filter,
|
|
183
192
|
...patch,
|
|
@@ -293,6 +302,29 @@ var DbSpace = class {
|
|
|
293
302
|
const adapter = this._getAdminAdapter();
|
|
294
303
|
if (adapter.dropViewByName) await adapter.dropViewByName(viewName);
|
|
295
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Drops a group of mutually referencing tables as one operation.
|
|
307
|
+
* Used by schema sync to remove a foreign-key cycle no longer in the schema.
|
|
308
|
+
* @since 0.1.128
|
|
309
|
+
*/
|
|
310
|
+
async dropTablesByName(tableNames) {
|
|
311
|
+
await this._getAdminAdapter().dropTablesByName(tableNames);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Live foreign keys referencing `tableName`, or `undefined` when the
|
|
315
|
+
* adapter cannot introspect them. Used by schema sync for drop ordering
|
|
316
|
+
* and surviving-reference checks of tables without a registered readable.
|
|
317
|
+
* @since 0.1.128
|
|
318
|
+
*/
|
|
319
|
+
async getReferencingForeignKeys(tableName) {
|
|
320
|
+
return this._getAdminAdapter().getReferencingForeignKeys?.(tableName);
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* A factory-fresh adapter with NO registered readable. Only the name-taking
|
|
324
|
+
* primitives may run on it (`dropTableByName`, `dropViewByName`,
|
|
325
|
+
* `dropTablesByName`, `getReferencingForeignKeys`) — adapters derive the
|
|
326
|
+
* schema for those from the driver/connection, not from a bound table.
|
|
327
|
+
*/
|
|
296
328
|
_getAdminAdapter() {
|
|
297
329
|
return this._adminAdapter ??= this.adapterFactory();
|
|
298
330
|
}
|
|
@@ -340,4 +372,4 @@ function translateQueryTree(node, resolveField) {
|
|
|
340
372
|
return { [leftField]: { [comp.op]: comp.right } };
|
|
341
373
|
}
|
|
342
374
|
//#endregion
|
|
343
|
-
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, ApplicationIntegrity, AtscriptDbReadable, AtscriptDbTable, AtscriptDbView, BaseDbAdapter, CasExhaustedError, DEFAULT_DB_SPACE, DbEncryption, DbError, DbSpace, DocumentFieldMapper, FieldMappingStrategy, IntegrityStrategy, NativeIntegrity, NoopLogger, RelationalFieldMapper, TableMetadata, UniquSelect, assertGeoPoint, assertNoVersionWrites, buildDbValidator, buildValidationContext, computeInsights, createDbValidatorPlugin, createFailureCollector, decomposePatch, forceNavNonOptional, getDbFieldOp, getKeyProps, guardAggregate, guardFilter, guardQuery, isDbFieldOp, isGeoIndexableType, isGeoPointType, isNavRelation, isPrimitive, resolveDesignType, separateCas, separateFieldOps, translateQueryTree, walkFilter, withOptimisticRetry };
|
|
375
|
+
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, ApplicationIntegrity, AtscriptDbReadable, AtscriptDbTable, AtscriptDbView, BaseDbAdapter, CasExhaustedError, DEFAULT_DB_SPACE, DbEncryption, DbError, DbSpace, DocumentFieldMapper, FieldMappingStrategy, IntegrityStrategy, NativeIntegrity, NoopLogger, RelationalFieldMapper, TableMetadata, UniquSelect, assertGeoPoint, assertNoVersionWrites, buildDbValidator, buildValidationContext, checkHavingKeys, classifyQueryPath, collectQueryPaths, computeInsights, createDbValidatorPlugin, createFailureCollector, decomposePatch, findAncestorInSet, forceNavNonOptional, getDbFieldOp, getKeyProps, guardAggregate, guardFilter, guardPath, guardPaths, guardQuery, isAtscriptDbView, isDbFieldOp, isEmptyObject, isGeoIndexableType, isGeoPointType, isNavRelation, isPlainObject, isPrimitive, reconcileCas, resolveDesignType, separateCas, separateFieldOps, sortFieldNames, translateQueryTree, unsupportedOperatorMessage, walkFilter, withOptimisticRetry };
|
|
@@ -109,6 +109,29 @@ declare function separateFieldOps(data: Record<string, unknown>): TFieldOps | un
|
|
|
109
109
|
*
|
|
110
110
|
* Zero-allocation on the no-op (no `$cas`) path.
|
|
111
111
|
*/
|
|
112
|
+
/**
|
|
113
|
+
* Reconciles the two spellings of one write item's expected version, in
|
|
114
|
+
* place (since 0.1.128): the SDK operator `$cas: { [versionColumn]: N }` and a
|
|
115
|
+
* top-level `[versionColumn]: N` (the HTTP wire shape, where a body's version
|
|
116
|
+
* column is a CAS predicate, not a SET). Shared by the moost-db controller
|
|
117
|
+
* (`target: "cas"` — normalises to `$cas` for the table) and the db-client
|
|
118
|
+
* (`target: "version"` — normalises to the wire shape).
|
|
119
|
+
*
|
|
120
|
+
* - neither present → no-op, returns `undefined`
|
|
121
|
+
* - one present → normalised to `target`, returns the expected version
|
|
122
|
+
* - both present and equal → normalised once
|
|
123
|
+
* - both present and different → `DbError("INVALID_QUERY")` at path `$cas`:
|
|
124
|
+
* `Ambiguous version: "<col>" and "$cas.<col>" differ`
|
|
125
|
+
* - `$cas` malformed → {@link separateCas}'s own error (checked first, so a
|
|
126
|
+
* malformed `$cas` is never reported as "ambiguous")
|
|
127
|
+
* - `$cas: undefined` counts as absent (the key is dropped)
|
|
128
|
+
* - `versionColumn === undefined` with a `$cas` → {@link separateCas}'s
|
|
129
|
+
* "table has no @db.column.version" error
|
|
130
|
+
*
|
|
131
|
+
* Only a finite number counts as a present version column value — anything
|
|
132
|
+
* else is left untouched for the write validator to reject.
|
|
133
|
+
*/
|
|
134
|
+
declare function reconcileCas(data: Record<string, unknown>, versionColumn: string | undefined, target: "cas" | "version"): number | undefined;
|
|
112
135
|
declare function separateCas(data: Record<string, unknown>, versionColumn?: string): number | undefined;
|
|
113
136
|
//#endregion
|
|
114
|
-
export { $mul as a, $update as c, TDbFieldOp as d, TFieldOps as f,
|
|
137
|
+
export { separateFieldOps as _, $mul as a, $update as c, TDbFieldOp as d, TFieldOps as f, separateCas as g, reconcileCas as h, $insert as i, $upsert as l, isDbFieldOp as m, $dec as n, $remove as o, getDbFieldOp as p, $inc as r, $replace as s, $cas as t, TDbCas as u };
|
|
@@ -109,6 +109,29 @@ declare function separateFieldOps(data: Record<string, unknown>): TFieldOps | un
|
|
|
109
109
|
*
|
|
110
110
|
* Zero-allocation on the no-op (no `$cas`) path.
|
|
111
111
|
*/
|
|
112
|
+
/**
|
|
113
|
+
* Reconciles the two spellings of one write item's expected version, in
|
|
114
|
+
* place (since 0.1.128): the SDK operator `$cas: { [versionColumn]: N }` and a
|
|
115
|
+
* top-level `[versionColumn]: N` (the HTTP wire shape, where a body's version
|
|
116
|
+
* column is a CAS predicate, not a SET). Shared by the moost-db controller
|
|
117
|
+
* (`target: "cas"` — normalises to `$cas` for the table) and the db-client
|
|
118
|
+
* (`target: "version"` — normalises to the wire shape).
|
|
119
|
+
*
|
|
120
|
+
* - neither present → no-op, returns `undefined`
|
|
121
|
+
* - one present → normalised to `target`, returns the expected version
|
|
122
|
+
* - both present and equal → normalised once
|
|
123
|
+
* - both present and different → `DbError("INVALID_QUERY")` at path `$cas`:
|
|
124
|
+
* `Ambiguous version: "<col>" and "$cas.<col>" differ`
|
|
125
|
+
* - `$cas` malformed → {@link separateCas}'s own error (checked first, so a
|
|
126
|
+
* malformed `$cas` is never reported as "ambiguous")
|
|
127
|
+
* - `$cas: undefined` counts as absent (the key is dropped)
|
|
128
|
+
* - `versionColumn === undefined` with a `$cas` → {@link separateCas}'s
|
|
129
|
+
* "table has no @db.column.version" error
|
|
130
|
+
*
|
|
131
|
+
* Only a finite number counts as a present version column value — anything
|
|
132
|
+
* else is left untouched for the write validator to reject.
|
|
133
|
+
*/
|
|
134
|
+
declare function reconcileCas(data: Record<string, unknown>, versionColumn: string | undefined, target: "cas" | "version"): number | undefined;
|
|
112
135
|
declare function separateCas(data: Record<string, unknown>, versionColumn?: string): number | undefined;
|
|
113
136
|
//#endregion
|
|
114
|
-
export { $mul as a, $update as c, TDbFieldOp as d, TFieldOps as f,
|
|
137
|
+
export { separateFieldOps as _, $mul as a, $update as c, TDbFieldOp as d, TFieldOps as f, separateCas as g, reconcileCas as h, $insert as i, $upsert as l, isDbFieldOp as m, $dec as n, $remove as o, getDbFieldOp as p, $inc as r, $replace as s, $cas as t, TDbCas as u };
|
package/dist/ops.cjs
CHANGED
|
@@ -144,6 +144,48 @@ function separateFieldOps(data) {
|
|
|
144
144
|
*
|
|
145
145
|
* Zero-allocation on the no-op (no `$cas`) path.
|
|
146
146
|
*/
|
|
147
|
+
/**
|
|
148
|
+
* Reconciles the two spellings of one write item's expected version, in
|
|
149
|
+
* place (since 0.1.128): the SDK operator `$cas: { [versionColumn]: N }` and a
|
|
150
|
+
* top-level `[versionColumn]: N` (the HTTP wire shape, where a body's version
|
|
151
|
+
* column is a CAS predicate, not a SET). Shared by the moost-db controller
|
|
152
|
+
* (`target: "cas"` — normalises to `$cas` for the table) and the db-client
|
|
153
|
+
* (`target: "version"` — normalises to the wire shape).
|
|
154
|
+
*
|
|
155
|
+
* - neither present → no-op, returns `undefined`
|
|
156
|
+
* - one present → normalised to `target`, returns the expected version
|
|
157
|
+
* - both present and equal → normalised once
|
|
158
|
+
* - both present and different → `DbError("INVALID_QUERY")` at path `$cas`:
|
|
159
|
+
* `Ambiguous version: "<col>" and "$cas.<col>" differ`
|
|
160
|
+
* - `$cas` malformed → {@link separateCas}'s own error (checked first, so a
|
|
161
|
+
* malformed `$cas` is never reported as "ambiguous")
|
|
162
|
+
* - `$cas: undefined` counts as absent (the key is dropped)
|
|
163
|
+
* - `versionColumn === undefined` with a `$cas` → {@link separateCas}'s
|
|
164
|
+
* "table has no @db.column.version" error
|
|
165
|
+
*
|
|
166
|
+
* Only a finite number counts as a present version column value — anything
|
|
167
|
+
* else is left untouched for the write validator to reject.
|
|
168
|
+
*/
|
|
169
|
+
function reconcileCas(data, versionColumn, target) {
|
|
170
|
+
if ("$cas" in data && data.$cas === void 0) delete data.$cas;
|
|
171
|
+
const versionValue = versionColumn === void 0 ? void 0 : data[versionColumn];
|
|
172
|
+
const hasVersion = typeof versionValue === "number" && Number.isFinite(versionValue);
|
|
173
|
+
const hasCas = "$cas" in data;
|
|
174
|
+
if (!hasCas && !hasVersion) return void 0;
|
|
175
|
+
let expected;
|
|
176
|
+
if (hasCas) {
|
|
177
|
+
expected = separateCas(data, versionColumn);
|
|
178
|
+
if (hasVersion && versionValue !== expected) throw new require_db_error.DbError("INVALID_QUERY", [{
|
|
179
|
+
path: "$cas",
|
|
180
|
+
message: `Ambiguous version: "${versionColumn}" and "$cas.${versionColumn}" differ`
|
|
181
|
+
}]);
|
|
182
|
+
} else expected = versionValue;
|
|
183
|
+
if (target === "cas") {
|
|
184
|
+
delete data[versionColumn];
|
|
185
|
+
data.$cas = { [versionColumn]: expected };
|
|
186
|
+
} else data[versionColumn] = expected;
|
|
187
|
+
return expected;
|
|
188
|
+
}
|
|
147
189
|
function separateCas(data, versionColumn) {
|
|
148
190
|
if (!("$cas" in data)) return void 0;
|
|
149
191
|
if (versionColumn === void 0) throw new require_db_error.DbError("INVALID_QUERY", [{
|
|
@@ -192,5 +234,6 @@ exports.$update = $update;
|
|
|
192
234
|
exports.$upsert = $upsert;
|
|
193
235
|
exports.getDbFieldOp = getDbFieldOp;
|
|
194
236
|
exports.isDbFieldOp = isDbFieldOp;
|
|
237
|
+
exports.reconcileCas = reconcileCas;
|
|
195
238
|
exports.separateCas = separateCas;
|
|
196
239
|
exports.separateFieldOps = separateFieldOps;
|
package/dist/ops.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as $mul, c as $update, d as TDbFieldOp, f as TFieldOps, g as
|
|
2
|
-
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, TDbCas, TDbFieldOp, TFieldOps, getDbFieldOp, isDbFieldOp, separateCas, separateFieldOps };
|
|
1
|
+
import { _ as separateFieldOps, a as $mul, c as $update, d as TDbFieldOp, f as TFieldOps, g as separateCas, h as reconcileCas, i as $insert, l as $upsert, m as isDbFieldOp, n as $dec, o as $remove, p as getDbFieldOp, r as $inc, s as $replace, t as $cas, u as TDbCas } from "./ops-AqhV7s9o.cjs";
|
|
2
|
+
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, TDbCas, TDbFieldOp, TFieldOps, getDbFieldOp, isDbFieldOp, reconcileCas, separateCas, separateFieldOps };
|
package/dist/ops.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as $mul, c as $update, d as TDbFieldOp, f as TFieldOps, g as
|
|
2
|
-
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, TDbCas, TDbFieldOp, TFieldOps, getDbFieldOp, isDbFieldOp, separateCas, separateFieldOps };
|
|
1
|
+
import { _ as separateFieldOps, a as $mul, c as $update, d as TDbFieldOp, f as TFieldOps, g as separateCas, h as reconcileCas, i as $insert, l as $upsert, m as isDbFieldOp, n as $dec, o as $remove, p as getDbFieldOp, r as $inc, s as $replace, t as $cas, u as TDbCas } from "./ops-AqhV7s9o.mjs";
|
|
2
|
+
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, TDbCas, TDbFieldOp, TFieldOps, getDbFieldOp, isDbFieldOp, reconcileCas, separateCas, separateFieldOps };
|
package/dist/ops.mjs
CHANGED
|
@@ -143,6 +143,48 @@ function separateFieldOps(data) {
|
|
|
143
143
|
*
|
|
144
144
|
* Zero-allocation on the no-op (no `$cas`) path.
|
|
145
145
|
*/
|
|
146
|
+
/**
|
|
147
|
+
* Reconciles the two spellings of one write item's expected version, in
|
|
148
|
+
* place (since 0.1.128): the SDK operator `$cas: { [versionColumn]: N }` and a
|
|
149
|
+
* top-level `[versionColumn]: N` (the HTTP wire shape, where a body's version
|
|
150
|
+
* column is a CAS predicate, not a SET). Shared by the moost-db controller
|
|
151
|
+
* (`target: "cas"` — normalises to `$cas` for the table) and the db-client
|
|
152
|
+
* (`target: "version"` — normalises to the wire shape).
|
|
153
|
+
*
|
|
154
|
+
* - neither present → no-op, returns `undefined`
|
|
155
|
+
* - one present → normalised to `target`, returns the expected version
|
|
156
|
+
* - both present and equal → normalised once
|
|
157
|
+
* - both present and different → `DbError("INVALID_QUERY")` at path `$cas`:
|
|
158
|
+
* `Ambiguous version: "<col>" and "$cas.<col>" differ`
|
|
159
|
+
* - `$cas` malformed → {@link separateCas}'s own error (checked first, so a
|
|
160
|
+
* malformed `$cas` is never reported as "ambiguous")
|
|
161
|
+
* - `$cas: undefined` counts as absent (the key is dropped)
|
|
162
|
+
* - `versionColumn === undefined` with a `$cas` → {@link separateCas}'s
|
|
163
|
+
* "table has no @db.column.version" error
|
|
164
|
+
*
|
|
165
|
+
* Only a finite number counts as a present version column value — anything
|
|
166
|
+
* else is left untouched for the write validator to reject.
|
|
167
|
+
*/
|
|
168
|
+
function reconcileCas(data, versionColumn, target) {
|
|
169
|
+
if ("$cas" in data && data.$cas === void 0) delete data.$cas;
|
|
170
|
+
const versionValue = versionColumn === void 0 ? void 0 : data[versionColumn];
|
|
171
|
+
const hasVersion = typeof versionValue === "number" && Number.isFinite(versionValue);
|
|
172
|
+
const hasCas = "$cas" in data;
|
|
173
|
+
if (!hasCas && !hasVersion) return void 0;
|
|
174
|
+
let expected;
|
|
175
|
+
if (hasCas) {
|
|
176
|
+
expected = separateCas(data, versionColumn);
|
|
177
|
+
if (hasVersion && versionValue !== expected) throw new DbError("INVALID_QUERY", [{
|
|
178
|
+
path: "$cas",
|
|
179
|
+
message: `Ambiguous version: "${versionColumn}" and "$cas.${versionColumn}" differ`
|
|
180
|
+
}]);
|
|
181
|
+
} else expected = versionValue;
|
|
182
|
+
if (target === "cas") {
|
|
183
|
+
delete data[versionColumn];
|
|
184
|
+
data.$cas = { [versionColumn]: expected };
|
|
185
|
+
} else data[versionColumn] = expected;
|
|
186
|
+
return expected;
|
|
187
|
+
}
|
|
146
188
|
function separateCas(data, versionColumn) {
|
|
147
189
|
if (!("$cas" in data)) return void 0;
|
|
148
190
|
if (versionColumn === void 0) throw new DbError("INVALID_QUERY", [{
|
|
@@ -180,4 +222,4 @@ function separateCas(data, versionColumn) {
|
|
|
180
222
|
return foundValue;
|
|
181
223
|
}
|
|
182
224
|
//#endregion
|
|
183
|
-
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, getDbFieldOp, isDbFieldOp, separateCas, separateFieldOps };
|
|
225
|
+
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, getDbFieldOp, isDbFieldOp, reconcileCas, separateCas, separateFieldOps };
|
package/dist/plugin.cjs
CHANGED
|
@@ -405,7 +405,13 @@ const dbColumnAnnotations = {
|
|
|
405
405
|
description: "Starting value for the auto-increment sequence. Adapter-specific behavior; some adapters may ignore this."
|
|
406
406
|
},
|
|
407
407
|
validate(token, args, doc) {
|
|
408
|
-
|
|
408
|
+
const errors = require_validation_utils.validateFieldBaseType(token, doc, "db.default.increment", "number");
|
|
409
|
+
if (token.parentNode.countAnnotations("meta.id") === 0) errors.push({
|
|
410
|
+
message: `@db.default.increment on a field without @meta.id — auto-increment columns must be primary-key columns for schema sync to manage them`,
|
|
411
|
+
severity: 2,
|
|
412
|
+
range: token.range
|
|
413
|
+
});
|
|
414
|
+
return errors;
|
|
409
415
|
}
|
|
410
416
|
}),
|
|
411
417
|
uuid: new _atscript_core.AnnotationSpec({
|
|
@@ -506,7 +512,7 @@ const dbColumnAnnotations = {
|
|
|
506
512
|
function columnCapability(capability, verb) {
|
|
507
513
|
const example = capability === "filterable" ? " @db.column.filterable\n email: string\n" : " @db.column.sortable\n createdAt: number.timestamp\n";
|
|
508
514
|
return new _atscript_core.AnnotationSpec({
|
|
509
|
-
description: `Marks a column as ${capability} in the readable controller's query/pages endpoints. Relevant only when the host \`@db.table\` interface opts into strict mode with \`@db.table.${capability} 'manual'\`; otherwise ${verb} is open on
|
|
515
|
+
description: `Marks a column as ${capability} in the readable controller's query/pages endpoints. Relevant only when the host \`@db.table\` interface opts into strict mode with \`@db.table.${capability} 'manual'\`; otherwise ${verb} is open on every column the adapter can handle (default-open, back-compat) and the annotation is a no-op. Adapter capability always wins: \`@db.json\` / array columns stay non-sortable (and non-filterable on SQL adapters) even when annotated. \`/meta.fields\` advertises exactly what the gate accepts; index-backed columns additionally carry the advisory \`indexed\` flag.
|
|
510
516
|
|
|
511
517
|
**Example:**
|
|
512
518
|
\`\`\`atscript
|
|
@@ -598,7 +604,7 @@ const dbIndexAnnotations = { index: {
|
|
|
598
604
|
//#region src/plugin/annotations/rel.ts
|
|
599
605
|
const dbRelAnnotations = { rel: {
|
|
600
606
|
FK: new _atscript_core.AnnotationSpec({
|
|
601
|
-
description: "Declares a foreign key reference on this field. The field must use a chain reference type (e.g., `User.id`) whose target is a primary key (`@meta.id`) or unique (`@db.index.unique`) field.\n\n**Dual role:**\n- On a `@db.table` interface, `@db.rel.FK` additionally drives DB-relation semantics — relation loading with `@db.rel.to` / `@db.rel.from`, junction pairing with `@db.rel.via`, etc.\n- On any other interface (value-help sources, WF forms, plain interfaces), `@db.rel.FK` acts purely as the value-help indicator: the client-side picker resolver uses it to decide which fields render a value-help picker. The target's `@db.http.path` (stamped by its readable controller) supplies the picker URL.\n\n**Example:**\n```atscript\n@db.rel.FK\nauthorId: User.id\n\n// With alias (required when multiple FKs point to the same type)\n@db.rel.FK \"author\"\nauthorId: User.id\n```\n",
|
|
607
|
+
description: "Declares a foreign key reference on this field. The field must use a chain reference type (e.g., `User.id`) whose target is a primary key (`@meta.id`) or unique (`@db.index.unique`) field.\n\n**Dual role:**\n- On a `@db.table` interface, `@db.rel.FK` additionally drives DB-relation semantics — relation loading with `@db.rel.to` / `@db.rel.from`, junction pairing with `@db.rel.via`, etc.\n- On any other interface (value-help sources, WF forms, plain interfaces), `@db.rel.FK` acts purely as the value-help indicator: the client-side picker resolver uses it to decide which fields render a value-help picker. The target's `@db.http.path` (stamped by its readable controller) supplies the picker URL.\n- In `/meta` (and `/meta/form/:name`) the marker is inherited through reference chains: a field declared as `code: Issue.code` where `Issue.code: Dict.code` carries `@db.rel.FK` is served with `ref` pointing at the terminal field (`Dict.code`) and `db.rel.FK: true`, so view fields get the dictionary picker. Runtime metadata is untouched.\n\n**Example:**\n```atscript\n@db.rel.FK\nauthorId: User.id\n\n// With alias (required when multiple FKs point to the same type)\n@db.rel.FK \"author\"\nauthorId: User.id\n```\n",
|
|
602
608
|
nodeType: ["prop"],
|
|
603
609
|
passedWhenReferred: false,
|
|
604
610
|
argument: {
|
|
@@ -1211,9 +1217,10 @@ function tableCapability(capability) {
|
|
|
1211
1217
|
const example = capability === "filterable" ? " @db.column.filterable\n email: string\n // other fields not filterable via the controller\n" : " @db.column.sortable\n createdAt: number.timestamp\n // other fields not sortable via the controller\n";
|
|
1212
1218
|
const verb = capability === "filterable" ? "filter" : "sort";
|
|
1213
1219
|
return new _atscript_core.AnnotationSpec({
|
|
1214
|
-
description: `Controls ${verb}-gating on the readable controller's \`/query\` and \`/pages\` endpoints.\n\n- **\`'auto'\`** (default when the annotation is absent) — every column is ${capability}
|
|
1220
|
+
description: `Controls ${verb}-gating on the readable controller's \`/query\` and \`/pages\` endpoints.\n\n- **\`'auto'\`** (default when the annotation is absent) — every column the adapter can ${verb} is ${capability} (JSON / array columns are never sortable, and not filterable on SQL adapters).
|
|
1221
|
+
- **\`'manual'\`** — only fields annotated \`@db.column.${capability}\` are ${capability}; all others are rejected with HTTP 400.
|
|
1215
1222
|
|
|
1216
|
-
Writing the annotation explicitly as \`@db.table.${capability} 'auto'\` has the same runtime effect as omitting it; use it to document intent.
|
|
1223
|
+
\`/meta.fields[*].` + capability + "` advertises exactly what the gate accepts in either mode. The policy applies to " + (capability === "filterable" ? "filters only — `$groupBy`, `$having` keys and aggregate fields use the adapter's physical capability.\n\n" : "`$sort` only.\n\n") + `Writing the annotation explicitly as \`@db.table.${capability} 'auto'\` has the same runtime effect as omitting it; use it to document intent.
|
|
1217
1224
|
|
|
1218
1225
|
**Example:**
|
|
1219
1226
|
\`\`\`atscript
|
package/dist/plugin.mjs
CHANGED
|
@@ -370,7 +370,13 @@ const dbColumnAnnotations = {
|
|
|
370
370
|
description: "Starting value for the auto-increment sequence. Adapter-specific behavior; some adapters may ignore this."
|
|
371
371
|
},
|
|
372
372
|
validate(token, args, doc) {
|
|
373
|
-
|
|
373
|
+
const errors = validateFieldBaseType(token, doc, "db.default.increment", "number");
|
|
374
|
+
if (token.parentNode.countAnnotations("meta.id") === 0) errors.push({
|
|
375
|
+
message: `@db.default.increment on a field without @meta.id — auto-increment columns must be primary-key columns for schema sync to manage them`,
|
|
376
|
+
severity: 2,
|
|
377
|
+
range: token.range
|
|
378
|
+
});
|
|
379
|
+
return errors;
|
|
374
380
|
}
|
|
375
381
|
}),
|
|
376
382
|
uuid: new AnnotationSpec({
|
|
@@ -471,7 +477,7 @@ const dbColumnAnnotations = {
|
|
|
471
477
|
function columnCapability(capability, verb) {
|
|
472
478
|
const example = capability === "filterable" ? " @db.column.filterable\n email: string\n" : " @db.column.sortable\n createdAt: number.timestamp\n";
|
|
473
479
|
return new AnnotationSpec({
|
|
474
|
-
description: `Marks a column as ${capability} in the readable controller's query/pages endpoints. Relevant only when the host \`@db.table\` interface opts into strict mode with \`@db.table.${capability} 'manual'\`; otherwise ${verb} is open on
|
|
480
|
+
description: `Marks a column as ${capability} in the readable controller's query/pages endpoints. Relevant only when the host \`@db.table\` interface opts into strict mode with \`@db.table.${capability} 'manual'\`; otherwise ${verb} is open on every column the adapter can handle (default-open, back-compat) and the annotation is a no-op. Adapter capability always wins: \`@db.json\` / array columns stay non-sortable (and non-filterable on SQL adapters) even when annotated. \`/meta.fields\` advertises exactly what the gate accepts; index-backed columns additionally carry the advisory \`indexed\` flag.
|
|
475
481
|
|
|
476
482
|
**Example:**
|
|
477
483
|
\`\`\`atscript
|
|
@@ -563,7 +569,7 @@ const dbIndexAnnotations = { index: {
|
|
|
563
569
|
//#region src/plugin/annotations/rel.ts
|
|
564
570
|
const dbRelAnnotations = { rel: {
|
|
565
571
|
FK: new AnnotationSpec({
|
|
566
|
-
description: "Declares a foreign key reference on this field. The field must use a chain reference type (e.g., `User.id`) whose target is a primary key (`@meta.id`) or unique (`@db.index.unique`) field.\n\n**Dual role:**\n- On a `@db.table` interface, `@db.rel.FK` additionally drives DB-relation semantics — relation loading with `@db.rel.to` / `@db.rel.from`, junction pairing with `@db.rel.via`, etc.\n- On any other interface (value-help sources, WF forms, plain interfaces), `@db.rel.FK` acts purely as the value-help indicator: the client-side picker resolver uses it to decide which fields render a value-help picker. The target's `@db.http.path` (stamped by its readable controller) supplies the picker URL.\n\n**Example:**\n```atscript\n@db.rel.FK\nauthorId: User.id\n\n// With alias (required when multiple FKs point to the same type)\n@db.rel.FK \"author\"\nauthorId: User.id\n```\n",
|
|
572
|
+
description: "Declares a foreign key reference on this field. The field must use a chain reference type (e.g., `User.id`) whose target is a primary key (`@meta.id`) or unique (`@db.index.unique`) field.\n\n**Dual role:**\n- On a `@db.table` interface, `@db.rel.FK` additionally drives DB-relation semantics — relation loading with `@db.rel.to` / `@db.rel.from`, junction pairing with `@db.rel.via`, etc.\n- On any other interface (value-help sources, WF forms, plain interfaces), `@db.rel.FK` acts purely as the value-help indicator: the client-side picker resolver uses it to decide which fields render a value-help picker. The target's `@db.http.path` (stamped by its readable controller) supplies the picker URL.\n- In `/meta` (and `/meta/form/:name`) the marker is inherited through reference chains: a field declared as `code: Issue.code` where `Issue.code: Dict.code` carries `@db.rel.FK` is served with `ref` pointing at the terminal field (`Dict.code`) and `db.rel.FK: true`, so view fields get the dictionary picker. Runtime metadata is untouched.\n\n**Example:**\n```atscript\n@db.rel.FK\nauthorId: User.id\n\n// With alias (required when multiple FKs point to the same type)\n@db.rel.FK \"author\"\nauthorId: User.id\n```\n",
|
|
567
573
|
nodeType: ["prop"],
|
|
568
574
|
passedWhenReferred: false,
|
|
569
575
|
argument: {
|
|
@@ -1176,9 +1182,10 @@ function tableCapability(capability) {
|
|
|
1176
1182
|
const example = capability === "filterable" ? " @db.column.filterable\n email: string\n // other fields not filterable via the controller\n" : " @db.column.sortable\n createdAt: number.timestamp\n // other fields not sortable via the controller\n";
|
|
1177
1183
|
const verb = capability === "filterable" ? "filter" : "sort";
|
|
1178
1184
|
return new AnnotationSpec({
|
|
1179
|
-
description: `Controls ${verb}-gating on the readable controller's \`/query\` and \`/pages\` endpoints.\n\n- **\`'auto'\`** (default when the annotation is absent) — every column is ${capability}
|
|
1185
|
+
description: `Controls ${verb}-gating on the readable controller's \`/query\` and \`/pages\` endpoints.\n\n- **\`'auto'\`** (default when the annotation is absent) — every column the adapter can ${verb} is ${capability} (JSON / array columns are never sortable, and not filterable on SQL adapters).
|
|
1186
|
+
- **\`'manual'\`** — only fields annotated \`@db.column.${capability}\` are ${capability}; all others are rejected with HTTP 400.
|
|
1180
1187
|
|
|
1181
|
-
Writing the annotation explicitly as \`@db.table.${capability} 'auto'\` has the same runtime effect as omitting it; use it to document intent.
|
|
1188
|
+
\`/meta.fields[*].` + capability + "` advertises exactly what the gate accepts in either mode. The policy applies to " + (capability === "filterable" ? "filters only — `$groupBy`, `$having` keys and aggregate fields use the adapter's physical capability.\n\n" : "`$sort` only.\n\n") + `Writing the annotation explicitly as \`@db.table.${capability} 'auto'\` has the same runtime effect as omitting it; use it to document intent.
|
|
1182
1189
|
|
|
1183
1190
|
**Example:**
|
|
1184
1191
|
\`\`\`atscript
|
package/dist/rel.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as TDbForeignKey, Ct as TWriteTableResolver, J as TDbRelation, Nt as TGenericLogger, Ot as TableMetadata, bt as TTableResolver, c as BaseDbAdapter } from "./db-readable-CkbZn_z-.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 {
|
|
1
|
+
import { B as TDbForeignKey, Ct as TWriteTableResolver, J as TDbRelation, Nt as TGenericLogger, Ot as TableMetadata, bt as TTableResolver, c as BaseDbAdapter } from "./db-readable-BTl4NzNN.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";
|