@px-petals/roku.motion-web 1.0.0 → 1.1.0

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.
Files changed (77) hide show
  1. package/bands/bands.js +3 -1
  2. package/bundle/bundle.js +56 -0
  3. package/list/list.js +12 -1
  4. package/lottie/lottie.layers.js +64 -11
  5. package/lottie/lottie.paint.js +30 -0
  6. package/lottie/lottie.paths.js +19 -4
  7. package/lottie/lottie.props.js +18 -8
  8. package/lottie/lottie.shapes.js +2 -1
  9. package/mixer/mixer.js +31 -8
  10. package/mixer/mixer.mix.js +17 -0
  11. package/motion/motion.convert.js +32 -1
  12. package/motion/motion.flow.js +20 -2
  13. package/motion/motion.machine.js +28 -13
  14. package/motion/motion.values.js +13 -9
  15. package/motion.js +196 -20
  16. package/package.json +10 -16
  17. package/player/player.js +25 -5
  18. package/readme.md +78 -3
  19. package/script/luau/lib/base/base.js +202 -0
  20. package/script/luau/lib/base/base.table.js +189 -0
  21. package/script/luau/lib/lib.args.js +152 -0
  22. package/script/luau/lib/lib.js +23 -0
  23. package/script/luau/lib/math/math.bit32.js +144 -0
  24. package/script/luau/lib/math/math.js +288 -0
  25. package/script/luau/lib/math/math.vector.js +125 -0
  26. package/script/luau/lib/string/string.format.js +143 -0
  27. package/script/luau/lib/string/string.js +160 -0
  28. package/script/luau/lib/string/string.pattern.js +505 -0
  29. package/script/luau/load/load.js +362 -0
  30. package/script/luau/load/load.ops.js +79 -0
  31. package/script/luau/luau.convert.js +259 -0
  32. package/script/luau/luau.js +132 -0
  33. package/script/luau/luau.md +111 -0
  34. package/script/luau/vm/vm.js +167 -0
  35. package/script/luau/vm/vm.meta.js +401 -0
  36. package/script/luau/vm/vm.num.js +236 -0
  37. package/script/luau/vm/vm.run.js +842 -0
  38. package/script/luau/vm/vm.value.js +406 -0
  39. package/script/play/play.js +199 -0
  40. package/script/play/play.objects.js +295 -0
  41. package/script/play/play.scope.js +91 -0
  42. package/script/rive/api/api.statics.js +31 -0
  43. package/script/rive/api/api.type.js +188 -0
  44. package/script/rive/api/artboard/artboard.js +89 -0
  45. package/script/rive/api/assets/assets.js +72 -0
  46. package/script/rive/api/data/data.value.js +99 -0
  47. package/script/rive/api/draw/draw.contours.js +163 -0
  48. package/script/rive/api/draw/draw.measure.js +196 -0
  49. package/script/rive/api/draw/draw.paint.js +235 -0
  50. package/script/rive/api/draw/draw.path.js +153 -0
  51. package/script/rive/api/draw/draw.renderer.js +117 -0
  52. package/script/rive/api/math/math.color.js +96 -0
  53. package/script/rive/api/math/math.mat2d.js +180 -0
  54. package/script/rive/api/math/math.vector.js +121 -0
  55. package/script/rive/api/media/media.audio.js +113 -0
  56. package/script/rive/api/media/media.draw.js +73 -0
  57. package/script/rive/api/media/media.image.js +61 -0
  58. package/script/rive/api/media/media.mesh.js +50 -0
  59. package/script/rive/api/pointer/pointer.event.js +64 -0
  60. package/script/rive/api/pointer/pointer.route.js +69 -0
  61. package/script/rive/api/text/text.font.js +129 -0
  62. package/script/rive/api/text/text.js +278 -0
  63. package/script/rive/api/text/text.layout.js +259 -0
  64. package/script/rive/context/context.js +76 -0
  65. package/script/rive/context/context.memory.js +196 -0
  66. package/script/rive/context/context.viewmodel.js +302 -0
  67. package/script/rive/context/lists/lists.js +160 -0
  68. package/script/rive/context/lists/lists.values.js +281 -0
  69. package/script/rive/host/host.globals.js +44 -0
  70. package/script/rive/host/host.js +533 -0
  71. package/script/rive/host/host.node.js +45 -0
  72. package/script/rive/rive.js +23 -0
  73. package/script/rive/rive.md +180 -0
  74. package/script/script.js +11 -0
  75. package/skin/skin.deform.js +514 -0
  76. package/skin/skin.ik.js +260 -0
  77. package/skin/skin.js +297 -0
