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:
@@ -1030,11 +1030,23 @@ const MAX_BUDGET_MS = 3_600_000
|
||||
// vocabulary of one is a vocabulary that can grow without a version.
|
||||
const PARAM_DURATIONS = ['minutes']
|
||||
|
||||
// A param that holds several values (MODULE_API 1.14.0; Rust PLAN_REDESIGNS
|
||||
// §12.3, D344). Two types, because the one step that needed it picks strings
|
||||
// from a source and an int list is the same check one type over; a list of
|
||||
// booleans, dates or links is a shape nothing has asked for, and every control
|
||||
// the editor draws for a list is a picker or a tag box.
|
||||
const MULTIPLE_PARAM_TYPES = ['string', 'int']
|
||||
// `max` when the declaration leaves it out, and the most it may say. The ceiling
|
||||
// is a bound on one step's params, not a number anyone should reach: a list of a
|
||||
// hundred is a step nobody can read back.
|
||||
const DEFAULT_MULTIPLE_MAX = 20
|
||||
const MAX_MULTIPLE_MAX = 100
|
||||
|
||||
// `call` names the registration in the error, because a template's inputs are
|
||||
// checked by this same function (they ARE params: one control each, with a
|
||||
// name, a type, an example and maybe a source).
|
||||
function checkActionParam(actionId, entry, seen, call = 'registerEventActions') {
|
||||
const { name, type, required, example, description, source, duration } = entry || {}
|
||||
const { name, type, required, example, description, source, duration, multiple, max } = entry || {}
|
||||
const where = `${call}: ${actionId}`
|
||||
if (!PARAM_NAME.test(name || '')) throw new Error(`${where}: bad param name "${name}"`)
|
||||
if (seen.has(name)) throw new Error(`${where}: param "${name}" declared twice`)
|
||||
@@ -1072,6 +1084,35 @@ function checkActionParam(actionId, entry, seen, call = 'registerEventActions')
|
||||
throw new Error(`${where}: param "${name}" is a duration and must be int or float, not ${type}`)
|
||||
}
|
||||
}
|
||||
// **`multiple` makes the value a list** (1.14.0, §12.3). The example stays ONE
|
||||
// value — it is the placeholder of the box a value is typed into — and the
|
||||
// value a step stores is a JSON array of them (`events/spec.js`).
|
||||
//
|
||||
// Refused on a duration, because a list of lengths of time is not a hold the
|
||||
// editor can keep a phase open for, and on a template's inputs, because
|
||||
// `build()` is a module's own code reading `inputs.x` as one value and this
|
||||
// version does not change what it is handed.
|
||||
if (multiple !== undefined && multiple !== null && multiple !== false) {
|
||||
if (multiple !== true) throw new Error(`${where}: param "${name}" multiple must be true or left out`)
|
||||
if (call === 'registerEventTemplates') {
|
||||
throw new Error(`${where}: input "${name}" cannot be multiple; a template's inputs are single values`)
|
||||
}
|
||||
if (!MULTIPLE_PARAM_TYPES.includes(type)) {
|
||||
throw new Error(`${where}: param "${name}" is multiple and must be ${MULTIPLE_PARAM_TYPES.join(' or ')}, not ${type}`)
|
||||
}
|
||||
if (duration !== undefined && duration !== null) {
|
||||
throw new Error(`${where}: param "${name}" cannot be both multiple and a duration`)
|
||||
}
|
||||
if (Array.isArray(example)) {
|
||||
throw new Error(`${where}: param "${name}" needs ONE example, not a list (it is the placeholder of one value)`)
|
||||
}
|
||||
}
|
||||
if (max !== undefined && max !== null) {
|
||||
if (multiple !== true) throw new Error(`${where}: param "${name}" has a max but is not multiple`)
|
||||
if (!Number.isInteger(max) || max < 1 || max > MAX_MULTIPLE_MAX) {
|
||||
throw new Error(`${where}: param "${name}" max must be 1..${MAX_MULTIPLE_MAX}, got "${max}"`)
|
||||
}
|
||||
}
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
@@ -1080,6 +1121,9 @@ function checkActionParam(actionId, entry, seen, call = 'registerEventActions')
|
||||
source: source === undefined ? null : source,
|
||||
description: description || '',
|
||||
...(duration ? { duration } : {}),
|
||||
// Absent rather than `false` on every other param, as `duration` is, so a
|
||||
// module that never says it reads back exactly the declaration it wrote.
|
||||
...(multiple === true ? { multiple: true, max: max ?? DEFAULT_MULTIPLE_MAX } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2164,6 +2208,9 @@ module.exports = {
|
||||
allEventTemplates,
|
||||
eventTemplate,
|
||||
PARAM_DURATIONS,
|
||||
MULTIPLE_PARAM_TYPES,
|
||||
DEFAULT_MULTIPLE_MAX,
|
||||
MAX_MULTIPLE_MAX,
|
||||
resolveOptionSource,
|
||||
allEngagementSeeds,
|
||||
engagementSeedsFor,
|
||||
|
||||
Reference in New Issue
Block a user