velocious 1.0.443 → 1.0.445
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/README.md +2 -0
- package/build/database/record/acts-as-list.js +89 -24
- package/build/database/record/index.js +12 -1
- package/build/frontend-model-controller.js +26 -0
- package/build/frontend-models/base.js +11 -1
- package/build/src/database/record/acts-as-list.d.ts.map +1 -1
- package/build/src/database/record/acts-as-list.js +80 -23
- package/build/src/database/record/index.d.ts +7 -2
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +11 -2
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +24 -1
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +12 -2
- package/package.json +1 -1
- package/src/database/record/acts-as-list.js +89 -24
- package/src/database/record/index.js +12 -1
- package/src/frontend-model-controller.js +26 -0
- package/src/frontend-models/base.js +11 -1
package/README.md
CHANGED
|
@@ -1597,6 +1597,8 @@ configuration.getErrorEvents().on("all-error", ({error, errorType}) => {
|
|
|
1597
1597
|
})
|
|
1598
1598
|
```
|
|
1599
1599
|
|
|
1600
|
+
Genuinely unexpected frontend-model command failures reach this bus too. The frontend-model controller catches them to return a client-safe `Request failed.` response, but it also emits them as `framework-error`/`all-error` (with `context.frontendModelEndpoint === true`) so they are reported instead of being silently swallowed. Expected user-flow errors are excluded: validation failures are forwarded with their real message (for example `Name can't be blank`) rather than the generic one, and `error.velocious`-annotated / `safeToExpose` errors keep their own message — none of these reach the error bus.
|
|
1601
|
+
|
|
1600
1602
|
## Use the Websocket client API (HTTP-like)
|
|
1601
1603
|
|
|
1602
1604
|
```js
|
|
@@ -67,14 +67,15 @@ export default function registerActsAsListCallbacks(modelClass, positionColumn,
|
|
|
67
67
|
@type {typeof import("./index.js").default} */ (record.constructor)
|
|
68
68
|
const posColumn = modelClass.getColumnNameForAttributeName(positionColumn)
|
|
69
69
|
const scopeCol = modelClass.getColumnNameForAttributeName(scope)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const changes =
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
/** @type {Record<string, ?>} */
|
|
71
|
+
const rawAttributes = record._attributes || {}
|
|
72
|
+
/** @type {Record<string, ?>} */
|
|
73
|
+
const changes = record._changes || {}
|
|
74
|
+
/** @type {Set<string>} */
|
|
75
|
+
const assignedAttributeNames = record._assignedAttributeNames || new Set()
|
|
76
76
|
const posChanged = posColumn in changes
|
|
77
77
|
const scopeChanged = scopeCol in changes
|
|
78
|
+
const posAssigned = assignedAttributeNames.has(positionColumn)
|
|
78
79
|
|
|
79
80
|
if (!posChanged && !scopeChanged) return
|
|
80
81
|
|
|
@@ -102,18 +103,13 @@ export default function registerActsAsListCallbacks(modelClass, positionColumn,
|
|
|
102
103
|
if (oldPosition == null || newPosition == null) return
|
|
103
104
|
if (newPosition === oldPosition && newScopeValue === oldScopeValue) return
|
|
104
105
|
|
|
105
|
-
// Move the record out of the way before shifting others to avoid
|
|
106
|
-
// intermediate UNIQUE constraint violations. Use the old scope value
|
|
107
|
-
// for the move-out-of-way because the record is still in the old scope.
|
|
108
|
-
await moveOutOfWay({record, positionColumn, scope, scopeValue: oldScopeValue})
|
|
109
|
-
setShiftingFlag(record, false)
|
|
110
|
-
|
|
111
106
|
if (scopeChanged && oldScopeValue !== newScopeValue) {
|
|
112
|
-
let targetPosition = newPosition
|
|
113
|
-
|
|
114
107
|
// When only the scope changes without a new position, append to the end
|
|
115
108
|
// of the new scope. There is no target-scope row to shift out of the way.
|
|
116
|
-
if (!
|
|
109
|
+
if (!posAssigned) {
|
|
110
|
+
await moveOutOfWay({record, positionColumn, scope, scopeValue: oldScopeValue})
|
|
111
|
+
setShiftingFlag(record, false)
|
|
112
|
+
|
|
117
113
|
const highestNew = await highestPositionInScope({record, positionColumn, scope, scopeValue: newScopeValue})
|
|
118
114
|
const nextPos = highestNew + 1
|
|
119
115
|
|
|
@@ -122,11 +118,17 @@ export default function registerActsAsListCallbacks(modelClass, positionColumn,
|
|
|
122
118
|
return
|
|
123
119
|
}
|
|
124
120
|
|
|
121
|
+
await moveOutOfWay({record, positionColumn, scope, scopeValue: oldScopeValue, targetScopeValue: newScopeValue})
|
|
122
|
+
setShiftingFlag(record, false)
|
|
125
123
|
await shiftPositionsDown({record, positionColumn, scope, scopeValue: oldScopeValue, fromPosition: oldPosition + 1})
|
|
126
|
-
await shiftPositionsUp({record, positionColumn, scope, scopeValue: newScopeValue, fromPosition:
|
|
124
|
+
await shiftPositionsUp({record, positionColumn, scope, scopeValue: newScopeValue, fromPosition: newPosition, excludeRecordId: record.id()})
|
|
125
|
+
await placeMovedRecord({record, positionColumn, scope, scopeValue: newScopeValue, position: newPosition})
|
|
127
126
|
return
|
|
128
127
|
}
|
|
129
128
|
|
|
129
|
+
await moveOutOfWay({record, positionColumn, scope, scopeValue: oldScopeValue})
|
|
130
|
+
setShiftingFlag(record, false)
|
|
131
|
+
|
|
130
132
|
if (newPosition < oldPosition) {
|
|
131
133
|
await shiftPositionsUp({record, positionColumn, scope, fromPosition: newPosition, toPosition: oldPosition})
|
|
132
134
|
} else if (newPosition > oldPosition) {
|
|
@@ -146,6 +148,53 @@ export default function registerActsAsListCallbacks(modelClass, positionColumn,
|
|
|
146
148
|
})
|
|
147
149
|
}
|
|
148
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Places a moved row after surrounding rows have shifted.
|
|
153
|
+
* @param {object} args - Arguments.
|
|
154
|
+
* @param {import("./index.js").default} args.record - Model instance.
|
|
155
|
+
* @param {string} args.positionColumn - Position attribute name.
|
|
156
|
+
* @param {string} args.scope - Scope attribute name.
|
|
157
|
+
* @param {string | number} args.scopeValue - Destination scope value.
|
|
158
|
+
* @param {number} args.position - Destination position.
|
|
159
|
+
* @returns {Promise<void>} Resolves after placement.
|
|
160
|
+
*/
|
|
161
|
+
async function placeMovedRecord({record, positionColumn, scope, scopeValue, position}) {
|
|
162
|
+
const modelClass = /** @type {typeof import("./index.js").default} */ (record.constructor)
|
|
163
|
+
const connection = modelClass.connection()
|
|
164
|
+
const tableSql = connection.quoteTable(modelClass._getTable().getName())
|
|
165
|
+
const scopeCol = modelClass.getColumnNameForAttributeName(scope)
|
|
166
|
+
const posCol = modelClass.getColumnNameForAttributeName(positionColumn)
|
|
167
|
+
const preservedChanges = {...record._changes}
|
|
168
|
+
const scopeColumnSql = connection.quoteColumn(scopeCol)
|
|
169
|
+
const positionColumnSql = connection.quoteColumn(posCol)
|
|
170
|
+
const primaryKeySql = connection.quoteColumn(modelClass.primaryKey())
|
|
171
|
+
|
|
172
|
+
delete preservedChanges[scopeCol]
|
|
173
|
+
delete preservedChanges[posCol]
|
|
174
|
+
|
|
175
|
+
await connection.query(
|
|
176
|
+
`UPDATE ${tableSql} SET ${scopeColumnSql} = ${connection.quote(scopeValue)}, ${positionColumnSql} = ${connection.quote(position)} WHERE ${primaryKeySql} = ${connection.quote(record.id())}`
|
|
177
|
+
)
|
|
178
|
+
await record._reloadWithId(record.id())
|
|
179
|
+
record._changes = preservedChanges
|
|
180
|
+
clearBelongsToChangeForScope(record)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Clears dirty belongs-to state for the scope FK after direct placement.
|
|
185
|
+
* @param {import("./index.js").default} record - Model instance.
|
|
186
|
+
* @returns {void} Nothing.
|
|
187
|
+
*/
|
|
188
|
+
function clearBelongsToChangeForScope(record) {
|
|
189
|
+
for (const relationshipName in record._instanceRelationships || {}) {
|
|
190
|
+
const relationship = record._instanceRelationships[relationshipName]
|
|
191
|
+
|
|
192
|
+
if (relationship.getType() !== "belongsTo") continue
|
|
193
|
+
|
|
194
|
+
relationship.setDirty(false)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
149
198
|
/**
|
|
150
199
|
* Bumps positions UP by 1 in the range [fromPosition, toPosition) within the
|
|
151
200
|
* same scope. Updates in descending order to avoid intermediate UNIQUE
|
|
@@ -157,9 +206,10 @@ export default function registerActsAsListCallbacks(modelClass, positionColumn,
|
|
|
157
206
|
* @param {number} args.fromPosition - Starting position (inclusive).
|
|
158
207
|
* @param {number} [args.toPosition] - Ending position (exclusive).
|
|
159
208
|
* @param {string | number} [args.scopeValue] - Explicit scope value.
|
|
209
|
+
* @param {string | number} [args.excludeRecordId] - Record id to exclude from shifts.
|
|
160
210
|
* @returns {Promise<void>}
|
|
161
211
|
*/
|
|
162
|
-
async function shiftPositionsUp({record, positionColumn, scope, fromPosition, toPosition, scopeValue}) {
|
|
212
|
+
async function shiftPositionsUp({record, positionColumn, scope, fromPosition, toPosition, scopeValue, excludeRecordId}) {
|
|
163
213
|
const modelClass = /**
|
|
164
214
|
* Narrows the runtime value to the documented type.
|
|
165
215
|
@type {typeof import("./index.js").default} */ (record.constructor)
|
|
@@ -173,16 +223,25 @@ async function shiftPositionsUp({record, positionColumn, scope, fromPosition, to
|
|
|
173
223
|
const positionColumnName = modelClass.getColumnNameForAttributeName(positionColumn)
|
|
174
224
|
const positionColumnSql = connection.quoteColumn(positionColumnName)
|
|
175
225
|
const scopeColumnSql = connection.quoteColumn(scopeColumnName)
|
|
226
|
+
const primaryKeySql = connection.quoteColumn(modelClass.primaryKey())
|
|
176
227
|
const tableSql = connection.quoteTable(tableName)
|
|
177
228
|
const quotedScope = connection.quote(resolvedScopeValue)
|
|
178
229
|
|
|
179
230
|
// Load rows in descending order so we bump the highest first
|
|
180
231
|
let query = modelClass
|
|
232
|
+
.select(modelClass.primaryKey())
|
|
181
233
|
.select(positionColumn)
|
|
182
234
|
.where({[scopeColumnName]: resolvedScopeValue})
|
|
183
235
|
.where(`${positionColumnSql} >= ${connection.quote(fromPosition)}`)
|
|
236
|
+
.where(`${positionColumnSql} > 0`)
|
|
184
237
|
.order(`${positionColumnSql} DESC`)
|
|
185
238
|
|
|
239
|
+
const recordIdToExclude = excludeRecordId || (record.isPersisted() ? record.id() : null)
|
|
240
|
+
|
|
241
|
+
if (recordIdToExclude != null) {
|
|
242
|
+
query = query.where(`${primaryKeySql} != ${connection.quote(recordIdToExclude)}`)
|
|
243
|
+
}
|
|
244
|
+
|
|
186
245
|
if (toPosition != null) {
|
|
187
246
|
query = query.where(`${positionColumnSql} < ${connection.quote(toPosition)}`)
|
|
188
247
|
}
|
|
@@ -196,7 +255,7 @@ async function shiftPositionsUp({record, positionColumn, scope, fromPosition, to
|
|
|
196
255
|
const currentPos = Number(row.readAttribute(positionColumn))
|
|
197
256
|
|
|
198
257
|
await connection.query(
|
|
199
|
-
`UPDATE ${tableSql} SET ${positionColumnSql} = ${positionColumnSql} + 1 WHERE ${scopeColumnSql} = ${quotedScope} AND ${positionColumnSql} = ${connection.quote(currentPos)}`
|
|
258
|
+
`UPDATE ${tableSql} SET ${positionColumnSql} = ${positionColumnSql} + 1 WHERE ${primaryKeySql} = ${connection.quote(row.id())} AND ${scopeColumnSql} = ${quotedScope} AND ${positionColumnSql} = ${connection.quote(currentPos)}`
|
|
200
259
|
)
|
|
201
260
|
}
|
|
202
261
|
} finally {
|
|
@@ -231,14 +290,18 @@ async function shiftPositionsDown({record, positionColumn, scope, fromPosition,
|
|
|
231
290
|
const positionColumnName = modelClass.getColumnNameForAttributeName(positionColumn)
|
|
232
291
|
const positionColumnSql = connection.quoteColumn(positionColumnName)
|
|
233
292
|
const scopeColumnSql = connection.quoteColumn(scopeColumnName)
|
|
293
|
+
const primaryKeySql = connection.quoteColumn(modelClass.primaryKey())
|
|
234
294
|
const tableSql = connection.quoteTable(tableName)
|
|
235
295
|
const quotedScope = connection.quote(resolvedScopeValue)
|
|
236
296
|
|
|
237
297
|
// Load rows in ascending order so we shift the lowest gap first
|
|
238
298
|
let query = modelClass
|
|
299
|
+
.select(modelClass.primaryKey())
|
|
239
300
|
.select(positionColumn)
|
|
240
301
|
.where({[scopeColumnName]: resolvedScopeValue})
|
|
241
302
|
.where(`${positionColumnSql} >= ${connection.quote(fromPosition)}`)
|
|
303
|
+
.where(`${positionColumnSql} > 0`)
|
|
304
|
+
.where(`${primaryKeySql} != ${connection.quote(record.id())}`)
|
|
242
305
|
.order({column: positionColumnName, direction: "ASC"})
|
|
243
306
|
|
|
244
307
|
if (toPosition != null) {
|
|
@@ -254,7 +317,7 @@ async function shiftPositionsDown({record, positionColumn, scope, fromPosition,
|
|
|
254
317
|
const currentPos = Number(row.readAttribute(positionColumn))
|
|
255
318
|
|
|
256
319
|
await connection.query(
|
|
257
|
-
`UPDATE ${tableSql} SET ${positionColumnSql} = ${positionColumnSql} - 1 WHERE ${scopeColumnSql} = ${quotedScope} AND ${positionColumnSql} = ${connection.quote(currentPos)}`
|
|
320
|
+
`UPDATE ${tableSql} SET ${positionColumnSql} = ${positionColumnSql} - 1 WHERE ${primaryKeySql} = ${connection.quote(row.id())} AND ${scopeColumnSql} = ${quotedScope} AND ${positionColumnSql} = ${connection.quote(currentPos)}`
|
|
258
321
|
)
|
|
259
322
|
}
|
|
260
323
|
} finally {
|
|
@@ -342,21 +405,23 @@ function resolveScopeValue(record, scope) {
|
|
|
342
405
|
* @param {import("./index.js").default} args.record - Model instance.
|
|
343
406
|
* @param {string} args.positionColumn - camelCase position attribute.
|
|
344
407
|
* @param {string} args.scope - camelCase scope attribute.
|
|
345
|
-
* @param {string | number | null} [args.scopeValue] -
|
|
408
|
+
* @param {string | number | null} [args.scopeValue] - Scope containing the record before move-out.
|
|
409
|
+
* @param {string | number | null} [args.targetScopeValue] - Temporary scope value to assign.
|
|
346
410
|
* @returns {Promise<void>}
|
|
347
411
|
*/
|
|
348
|
-
async function moveOutOfWay({record, positionColumn, scope, scopeValue}) {
|
|
412
|
+
async function moveOutOfWay({record, positionColumn, scope, scopeValue, targetScopeValue}) {
|
|
349
413
|
const modelClass = /**
|
|
350
414
|
* Narrows the runtime value to the documented type.
|
|
351
415
|
@type {typeof import("./index.js").default} */ (record.constructor)
|
|
352
416
|
const connection = modelClass.connection()
|
|
353
417
|
const tableName = modelClass._getTable().getName()
|
|
354
418
|
const resolvedScopeValue = scopeValue != null ? scopeValue : resolveScopeValue(record, scope)
|
|
419
|
+
const resolvedTargetScopeValue = targetScopeValue != null ? targetScopeValue : resolvedScopeValue
|
|
355
420
|
|
|
356
421
|
if (resolvedScopeValue == null) return
|
|
422
|
+
if (resolvedTargetScopeValue == null) return
|
|
357
423
|
|
|
358
|
-
const
|
|
359
|
-
const tempPosition = highest + 10000
|
|
424
|
+
const tempPosition = -record.id()
|
|
360
425
|
const positionColumnSql = connection.quoteColumn(modelClass.getColumnNameForAttributeName(positionColumn))
|
|
361
426
|
const scopeColumnSql = connection.quoteColumn(modelClass.getColumnNameForAttributeName(scope))
|
|
362
427
|
const tableSql = connection.quoteTable(tableName)
|
|
@@ -366,7 +431,7 @@ async function moveOutOfWay({record, positionColumn, scope, scopeValue}) {
|
|
|
366
431
|
|
|
367
432
|
try {
|
|
368
433
|
await connection.query(
|
|
369
|
-
`UPDATE ${tableSql} SET ${positionColumnSql} = ${connection.quote(tempPosition)} WHERE ${scopeColumnSql} = ${connection.quote(resolvedScopeValue)} AND ${pkSql} = ${connection.quote(record.id())}`
|
|
434
|
+
`UPDATE ${tableSql} SET ${scopeColumnSql} = ${connection.quote(resolvedTargetScopeValue)}, ${positionColumnSql} = ${connection.quote(tempPosition)} WHERE ${scopeColumnSql} = ${connection.quote(resolvedScopeValue)} AND ${pkSql} = ${connection.quote(record.id())}`
|
|
370
435
|
)
|
|
371
436
|
} finally {
|
|
372
437
|
// Don't clear the flag here — the caller will do that after shifts
|
|
@@ -435,6 +435,12 @@ class VelociousDatabaseRecord {
|
|
|
435
435
|
@type {Record<string, ?>} */
|
|
436
436
|
_changes = {}
|
|
437
437
|
|
|
438
|
+
/**
|
|
439
|
+
* Attribute names explicitly assigned in the current update call.
|
|
440
|
+
@type {Set<string> | undefined}
|
|
441
|
+
*/
|
|
442
|
+
_assignedAttributeNames = undefined
|
|
443
|
+
|
|
438
444
|
/**
|
|
439
445
|
* Columns as hash.
|
|
440
446
|
@type {Record<string, import("../drivers/base-column.js").default>} */
|
|
@@ -2414,6 +2420,8 @@ class VelociousDatabaseRecord {
|
|
|
2414
2420
|
})
|
|
2415
2421
|
})
|
|
2416
2422
|
|
|
2423
|
+
this._assignedAttributeNames = undefined
|
|
2424
|
+
|
|
2417
2425
|
return result
|
|
2418
2426
|
}
|
|
2419
2427
|
|
|
@@ -2512,7 +2520,7 @@ class VelociousDatabaseRecord {
|
|
|
2512
2520
|
|
|
2513
2521
|
/**
|
|
2514
2522
|
* Resolves a relationship foreign-key column to this model's public attribute name.
|
|
2515
|
-
* @param {import("./instance-relationships/base.js").default
|
|
2523
|
+
* @param {import("./instance-relationships/base.js").default<typeof VelociousDatabaseRecord, typeof VelociousDatabaseRecord>} instanceRelationship - Relationship instance.
|
|
2516
2524
|
* @returns {string} Attribute name accepted by setAttribute/assign.
|
|
2517
2525
|
*/
|
|
2518
2526
|
_relationshipForeignKeyAttribute(instanceRelationship) {
|
|
@@ -3374,7 +3382,9 @@ class VelociousDatabaseRecord {
|
|
|
3374
3382
|
* @returns {void} - No return value.
|
|
3375
3383
|
*/
|
|
3376
3384
|
assign(attributesToAssign) {
|
|
3385
|
+
this._assignedAttributeNames ||= new Set()
|
|
3377
3386
|
for (const attributeToAssign in attributesToAssign) {
|
|
3387
|
+
this._assignedAttributeNames.add(attributeToAssign)
|
|
3378
3388
|
this.setAttribute(attributeToAssign, attributesToAssign[attributeToAssign])
|
|
3379
3389
|
}
|
|
3380
3390
|
}
|
|
@@ -4178,6 +4188,7 @@ class VelociousDatabaseRecord {
|
|
|
4178
4188
|
|
|
4179
4189
|
this._attributes = reloadedModel.rawAttributes()
|
|
4180
4190
|
this._changes = {}
|
|
4191
|
+
this._assignedAttributeNames = undefined
|
|
4181
4192
|
}
|
|
4182
4193
|
|
|
4183
4194
|
/**
|
|
@@ -218,6 +218,14 @@ function frontendModelClientMessageForError(error) {
|
|
|
218
218
|
return error.message
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
// Validation failures are expected user-flow errors. Always forward the
|
|
222
|
+
// validation summary so the client shows the real reason (e.g. "Name can't
|
|
223
|
+
// be blank") instead of the generic "Request failed." message, regardless of
|
|
224
|
+
// whether the raising code also attached error.velocious metadata.
|
|
225
|
+
if (error instanceof ValidationError) {
|
|
226
|
+
return error.message
|
|
227
|
+
}
|
|
228
|
+
|
|
221
229
|
if (frontendModelErrorHasVelociousMetadata(error) && error instanceof Error) {
|
|
222
230
|
return error.message
|
|
223
231
|
}
|
|
@@ -2827,6 +2835,24 @@ export default class FrontendModelController extends Controller {
|
|
|
2827
2835
|
model: resolvedModel,
|
|
2828
2836
|
requestId
|
|
2829
2837
|
}])
|
|
2838
|
+
|
|
2839
|
+
// Surface genuinely unexpected backend failures on the framework-error
|
|
2840
|
+
// channel so process-level bug reporters capture them, instead of the
|
|
2841
|
+
// controller silently swallowing them behind the generic "Request
|
|
2842
|
+
// failed." client message. Developer-annotated user-flow errors
|
|
2843
|
+
// (`error.velocious` metadata — handled by the early return above),
|
|
2844
|
+
// validation errors, and deliberately client-safe VelociousErrors are
|
|
2845
|
+
// expected and must NOT be reported as framework errors.
|
|
2846
|
+
if (!(error instanceof ValidationError) && !(error instanceof VelociousError && error.safeToExpose)) {
|
|
2847
|
+
const errorPayload = {
|
|
2848
|
+
context: {action, commandType, frontendModelEndpoint: true, model: resolvedModel, requestId},
|
|
2849
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
2850
|
+
request: this.getRequest()
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
this.getConfiguration().getErrorEvents().emit("framework-error", errorPayload)
|
|
2854
|
+
this.getConfiguration().getErrorEvents().emit("all-error", {...errorPayload, errorType: "framework-error"})
|
|
2855
|
+
}
|
|
2830
2856
|
}
|
|
2831
2857
|
|
|
2832
2858
|
/**
|
|
@@ -3967,7 +3967,7 @@ export default class FrontendModelBase {
|
|
|
3967
3967
|
|
|
3968
3968
|
const error = /**
|
|
3969
3969
|
* Narrows the runtime value to the documented type.
|
|
3970
|
-
@type {Error & {velocious?: Record<string, ?>, errorType?: string, validationErrors?: Record<string,
|
|
3970
|
+
@type {Error & {velocious?: Record<string, ?>, errorType?: string, validationErrors?: Record<string, ?>, debugErrorClass?: string, debugBacktrace?: string[]}} */ (new Error(errorMessage))
|
|
3971
3971
|
if (response.velocious && typeof response.velocious === "object") {
|
|
3972
3972
|
error.velocious = response.velocious
|
|
3973
3973
|
}
|
|
@@ -3977,6 +3977,16 @@ export default class FrontendModelBase {
|
|
|
3977
3977
|
if (response.validationErrors && typeof response.validationErrors === "object") {
|
|
3978
3978
|
error.validationErrors = response.validationErrors
|
|
3979
3979
|
}
|
|
3980
|
+
// Forward server-provided debug detail (included only when the backend
|
|
3981
|
+
// deems the requester allowed to see it, e.g. an admin) so callers can
|
|
3982
|
+
// render the real error class and stack trace instead of the generic
|
|
3983
|
+
// client-safe message.
|
|
3984
|
+
if (typeof response.debugErrorClass === "string") {
|
|
3985
|
+
error.debugErrorClass = response.debugErrorClass
|
|
3986
|
+
}
|
|
3987
|
+
if (Array.isArray(response.debugBacktrace)) {
|
|
3988
|
+
error.debugBacktrace = response.debugBacktrace
|
|
3989
|
+
}
|
|
3980
3990
|
throw error
|
|
3981
3991
|
}
|
|
3982
3992
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acts-as-list.d.ts","sourceRoot":"","sources":["../../../../src/database/record/acts-as-list.js"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;GAcG;AACH,gEANW,cAAc,YAAY,EAAE,OAAO,kBACnC,MAAM,aAEd;IAAwB,KAAK,EAArB,MAAM;CACd,GAAU,IAAI,
|
|
1
|
+
{"version":3,"file":"acts-as-list.d.ts","sourceRoot":"","sources":["../../../../src/database/record/acts-as-list.js"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;GAcG;AACH,gEANW,cAAc,YAAY,EAAE,OAAO,kBACnC,MAAM,aAEd;IAAwB,KAAK,EAArB,MAAM;CACd,GAAU,IAAI,CAyGhB"}
|