@@ -0,0 +1,302 @@
1
+ /**
2
+ * The view model a script reaches through its context: `ViewModel` (named
3
+ * properties, nested view models) and the typed properties it hands out
4
+ * (`Property` for numbers, strings, booleans, colours and enums;
5
+ * `PropertyTrigger`; `PropertyViewModel`). All of it is a thin skin over a
6
+ * `ViewModelAdapter` the player implements, so the host never knows which
7
+ * player it runs in.
8
+ *
9
+ * Paths are dotted: a script that asks a view model at `a.b` for `c` reads
10
+ * the adapter at `a.b.c`.
11
+ */
12
+
13
+ import { LuaClosure, NativeFunction, fail } from '../../luau/vm/vm.value.js'
14
+ import { decodeUtf8, encodeUtf8 } from '../../luau/luau.convert.js'
15
+ import { Wrapped, defineType, text } from '../api/api.type.js'
16
+ import { listType } from './lists/lists.js'
17
+
18
+ /**
19
+ * What a player gives the host to read and write its view model.
20
+ *
21
+ * @typedef {object} ViewModelAdapter
22
+ * @property {(path: string) => any} get the value at a dotted path: a number, string, boolean (a colour is an ARGB number, an enum its value name), or undefined when there is none
23
+ * @property {(path: string, value: any) => void} set
24
+ * @property {(path: string, fn: (value: any) => void) => (() => void)} subscribe calls `fn` when the value changes (or a trigger fires); returns how to stop
25
+ * @property {(path: string) => (string | undefined)} [kind] `number`, `string`, `boolean`, `color`, `enum`, `trigger`, `viewModel`, `list`, or undefined for no such property; without it the kind is read from the value's type
26
+ * @property {(path: string) => void} [fire] fires a trigger; without it a trigger is set to `true`
27
+ * @property {(path: string) => number} [listSize] the items of a list property
28
+ * @property {(path: string, index: number) => (ViewModelAdapter | undefined)} [listGet] the view model of item `index` (0-based), as an adapter of its own whose paths start at its root
29
+ * @property {(path: string, item: ViewModelAdapter, index: number) => void} [listInsert] puts a view model into the list at `index` (0-based); the list notifies its subscribers
30
+ * @property {(path: string, index: number) => (ViewModelAdapter | undefined)} [listRemove] takes item `index` out and returns it
31
+ * @property {(path: string, a: number, b: number) => void} [listSwap] swaps two items
32
+ * @property {(path: string) => void} [listClear] empties the list in one change; without it `clear` removes the items one by one
33
+ * @property {(path: string, name?: string) => (ViewModelAdapter | undefined)} [instance] a new instance of the view model at `path` ('' for the root), from the named instance of the file when `name` is given; not in any list yet
34
+ *
35
+ * The three asset kinds `image`, `font` and `blob` hold an asset's name (`get` gives it, `set` takes it).
36
+ */
37
+
38
+ /**
39
+ * The state every view model object of one script host shares.
40
+ *
41
+ * @typedef {object} Scope
42
+ * @property {ViewModelAdapter} adapter
43
+ * @property {Record<string, PropertyData>} props property objects by `kind:path`, so a property is the same object each time it is asked for
44
+ * @property {(fn: any, args: any[]) => void} call calls a Lua function, keeping the host's error handling
45
+ * @property {() => void} wake tells the host something changed
46
+ * @property {(adapter: ViewModelAdapter) => Scope} [scopeFor] the scope of another adapter (a list's item); without it items get scopes of their own
47
+ * @property {(kind: string, name: string) => any} [asset] an asset's Lua value (`image`, `font`, `blob`) by name
48
+ */
49
+
50
+ /**
51
+ * @typedef {object} PropertyData
52
+ * @property {Scope} scope
53
+ * @property {string} path
54
+ * @property {string} kind
55
+ * @property {{ cb: any, anchor: any }[]} listeners
56
+ * @property {(() => void) | null} unsubscribe
57
+ * @property {any} wrapper
58
+ */
59
+
60
+ /**
61
+ * @param {Scope} scope
62
+ * @param {string} path
63
+ * @returns {string | undefined}
64
+ */
65
+ function kindOf(scope, path) {
66
+ const a = scope.adapter
67
+ if (a.kind) return a.kind(path)
68
+ const v = a.get(path)
69
+ return typeof v === 'number' ? 'number' : typeof v === 'string' ? 'string' : typeof v === 'boolean' ? 'boolean' : Array.isArray(v) ? 'list' : undefined
70
+ }
71
+
72
+ /**
73
+ * @param {string} kind
74
+ * @returns {boolean} whether the property holds an asset's name
75
+ */
76
+ function isAsset(kind) {
77
+ return kind === 'image' || kind === 'font' || kind === 'blob'
78
+ }
79
+
80
+ /**
81
+ * A host value as the Lua value a property holds.
82
+ *
83
+ * @param {any} v
84
+ * @returns {any}
85
+ */
86
+ function toLuaValue(v) {
87
+ return typeof v === 'string' ? encodeUtf8(v) : v === null ? undefined : v
88
+ }
89
+
90
+ /**
91
+ * @param {string} kind
92
+ * @param {any} v a Lua value
93
+ * @returns {any} the host value to store
94
+ */
95
+ function fromLuaValue(kind, v) {
96
+ if (isAsset(kind)) {
97
+ const name = v instanceof Wrapped && v.host ? v.host.name : undefined
98
+ if (typeof name !== 'string') throw fail('a ' + kind + ' property takes a ' + kind)
99
+ return name
100
+ }
101
+ if (kind === 'number' || kind === 'color') {
102
+ if (typeof v !== 'number') throw fail('a ' + kind + ' property takes a number')
103
+ return v
104
+ }
105
+ if (kind === 'boolean') {
106
+ if (typeof v !== 'boolean') throw fail('a boolean property takes a boolean')
107
+ return v
108
+ }
109
+ if (kind === 'string' || kind === 'enum') {
110
+ if (typeof v !== 'string') throw fail('a ' + kind + ' property takes a string')
111
+ return decodeUtf8(v)
112
+ }
113
+ throw fail('cannot set a ' + kind + ' property')
114
+ }
115
+
116
+ // Properties
117
+ // ■■■■■■■■■■
118
+
119
+ /**
120
+ * Adds a listener or takes one away; both take `(callback)` or `(anchor, callback)`.
121
+ *
122
+ * @param {PropertyData} p
123
+ * @param {any[]} args with the property first
124
+ * @param {boolean} add
125
+ */
126
+ function listen(p, args, add) {
127
+ const withAnchor = args[2] !== undefined
128
+ const cb = withAnchor ? args[2] : args[1]
129
+ const anchor = withAnchor ? args[1] : undefined
130
+ if (!(cb instanceof NativeFunction) && !(cb instanceof LuaClosure)) throw fail('addListener needs a function')
131
+ if (add) {
132
+ p.listeners.push({ cb, anchor })
133
+ if (!p.unsubscribe) {
134
+ p.unsubscribe = p.scope.adapter.subscribe(p.path, function () {
135
+ const snapshot = p.listeners.slice()
136
+ for (let n = 0; n < snapshot.length; n++) p.scope.call(snapshot[n].cb, [ snapshot[n].anchor !== undefined ? snapshot[n].anchor : p.wrapper ])
137
+ p.scope.wake()
138
+ })
139
+ }
140
+ return
141
+ }
142
+ for (let n = 0; n < p.listeners.length; n++) {
143
+ if (p.listeners[n].cb === cb) {
144
+ p.listeners.splice(n, 1)
145
+ break
146
+ }
147
+ }
148
+ if (p.listeners.length === 0 && p.unsubscribe) {
149
+ p.unsubscribe()
150
+ p.unsubscribe = null
151
+ }
152
+ }
153
+
154
+ /**
155
+ * @param {PropertyData} p
156
+ * @returns {any} the asset the property names, or nil
157
+ */
158
+ function assetValue(p) {
159
+ const name = p.scope.adapter.get(p.path)
160
+ return typeof name === 'string' && p.scope.asset ? p.scope.asset(p.kind, name) : undefined
161
+ }
162
+
163
+ /** @type {Record<string, (p: PropertyData, args: any[]) => any>} */
164
+ export const listenerMethods = {
165
+ addListener: (p, args) => listen(p, args, true),
166
+ removeListener: (p, args) => listen(p, args, false),
167
+ }
168
+
169
+ export const Property = defineType('Property', {
170
+ fields: {
171
+ value: {
172
+ get: (p) => (isAsset(p.kind) ? assetValue(p) : toLuaValue(p.scope.adapter.get(p.path))),
173
+ set: (p, v) => {
174
+ p.scope.adapter.set(p.path, fromLuaValue(p.kind, v))
175
+ },
176
+ },
177
+ },
178
+ methods: listenerMethods,
179
+ })
180
+
181
+ export const PropertyTrigger = defineType('PropertyTrigger', {
182
+ methods: {
183
+ addListener: listenerMethods.addListener,
184
+ removeListener: listenerMethods.removeListener,
185
+ fire: (p) => {
186
+ const a = p.scope.adapter
187
+ if (a.fire) a.fire(p.path)
188
+ else a.set(p.path, true)
189
+ },
190
+ },
191
+ })
192
+
193
+ export const PropertyViewModel = defineType('PropertyViewModel', {
194
+ fields: {
195
+ value: { get: (p) => viewModelAt(p.scope, p.path) },
196
+ },
197
+ methods: listenerMethods,
198
+ })
199
+
200
+ const PropertyList = listType({ listenerMethods, scopeOf, viewModelAt })
201
+
202
+ /**
203
+ * The one property object for a path and kind.
204
+ *
205
+ * @param {Scope} scope
206
+ * @param {string} path
207
+ * @param {string} kind
208
+ * @returns {any} its Lua wrapper
209
+ */
210
+ function propertyAt(scope, path, kind) {
211
+ const key = kind + ':' + path
212
+ let p = scope.props[key]
213
+ if (!p) {
214
+ p = { scope, path, kind, listeners: [], unsubscribe: null, wrapper: null }
215
+ const type = kind === 'trigger' ? PropertyTrigger : kind === 'viewModel' ? PropertyViewModel : kind === 'list' ? PropertyList : Property
216
+ p.wrapper = type.wrap(p)
217
+ scope.props[key] = p
218
+ }
219
+ return p.wrapper
220
+ }
221
+
222
+ // View models
223
+ // ■■■■■■■■■■■
224
+
225
+ /**
226
+ * @typedef {object} ViewModelData
227
+ * @property {Scope} scope
228
+ * @property {string} prefix '' or a dotted path ending in '.'
229
+ */
230
+
231
+ /**
232
+ * @param {Scope} scope
233
+ * @param {string} path the dotted path of the view model; '' for the root
234
+ * @returns {any} a ViewModel wrapper
235
+ */
236
+ export function viewModelAt(scope, path) {
237
+ return ViewModel.wrap({ scope, prefix: path === '' ? '' : path + '.' })
238
+ }
239
+
240
+ /**
241
+ * @param {string} kind what the method wants
242
+ * @returns {(h: ViewModelData, args: any[]) => any}
243
+ */
244
+ function getter(kind) {
245
+ return function (h, args) {
246
+ const name = text(args, 1, 'get' + kind)
247
+ const path = h.prefix + name
248
+ const actual = kindOf(h.scope, path)
249
+ // without kinds from the adapter, a number may be a colour
250
+ const ok = actual === kind || (kind === 'color' && actual === 'number' && !h.scope.adapter.kind) || (kind === 'enum' && actual === 'string' && !h.scope.adapter.kind)
251
+ return ok ? propertyAt(h.scope, path, kind) : undefined
252
+ }
253
+ }
254
+
255
+ /**
256
+ * The scope of another adapter (a list's item, a new instance) beside this one.
257
+ *
258
+ * @param {Scope} scope
259
+ * @param {ViewModelAdapter} adapter
260
+ * @returns {Scope}
261
+ */
262
+ export function scopeOf(scope, adapter) {
263
+ if (scope.scopeFor) return scope.scopeFor(adapter)
264
+ return { adapter, props: {}, call: scope.call, wake: scope.wake, asset: scope.asset }
265
+ }
266
+
267
+ /** @type {Record<string, (h: ViewModelData, args: any[]) => any>} */
268
+ const viewModelMethods = {
269
+ getNumber: getter('number'),
270
+ getString: getter('string'),
271
+ getBoolean: getter('boolean'),
272
+ getColor: getter('color'),
273
+ getEnum: getter('enum'),
274
+ getTrigger: getter('trigger'),
275
+ getViewModel: getter('viewModel'),
276
+ // lists, assets and creating instances need a player behind them
277
+ getList: getter('list'),
278
+ getImage: getter('image'),
279
+ getFont: getter('font'),
280
+ getBlob: getter('blob'),
281
+ // a new instance of this view model, optionally from a named instance of the file
282
+ instance: (h, args) => {
283
+ const a = h.scope.adapter
284
+ const made = a.instance ? a.instance(h.prefix === '' ? '' : h.prefix.slice(0, -1), args[1] === undefined ? undefined : text(args, 1, 'instance')) : undefined
285
+ return made ? viewModelAt(scopeOf(h.scope, made), '') : undefined
286
+ },
287
+ getIndex: () => -1,
288
+ }
289
+
290
+ export const ViewModel = defineType('ViewModel', {
291
+ fields: {
292
+ name: { get: () => '' },
293
+ },
294
+ methods: viewModelMethods,
295
+ // `vm.title` reads the property of that name, as a typed script's `Data.X` does
296
+ index: (h, key) => {
297
+ if (typeof key !== 'string') return undefined
298
+ const path = h.prefix + decodeUtf8(key)
299
+ const kind = kindOf(h.scope, path)
300
+ return kind === undefined ? undefined : propertyAt(h.scope, path, kind)
301
+ },
302
+ })
@@ -0,0 +1,160 @@
1
+ /**
2
+ * `PropertyList`: a view-model list property a script reads and changes,
3
+ * as Rive documents it. Items are view models, 1-based: `list[1]`,
4
+ * `list.length`, `push`, `insert(vm, index)`, `swap`, `pop`, `shift`,
5
+ * `clear`, `remove`, `removeAllOf`, `removeAt`, and `addListener`.
6
+ *
7
+ * The items are adapters of their own (`ViewModelAdapter.listGet`), so an item
8
+ * is read and written like any view model; what changes the list goes through
9
+ * `listInsert`, `listRemove` and `listSwap`, which the player turns into rows.
10
+ */
11
+
12
+ import { fail } from '../../../luau/vm/vm.value.js'
13
+ import { Wrapped, defineType, num } from '../../api/api.type.js'
14
+
15
+ /** @import { PropertyData, ViewModelData } from '../context.viewmodel.js' */
16
+
17
+ /**
18
+ * @typedef {object} ListDeps what the view model gives the list type (passed in, so the two modules do not import each other)
19
+ * @property {Record<string, (p: PropertyData, args: any[]) => any>} listenerMethods
20
+ * @property {(scope: any, adapter: any) => any} scopeOf
21
+ * @property {(scope: any, path: string) => any} viewModelAt
22
+ */
23
+
24
+ /** @type {ListDeps} */
25
+ let deps
26
+
27
+ /**
28
+ * @param {PropertyData} p
29
+ * @returns {import('../context.viewmodel.js').ViewModelAdapter}
30
+ */
31
+ function adapterOf(p) {
32
+ return p.scope.adapter
33
+ }
34
+
35
+ /**
36
+ * @param {PropertyData} p
37
+ * @returns {number}
38
+ */
39
+ function sizeOf(p) {
40
+ const a = adapterOf(p)
41
+ return a.listSize ? a.listSize(p.path) : 0
42
+ }
43
+
44
+ /**
45
+ * @param {PropertyData} p
46
+ * @param {number} index 0-based
47
+ * @returns {any} the item's ViewModel, or nil
48
+ */
49
+ function itemAt(p, index) {
50
+ const a = adapterOf(p)
51
+ const item = index >= 0 && a.listGet ? a.listGet(p.path, index) : undefined
52
+ return item ? deps.viewModelAt(deps.scopeOf(p.scope, item), '') : undefined
53
+ }
54
+
55
+ /**
56
+ * @param {any[]} args
57
+ * @param {number} i
58
+ * @param {string} fname
59
+ * @returns {import('../context.viewmodel.js').ViewModelAdapter} the adapter of a view model argument that is an instance
60
+ */
61
+ function instanceArg(args, i, fname) {
62
+ const w = args[i]
63
+ /** @type {ViewModelData | undefined} */
64
+ const h = w instanceof Wrapped ? w.host : undefined
65
+ if (!h || !h.scope || h.prefix !== '') throw fail("invalid argument #" + (i + 1) + " to '" + fname + "' (ViewModel instance expected)")
66
+ return h.scope.adapter
67
+ }
68
+
69
+ /**
70
+ * @param {PropertyData} p
71
+ * @param {number} index 0-based
72
+ * @returns {any} what was removed, as a ViewModel
73
+ */
74
+ function take(p, index) {
75
+ const a = adapterOf(p)
76
+ if (index < 0 || index >= sizeOf(p) || !a.listRemove) return undefined
77
+ const item = a.listRemove(p.path, index)
78
+ return item ? deps.viewModelAt(deps.scopeOf(p.scope, item), '') : undefined
79
+ }
80
+
81
+ /**
82
+ * @param {PropertyData} p
83
+ * @param {import('../context.viewmodel.js').ViewModelAdapter} item
84
+ * @returns {number} its 0-based place, -1 when absent
85
+ */
86
+ function find(p, item) {
87
+ const a = adapterOf(p)
88
+ const n = sizeOf(p)
89
+ for (let i = 0; i < n; i++) if (a.listGet && a.listGet(p.path, i) === item) return i
90
+ return -1
91
+ }
92
+
93
+ /**
94
+ * @param {PropertyData} p
95
+ * @param {number} n 1-based index, as a script writes it
96
+ * @param {string} fname
97
+ * @param {number} max the highest allowed
98
+ * @returns {number} 0-based
99
+ */
100
+ function place(p, n, fname, max) {
101
+ if (n !== Math.floor(n) || n < 1 || n > max) throw fail("invalid index to '" + fname + "' (" + n + ' of ' + max + ')')
102
+ return n - 1
103
+ }
104
+
105
+ /**
106
+ * @param {ListDeps} d
107
+ * @returns {import('../../api/api.type.js').HostType}
108
+ */
109
+ export function listType(d) {
110
+ deps = d
111
+ const listenerMethods = d.listenerMethods
112
+ return defineType('PropertyList', {
113
+ fields: {
114
+ length: { get: (p) => sizeOf(p) },
115
+ },
116
+ methods: {
117
+ addListener: listenerMethods.addListener,
118
+ removeListener: listenerMethods.removeListener,
119
+ push: (p, args) => {
120
+ const a = adapterOf(p)
121
+ if (a.listInsert) a.listInsert(p.path, instanceArg(args, 1, 'push'), sizeOf(p))
122
+ },
123
+ insert: (p, args) => {
124
+ const a = adapterOf(p)
125
+ const at = place(p, num(args, 2, 'insert'), 'insert', sizeOf(p) + 1)
126
+ if (a.listInsert) a.listInsert(p.path, instanceArg(args, 1, 'insert'), at)
127
+ },
128
+ swap: (p, args) => {
129
+ const a = adapterOf(p)
130
+ const n = sizeOf(p)
131
+ const i = place(p, num(args, 1, 'swap'), 'swap', n)
132
+ const j = place(p, num(args, 2, 'swap'), 'swap', n)
133
+ if (a.listSwap && i !== j) a.listSwap(p.path, i, j)
134
+ },
135
+ pop: (p) => take(p, sizeOf(p) - 1),
136
+ shift: (p) => take(p, 0),
137
+ clear: (p) => {
138
+ const a = adapterOf(p)
139
+ if (a.listClear) return void a.listClear(p.path)
140
+ for (let n = sizeOf(p) - 1; n >= 0; n--) take(p, n)
141
+ },
142
+ removeAt: (p, args) => {
143
+ take(p, place(p, num(args, 1, 'removeAt'), 'removeAt', sizeOf(p)))
144
+ },
145
+ remove: (p, args) => {
146
+ const at = find(p, instanceArg(args, 1, 'remove'))
147
+ if (at >= 0) take(p, at)
148
+ },
149
+ removeAllOf: (p, args) => {
150
+ const item = instanceArg(args, 1, 'removeAllOf')
151
+ for (let at = find(p, item); at >= 0; at = find(p, item)) take(p, at)
152
+ },
153
+ },
154
+ meta: {
155
+ __len: (vm, args) => sizeOf(args[0].host),
156
+ },
157
+ // `list[1]`: items are 1-based
158
+ index: (p, key) => (typeof key === 'number' ? itemAt(p, key - 1) : undefined),
159
+ })
160
+ }