feat(events): a param that holds several values (MODULE_API 1.14.0)
Rust PLAN_REDESIGNS §12.3 (D344): an event action param may declare `multiple: true`, with an optional `max` (1-100, 20 when left out), on a `string` or `int` param. The permission reward steps take several permissions in one step through it. - registries: the flag and `max` are checked at registration and served in the catalog; refused on other types, on a duration, on a template's inputs, and with a list as the example. A param that does not say it reads back exactly as declared, without the keys. - spec: the value is a JSON array, each item the type's literal, duplicates dropped in order, at most `max`; a bare value is a list of one; `required` means at least one; an empty item is refused. - authorize: a multiple param's example is priced as a list of one, since cost() is handed the array. - editor: a multi-select. Chips above a grouped, filterable checklist for a source that answered; the typeahead for a searchable source; a tag box otherwise. Ticking past max is refused in words. A new step holds the example as a list of one. - run console: params read as `name: value`, a list joined by commas. - MODULE_API 1.13.0 -> 1.14.0 on both halves; engagement-triggers.json and the catalog's OpenAPI note regenerated. Additive only: Module-uo (`^1.10.0`) declares no multiple param. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -144,7 +144,9 @@ function priceOf(action, params) {
|
||||
function dimensionsOf(action) {
|
||||
const params = {}
|
||||
for (const p of action?.params || []) {
|
||||
if (p.example !== undefined && p.example !== null) params[p.name] = p.example
|
||||
// A `multiple` param's example is one value and `cost()` is handed a list
|
||||
// (1.14.0), so it is priced as a list of one.
|
||||
if (p.example !== undefined && p.example !== null) params[p.name] = p.multiple ? [p.example] : p.example
|
||||
}
|
||||
const priced = priceOf(action, params)
|
||||
return priced ? Object.keys(priced).sort() : []
|
||||
|
||||
@@ -323,6 +323,37 @@ function validateAdvance(raw, path, errors) {
|
||||
return { on: triggerId, where: checked.conditions, count, dormant: false }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `multiple` param's value as the list it is (MODULE_API 1.14.0, Rust
|
||||
* PLAN_REDESIGNS §12.3).
|
||||
*
|
||||
* `{ value: [...] }` or `{ error }`. **A bare value is a list of one**, so a
|
||||
* spec written by hand or over the API before its author knew the param took
|
||||
* several does not trip on it. Absent, `null`, `''` and `[]` are all the empty
|
||||
* list, which the caller reads as unset — `required` means at least one.
|
||||
*
|
||||
* Each element is the type's literal, checked by the same `checkLiteral` a
|
||||
* single value is; an empty string inside the list is refused rather than kept,
|
||||
* because a blank chip is nothing anyone picked. Duplicates are dropped keeping
|
||||
* the first, so the order the author picked in is the order the module reads.
|
||||
*/
|
||||
function checkList(p, raw) {
|
||||
if (raw === undefined || raw === null || raw === '') return { value: [] }
|
||||
const items = Array.isArray(raw) ? raw : [raw]
|
||||
const out = []
|
||||
const seen = new Set()
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i] === '') return { error: `item ${i + 1} is empty` }
|
||||
const checked = checkLiteral(p.type, items[i])
|
||||
if (checked.error) return { error: `item ${i + 1} ${checked.error}` }
|
||||
if (seen.has(checked.value)) continue
|
||||
seen.add(checked.value)
|
||||
out.push(checked.value)
|
||||
}
|
||||
if (out.length > p.max) return { error: `holds ${out.length} values; the most is ${p.max}` }
|
||||
return { value: out }
|
||||
}
|
||||
|
||||
/**
|
||||
* Check one authored param object against an action's declared params.
|
||||
*
|
||||
@@ -349,6 +380,14 @@ function checkParams(declaration, raw, path) {
|
||||
|
||||
for (const p of declaration.params || []) {
|
||||
const value = given[p.name]
|
||||
if (p.multiple) {
|
||||
const listed = checkList(p, value)
|
||||
if (listed.error) errors.push(`${path}.params: "${p.name}" ${listed.error}`)
|
||||
else if (!listed.value.length) {
|
||||
if (p.required) errors.push(`${path}.params: "${p.name}" is required`)
|
||||
} else params[p.name] = listed.value
|
||||
continue
|
||||
}
|
||||
if (value === undefined || value === null || value === '') {
|
||||
if (p.required) errors.push(`${path}.params: "${p.name}" is required`)
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user