@atscript/moost-db 0.1.140 → 0.1.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +88 -36
- package/dist/index.d.cts +55 -23
- package/dist/index.d.mts +55 -23
- package/dist/index.mjs +89 -37
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -546,14 +546,18 @@ function resolveProp(type, field) {
|
|
|
546
546
|
/**
|
|
547
547
|
* Follows `def.ref` hop by hop until a prop without a `ref` (a primary key or
|
|
548
548
|
* a plain column) is reached. Cycle-safe (visited on `<typeId>.<field>`) and
|
|
549
|
-
* bounded by chain length.
|
|
549
|
+
* bounded by chain length. Only chain hops count — a plain ref (`field: ""`,
|
|
550
|
+
* a column typed with a named type) is the end of the chain. A hop into a
|
|
551
|
+
* `@db.alias` type (a view's join alias, since 0.1.141) continues on the
|
|
552
|
+
* aliased table / view — the alias is no value domain for a picker.
|
|
550
553
|
*/
|
|
551
554
|
function resolveTerminalRef(def) {
|
|
552
555
|
const ref = def.ref;
|
|
553
|
-
if (!ref) return void 0;
|
|
556
|
+
if (!ref?.field) return void 0;
|
|
554
557
|
let type = ref.type();
|
|
555
558
|
let field = ref.field;
|
|
556
559
|
if (!type) return void 0;
|
|
560
|
+
type = (0, _atscript_db.aliasTargetOf)(type) ?? type;
|
|
557
561
|
let fk = def.metadata.has("db.rel.FK");
|
|
558
562
|
const visited = new Set([`${type.id ?? ""}.${field}`]);
|
|
559
563
|
for (;;) {
|
|
@@ -561,9 +565,10 @@ function resolveTerminalRef(def) {
|
|
|
561
565
|
if (!prop) break;
|
|
562
566
|
if (prop.metadata.has("db.rel.FK")) fk = true;
|
|
563
567
|
const next = prop.ref;
|
|
564
|
-
if (!next) break;
|
|
565
|
-
const
|
|
566
|
-
if (!
|
|
568
|
+
if (!next?.field) break;
|
|
569
|
+
const resolved = next.type();
|
|
570
|
+
if (!resolved) break;
|
|
571
|
+
const nextType = (0, _atscript_db.aliasTargetOf)(resolved) ?? resolved;
|
|
567
572
|
const key = `${nextType.id ?? ""}.${next.field}`;
|
|
568
573
|
if (visited.has(key)) break;
|
|
569
574
|
visited.add(key);
|
|
@@ -615,7 +620,7 @@ function walk(node, def, shallow) {
|
|
|
615
620
|
const sProp = sType.props[name];
|
|
616
621
|
if (!sProp) continue;
|
|
617
622
|
if (isNav(prop.metadata)) continue;
|
|
618
|
-
if (prop.ref && sProp.ref && !("type" in sProp.ref.type)) {
|
|
623
|
+
if (prop.ref?.field && sProp.ref && !("type" in sProp.ref.type)) {
|
|
619
624
|
const terminal = resolveTerminalRef(prop);
|
|
620
625
|
if (terminal) {
|
|
621
626
|
const direct = prop.ref.type();
|
|
@@ -732,13 +737,17 @@ let AsReadableController = class AsReadableController {
|
|
|
732
737
|
* The shallow shape emits `{ field, type: { id, metadata } }` for every FK,
|
|
733
738
|
* which carries the target's `db.http.path` so clients can resolve value-help
|
|
734
739
|
* URLs and lazy-fetch target `/meta` when deeper structure is needed. Nav
|
|
735
|
-
* props (`@db.rel.from` / `@db.rel.to` / `@db.rel.via`)
|
|
736
|
-
* and always expand fully regardless of
|
|
737
|
-
* clients need is unaffected.
|
|
740
|
+
* props (`@db.rel.from` / `@db.rel.to` / `@db.rel.via`) carry only a plain
|
|
741
|
+
* `ref` (`field: ""`) and their bodies always expand fully regardless of
|
|
742
|
+
* `refDepth` — the write-payload shape clients need is unaffected.
|
|
738
743
|
*
|
|
739
|
-
* Annotation whitelist: keeps `meta.*`, `expect.*`,
|
|
740
|
-
*
|
|
741
|
-
*
|
|
744
|
+
* Annotation whitelist: keeps `meta.*`, `expect.*`, `db.rel.*`, the `db.*`
|
|
745
|
+
* keys the shared db validator plugin reads in db-client (`db.json`,
|
|
746
|
+
* `db.patch.strategy`, `db.default*`, `db.column.version`,
|
|
747
|
+
* `db.column.derived`) and the client-facing `db.http.path` /
|
|
748
|
+
* `db.writeOnly`; strips every other `db.*` (table, column, index, etc.).
|
|
749
|
+
* Override in subclass to customise — keep the validator keys, or client
|
|
750
|
+
* preflight diverges from the server.
|
|
742
751
|
*/
|
|
743
752
|
getSerializeOptions() {
|
|
744
753
|
return {
|
|
@@ -748,7 +757,7 @@ let AsReadableController = class AsReadableController {
|
|
|
748
757
|
key,
|
|
749
758
|
value
|
|
750
759
|
};
|
|
751
|
-
if (key === "db.json" || key === "db.patch.strategy" || key.startsWith("db.default") || key === "db.http.path" || key === "db.writeOnly" || key === "db.column.version") return {
|
|
760
|
+
if (key === "db.json" || key === "db.patch.strategy" || key.startsWith("db.default") || key === "db.http.path" || key === "db.writeOnly" || key === "db.column.version" || key === "db.column.derived") return {
|
|
752
761
|
key,
|
|
753
762
|
value
|
|
754
763
|
};
|
|
@@ -973,10 +982,14 @@ AsReadableController = __decorate([UseValidationErrorTransform(), __decorateMeta
|
|
|
973
982
|
])], AsReadableController);
|
|
974
983
|
//#endregion
|
|
975
984
|
//#region src/actions/verdict.ts
|
|
976
|
-
/** Assert that a `disabled` predicate returned
|
|
985
|
+
/** Assert that a `disabled` predicate returned an array of the expected length; throws HTTP 500 otherwise. */
|
|
977
986
|
function assertVerdictLength(action, verdicts, expected) {
|
|
978
987
|
if (!Array.isArray(verdicts) || verdicts.length !== expected) throw new _moostjs_event_http.HttpError(500, `Action "${action}" disabled predicate returned an invalid verdict array`);
|
|
979
988
|
}
|
|
989
|
+
/** The reason a verdict carries — a non-empty string; `undefined` for `true` / falsy verdicts. */
|
|
990
|
+
function verdictReason(verdict) {
|
|
991
|
+
return typeof verdict === "string" && verdict !== "" ? verdict : void 0;
|
|
992
|
+
}
|
|
980
993
|
//#endregion
|
|
981
994
|
//#region src/actions/list-augmenter.ts
|
|
982
995
|
const candidateCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -1017,7 +1030,8 @@ function computeStripFields(candidates, resolvedProjection) {
|
|
|
1017
1030
|
return strip;
|
|
1018
1031
|
}
|
|
1019
1032
|
/**
|
|
1020
|
-
* Sets `$actions` on every row
|
|
1033
|
+
* Sets `$actions` on every row (plus `$disabledReasons` on rows where a
|
|
1034
|
+
* predicate returned a reason string) and strips the columns fetched only for an
|
|
1021
1035
|
* action's `requiredFields` — IN PLACE; returns the same array, typed as
|
|
1022
1036
|
* augmented.
|
|
1023
1037
|
*/
|
|
@@ -1034,15 +1048,19 @@ function augmentRowsWithActions(args) {
|
|
|
1034
1048
|
for (let i = 0; i < rows.length; i++) {
|
|
1035
1049
|
const row = rows[i];
|
|
1036
1050
|
const names = [];
|
|
1051
|
+
let reasons;
|
|
1037
1052
|
for (let j = 0; j < candidates.length; j++) {
|
|
1038
|
-
const
|
|
1039
|
-
|
|
1040
|
-
|
|
1053
|
+
const name = candidates[j].envelope.info.name;
|
|
1054
|
+
const verdict = verdicts[j]?.[i];
|
|
1055
|
+
if (!verdict) {
|
|
1056
|
+
names.push(name);
|
|
1041
1057
|
continue;
|
|
1042
1058
|
}
|
|
1043
|
-
|
|
1059
|
+
const reason = verdictReason(verdict);
|
|
1060
|
+
if (reason !== void 0) (reasons ??= {})[name] = reason;
|
|
1044
1061
|
}
|
|
1045
1062
|
row.$actions = names;
|
|
1063
|
+
if (reasons) row.$disabledReasons = reasons;
|
|
1046
1064
|
}
|
|
1047
1065
|
if (resolvedProjection !== null) {
|
|
1048
1066
|
const stripFields = computeStripFields(candidates, resolvedProjection);
|
|
@@ -2433,6 +2451,7 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
2433
2451
|
if (geoIndexedPhysical.has(fd.physicalName)) entry.geo = true;
|
|
2434
2452
|
if (this._writeOnlySet.has(path)) entry.writeOnly = true;
|
|
2435
2453
|
if (cap.bucketable) entry.bucketable = true;
|
|
2454
|
+
if (fd.derived) entry.derived = true;
|
|
2436
2455
|
fields[path] = entry;
|
|
2437
2456
|
}
|
|
2438
2457
|
return {
|
|
@@ -3116,6 +3135,7 @@ function assertExposed(app, models, options) {
|
|
|
3116
3135
|
}
|
|
3117
3136
|
const missing = [];
|
|
3118
3137
|
for (const model of models) {
|
|
3138
|
+
if ((0, _atscript_db.aliasTargetOf)(model)) continue;
|
|
3119
3139
|
const httpPath = model.metadata.get("db.http.path");
|
|
3120
3140
|
if (!auditAll && httpPath === void 0) continue;
|
|
3121
3141
|
if (excluded.has(model) || exposed.has(model)) continue;
|
|
@@ -3127,31 +3147,58 @@ function assertExposed(app, models, options) {
|
|
|
3127
3147
|
}
|
|
3128
3148
|
//#endregion
|
|
3129
3149
|
//#region src/actions/action-disabled-error.ts
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3150
|
+
/** Distinct reasons quoted in a mixed `'rows'` message before `(+N more)`. */
|
|
3151
|
+
const MAX_LISTED_REASONS = 3;
|
|
3152
|
+
function sharedReason(reasons) {
|
|
3153
|
+
const first = reasons[0];
|
|
3154
|
+
if (first === null || first === void 0) return void 0;
|
|
3155
|
+
return reasons.every((r) => r === first) ? first : void 0;
|
|
3156
|
+
}
|
|
3157
|
+
function rowsMessage(action, count, reasons) {
|
|
3158
|
+
const base = `Action "${action}" is disabled for ${count} of the selected rows`;
|
|
3159
|
+
const distinct = [...new Set(reasons.filter((r) => r !== null))];
|
|
3160
|
+
if (distinct.length === 0) return base;
|
|
3161
|
+
const listed = distinct.slice(0, MAX_LISTED_REASONS).join("; ");
|
|
3162
|
+
const more = distinct.length - MAX_LISTED_REASONS;
|
|
3163
|
+
return `${base}: ${listed}${more > 0 ? ` (+${more} more)` : ""}`;
|
|
3133
3164
|
}
|
|
3134
3165
|
/**
|
|
3135
3166
|
* Thrown by the gate interceptor when `disabled` returns truthy. Composes
|
|
3136
3167
|
* with Moost's existing error mapper to produce HTTP 409 with the wire body
|
|
3137
3168
|
* defined by {@link ActionDisabledErrorBody}.
|
|
3138
3169
|
*
|
|
3139
|
-
* - `'row'`-level rejection: pass `(action, id)` — the
|
|
3140
|
-
*
|
|
3141
|
-
*
|
|
3142
|
-
* of
|
|
3170
|
+
* - `'row'`-level rejection: pass `(action, id, undefined, [reason])` — the
|
|
3171
|
+
* body emits `id` (+ `reason` when given).
|
|
3172
|
+
* - `'rows'`-level rejection: pass `(action, undefined, ids, reasons?)` — the
|
|
3173
|
+
* body emits `ids` (the FULL list of failing IDs in reject mode; the FULL
|
|
3174
|
+
* list of request IDs in skip mode with zero survivors) and, when any
|
|
3175
|
+
* reason exists, `reasons` aligned with `ids`.
|
|
3176
|
+
*
|
|
3177
|
+
* `reasons` entries are verdict reasons (`verdictReason`): anything but a
|
|
3178
|
+
* non-empty string counts as "no reason".
|
|
3143
3179
|
*/
|
|
3144
3180
|
var ActionDisabledError = class extends _moostjs_event_http.HttpError {
|
|
3145
3181
|
name = "ActionDisabledError";
|
|
3146
|
-
constructor(action, id, ids) {
|
|
3182
|
+
constructor(action, id, ids, reasons) {
|
|
3147
3183
|
const body = {
|
|
3148
3184
|
name: "ActionDisabledError",
|
|
3149
|
-
message:
|
|
3185
|
+
message: "",
|
|
3150
3186
|
statusCode: 409,
|
|
3151
3187
|
action
|
|
3152
3188
|
};
|
|
3153
|
-
if (ids !== void 0)
|
|
3154
|
-
|
|
3189
|
+
if (ids !== void 0) {
|
|
3190
|
+
const aligned = ids.map((_, i) => verdictReason(reasons?.[i]) ?? null);
|
|
3191
|
+
const reason = sharedReason(aligned);
|
|
3192
|
+
body.message = reason ?? rowsMessage(action, ids.length, aligned);
|
|
3193
|
+
body.ids = ids;
|
|
3194
|
+
if (reason !== void 0) body.reason = reason;
|
|
3195
|
+
if (aligned.some((r) => r !== null)) body.reasons = aligned;
|
|
3196
|
+
} else {
|
|
3197
|
+
const reason = verdictReason(reasons?.[0]);
|
|
3198
|
+
body.message = reason ?? `Action "${action}" is disabled for this row`;
|
|
3199
|
+
if (id !== void 0) body.id = id;
|
|
3200
|
+
if (reason !== void 0) body.reason = reason;
|
|
3201
|
+
}
|
|
3155
3202
|
super(409, body);
|
|
3156
3203
|
}
|
|
3157
3204
|
};
|
|
@@ -3442,7 +3489,7 @@ function buildGateInterceptor(opts) {
|
|
|
3442
3489
|
if (level === "row") {
|
|
3443
3490
|
const verdicts = disabled([await ctx.get(dbActionRowSlot)]);
|
|
3444
3491
|
assertVerdictLength(action, verdicts, 1);
|
|
3445
|
-
if (verdicts[0]) throw new ActionDisabledError(action, await ctx.get(dbActionIdSlot));
|
|
3492
|
+
if (verdicts[0]) throw new ActionDisabledError(action, await ctx.get(dbActionIdSlot), void 0, [verdictReason(verdicts[0])]);
|
|
3446
3493
|
return;
|
|
3447
3494
|
}
|
|
3448
3495
|
const ids = await ctx.get(dbActionIdsSlot);
|
|
@@ -3452,26 +3499,30 @@ function buildGateInterceptor(opts) {
|
|
|
3452
3499
|
const verdicts = disabled(existingRows);
|
|
3453
3500
|
assertVerdictLength(action, verdicts, existingRows.length);
|
|
3454
3501
|
const failingIds = [];
|
|
3502
|
+
const failingReasons = [];
|
|
3455
3503
|
const passingRows = [];
|
|
3456
3504
|
const passingIds = [];
|
|
3457
3505
|
let verdictIndex = 0;
|
|
3458
3506
|
for (let i = 0; i < ids.length; i++) {
|
|
3459
3507
|
const row = rows[i];
|
|
3460
|
-
|
|
3461
|
-
|
|
3508
|
+
const verdict = row === void 0 ? void 0 : verdicts[verdictIndex++];
|
|
3509
|
+
if (row === void 0 || verdict) {
|
|
3510
|
+
failingIds.push(ids[i]);
|
|
3511
|
+
failingReasons.push(verdictReason(verdict));
|
|
3512
|
+
} else {
|
|
3462
3513
|
passingRows.push(row);
|
|
3463
3514
|
passingIds.push(ids[i]);
|
|
3464
3515
|
}
|
|
3465
3516
|
}
|
|
3466
3517
|
if (onDisabledRows === "skip") {
|
|
3467
|
-
if (passingRows.length === 0) throw new ActionDisabledError(action, void 0, [...ids]);
|
|
3518
|
+
if (passingRows.length === 0) throw new ActionDisabledError(action, void 0, [...ids], failingReasons);
|
|
3468
3519
|
if (failingIds.length > 0) {
|
|
3469
3520
|
ctx.set(dbActionRowsSlot, Promise.resolve(passingRows));
|
|
3470
3521
|
ctx.set(dbActionIdsSlot, Promise.resolve(passingIds));
|
|
3471
3522
|
}
|
|
3472
3523
|
return;
|
|
3473
3524
|
}
|
|
3474
|
-
if (failingIds.length > 0) throw new ActionDisabledError(action, void 0, failingIds);
|
|
3525
|
+
if (failingIds.length > 0) throw new ActionDisabledError(action, void 0, failingIds, failingReasons);
|
|
3475
3526
|
}, GATE_PRIORITY);
|
|
3476
3527
|
}
|
|
3477
3528
|
/** Thin interceptor for `@DbActionRow*` without `disabled` — injects only the bound table. */
|
|
@@ -3760,7 +3811,8 @@ function InputForm(formType, validatorOpts) {
|
|
|
3760
3811
|
/**
|
|
3761
3812
|
* Lift a per-row predicate into the batch shape required by
|
|
3762
3813
|
* `@DbAction` opts.`disabled` and class-level dict `disabled`. Polarity is
|
|
3763
|
-
* preserved — `true` from `fn` means the action is disabled for that row
|
|
3814
|
+
* preserved — `true` from `fn` means the action is disabled for that row; a
|
|
3815
|
+
* non-empty string disables it with that reason.
|
|
3764
3816
|
*
|
|
3765
3817
|
* ```ts
|
|
3766
3818
|
* @DbAction<Order>('archive', {
|
package/dist/index.d.cts
CHANGED
|
@@ -77,13 +77,17 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
77
77
|
* The shallow shape emits `{ field, type: { id, metadata } }` for every FK,
|
|
78
78
|
* which carries the target's `db.http.path` so clients can resolve value-help
|
|
79
79
|
* URLs and lazy-fetch target `/meta` when deeper structure is needed. Nav
|
|
80
|
-
* props (`@db.rel.from` / `@db.rel.to` / `@db.rel.via`)
|
|
81
|
-
* and always expand fully regardless of
|
|
82
|
-
* clients need is unaffected.
|
|
80
|
+
* props (`@db.rel.from` / `@db.rel.to` / `@db.rel.via`) carry only a plain
|
|
81
|
+
* `ref` (`field: ""`) and their bodies always expand fully regardless of
|
|
82
|
+
* `refDepth` — the write-payload shape clients need is unaffected.
|
|
83
83
|
*
|
|
84
|
-
* Annotation whitelist: keeps `meta.*`, `expect.*`,
|
|
85
|
-
*
|
|
86
|
-
*
|
|
84
|
+
* Annotation whitelist: keeps `meta.*`, `expect.*`, `db.rel.*`, the `db.*`
|
|
85
|
+
* keys the shared db validator plugin reads in db-client (`db.json`,
|
|
86
|
+
* `db.patch.strategy`, `db.default*`, `db.column.version`,
|
|
87
|
+
* `db.column.derived`) and the client-facing `db.http.path` /
|
|
88
|
+
* `db.writeOnly`; strips every other `db.*` (table, column, index, etc.).
|
|
89
|
+
* Override in subclass to customise — keep the validator keys, or client
|
|
90
|
+
* preflight diverges from the server.
|
|
87
91
|
*/
|
|
88
92
|
protected getSerializeOptions(): TSerializeOptions;
|
|
89
93
|
private _queryControlsValidator?;
|
|
@@ -589,7 +593,7 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
589
593
|
*
|
|
590
594
|
* Convention (not enforced): name decoration keys with a `$` prefix, like
|
|
591
595
|
* `$actions` and `$distance`, so they can never collide with a field name.
|
|
592
|
-
* Do not overwrite `$actions`. Columns the
|
|
596
|
+
* Do not overwrite `$actions` or `$disabledReasons`. Columns the
|
|
593
597
|
* hook needs but the client did not select must be added in
|
|
594
598
|
* {@link transformProjection} — they are then part of the response.
|
|
595
599
|
*
|
|
@@ -1006,6 +1010,15 @@ declare class AsJsonValueHelpController<T extends TAtscriptAnnotatedType = TAtsc
|
|
|
1006
1010
|
}
|
|
1007
1011
|
//#endregion
|
|
1008
1012
|
//#region src/actions/types.d.ts
|
|
1013
|
+
/**
|
|
1014
|
+
* One entry of a `disabled` predicate's result. Truthy = the action is
|
|
1015
|
+
* disabled for that row; falsy (`false`, `""`) = enabled. A non-empty string
|
|
1016
|
+
* disables the action AND carries a human-readable reason — surfaced in the
|
|
1017
|
+
* 409 `ActionDisabledError` message/body and in the row's `$disabledReasons`.
|
|
1018
|
+
*
|
|
1019
|
+
* @since 0.1.141 (`string`)
|
|
1020
|
+
*/
|
|
1021
|
+
type TDbActionDisabledVerdict = boolean | string;
|
|
1009
1022
|
/** `'rows'`-level batch policy — controls whether failing rows reject or are filtered out. */
|
|
1010
1023
|
type TOnDisabledRows = "reject" | "skip";
|
|
1011
1024
|
/**
|
|
@@ -1034,15 +1047,17 @@ interface WithGate<TRow, R extends readonly FlatKey<TRow>[]> {
|
|
|
1034
1047
|
*/
|
|
1035
1048
|
requiredFields: R;
|
|
1036
1049
|
/**
|
|
1037
|
-
* Sync batch gate predicate — returns a parallel
|
|
1038
|
-
*
|
|
1050
|
+
* Sync batch gate predicate — returns a parallel array aligned with the
|
|
1051
|
+
* input. Per entry: truthy = disabled for the corresponding row; a
|
|
1052
|
+
* non-empty string also gives the reason (see
|
|
1053
|
+
* {@link TDbActionDisabledVerdict}). The `rows`
|
|
1039
1054
|
* argument is type-narrowed to `Pick<FlatOf<TRow>, R[number]>[]`; reading
|
|
1040
1055
|
* a field not listed in `requiredFields` is a compile error.
|
|
1041
1056
|
*
|
|
1042
1057
|
* Promise return is NOT permitted — the predicate is consumed in the
|
|
1043
1058
|
* same tick by the gate and the augmenter.
|
|
1044
1059
|
*/
|
|
1045
|
-
disabled?: (rows: DisabledRowsArg<TRow, R>) =>
|
|
1060
|
+
disabled?: (rows: DisabledRowsArg<TRow, R>) => TDbActionDisabledVerdict[];
|
|
1046
1061
|
/**
|
|
1047
1062
|
* `'rows'`-level batch policy. Default `'reject'`.
|
|
1048
1063
|
*
|
|
@@ -1062,7 +1077,7 @@ interface WithGate<TRow, R extends readonly FlatKey<TRow>[]> {
|
|
|
1062
1077
|
*/
|
|
1063
1078
|
interface LooseGate {
|
|
1064
1079
|
requiredFields?: string[];
|
|
1065
|
-
disabled?: (rows: any[]) =>
|
|
1080
|
+
disabled?: (rows: any[]) => TDbActionDisabledVerdict[];
|
|
1066
1081
|
onDisabledRows?: TOnDisabledRows;
|
|
1067
1082
|
}
|
|
1068
1083
|
type GateOpts<TRow, R extends readonly FlatKey<TRow>[]> = unknown extends TRow ? LooseGate : NoGate | WithGate<TRow, R>;
|
|
@@ -1700,17 +1715,21 @@ declare const useDbActionInput: import("@wooksjs/event-core").WookComposable<{
|
|
|
1700
1715
|
* Wire-body shape for server-side gate rejections. The `name` discriminator
|
|
1701
1716
|
* lets `@atscript/db-client` recognise the response and construct the typed
|
|
1702
1717
|
* `ActionDisabledError` subclass. The shape extends Moost's standard
|
|
1703
|
-
* `ServerError` envelope (`{ message, statusCode, errors? }`) with
|
|
1704
|
-
* additional fields:
|
|
1718
|
+
* `ServerError` envelope (`{ message, statusCode, errors? }`) with:
|
|
1705
1719
|
*
|
|
1706
1720
|
* - `name: 'ActionDisabledError'` — discriminator the client matches.
|
|
1707
1721
|
* - `action` — the `@DbAction` name that rejected the request.
|
|
1708
1722
|
* - `id?` — present only for `'row'`-level rejections.
|
|
1709
1723
|
* - `ids?` — present only for `'rows'`-level rejections.
|
|
1724
|
+
* - `reason?` — the reason shared by every rejected row, when the
|
|
1725
|
+
* `disabled` predicate returned one (since 0.1.141).
|
|
1726
|
+
* - `reasons?` — `'rows'` level only: index-aligned with `ids`, `null` where
|
|
1727
|
+
* that row carries no reason. Present only when at least one reason exists
|
|
1728
|
+
* (since 0.1.141).
|
|
1710
1729
|
*
|
|
1711
1730
|
* `message` is populated with a human-readable string so generic
|
|
1712
1731
|
* `ClientError` consumers (which read `body.message`) still get something
|
|
1713
|
-
* useful without typed-catch dispatch.
|
|
1732
|
+
* useful without typed-catch dispatch — the reason itself when there is one.
|
|
1714
1733
|
*/
|
|
1715
1734
|
interface ActionDisabledErrorBody {
|
|
1716
1735
|
name: "ActionDisabledError";
|
|
@@ -1719,27 +1738,37 @@ interface ActionDisabledErrorBody {
|
|
|
1719
1738
|
action: string;
|
|
1720
1739
|
id?: Record<string, unknown>;
|
|
1721
1740
|
ids?: Record<string, unknown>[];
|
|
1741
|
+
/** @since 0.1.141 */
|
|
1742
|
+
reason?: string;
|
|
1743
|
+
/** @since 0.1.141 */
|
|
1744
|
+
reasons?: (string | null)[];
|
|
1722
1745
|
}
|
|
1723
1746
|
/**
|
|
1724
1747
|
* Thrown by the gate interceptor when `disabled` returns truthy. Composes
|
|
1725
1748
|
* with Moost's existing error mapper to produce HTTP 409 with the wire body
|
|
1726
1749
|
* defined by {@link ActionDisabledErrorBody}.
|
|
1727
1750
|
*
|
|
1728
|
-
* - `'row'`-level rejection: pass `(action, id)` — the
|
|
1729
|
-
*
|
|
1730
|
-
*
|
|
1731
|
-
* of
|
|
1751
|
+
* - `'row'`-level rejection: pass `(action, id, undefined, [reason])` — the
|
|
1752
|
+
* body emits `id` (+ `reason` when given).
|
|
1753
|
+
* - `'rows'`-level rejection: pass `(action, undefined, ids, reasons?)` — the
|
|
1754
|
+
* body emits `ids` (the FULL list of failing IDs in reject mode; the FULL
|
|
1755
|
+
* list of request IDs in skip mode with zero survivors) and, when any
|
|
1756
|
+
* reason exists, `reasons` aligned with `ids`.
|
|
1757
|
+
*
|
|
1758
|
+
* `reasons` entries are verdict reasons (`verdictReason`): anything but a
|
|
1759
|
+
* non-empty string counts as "no reason".
|
|
1732
1760
|
*/
|
|
1733
1761
|
declare class ActionDisabledError extends HttpError<ActionDisabledErrorBody> {
|
|
1734
1762
|
name: string;
|
|
1735
|
-
constructor(action: string, id?: Record<string, unknown>, ids?: Record<string, unknown>[]);
|
|
1763
|
+
constructor(action: string, id?: Record<string, unknown>, ids?: Record<string, unknown>[], reasons?: readonly (string | null | undefined)[]);
|
|
1736
1764
|
}
|
|
1737
1765
|
//#endregion
|
|
1738
1766
|
//#region src/actions/per-row.d.ts
|
|
1739
1767
|
/**
|
|
1740
1768
|
* Lift a per-row predicate into the batch shape required by
|
|
1741
1769
|
* `@DbAction` opts.`disabled` and class-level dict `disabled`. Polarity is
|
|
1742
|
-
* preserved — `true` from `fn` means the action is disabled for that row
|
|
1770
|
+
* preserved — `true` from `fn` means the action is disabled for that row; a
|
|
1771
|
+
* non-empty string disables it with that reason.
|
|
1743
1772
|
*
|
|
1744
1773
|
* ```ts
|
|
1745
1774
|
* @DbAction<Order>('archive', {
|
|
@@ -1748,7 +1777,7 @@ declare class ActionDisabledError extends HttpError<ActionDisabledErrorBody> {
|
|
|
1748
1777
|
* })
|
|
1749
1778
|
* ```
|
|
1750
1779
|
*/
|
|
1751
|
-
declare const perRow: <TRow>(fn: (row: TRow) =>
|
|
1780
|
+
declare const perRow: <TRow>(fn: (row: TRow) => TDbActionDisabledVerdict) => (rows: TRow[]) => TDbActionDisabledVerdict[];
|
|
1752
1781
|
//#endregion
|
|
1753
1782
|
//#region src/permissions/crud-controls.d.ts
|
|
1754
1783
|
declare const QUERY_CONTROLS: readonly string[];
|
|
@@ -1785,7 +1814,10 @@ declare function resolveProp(type: TAtscriptAnnotatedType, field: string): TAtsc
|
|
|
1785
1814
|
/**
|
|
1786
1815
|
* Follows `def.ref` hop by hop until a prop without a `ref` (a primary key or
|
|
1787
1816
|
* a plain column) is reached. Cycle-safe (visited on `<typeId>.<field>`) and
|
|
1788
|
-
* bounded by chain length.
|
|
1817
|
+
* bounded by chain length. Only chain hops count — a plain ref (`field: ""`,
|
|
1818
|
+
* a column typed with a named type) is the end of the chain. A hop into a
|
|
1819
|
+
* `@db.alias` type (a view's join alias, since 0.1.141) continues on the
|
|
1820
|
+
* aliased table / view — the alias is no value domain for a picker.
|
|
1789
1821
|
*/
|
|
1790
1822
|
declare function resolveTerminalRef(def: TAtscriptAnnotatedType): TTerminalRef | undefined;
|
|
1791
1823
|
/**
|
|
@@ -1801,4 +1833,4 @@ declare function resolveTerminalRef(def: TAtscriptAnnotatedType): TTerminalRef |
|
|
|
1801
1833
|
*/
|
|
1802
1834
|
declare function applyTerminalRefs(serialized: TSerializedAnnotatedType, runtime: TAtscriptAnnotatedType, options: TSerializeOptions): TSerializedAnnotatedType;
|
|
1803
1835
|
//#endregion
|
|
1804
|
-
export { ActionDisabledError, type ActionDisabledErrorBody, AsDbController, AsDbReadableController, AsJsonValueHelpController, AsReadableController, AsValueHelpController, type AtscriptDbMate, type AtscriptDbMeta, type AtscriptDbParamsMeta, DEFAULT_DB_SPACE, DbAction, DbActionDefault, type DbActionEnvelope, DbActionID, DbActionIDs, type DbActionOpts, DbActionRow, DbActionRows, DbActions, DbRowActions, DbRowsActions, DbTableActions, FieldCapabilityIndex, type IdValidationSource, InputForm, ONE_CONTROLS, PAGES_CONTROLS, QUERY_CONTROLS, READABLE_DEF, ReadableController, TABLE_DEF, TAssertExposedOptions, type TCapabilityReadable, type TCapabilityVerdict, TControllerBindingOptions, type TCrudOp, type TCrudPermissions, type TDbActionInfo, type TDbActionInputFormMeta, type TDbActionIntent, type TDbActionLevel, type TDbActionMeta, type TDbActionParamKind, type TDbActionProcessor, type TDbActionsEntry, type TDbActionsEntryUnpinned, type TDbClassActionMeta, TDbDecorateContext, TDbDecorateEndpoint, type TDbRemoveGuardContext, type TDbWriteAction, type TDbWriteGuardContext, type TFieldCapability, type THttpErrorEntry, type TQueryPathOp, type TQueryPathRefs, TReadableBinding, type TReadableBindingMeta, type TTerminalRef, TableController, UseValidationErrorTransform, ValueHelpQuery, ViewController, applyTerminalRefs, assertExposed, badRequest, clearDbSpaces, collectQueryPaths, dbActionBodySlot, dbActionInputSlot, discoverActions, errorEnvelope, findReadableBinding, getAtscriptDbMate, getControllerFormType, perRow, provideDbSpace, resolveBoundReadable, resolveDbSpace, resolveProp, resolveTerminalRef, useDbActionId, useDbActionIds, useDbActionInput, useDbActionRow, useDbActionRows, validationErrorTransform };
|
|
1836
|
+
export { ActionDisabledError, type ActionDisabledErrorBody, AsDbController, AsDbReadableController, AsJsonValueHelpController, AsReadableController, AsValueHelpController, type AtscriptDbMate, type AtscriptDbMeta, type AtscriptDbParamsMeta, DEFAULT_DB_SPACE, DbAction, DbActionDefault, type DbActionEnvelope, DbActionID, DbActionIDs, type DbActionOpts, DbActionRow, DbActionRows, DbActions, DbRowActions, DbRowsActions, DbTableActions, FieldCapabilityIndex, type IdValidationSource, InputForm, ONE_CONTROLS, PAGES_CONTROLS, QUERY_CONTROLS, READABLE_DEF, ReadableController, TABLE_DEF, TAssertExposedOptions, type TCapabilityReadable, type TCapabilityVerdict, TControllerBindingOptions, type TCrudOp, type TCrudPermissions, type TDbActionDisabledVerdict, type TDbActionInfo, type TDbActionInputFormMeta, type TDbActionIntent, type TDbActionLevel, type TDbActionMeta, type TDbActionParamKind, type TDbActionProcessor, type TDbActionsEntry, type TDbActionsEntryUnpinned, type TDbClassActionMeta, TDbDecorateContext, TDbDecorateEndpoint, type TDbRemoveGuardContext, type TDbWriteAction, type TDbWriteGuardContext, type TFieldCapability, type THttpErrorEntry, type TQueryPathOp, type TQueryPathRefs, TReadableBinding, type TReadableBindingMeta, type TTerminalRef, TableController, UseValidationErrorTransform, ValueHelpQuery, ViewController, applyTerminalRefs, assertExposed, badRequest, clearDbSpaces, collectQueryPaths, dbActionBodySlot, dbActionInputSlot, discoverActions, errorEnvelope, findReadableBinding, getAtscriptDbMate, getControllerFormType, perRow, provideDbSpace, resolveBoundReadable, resolveDbSpace, resolveProp, resolveTerminalRef, useDbActionId, useDbActionIds, useDbActionInput, useDbActionRow, useDbActionRows, validationErrorTransform };
|
package/dist/index.d.mts
CHANGED
|
@@ -77,13 +77,17 @@ declare abstract class AsReadableController<T extends TAtscriptAnnotatedType = T
|
|
|
77
77
|
* The shallow shape emits `{ field, type: { id, metadata } }` for every FK,
|
|
78
78
|
* which carries the target's `db.http.path` so clients can resolve value-help
|
|
79
79
|
* URLs and lazy-fetch target `/meta` when deeper structure is needed. Nav
|
|
80
|
-
* props (`@db.rel.from` / `@db.rel.to` / `@db.rel.via`)
|
|
81
|
-
* and always expand fully regardless of
|
|
82
|
-
* clients need is unaffected.
|
|
80
|
+
* props (`@db.rel.from` / `@db.rel.to` / `@db.rel.via`) carry only a plain
|
|
81
|
+
* `ref` (`field: ""`) and their bodies always expand fully regardless of
|
|
82
|
+
* `refDepth` — the write-payload shape clients need is unaffected.
|
|
83
83
|
*
|
|
84
|
-
* Annotation whitelist: keeps `meta.*`, `expect.*`,
|
|
85
|
-
*
|
|
86
|
-
*
|
|
84
|
+
* Annotation whitelist: keeps `meta.*`, `expect.*`, `db.rel.*`, the `db.*`
|
|
85
|
+
* keys the shared db validator plugin reads in db-client (`db.json`,
|
|
86
|
+
* `db.patch.strategy`, `db.default*`, `db.column.version`,
|
|
87
|
+
* `db.column.derived`) and the client-facing `db.http.path` /
|
|
88
|
+
* `db.writeOnly`; strips every other `db.*` (table, column, index, etc.).
|
|
89
|
+
* Override in subclass to customise — keep the validator keys, or client
|
|
90
|
+
* preflight diverges from the server.
|
|
87
91
|
*/
|
|
88
92
|
protected getSerializeOptions(): TSerializeOptions;
|
|
89
93
|
private _queryControlsValidator?;
|
|
@@ -589,7 +593,7 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
589
593
|
*
|
|
590
594
|
* Convention (not enforced): name decoration keys with a `$` prefix, like
|
|
591
595
|
* `$actions` and `$distance`, so they can never collide with a field name.
|
|
592
|
-
* Do not overwrite `$actions`. Columns the
|
|
596
|
+
* Do not overwrite `$actions` or `$disabledReasons`. Columns the
|
|
593
597
|
* hook needs but the client did not select must be added in
|
|
594
598
|
* {@link transformProjection} — they are then part of the response.
|
|
595
599
|
*
|
|
@@ -1006,6 +1010,15 @@ declare class AsJsonValueHelpController<T extends TAtscriptAnnotatedType = TAtsc
|
|
|
1006
1010
|
}
|
|
1007
1011
|
//#endregion
|
|
1008
1012
|
//#region src/actions/types.d.ts
|
|
1013
|
+
/**
|
|
1014
|
+
* One entry of a `disabled` predicate's result. Truthy = the action is
|
|
1015
|
+
* disabled for that row; falsy (`false`, `""`) = enabled. A non-empty string
|
|
1016
|
+
* disables the action AND carries a human-readable reason — surfaced in the
|
|
1017
|
+
* 409 `ActionDisabledError` message/body and in the row's `$disabledReasons`.
|
|
1018
|
+
*
|
|
1019
|
+
* @since 0.1.141 (`string`)
|
|
1020
|
+
*/
|
|
1021
|
+
type TDbActionDisabledVerdict = boolean | string;
|
|
1009
1022
|
/** `'rows'`-level batch policy — controls whether failing rows reject or are filtered out. */
|
|
1010
1023
|
type TOnDisabledRows = "reject" | "skip";
|
|
1011
1024
|
/**
|
|
@@ -1034,15 +1047,17 @@ interface WithGate<TRow, R extends readonly FlatKey<TRow>[]> {
|
|
|
1034
1047
|
*/
|
|
1035
1048
|
requiredFields: R;
|
|
1036
1049
|
/**
|
|
1037
|
-
* Sync batch gate predicate — returns a parallel
|
|
1038
|
-
*
|
|
1050
|
+
* Sync batch gate predicate — returns a parallel array aligned with the
|
|
1051
|
+
* input. Per entry: truthy = disabled for the corresponding row; a
|
|
1052
|
+
* non-empty string also gives the reason (see
|
|
1053
|
+
* {@link TDbActionDisabledVerdict}). The `rows`
|
|
1039
1054
|
* argument is type-narrowed to `Pick<FlatOf<TRow>, R[number]>[]`; reading
|
|
1040
1055
|
* a field not listed in `requiredFields` is a compile error.
|
|
1041
1056
|
*
|
|
1042
1057
|
* Promise return is NOT permitted — the predicate is consumed in the
|
|
1043
1058
|
* same tick by the gate and the augmenter.
|
|
1044
1059
|
*/
|
|
1045
|
-
disabled?: (rows: DisabledRowsArg<TRow, R>) =>
|
|
1060
|
+
disabled?: (rows: DisabledRowsArg<TRow, R>) => TDbActionDisabledVerdict[];
|
|
1046
1061
|
/**
|
|
1047
1062
|
* `'rows'`-level batch policy. Default `'reject'`.
|
|
1048
1063
|
*
|
|
@@ -1062,7 +1077,7 @@ interface WithGate<TRow, R extends readonly FlatKey<TRow>[]> {
|
|
|
1062
1077
|
*/
|
|
1063
1078
|
interface LooseGate {
|
|
1064
1079
|
requiredFields?: string[];
|
|
1065
|
-
disabled?: (rows: any[]) =>
|
|
1080
|
+
disabled?: (rows: any[]) => TDbActionDisabledVerdict[];
|
|
1066
1081
|
onDisabledRows?: TOnDisabledRows;
|
|
1067
1082
|
}
|
|
1068
1083
|
type GateOpts<TRow, R extends readonly FlatKey<TRow>[]> = unknown extends TRow ? LooseGate : NoGate | WithGate<TRow, R>;
|
|
@@ -1700,17 +1715,21 @@ declare const useDbActionInput: import("@wooksjs/event-core").WookComposable<{
|
|
|
1700
1715
|
* Wire-body shape for server-side gate rejections. The `name` discriminator
|
|
1701
1716
|
* lets `@atscript/db-client` recognise the response and construct the typed
|
|
1702
1717
|
* `ActionDisabledError` subclass. The shape extends Moost's standard
|
|
1703
|
-
* `ServerError` envelope (`{ message, statusCode, errors? }`) with
|
|
1704
|
-
* additional fields:
|
|
1718
|
+
* `ServerError` envelope (`{ message, statusCode, errors? }`) with:
|
|
1705
1719
|
*
|
|
1706
1720
|
* - `name: 'ActionDisabledError'` — discriminator the client matches.
|
|
1707
1721
|
* - `action` — the `@DbAction` name that rejected the request.
|
|
1708
1722
|
* - `id?` — present only for `'row'`-level rejections.
|
|
1709
1723
|
* - `ids?` — present only for `'rows'`-level rejections.
|
|
1724
|
+
* - `reason?` — the reason shared by every rejected row, when the
|
|
1725
|
+
* `disabled` predicate returned one (since 0.1.141).
|
|
1726
|
+
* - `reasons?` — `'rows'` level only: index-aligned with `ids`, `null` where
|
|
1727
|
+
* that row carries no reason. Present only when at least one reason exists
|
|
1728
|
+
* (since 0.1.141).
|
|
1710
1729
|
*
|
|
1711
1730
|
* `message` is populated with a human-readable string so generic
|
|
1712
1731
|
* `ClientError` consumers (which read `body.message`) still get something
|
|
1713
|
-
* useful without typed-catch dispatch.
|
|
1732
|
+
* useful without typed-catch dispatch — the reason itself when there is one.
|
|
1714
1733
|
*/
|
|
1715
1734
|
interface ActionDisabledErrorBody {
|
|
1716
1735
|
name: "ActionDisabledError";
|
|
@@ -1719,27 +1738,37 @@ interface ActionDisabledErrorBody {
|
|
|
1719
1738
|
action: string;
|
|
1720
1739
|
id?: Record<string, unknown>;
|
|
1721
1740
|
ids?: Record<string, unknown>[];
|
|
1741
|
+
/** @since 0.1.141 */
|
|
1742
|
+
reason?: string;
|
|
1743
|
+
/** @since 0.1.141 */
|
|
1744
|
+
reasons?: (string | null)[];
|
|
1722
1745
|
}
|
|
1723
1746
|
/**
|
|
1724
1747
|
* Thrown by the gate interceptor when `disabled` returns truthy. Composes
|
|
1725
1748
|
* with Moost's existing error mapper to produce HTTP 409 with the wire body
|
|
1726
1749
|
* defined by {@link ActionDisabledErrorBody}.
|
|
1727
1750
|
*
|
|
1728
|
-
* - `'row'`-level rejection: pass `(action, id)` — the
|
|
1729
|
-
*
|
|
1730
|
-
*
|
|
1731
|
-
* of
|
|
1751
|
+
* - `'row'`-level rejection: pass `(action, id, undefined, [reason])` — the
|
|
1752
|
+
* body emits `id` (+ `reason` when given).
|
|
1753
|
+
* - `'rows'`-level rejection: pass `(action, undefined, ids, reasons?)` — the
|
|
1754
|
+
* body emits `ids` (the FULL list of failing IDs in reject mode; the FULL
|
|
1755
|
+
* list of request IDs in skip mode with zero survivors) and, when any
|
|
1756
|
+
* reason exists, `reasons` aligned with `ids`.
|
|
1757
|
+
*
|
|
1758
|
+
* `reasons` entries are verdict reasons (`verdictReason`): anything but a
|
|
1759
|
+
* non-empty string counts as "no reason".
|
|
1732
1760
|
*/
|
|
1733
1761
|
declare class ActionDisabledError extends HttpError<ActionDisabledErrorBody> {
|
|
1734
1762
|
name: string;
|
|
1735
|
-
constructor(action: string, id?: Record<string, unknown>, ids?: Record<string, unknown>[]);
|
|
1763
|
+
constructor(action: string, id?: Record<string, unknown>, ids?: Record<string, unknown>[], reasons?: readonly (string | null | undefined)[]);
|
|
1736
1764
|
}
|
|
1737
1765
|
//#endregion
|
|
1738
1766
|
//#region src/actions/per-row.d.ts
|
|
1739
1767
|
/**
|
|
1740
1768
|
* Lift a per-row predicate into the batch shape required by
|
|
1741
1769
|
* `@DbAction` opts.`disabled` and class-level dict `disabled`. Polarity is
|
|
1742
|
-
* preserved — `true` from `fn` means the action is disabled for that row
|
|
1770
|
+
* preserved — `true` from `fn` means the action is disabled for that row; a
|
|
1771
|
+
* non-empty string disables it with that reason.
|
|
1743
1772
|
*
|
|
1744
1773
|
* ```ts
|
|
1745
1774
|
* @DbAction<Order>('archive', {
|
|
@@ -1748,7 +1777,7 @@ declare class ActionDisabledError extends HttpError<ActionDisabledErrorBody> {
|
|
|
1748
1777
|
* })
|
|
1749
1778
|
* ```
|
|
1750
1779
|
*/
|
|
1751
|
-
declare const perRow: <TRow>(fn: (row: TRow) =>
|
|
1780
|
+
declare const perRow: <TRow>(fn: (row: TRow) => TDbActionDisabledVerdict) => (rows: TRow[]) => TDbActionDisabledVerdict[];
|
|
1752
1781
|
//#endregion
|
|
1753
1782
|
//#region src/permissions/crud-controls.d.ts
|
|
1754
1783
|
declare const QUERY_CONTROLS: readonly string[];
|
|
@@ -1785,7 +1814,10 @@ declare function resolveProp(type: TAtscriptAnnotatedType, field: string): TAtsc
|
|
|
1785
1814
|
/**
|
|
1786
1815
|
* Follows `def.ref` hop by hop until a prop without a `ref` (a primary key or
|
|
1787
1816
|
* a plain column) is reached. Cycle-safe (visited on `<typeId>.<field>`) and
|
|
1788
|
-
* bounded by chain length.
|
|
1817
|
+
* bounded by chain length. Only chain hops count — a plain ref (`field: ""`,
|
|
1818
|
+
* a column typed with a named type) is the end of the chain. A hop into a
|
|
1819
|
+
* `@db.alias` type (a view's join alias, since 0.1.141) continues on the
|
|
1820
|
+
* aliased table / view — the alias is no value domain for a picker.
|
|
1789
1821
|
*/
|
|
1790
1822
|
declare function resolveTerminalRef(def: TAtscriptAnnotatedType): TTerminalRef | undefined;
|
|
1791
1823
|
/**
|
|
@@ -1801,4 +1833,4 @@ declare function resolveTerminalRef(def: TAtscriptAnnotatedType): TTerminalRef |
|
|
|
1801
1833
|
*/
|
|
1802
1834
|
declare function applyTerminalRefs(serialized: TSerializedAnnotatedType, runtime: TAtscriptAnnotatedType, options: TSerializeOptions): TSerializedAnnotatedType;
|
|
1803
1835
|
//#endregion
|
|
1804
|
-
export { ActionDisabledError, type ActionDisabledErrorBody, AsDbController, AsDbReadableController, AsJsonValueHelpController, AsReadableController, AsValueHelpController, type AtscriptDbMate, type AtscriptDbMeta, type AtscriptDbParamsMeta, DEFAULT_DB_SPACE, DbAction, DbActionDefault, type DbActionEnvelope, DbActionID, DbActionIDs, type DbActionOpts, DbActionRow, DbActionRows, DbActions, DbRowActions, DbRowsActions, DbTableActions, FieldCapabilityIndex, type IdValidationSource, InputForm, ONE_CONTROLS, PAGES_CONTROLS, QUERY_CONTROLS, READABLE_DEF, ReadableController, TABLE_DEF, TAssertExposedOptions, type TCapabilityReadable, type TCapabilityVerdict, TControllerBindingOptions, type TCrudOp, type TCrudPermissions, type TDbActionInfo, type TDbActionInputFormMeta, type TDbActionIntent, type TDbActionLevel, type TDbActionMeta, type TDbActionParamKind, type TDbActionProcessor, type TDbActionsEntry, type TDbActionsEntryUnpinned, type TDbClassActionMeta, TDbDecorateContext, TDbDecorateEndpoint, type TDbRemoveGuardContext, type TDbWriteAction, type TDbWriteGuardContext, type TFieldCapability, type THttpErrorEntry, type TQueryPathOp, type TQueryPathRefs, TReadableBinding, type TReadableBindingMeta, type TTerminalRef, TableController, UseValidationErrorTransform, ValueHelpQuery, ViewController, applyTerminalRefs, assertExposed, badRequest, clearDbSpaces, collectQueryPaths, dbActionBodySlot, dbActionInputSlot, discoverActions, errorEnvelope, findReadableBinding, getAtscriptDbMate, getControllerFormType, perRow, provideDbSpace, resolveBoundReadable, resolveDbSpace, resolveProp, resolveTerminalRef, useDbActionId, useDbActionIds, useDbActionInput, useDbActionRow, useDbActionRows, validationErrorTransform };
|
|
1836
|
+
export { ActionDisabledError, type ActionDisabledErrorBody, AsDbController, AsDbReadableController, AsJsonValueHelpController, AsReadableController, AsValueHelpController, type AtscriptDbMate, type AtscriptDbMeta, type AtscriptDbParamsMeta, DEFAULT_DB_SPACE, DbAction, DbActionDefault, type DbActionEnvelope, DbActionID, DbActionIDs, type DbActionOpts, DbActionRow, DbActionRows, DbActions, DbRowActions, DbRowsActions, DbTableActions, FieldCapabilityIndex, type IdValidationSource, InputForm, ONE_CONTROLS, PAGES_CONTROLS, QUERY_CONTROLS, READABLE_DEF, ReadableController, TABLE_DEF, TAssertExposedOptions, type TCapabilityReadable, type TCapabilityVerdict, TControllerBindingOptions, type TCrudOp, type TCrudPermissions, type TDbActionDisabledVerdict, type TDbActionInfo, type TDbActionInputFormMeta, type TDbActionIntent, type TDbActionLevel, type TDbActionMeta, type TDbActionParamKind, type TDbActionProcessor, type TDbActionsEntry, type TDbActionsEntryUnpinned, type TDbClassActionMeta, TDbDecorateContext, TDbDecorateEndpoint, type TDbRemoveGuardContext, type TDbWriteAction, type TDbWriteGuardContext, type TFieldCapability, type THttpErrorEntry, type TQueryPathOp, type TQueryPathRefs, TReadableBinding, type TReadableBindingMeta, type TTerminalRef, TableController, UseValidationErrorTransform, ValueHelpQuery, ViewController, applyTerminalRefs, assertExposed, badRequest, clearDbSpaces, collectQueryPaths, dbActionBodySlot, dbActionInputSlot, discoverActions, errorEnvelope, findReadableBinding, getAtscriptDbMate, getControllerFormType, perRow, provideDbSpace, resolveBoundReadable, resolveDbSpace, resolveProp, resolveTerminalRef, useDbActionId, useDbActionIds, useDbActionInput, useDbActionRow, useDbActionRows, validationErrorTransform };
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { ValidatorError, defineAnnotatedType, isAnnotatedType, serializeAnnotate
|
|
|
3
3
|
import { Body, Delete, Get, HttpError, Patch, Post, Put, Query, Url } from "@moostjs/event-http";
|
|
4
4
|
import { ApplyDecorators, Controller, Inherit, Inject, Intercept, Moost, Optional, Param, Provide, Resolve, TInterceptorPriority, defineBeforeInterceptor, defineInterceptor, getMoostMate, useControllerContext } from "moost";
|
|
5
5
|
import { parseUrl } from "@uniqu/url";
|
|
6
|
-
import { ADAPTER_FILTER_REASON, ALL_AGGREGATE_FNS, DbError, ENCRYPTED_REASON, acceptedOperatorsHint, bucketSourceVerdict, canFilterLeaf, checkHavingKeys, classifyQueryPath, collectQueryPaths, collectQueryPaths as collectQueryPaths$1, findAncestorInSet, isJsonValueField, isPlainObject, narrowerFilterOps, normalizeComputedSelect, reconcileCas, unsupportedOperatorMessage } from "@atscript/db";
|
|
6
|
+
import { ADAPTER_FILTER_REASON, ALL_AGGREGATE_FNS, DbError, ENCRYPTED_REASON, acceptedOperatorsHint, aliasTargetOf, bucketSourceVerdict, canFilterLeaf, checkHavingKeys, classifyQueryPath, collectQueryPaths, collectQueryPaths as collectQueryPaths$1, findAncestorInSet, isJsonValueField, isPlainObject, narrowerFilterOps, normalizeComputedSelect, reconcileCas, unsupportedOperatorMessage } from "@atscript/db";
|
|
7
7
|
import { BUCKET_UNITS } from "@uniqu/core";
|
|
8
8
|
import { buildMemoryPredicate, projectRow, sortRows } from "@atscript/db-memory";
|
|
9
9
|
import { cached, current, defineWook, key } from "@wooksjs/event-core";
|
|
@@ -545,14 +545,18 @@ function resolveProp(type, field) {
|
|
|
545
545
|
/**
|
|
546
546
|
* Follows `def.ref` hop by hop until a prop without a `ref` (a primary key or
|
|
547
547
|
* a plain column) is reached. Cycle-safe (visited on `<typeId>.<field>`) and
|
|
548
|
-
* bounded by chain length.
|
|
548
|
+
* bounded by chain length. Only chain hops count — a plain ref (`field: ""`,
|
|
549
|
+
* a column typed with a named type) is the end of the chain. A hop into a
|
|
550
|
+
* `@db.alias` type (a view's join alias, since 0.1.141) continues on the
|
|
551
|
+
* aliased table / view — the alias is no value domain for a picker.
|
|
549
552
|
*/
|
|
550
553
|
function resolveTerminalRef(def) {
|
|
551
554
|
const ref = def.ref;
|
|
552
|
-
if (!ref) return void 0;
|
|
555
|
+
if (!ref?.field) return void 0;
|
|
553
556
|
let type = ref.type();
|
|
554
557
|
let field = ref.field;
|
|
555
558
|
if (!type) return void 0;
|
|
559
|
+
type = aliasTargetOf(type) ?? type;
|
|
556
560
|
let fk = def.metadata.has("db.rel.FK");
|
|
557
561
|
const visited = new Set([`${type.id ?? ""}.${field}`]);
|
|
558
562
|
for (;;) {
|
|
@@ -560,9 +564,10 @@ function resolveTerminalRef(def) {
|
|
|
560
564
|
if (!prop) break;
|
|
561
565
|
if (prop.metadata.has("db.rel.FK")) fk = true;
|
|
562
566
|
const next = prop.ref;
|
|
563
|
-
if (!next) break;
|
|
564
|
-
const
|
|
565
|
-
if (!
|
|
567
|
+
if (!next?.field) break;
|
|
568
|
+
const resolved = next.type();
|
|
569
|
+
if (!resolved) break;
|
|
570
|
+
const nextType = aliasTargetOf(resolved) ?? resolved;
|
|
566
571
|
const key = `${nextType.id ?? ""}.${next.field}`;
|
|
567
572
|
if (visited.has(key)) break;
|
|
568
573
|
visited.add(key);
|
|
@@ -614,7 +619,7 @@ function walk(node, def, shallow) {
|
|
|
614
619
|
const sProp = sType.props[name];
|
|
615
620
|
if (!sProp) continue;
|
|
616
621
|
if (isNav(prop.metadata)) continue;
|
|
617
|
-
if (prop.ref && sProp.ref && !("type" in sProp.ref.type)) {
|
|
622
|
+
if (prop.ref?.field && sProp.ref && !("type" in sProp.ref.type)) {
|
|
618
623
|
const terminal = resolveTerminalRef(prop);
|
|
619
624
|
if (terminal) {
|
|
620
625
|
const direct = prop.ref.type();
|
|
@@ -731,13 +736,17 @@ let AsReadableController = class AsReadableController {
|
|
|
731
736
|
* The shallow shape emits `{ field, type: { id, metadata } }` for every FK,
|
|
732
737
|
* which carries the target's `db.http.path` so clients can resolve value-help
|
|
733
738
|
* URLs and lazy-fetch target `/meta` when deeper structure is needed. Nav
|
|
734
|
-
* props (`@db.rel.from` / `@db.rel.to` / `@db.rel.via`)
|
|
735
|
-
* and always expand fully regardless of
|
|
736
|
-
* clients need is unaffected.
|
|
739
|
+
* props (`@db.rel.from` / `@db.rel.to` / `@db.rel.via`) carry only a plain
|
|
740
|
+
* `ref` (`field: ""`) and their bodies always expand fully regardless of
|
|
741
|
+
* `refDepth` — the write-payload shape clients need is unaffected.
|
|
737
742
|
*
|
|
738
|
-
* Annotation whitelist: keeps `meta.*`, `expect.*`,
|
|
739
|
-
*
|
|
740
|
-
*
|
|
743
|
+
* Annotation whitelist: keeps `meta.*`, `expect.*`, `db.rel.*`, the `db.*`
|
|
744
|
+
* keys the shared db validator plugin reads in db-client (`db.json`,
|
|
745
|
+
* `db.patch.strategy`, `db.default*`, `db.column.version`,
|
|
746
|
+
* `db.column.derived`) and the client-facing `db.http.path` /
|
|
747
|
+
* `db.writeOnly`; strips every other `db.*` (table, column, index, etc.).
|
|
748
|
+
* Override in subclass to customise — keep the validator keys, or client
|
|
749
|
+
* preflight diverges from the server.
|
|
741
750
|
*/
|
|
742
751
|
getSerializeOptions() {
|
|
743
752
|
return {
|
|
@@ -747,7 +756,7 @@ let AsReadableController = class AsReadableController {
|
|
|
747
756
|
key,
|
|
748
757
|
value
|
|
749
758
|
};
|
|
750
|
-
if (key === "db.json" || key === "db.patch.strategy" || key.startsWith("db.default") || key === "db.http.path" || key === "db.writeOnly" || key === "db.column.version") return {
|
|
759
|
+
if (key === "db.json" || key === "db.patch.strategy" || key.startsWith("db.default") || key === "db.http.path" || key === "db.writeOnly" || key === "db.column.version" || key === "db.column.derived") return {
|
|
751
760
|
key,
|
|
752
761
|
value
|
|
753
762
|
};
|
|
@@ -972,10 +981,14 @@ AsReadableController = __decorate([UseValidationErrorTransform(), __decorateMeta
|
|
|
972
981
|
])], AsReadableController);
|
|
973
982
|
//#endregion
|
|
974
983
|
//#region src/actions/verdict.ts
|
|
975
|
-
/** Assert that a `disabled` predicate returned
|
|
984
|
+
/** Assert that a `disabled` predicate returned an array of the expected length; throws HTTP 500 otherwise. */
|
|
976
985
|
function assertVerdictLength(action, verdicts, expected) {
|
|
977
986
|
if (!Array.isArray(verdicts) || verdicts.length !== expected) throw new HttpError(500, `Action "${action}" disabled predicate returned an invalid verdict array`);
|
|
978
987
|
}
|
|
988
|
+
/** The reason a verdict carries — a non-empty string; `undefined` for `true` / falsy verdicts. */
|
|
989
|
+
function verdictReason(verdict) {
|
|
990
|
+
return typeof verdict === "string" && verdict !== "" ? verdict : void 0;
|
|
991
|
+
}
|
|
979
992
|
//#endregion
|
|
980
993
|
//#region src/actions/list-augmenter.ts
|
|
981
994
|
const candidateCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -1016,7 +1029,8 @@ function computeStripFields(candidates, resolvedProjection) {
|
|
|
1016
1029
|
return strip;
|
|
1017
1030
|
}
|
|
1018
1031
|
/**
|
|
1019
|
-
* Sets `$actions` on every row
|
|
1032
|
+
* Sets `$actions` on every row (plus `$disabledReasons` on rows where a
|
|
1033
|
+
* predicate returned a reason string) and strips the columns fetched only for an
|
|
1020
1034
|
* action's `requiredFields` — IN PLACE; returns the same array, typed as
|
|
1021
1035
|
* augmented.
|
|
1022
1036
|
*/
|
|
@@ -1033,15 +1047,19 @@ function augmentRowsWithActions(args) {
|
|
|
1033
1047
|
for (let i = 0; i < rows.length; i++) {
|
|
1034
1048
|
const row = rows[i];
|
|
1035
1049
|
const names = [];
|
|
1050
|
+
let reasons;
|
|
1036
1051
|
for (let j = 0; j < candidates.length; j++) {
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1052
|
+
const name = candidates[j].envelope.info.name;
|
|
1053
|
+
const verdict = verdicts[j]?.[i];
|
|
1054
|
+
if (!verdict) {
|
|
1055
|
+
names.push(name);
|
|
1040
1056
|
continue;
|
|
1041
1057
|
}
|
|
1042
|
-
|
|
1058
|
+
const reason = verdictReason(verdict);
|
|
1059
|
+
if (reason !== void 0) (reasons ??= {})[name] = reason;
|
|
1043
1060
|
}
|
|
1044
1061
|
row.$actions = names;
|
|
1062
|
+
if (reasons) row.$disabledReasons = reasons;
|
|
1045
1063
|
}
|
|
1046
1064
|
if (resolvedProjection !== null) {
|
|
1047
1065
|
const stripFields = computeStripFields(candidates, resolvedProjection);
|
|
@@ -2432,6 +2450,7 @@ let AsDbReadableController = _AsDbReadableController = class AsDbReadableControl
|
|
|
2432
2450
|
if (geoIndexedPhysical.has(fd.physicalName)) entry.geo = true;
|
|
2433
2451
|
if (this._writeOnlySet.has(path)) entry.writeOnly = true;
|
|
2434
2452
|
if (cap.bucketable) entry.bucketable = true;
|
|
2453
|
+
if (fd.derived) entry.derived = true;
|
|
2435
2454
|
fields[path] = entry;
|
|
2436
2455
|
}
|
|
2437
2456
|
return {
|
|
@@ -3115,6 +3134,7 @@ function assertExposed(app, models, options) {
|
|
|
3115
3134
|
}
|
|
3116
3135
|
const missing = [];
|
|
3117
3136
|
for (const model of models) {
|
|
3137
|
+
if (aliasTargetOf(model)) continue;
|
|
3118
3138
|
const httpPath = model.metadata.get("db.http.path");
|
|
3119
3139
|
if (!auditAll && httpPath === void 0) continue;
|
|
3120
3140
|
if (excluded.has(model) || exposed.has(model)) continue;
|
|
@@ -3126,31 +3146,58 @@ function assertExposed(app, models, options) {
|
|
|
3126
3146
|
}
|
|
3127
3147
|
//#endregion
|
|
3128
3148
|
//#region src/actions/action-disabled-error.ts
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3149
|
+
/** Distinct reasons quoted in a mixed `'rows'` message before `(+N more)`. */
|
|
3150
|
+
const MAX_LISTED_REASONS = 3;
|
|
3151
|
+
function sharedReason(reasons) {
|
|
3152
|
+
const first = reasons[0];
|
|
3153
|
+
if (first === null || first === void 0) return void 0;
|
|
3154
|
+
return reasons.every((r) => r === first) ? first : void 0;
|
|
3155
|
+
}
|
|
3156
|
+
function rowsMessage(action, count, reasons) {
|
|
3157
|
+
const base = `Action "${action}" is disabled for ${count} of the selected rows`;
|
|
3158
|
+
const distinct = [...new Set(reasons.filter((r) => r !== null))];
|
|
3159
|
+
if (distinct.length === 0) return base;
|
|
3160
|
+
const listed = distinct.slice(0, MAX_LISTED_REASONS).join("; ");
|
|
3161
|
+
const more = distinct.length - MAX_LISTED_REASONS;
|
|
3162
|
+
return `${base}: ${listed}${more > 0 ? ` (+${more} more)` : ""}`;
|
|
3132
3163
|
}
|
|
3133
3164
|
/**
|
|
3134
3165
|
* Thrown by the gate interceptor when `disabled` returns truthy. Composes
|
|
3135
3166
|
* with Moost's existing error mapper to produce HTTP 409 with the wire body
|
|
3136
3167
|
* defined by {@link ActionDisabledErrorBody}.
|
|
3137
3168
|
*
|
|
3138
|
-
* - `'row'`-level rejection: pass `(action, id)` — the
|
|
3139
|
-
*
|
|
3140
|
-
*
|
|
3141
|
-
* of
|
|
3169
|
+
* - `'row'`-level rejection: pass `(action, id, undefined, [reason])` — the
|
|
3170
|
+
* body emits `id` (+ `reason` when given).
|
|
3171
|
+
* - `'rows'`-level rejection: pass `(action, undefined, ids, reasons?)` — the
|
|
3172
|
+
* body emits `ids` (the FULL list of failing IDs in reject mode; the FULL
|
|
3173
|
+
* list of request IDs in skip mode with zero survivors) and, when any
|
|
3174
|
+
* reason exists, `reasons` aligned with `ids`.
|
|
3175
|
+
*
|
|
3176
|
+
* `reasons` entries are verdict reasons (`verdictReason`): anything but a
|
|
3177
|
+
* non-empty string counts as "no reason".
|
|
3142
3178
|
*/
|
|
3143
3179
|
var ActionDisabledError = class extends HttpError {
|
|
3144
3180
|
name = "ActionDisabledError";
|
|
3145
|
-
constructor(action, id, ids) {
|
|
3181
|
+
constructor(action, id, ids, reasons) {
|
|
3146
3182
|
const body = {
|
|
3147
3183
|
name: "ActionDisabledError",
|
|
3148
|
-
message:
|
|
3184
|
+
message: "",
|
|
3149
3185
|
statusCode: 409,
|
|
3150
3186
|
action
|
|
3151
3187
|
};
|
|
3152
|
-
if (ids !== void 0)
|
|
3153
|
-
|
|
3188
|
+
if (ids !== void 0) {
|
|
3189
|
+
const aligned = ids.map((_, i) => verdictReason(reasons?.[i]) ?? null);
|
|
3190
|
+
const reason = sharedReason(aligned);
|
|
3191
|
+
body.message = reason ?? rowsMessage(action, ids.length, aligned);
|
|
3192
|
+
body.ids = ids;
|
|
3193
|
+
if (reason !== void 0) body.reason = reason;
|
|
3194
|
+
if (aligned.some((r) => r !== null)) body.reasons = aligned;
|
|
3195
|
+
} else {
|
|
3196
|
+
const reason = verdictReason(reasons?.[0]);
|
|
3197
|
+
body.message = reason ?? `Action "${action}" is disabled for this row`;
|
|
3198
|
+
if (id !== void 0) body.id = id;
|
|
3199
|
+
if (reason !== void 0) body.reason = reason;
|
|
3200
|
+
}
|
|
3154
3201
|
super(409, body);
|
|
3155
3202
|
}
|
|
3156
3203
|
};
|
|
@@ -3441,7 +3488,7 @@ function buildGateInterceptor(opts) {
|
|
|
3441
3488
|
if (level === "row") {
|
|
3442
3489
|
const verdicts = disabled([await ctx.get(dbActionRowSlot)]);
|
|
3443
3490
|
assertVerdictLength(action, verdicts, 1);
|
|
3444
|
-
if (verdicts[0]) throw new ActionDisabledError(action, await ctx.get(dbActionIdSlot));
|
|
3491
|
+
if (verdicts[0]) throw new ActionDisabledError(action, await ctx.get(dbActionIdSlot), void 0, [verdictReason(verdicts[0])]);
|
|
3445
3492
|
return;
|
|
3446
3493
|
}
|
|
3447
3494
|
const ids = await ctx.get(dbActionIdsSlot);
|
|
@@ -3451,26 +3498,30 @@ function buildGateInterceptor(opts) {
|
|
|
3451
3498
|
const verdicts = disabled(existingRows);
|
|
3452
3499
|
assertVerdictLength(action, verdicts, existingRows.length);
|
|
3453
3500
|
const failingIds = [];
|
|
3501
|
+
const failingReasons = [];
|
|
3454
3502
|
const passingRows = [];
|
|
3455
3503
|
const passingIds = [];
|
|
3456
3504
|
let verdictIndex = 0;
|
|
3457
3505
|
for (let i = 0; i < ids.length; i++) {
|
|
3458
3506
|
const row = rows[i];
|
|
3459
|
-
|
|
3460
|
-
|
|
3507
|
+
const verdict = row === void 0 ? void 0 : verdicts[verdictIndex++];
|
|
3508
|
+
if (row === void 0 || verdict) {
|
|
3509
|
+
failingIds.push(ids[i]);
|
|
3510
|
+
failingReasons.push(verdictReason(verdict));
|
|
3511
|
+
} else {
|
|
3461
3512
|
passingRows.push(row);
|
|
3462
3513
|
passingIds.push(ids[i]);
|
|
3463
3514
|
}
|
|
3464
3515
|
}
|
|
3465
3516
|
if (onDisabledRows === "skip") {
|
|
3466
|
-
if (passingRows.length === 0) throw new ActionDisabledError(action, void 0, [...ids]);
|
|
3517
|
+
if (passingRows.length === 0) throw new ActionDisabledError(action, void 0, [...ids], failingReasons);
|
|
3467
3518
|
if (failingIds.length > 0) {
|
|
3468
3519
|
ctx.set(dbActionRowsSlot, Promise.resolve(passingRows));
|
|
3469
3520
|
ctx.set(dbActionIdsSlot, Promise.resolve(passingIds));
|
|
3470
3521
|
}
|
|
3471
3522
|
return;
|
|
3472
3523
|
}
|
|
3473
|
-
if (failingIds.length > 0) throw new ActionDisabledError(action, void 0, failingIds);
|
|
3524
|
+
if (failingIds.length > 0) throw new ActionDisabledError(action, void 0, failingIds, failingReasons);
|
|
3474
3525
|
}, GATE_PRIORITY);
|
|
3475
3526
|
}
|
|
3476
3527
|
/** Thin interceptor for `@DbActionRow*` without `disabled` — injects only the bound table. */
|
|
@@ -3759,7 +3810,8 @@ function InputForm(formType, validatorOpts) {
|
|
|
3759
3810
|
/**
|
|
3760
3811
|
* Lift a per-row predicate into the batch shape required by
|
|
3761
3812
|
* `@DbAction` opts.`disabled` and class-level dict `disabled`. Polarity is
|
|
3762
|
-
* preserved — `true` from `fn` means the action is disabled for that row
|
|
3813
|
+
* preserved — `true` from `fn` means the action is disabled for that row; a
|
|
3814
|
+
* non-empty string disables it with that reason.
|
|
3763
3815
|
*
|
|
3764
3816
|
* ```ts
|
|
3765
3817
|
* @DbAction<Order>('archive', {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/moost-db",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.142",
|
|
4
4
|
"description": "Generic database controller for Moost with Atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"annotations",
|
|
@@ -44,27 +44,27 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@uniqu/url": "^0.1.11",
|
|
47
|
-
"@atscript/db-memory": "^0.1.
|
|
47
|
+
"@atscript/db-memory": "^0.1.142"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@atscript/core": "^0.1.
|
|
51
|
-
"@atscript/typescript": "^0.1.
|
|
50
|
+
"@atscript/core": "^0.1.95",
|
|
51
|
+
"@atscript/typescript": "^0.1.95",
|
|
52
52
|
"@moostjs/event-http": "^0.6.37",
|
|
53
53
|
"@uniqu/core": "^0.1.11",
|
|
54
54
|
"@wooksjs/event-core": "^0.7.23",
|
|
55
55
|
"@wooksjs/event-http": "^0.7.23",
|
|
56
56
|
"@wooksjs/http-body": "^0.7.23",
|
|
57
57
|
"moost": "^0.6.37",
|
|
58
|
-
"unplugin-atscript": "^0.1.
|
|
58
|
+
"unplugin-atscript": "^0.1.95"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@atscript/typescript": "^0.1.
|
|
61
|
+
"@atscript/typescript": "^0.1.95",
|
|
62
62
|
"@moostjs/event-http": "^0.6.37",
|
|
63
63
|
"@uniqu/core": "^0.1.11",
|
|
64
64
|
"@wooksjs/event-core": "^0.7.23",
|
|
65
65
|
"@wooksjs/http-body": "^0.7.23",
|
|
66
66
|
"moost": "^0.6.37",
|
|
67
|
-
"@atscript/db": "^0.1.
|
|
67
|
+
"@atscript/db": "^0.1.142"
|
|
68
68
|
},
|
|
69
69
|
"scripts": {
|
|
70
70
|
"postinstall": "asc -f dts",
|