initiative-plugin-sdk 7.2.0 → 8.0.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.
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://initiativetasks.com/schemas/plugin-manifest-v1.json",
4
4
  "title": "Initiative plug-in manifest (the 'definition' body)",
5
- "description": "What a plug-in declares it can do. A plug-in is a container, which names its `service` and answers Initiative's calls, or declarative, which names its `hosts` and no service, and whose endpoints, connection checks and webhook events Initiative runs itself from the requests and JSONata expressions written here; one plug-in is never both. This is the 'definition' field of the document served at /.well-known/initiative-plugin.json, NOT that whole document: a registrar also requires protocol_version, public_id and kind alongside it, and refuses a definition served bare. Generated from the platform's own validator vocabulary. A manifest that satisfies this schema is well-formed, not necessarily acceptable. Cross-references (the endpoint a widget binds, a requires term's connection, an endpoint's service prefix), the direction-specific rules on an endpoint, the features/blocks cross-check in both directions, UTF-8 byte-size caps, the rules tying a connection's flow and token to its scope and fields, what a webhooks block names, what a vendor setup writes to, whether every expression parses, what a declarative request and its steps name, the bounds and unique ids of schedules, the unique keys and ids of fields, parts and actions, what a part's nodes and an action name, and a part's size and depth are enforced by the platform on publish and are not expressible here.",
5
+ "description": "What a plug-in declares it can do. A plug-in is a container, which names its `service` and answers Initiative's calls, or declarative, which names its `hosts` and no service, and whose endpoints, connection checks, webhook events, schedules and actions Initiative runs itself from the requests and JSONata expressions written here, keeping the values they write on items; one plug-in is never both. This is the 'definition' field of the document served at /.well-known/initiative-plugin.json, NOT that whole document: a registrar also requires protocol_version, public_id and kind alongside it, and refuses a definition served bare. Generated from the platform's own validator vocabulary. A manifest that satisfies this schema is well-formed, not necessarily acceptable. Cross-references (the endpoint a widget binds, a requires term's connection, an endpoint's service prefix), the direction-specific rules on an endpoint, the features/blocks cross-check in both directions, UTF-8 byte-size caps, the rules tying a connection's flow and token to its scope and fields, what a webhooks block names, what a vendor setup writes to, whether every expression parses, what a declarative request and its steps name, the bounds, ids and endpoints of schedules, which terms each kind's schedules, actions, fields and webhook events take, the unique keys and ids of fields, parts, actions and settings, what a part's nodes, a field's element actions and an action's endpoint and params name, the placeholders in a part's text, and a part's size and depth are enforced by the platform on publish and are not expressible here.",
6
6
  "type": "object",
7
7
  "required": [
8
8
  "plugin_kind",
@@ -78,7 +78,12 @@
78
78
  }
79
79
  ]
80
80
  },
81
- "description": "The scopes this plug-in asks a community to grant: what its installation and member tokens act with. The fixed scopes, and 'plugins:<public_id>' for each plug-in this one calls (at most 24 of those). The community grants some or all of them when it installs the plug-in, and a token never carries more than was granted. Writing implies reading. Absent means none."
81
+ "description": "The scopes this plug-in asks a community to grant: what its installation and member tokens act with. The fixed scopes, and 'plugins:<public_id>' for each plug-in this one uses, to call its public endpoints and read the values it shows on items (at most 24 of those). The community grants some or all of them when it installs the plug-in, and a token never carries more than was granted. Writing implies reading. Absent means none."
82
+ },
83
+ "calls_plugins": {
84
+ "type": "boolean",
85
+ "default": false,
86
+ "description": "This plug-in may act through any plug-in the community lets it use, not only those its scopes name as 'plugins:<public_id>'. When a community installs another plug-in, it is asked there whether this one may use it, which grants 'plugins:<public_id>' for it. Absent: only the plug-ins its scopes name."
82
87
  }
83
88
  },
84
89
  "description": "A container plug-in's service, which Initiative calls. Absent for a declarative plug-in, whose public id is its listing's."
@@ -137,6 +142,55 @@
137
142
  "$ref": "#/$defs/vendorAuth",
138
143
  "description": "Declarative plug-ins only."
139
144
  },
145
+ "scopes": {
146
+ "type": "array",
147
+ "maxItems": 56,
148
+ "uniqueItems": true,
149
+ "items": {
150
+ "anyOf": [
151
+ {
152
+ "enum": [
153
+ "projects:read",
154
+ "projects:write",
155
+ "files:read",
156
+ "files:write",
157
+ "queues:read",
158
+ "queues:write",
159
+ "counter_groups:read",
160
+ "counter_groups:write",
161
+ "calendars:read",
162
+ "calendars:write",
163
+ "dashboards:read",
164
+ "dashboards:write",
165
+ "posts:read",
166
+ "posts:write",
167
+ "galleries:read",
168
+ "galleries:write",
169
+ "wikis:read",
170
+ "wikis:write",
171
+ "comments:read",
172
+ "comments:write",
173
+ "relationships:read",
174
+ "relationships:write",
175
+ "tags:read",
176
+ "tags:write",
177
+ "properties:read",
178
+ "properties:write",
179
+ "sharing:read",
180
+ "sharing:write",
181
+ "members:read",
182
+ "initiatives:read",
183
+ "initiatives:moderate",
184
+ "community:admin"
185
+ ]
186
+ },
187
+ {
188
+ "$ref": "#/$defs/pluginScope"
189
+ }
190
+ ]
191
+ },
192
+ "description": "Declarative plug-ins: the scopes this plug-in asks a community to grant, all of them read scopes ('<resource>:read'). A declarative plug-in keeps its own values on items it can read and does not call Initiative, and Initiative keeps them only on items these scopes read. A container plug-in declares its scopes in its service block."
193
+ },
140
194
  "vendor": {
141
195
  "$ref": "#/$defs/vendor"
142
196
  },
@@ -156,7 +210,7 @@
156
210
  "items": {
157
211
  "$ref": "#/$defs/schedule"
158
212
  },
159
- "description": "What Initiative calls the plug-in's schedule hook for, and how often. At most 8."
213
+ "description": "What Initiative runs on an interval for each community: a container plug-in's schedule hook, or a declarative plug-in's read and the values it writes from the answer. At most 8."
160
214
  },
161
215
  "endpoints": {
162
216
  "type": "array",
@@ -197,7 +251,7 @@
197
251
  "items": {
198
252
  "$ref": "#/$defs/field"
199
253
  },
200
- "description": "How the plug-in's metadata is shown on items. A container plug-in's only: a declarative plug-in holds no installation token, so it keeps no metadata to show."
254
+ "description": "How the plug-in's values on items are shown there. Any plug-in may keep values on items: a container writes them with its installation token, and for a declarative plug-in Initiative writes them from the plug-in's expressions."
201
255
  },
202
256
  "parts": {
203
257
  "type": "array",
@@ -205,7 +259,7 @@
205
259
  "items": {
206
260
  "$ref": "#/$defs/part"
207
261
  },
208
- "description": "Pieces of items' pages and views, built from Initiative's components and bound to the plug-in's fields and actions. A container plug-in's only, as fields are."
262
+ "description": "Pieces of items' pages and views, built from Initiative's components and bound to the plug-in's fields and actions."
209
263
  },
210
264
  "actions": {
211
265
  "type": "array",
@@ -213,7 +267,31 @@
213
267
  "items": {
214
268
  "$ref": "#/$defs/action"
215
269
  },
216
- "description": "What a reader may run on an item, each one of the plug-in's write endpoints. A container plug-in's only: a declarative plug-in has no handler to do an action's work."
270
+ "description": "What a reader may run on an item: a container plug-in's write endpoints, which its handlers answer, or a declarative plug-in's calls and changes to its values, which Initiative makes."
271
+ },
272
+ "settings": {
273
+ "type": "array",
274
+ "maxItems": 16,
275
+ "items": {
276
+ "$ref": "#/$defs/setting"
277
+ },
278
+ "description": "Values the community's admins set on the install, which the plug-in's expressions and its parts' text read. At most 16."
279
+ },
280
+ "help": {
281
+ "type": "object",
282
+ "minProperties": 1,
283
+ "maxProperties": 40,
284
+ "propertyNames": {
285
+ "type": "string",
286
+ "maxLength": 12,
287
+ "pattern": "^[\\-ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]+$"
288
+ },
289
+ "additionalProperties": {
290
+ "type": "string",
291
+ "minLength": 1,
292
+ "maxLength": 2000
293
+ },
294
+ "description": "Help with the plug-in, keyed by language tag: markdown of at most 2000 characters in each language. Initiative shows it behind a '?' on the headers of the plug-in's parts and on its settings, in the reader's language, or failing that in any."
217
295
  }
218
296
  },
219
297
  "$defs": {
@@ -788,6 +866,17 @@
788
866
  "maxLength": 300,
789
867
  "description": "Where the signed JWT is posted, https. The answer's 'token' (or 'access_token') and 'expires_at' (or 'expires_in') are read. May name a vendor value as '{vendor.<key>}' and one of this connection's own fields as '{<key>}'."
790
868
  },
869
+ "exchange_body": {
870
+ "anyOf": [
871
+ {
872
+ "type": "object"
873
+ },
874
+ {
875
+ "$ref": "#/$defs/expression"
876
+ }
877
+ ],
878
+ "description": "A JSON body posted with the signed JWT, such as the narrower permissions a token is asked for. Absent: none. A string anywhere in it may name a vendor value as '{vendor.<key>}' and one of this connection's own fields as '{<key>}'. Or an expression answering the body, which reads only 'connection': this connection's own non-secret fields by key, its managed values included, as 'after_connect' stored them. A body that names a vendor value is written as an object. An answer that is not an object fails the token request, so the connection cannot be used until the expression answers one."
879
+ },
791
880
  "iss": {
792
881
  "type": "string",
793
882
  "minLength": 1,
@@ -845,7 +934,7 @@
845
934
  "items": {
846
935
  "$ref": "#/$defs/webhookEvent"
847
936
  },
848
- "description": "Declarative plug-ins: how a delivery becomes one of this plug-in's events. The first row whose 'when' holds emits; a delivery no row matches is dropped."
937
+ "description": "Declarative plug-ins: what a delivery does: emit one of this plug-in's events, change the plug-in's values, or both. The first row whose 'when' holds applies, its 'emit' and its 'write' both; a delivery no row matches is dropped."
849
938
  },
850
939
  "status": {
851
940
  "type": "array",
@@ -935,7 +1024,7 @@
935
1024
  },
936
1025
  "schedule": {
937
1026
  "type": "object",
938
- "description": "An interval at which Initiative calls the plug-in's schedule hook, once for each community that installed it, with when the call last succeeded there.",
1027
+ "description": "Something Initiative runs on an interval, once for each community that installed the plug-in, with when it last succeeded there as 'since'. A container plug-in's schedule calls its schedule hook, and names nothing more. A declarative plug-in's makes one of its read endpoints' calls as the installation and changes the plug-in's values from the answer: it names the 'endpoint' and the 'write', and 'params' when the endpoint takes any.",
939
1028
  "required": [
940
1029
  "id",
941
1030
  "every"
@@ -943,13 +1032,25 @@
943
1032
  "properties": {
944
1033
  "id": {
945
1034
  "$ref": "#/$defs/identifier",
946
- "description": "Unique within the manifest. The hook is told which schedule is due by it."
1035
+ "description": "Unique within the manifest. A container's hook is told which schedule is due by it. A declarative plug-in's differs from every connection's id: Initiative keys connection health checks by connection id."
947
1036
  },
948
1037
  "every": {
949
1038
  "type": "string",
950
1039
  "maxLength": 5,
951
1040
  "pattern": "^[0-9]+[mh]$",
952
1041
  "description": "How often: a whole number of minutes ('15m') or hours ('6h'), at least 5 minutes and at most 1440 minutes."
1042
+ },
1043
+ "endpoint": {
1044
+ "$ref": "#/$defs/namespacedId",
1045
+ "description": "Declarative plug-ins: a read endpoint this manifest declares, whose 'actors' include 'installation': no person is present when a schedule runs."
1046
+ },
1047
+ "params": {
1048
+ "$ref": "#/$defs/expression",
1049
+ "description": "Declarative plug-ins: the endpoint's params, as one expression answering an object. It reads 'since', when the schedule last succeeded in the community (ISO 8601), or null the first time; 'now'; 'connection', the non-secret fields of the connection the endpoint's first request names; 'settings' and 'aliases'. Absent: none."
1050
+ },
1051
+ "write": {
1052
+ "$ref": "#/$defs/expression",
1053
+ "description": "Declarative plug-ins: the changes Initiative makes to the plug-in's values from the endpoint's answer, as the install: a list of at most 100 targeted changes ('targetedChange'). It reads 'result', the endpoint's mapped answer, 'since', 'now', 'settings' and 'aliases'. Answering nothing changes nothing."
953
1054
  }
954
1055
  }
955
1056
  },
@@ -962,7 +1063,7 @@
962
1063
  "pluginScope": {
963
1064
  "type": "string",
964
1065
  "pattern": "^plugins:[\\-.0123456789_abcdefghijklmnopqrstuvwxyz]*\\.[\\-.0123456789_abcdefghijklmnopqrstuvwxyz]*$",
965
- "description": "'plugins:<public_id>': permission to call another plug-in's public endpoints through the deployment, as this community or as one of its members. Not one of the fixed scopes: the family is open, one per plug-in, and the public id after the prefix is the plug-in being called. The deployment never lets a plug-in address another directly; the call goes through it, and the plug-in called is handed its own references for whoever the call is for."
1066
+ "description": "'plugins:<public_id>': permission to call another plug-in's public endpoints through the deployment, as this community or as one of its members, and to read the values that plug-in shows on items this one can read. Not one of the fixed scopes: the family is open, one per plug-in, and the public id after the prefix is the plug-in being called. The deployment never lets a plug-in address another directly; the call goes through it, and the plug-in called is handed its own references for whoever the call is for."
966
1067
  },
967
1068
  "endpoint": {
968
1069
  "type": "object",
@@ -1188,7 +1289,7 @@
1188
1289
  },
1189
1290
  "field": {
1190
1291
  "type": "object",
1191
- "description": "How one of the plug-in's metadata keys is shown on Initiative's items. The plug-in keeps the value in Initiative, through its installation token; Initiative draws it with its own component for `kind` and computes its plain text itself, so a field filters, sorts and exports with no call to the plug-in. Which views show it is chosen by the community's managers, never by the plug-in. Initiative holds what is kept, and these caps are enforced there rather than here: a value of at most 8192 bytes as JSON, with a string of at most 255 characters to be found by; at most 32 keys and 65536 bytes on one item, and 256 keys and 1048576 bytes on the install.",
1292
+ "description": "How one of the plug-in's metadata keys is shown on Initiative's items. The value is kept in Initiative: a container plug-in writes it with its installation token, and for a declarative plug-in Initiative writes it from the plug-in's expressions. Initiative draws it with its own component for `kind` and computes its plain text itself, so a field filters, sorts and exports with no call to the plug-in. Which views show it is chosen by the community's managers, never by the plug-in. Initiative holds what is kept, and these caps are enforced there rather than here: a value of at most 8192 bytes as JSON, with a string of at most 255 characters to be found by, and each element of a list a value of its own, found by its 'ref'; at most 32 keys and 65536 bytes on one item, and 256 keys and 1048576 bytes on the install.",
1192
1293
  "required": [
1193
1294
  "key",
1194
1295
  "name",
@@ -1217,6 +1318,11 @@
1217
1318
  ],
1218
1319
  "description": "What the value is, and so how it is drawn: 'text' a string; 'number' a number; 'date' a 'YYYY-MM-DD' string; 'datetime' an ISO 8601 string with its offset; 'link' {url, text?}; 'badge' {text, tone?}; 'progress' {value, max}; 'checkbox' a boolean."
1219
1320
  },
1321
+ "list": {
1322
+ "type": "boolean",
1323
+ "default": false,
1324
+ "description": "Several values rather than one: the value is a list of elements, each one value of 'kind' with a 'ref' that tells it from the others ('listElement'). A change gives the whole list in 'set', or adds, replaces and removes elements by 'ref' with 'upsert' and 'remove'. Absent: one value, which a change sets."
1325
+ },
1220
1326
  "on": {
1221
1327
  "type": "array",
1222
1328
  "minItems": 1,
@@ -1249,11 +1355,83 @@
1249
1355
  ],
1250
1356
  "description": "A badge's tone when its value names none."
1251
1357
  },
1358
+ "states": {
1359
+ "type": "object",
1360
+ "minProperties": 1,
1361
+ "maxProperties": 4,
1362
+ "propertyNames": {
1363
+ "$ref": "#/$defs/identifier"
1364
+ },
1365
+ "additionalProperties": {
1366
+ "$ref": "#/$defs/stateFacet"
1367
+ },
1368
+ "description": "What state the value is in, as facets by id, at most 4, each with keys of its own. The value names a key of each facet it is in as 'state', {\"<facet>\": \"<key>\"}, and Initiative draws each as a badge, labelled in the reader's language. A list element, and a link, badge or progress value, carries 'state' beside its other properties; a text, number, date, datetime or checkbox value that carries one is written as an element is, without the 'ref': {\"value\": …, \"state\": …}."
1369
+ },
1370
+ "actions": {
1371
+ "type": "array",
1372
+ "minItems": 1,
1373
+ "maxItems": 16,
1374
+ "uniqueItems": true,
1375
+ "items": {
1376
+ "$ref": "#/$defs/identifier"
1377
+ },
1378
+ "description": "Declarative plug-ins, on a list field: actions offered on each of its elements, by id, each one offered on every item kind this field is. An action run from an element reads that element's 'ref' as 'element'."
1379
+ },
1252
1380
  "requires": {
1253
1381
  "$ref": "#/$defs/requires"
1254
1382
  }
1255
1383
  }
1256
1384
  },
1385
+ "stateFacet": {
1386
+ "type": "object",
1387
+ "description": "One facet of a field's state, such as whether a piece of work is open or what review it has: its name, and the keys a value may name.",
1388
+ "required": [
1389
+ "name",
1390
+ "values"
1391
+ ],
1392
+ "properties": {
1393
+ "name": {
1394
+ "$ref": "#/$defs/localizedText",
1395
+ "description": "What the facet is called."
1396
+ },
1397
+ "values": {
1398
+ "type": "object",
1399
+ "minProperties": 1,
1400
+ "maxProperties": 12,
1401
+ "propertyNames": {
1402
+ "$ref": "#/$defs/identifier"
1403
+ },
1404
+ "additionalProperties": {
1405
+ "$ref": "#/$defs/stateValue"
1406
+ },
1407
+ "description": "The keys a value may name, at most 12, each with its badge's words and tone."
1408
+ }
1409
+ },
1410
+ "additionalProperties": false
1411
+ },
1412
+ "stateValue": {
1413
+ "type": "object",
1414
+ "description": "One key of a state facet, as its badge shows it.",
1415
+ "required": [
1416
+ "label"
1417
+ ],
1418
+ "properties": {
1419
+ "label": {
1420
+ "$ref": "#/$defs/localizedText"
1421
+ },
1422
+ "tone": {
1423
+ "enum": [
1424
+ "accent",
1425
+ "positive",
1426
+ "negative",
1427
+ "warning",
1428
+ "neutral",
1429
+ "muted"
1430
+ ]
1431
+ }
1432
+ },
1433
+ "additionalProperties": false
1434
+ },
1257
1435
  "partNode": {
1258
1436
  "description": "One node of a part: one of Initiative's own components, told apart by 'type', with its props and, for a section or a stack, the nodes inside it.",
1259
1437
  "oneOf": [
@@ -1432,7 +1610,8 @@
1432
1610
  ],
1433
1611
  "properties": {
1434
1612
  "text": {
1435
- "$ref": "#/$defs/localizedText"
1613
+ "$ref": "#/$defs/localizedText",
1614
+ "description": "May name '{item.id}', the item's id, and '{settings.<key>}', one of this plug-in's settings, which Initiative fills in when it draws the part. Any other '{…}' is refused."
1436
1615
  },
1437
1616
  "tone": {
1438
1617
  "enum": [
@@ -1534,11 +1713,10 @@
1534
1713
  },
1535
1714
  "action": {
1536
1715
  "type": "object",
1537
- "description": "Something a reader runs on one item, from a part's button or the item's own menu. Initiative only connects: it calls the write endpoint as the installation, with the reader as 'viewer' and the item as 'subject' in the call's token, and the endpoint does the work, usually writing the plug-in's metadata. Initiative then answers the reader with the item's metadata as it stands.",
1716
+ "description": "Something a reader runs on one item, from a part's button, the item's own menu, or an element of a list field that offers it. Initiative first asks the reader for the 'params' the action names, when it names any. For a container plug-in, Initiative only connects: it calls the write endpoint as the installation, with those parameters, the reader as 'viewer' and the item as 'subject' in the call's token, and the endpoint does the work, usually writing the plug-in's metadata. For a declarative plug-in, Initiative does the work: it makes the endpoint's call, when the action names one, with those parameters, on the credential its 'actors' say, its expressions reading the item as 'item' and the element it was run from, if any, as 'element'; then it makes the change 'write' answers, when the action has one, to the plug-in's values on the item, as the install. Either way Initiative then answers the reader with the item's metadata as it stands.",
1538
1717
  "required": [
1539
1718
  "id",
1540
1719
  "name",
1541
- "endpoint",
1542
1720
  "on"
1543
1721
  ],
1544
1722
  "properties": {
@@ -1552,7 +1730,21 @@
1552
1730
  },
1553
1731
  "endpoint": {
1554
1732
  "$ref": "#/$defs/namespacedId",
1555
- "description": "A write endpoint this manifest declares."
1733
+ "description": "An endpoint this manifest declares. A container plug-in's action names a write, which its handler answers. A declarative plug-in's names a read or a write, a 'write' expression, or both: a read fetches what the 'write' keeps, a write changes something at the vendor."
1734
+ },
1735
+ "params": {
1736
+ "type": "array",
1737
+ "minItems": 1,
1738
+ "maxItems": 12,
1739
+ "uniqueItems": true,
1740
+ "items": {
1741
+ "$ref": "#/$defs/identifier"
1742
+ },
1743
+ "description": "Parameters of the action's endpoint, by key, that Initiative asks the reader for before it runs the action, in a form drawn from the endpoint's own declarations of them: each one's type and label, and its 'options' or 'options_from'. The endpoint is sent these and nothing else, so every parameter it requires is among them."
1744
+ },
1745
+ "write": {
1746
+ "$ref": "#/$defs/expression",
1747
+ "description": "Declarative plug-ins: the change Initiative makes to the plug-in's values on the action's item, as the install: one change ('change'). It reads 'item', the item ({kind, id, title}); 'held', this install's values on it, by key; 'result', the endpoint's mapped answer, absent when the action names no endpoint; 'params', what the reader answered; 'element', the 'ref' of the list element it was run from, absent when it was not; 'settings', 'aliases' and 'now'. Answering nothing changes nothing."
1556
1748
  },
1557
1749
  "on": {
1558
1750
  "type": "array",
@@ -1580,11 +1772,325 @@
1580
1772
  "default": false,
1581
1773
  "description": "Also offered in the item's own menu."
1582
1774
  },
1775
+ "admin_only": {
1776
+ "type": "boolean",
1777
+ "default": false,
1778
+ "description": "Only the community's admins run this action, whatever roles a placement allows. Who else may run it is chosen in the community, per initiative and per role, not declared here."
1779
+ },
1583
1780
  "requires": {
1584
1781
  "$ref": "#/$defs/requires"
1585
1782
  }
1586
1783
  }
1587
1784
  },
1785
+ "change": {
1786
+ "type": "object",
1787
+ "description": "A change to the plug-in's values on one item or on its install, as a declarative plug-in's 'write' answers it. 'set' writes values by key, as a container's client.metadata.set does; 'upsert' and 'remove' change a list field's elements by their 'ref'. A change gives at least one of the three, and changes each key with only one. Initiative makes it as the install, and holds what is kept to the caps a field's description gives.",
1788
+ "properties": {
1789
+ "set": {
1790
+ "type": "object",
1791
+ "propertyNames": {
1792
+ "$ref": "#/$defs/metadataKey"
1793
+ },
1794
+ "description": "Values by metadata key, each any JSON value of at most 8192 bytes; a null removes its key. A list field's value is its whole list of elements, each held to that cap."
1795
+ },
1796
+ "upsert": {
1797
+ "type": "object",
1798
+ "propertyNames": {
1799
+ "$ref": "#/$defs/metadataKey"
1800
+ },
1801
+ "additionalProperties": {
1802
+ "type": "array",
1803
+ "items": {
1804
+ "$ref": "#/$defs/listElement"
1805
+ }
1806
+ },
1807
+ "description": "Elements by metadata key ('listElement'): each is added to that key's list, or merged into the element there with its 'ref': the properties it gives replace that element's, the ones it leaves out stay, and its 'state' merges the same way by facet. So a write that knows one fact about an element changes that fact alone."
1808
+ },
1809
+ "remove": {
1810
+ "type": "object",
1811
+ "propertyNames": {
1812
+ "$ref": "#/$defs/metadataKey"
1813
+ },
1814
+ "additionalProperties": {
1815
+ "type": "array",
1816
+ "items": {
1817
+ "$ref": "#/$defs/elementRef"
1818
+ }
1819
+ },
1820
+ "description": "Refs by metadata key: the element with each is taken from that key's list. A ref the list does not hold changes nothing."
1821
+ }
1822
+ },
1823
+ "anyOf": [
1824
+ {
1825
+ "required": [
1826
+ "set"
1827
+ ]
1828
+ },
1829
+ {
1830
+ "required": [
1831
+ "upsert"
1832
+ ]
1833
+ },
1834
+ {
1835
+ "required": [
1836
+ "remove"
1837
+ ]
1838
+ }
1839
+ ],
1840
+ "additionalProperties": false
1841
+ },
1842
+ "targetedChange": {
1843
+ "type": "object",
1844
+ "description": "A change and where Initiative makes it. A webhook event's or a schedule's 'write' answers a list of at most 100, and cannot read what their targets hold.",
1845
+ "required": [
1846
+ "target"
1847
+ ],
1848
+ "properties": {
1849
+ "target": {
1850
+ "$ref": "#/$defs/changeTarget"
1851
+ },
1852
+ "set": {
1853
+ "type": "object",
1854
+ "propertyNames": {
1855
+ "$ref": "#/$defs/metadataKey"
1856
+ },
1857
+ "description": "As a change's: values by metadata key, a null removing its key."
1858
+ },
1859
+ "upsert": {
1860
+ "type": "object",
1861
+ "propertyNames": {
1862
+ "$ref": "#/$defs/metadataKey"
1863
+ },
1864
+ "additionalProperties": {
1865
+ "type": "array",
1866
+ "items": {
1867
+ "$ref": "#/$defs/listElement"
1868
+ }
1869
+ },
1870
+ "description": "As a change's: elements by metadata key, each added to its list or merged into the one with its 'ref', property by property and its 'state' facet by facet."
1871
+ },
1872
+ "remove": {
1873
+ "type": "object",
1874
+ "propertyNames": {
1875
+ "$ref": "#/$defs/metadataKey"
1876
+ },
1877
+ "additionalProperties": {
1878
+ "type": "array",
1879
+ "items": {
1880
+ "$ref": "#/$defs/elementRef"
1881
+ }
1882
+ },
1883
+ "description": "As a change's: refs by metadata key, each element with one taken from its list."
1884
+ }
1885
+ },
1886
+ "anyOf": [
1887
+ {
1888
+ "required": [
1889
+ "set"
1890
+ ]
1891
+ },
1892
+ {
1893
+ "required": [
1894
+ "upsert"
1895
+ ]
1896
+ },
1897
+ {
1898
+ "required": [
1899
+ "remove"
1900
+ ]
1901
+ }
1902
+ ],
1903
+ "additionalProperties": false
1904
+ },
1905
+ "changeTarget": {
1906
+ "description": "Where a change is made: {kind, id}, one item; {install: true}, the install itself; or {holding: {key, value}}, every item on which this install holds 'value' under 'key', as a container's client.metadata.find finds them. Initiative keeps each element of a list as a value of its own, so 'holding' reaches an element by its 'ref'. A target the install cannot read is skipped.",
1907
+ "oneOf": [
1908
+ {
1909
+ "type": "object",
1910
+ "required": [
1911
+ "kind",
1912
+ "id"
1913
+ ],
1914
+ "properties": {
1915
+ "kind": {
1916
+ "enum": [
1917
+ "task",
1918
+ "calendar_event",
1919
+ "queue_item",
1920
+ "counter",
1921
+ "gallery_image",
1922
+ "post"
1923
+ ]
1924
+ },
1925
+ "id": {
1926
+ "type": "integer",
1927
+ "minimum": 1
1928
+ }
1929
+ },
1930
+ "additionalProperties": false
1931
+ },
1932
+ {
1933
+ "type": "object",
1934
+ "required": [
1935
+ "install"
1936
+ ],
1937
+ "properties": {
1938
+ "install": {
1939
+ "const": true
1940
+ }
1941
+ },
1942
+ "additionalProperties": false
1943
+ },
1944
+ {
1945
+ "type": "object",
1946
+ "required": [
1947
+ "holding"
1948
+ ],
1949
+ "properties": {
1950
+ "holding": {
1951
+ "type": "object",
1952
+ "required": [
1953
+ "key",
1954
+ "value"
1955
+ ],
1956
+ "properties": {
1957
+ "key": {
1958
+ "$ref": "#/$defs/metadataKey"
1959
+ },
1960
+ "value": {
1961
+ "type": "string",
1962
+ "maxLength": 255,
1963
+ "description": "A value is found by its text: a string as it is, a number or a boolean as JSON writes it; a list element by its 'ref'."
1964
+ }
1965
+ },
1966
+ "additionalProperties": false
1967
+ }
1968
+ },
1969
+ "additionalProperties": false
1970
+ }
1971
+ ]
1972
+ },
1973
+ "listElement": {
1974
+ "type": "object",
1975
+ "description": "One element of a list field's value: one value of the field's 'kind', with a 'ref' that tells it from the list's others. A text, number, date, datetime or checkbox element holds its value in 'value'; a link element is {ref, url, text?}, a badge element {ref, text, tone?} and a progress element {ref, value, max}. Initiative keeps each element as a value of its own, of at most 8192 bytes as JSON. An 'upsert' may give only some of an element's properties: they are merged into the element with its 'ref'.",
1976
+ "required": [
1977
+ "ref"
1978
+ ],
1979
+ "properties": {
1980
+ "ref": {
1981
+ "$ref": "#/$defs/elementRef"
1982
+ },
1983
+ "value": {
1984
+ "type": [
1985
+ "string",
1986
+ "number",
1987
+ "boolean"
1988
+ ],
1989
+ "description": "A text, number, date, datetime or checkbox element's value, or a progress element's figure."
1990
+ },
1991
+ "url": {
1992
+ "type": "string",
1993
+ "description": "A link element's address."
1994
+ },
1995
+ "text": {
1996
+ "type": "string",
1997
+ "description": "A link element's words, or a badge element's."
1998
+ },
1999
+ "tone": {
2000
+ "enum": [
2001
+ "accent",
2002
+ "positive",
2003
+ "negative",
2004
+ "warning",
2005
+ "neutral",
2006
+ "muted"
2007
+ ],
2008
+ "description": "A badge element's tone."
2009
+ },
2010
+ "max": {
2011
+ "type": "number",
2012
+ "description": "A progress element's whole."
2013
+ },
2014
+ "state": {
2015
+ "type": "object",
2016
+ "propertyNames": {
2017
+ "$ref": "#/$defs/identifier"
2018
+ },
2019
+ "additionalProperties": {
2020
+ "$ref": "#/$defs/identifier"
2021
+ },
2022
+ "description": "A key of each of the field's state facets this element is in, by facet id."
2023
+ }
2024
+ },
2025
+ "additionalProperties": false
2026
+ },
2027
+ "elementRef": {
2028
+ "type": "string",
2029
+ "minLength": 1,
2030
+ "maxLength": 255,
2031
+ "description": "What tells a list's element from the others, such as the vendor's own id for what it shows: unique in its list, and how a change replaces or removes the element and a 'holding' target finds it."
2032
+ },
2033
+ "setting": {
2034
+ "type": "object",
2035
+ "description": "A value the community's admins see and may change on the install, which the plug-in's expressions read as 'settings.<key>' and its parts' text names as '{settings.<key>}'.",
2036
+ "required": [
2037
+ "key",
2038
+ "type",
2039
+ "label"
2040
+ ],
2041
+ "properties": {
2042
+ "key": {
2043
+ "$ref": "#/$defs/identifier",
2044
+ "description": "Unique among this manifest's settings."
2045
+ },
2046
+ "type": {
2047
+ "enum": [
2048
+ "string"
2049
+ ],
2050
+ "description": "What the value is: 'string', a line of text."
2051
+ },
2052
+ "label": {
2053
+ "$ref": "#/$defs/localizedText"
2054
+ },
2055
+ "description": {
2056
+ "$ref": "#/$defs/localizedText",
2057
+ "description": "A line under the label."
2058
+ },
2059
+ "unique": {
2060
+ "type": "boolean",
2061
+ "default": false,
2062
+ "description": "No two communities on a deployment hold the same value for this plug-in, and the value is a 'uniqueSettingValue'. An admin who renames it keeps the earlier values, which no other community may take and the plug-in's expressions read as 'aliases.<key>'."
2063
+ },
2064
+ "assigned": {
2065
+ "enum": [
2066
+ "community_name"
2067
+ ],
2068
+ "description": "A unique setting's only: Initiative gives it a value when the community installs the plug-in, which an admin may rename. 'community_name': the letters and digits of the community's name, in lowercase, with a short random suffix when another community holds that value."
2069
+ }
2070
+ },
2071
+ "if": {
2072
+ "required": [
2073
+ "assigned"
2074
+ ]
2075
+ },
2076
+ "then": {
2077
+ "required": [
2078
+ "unique"
2079
+ ],
2080
+ "properties": {
2081
+ "unique": {
2082
+ "const": true
2083
+ }
2084
+ }
2085
+ }
2086
+ },
2087
+ "uniqueSettingValue": {
2088
+ "type": "string",
2089
+ "minLength": 2,
2090
+ "maxLength": 16,
2091
+ "pattern": "^[a-z][0123456789abcdefghijklmnopqrstuvwxyz]*$",
2092
+ "description": "A unique setting's value: a lowercase letter, then lowercase letters and digits, two to 16 characters in all. It holds no '-', so a reference such as '<value>-<number>' reads one way."
2093
+ },
1588
2094
  "bundledDashboard": {
1589
2095
  "type": "object",
1590
2096
  "required": [
@@ -1732,7 +2238,7 @@
1732
2238
  "type": "string",
1733
2239
  "minLength": 1,
1734
2240
  "maxLength": 16384,
1735
- "description": "A JSONata expression (https://jsonata.org): standard JSONata, with no functions added. It reads one document. On the way out that holds 'params', the call's parameters as the caller sent them; 'connection', the non-secret fields of the connection the request names; 'connections', on an endpoint's requests and map, each connection its 'requires' names that the call has, by its id, holding the same fields; 'now', the time of the call in ISO 8601, which $now() and $millis() also answer; and 'steps', each earlier step's answer by its name. On the way back it also holds 'response', the answer being read: {status, headers, body}, with header names in lowercase. Each evaluation is bounded: at most 1000 milliseconds, 500 levels of nesting and an answer of 1048576 bytes as JSON. An expression that fails or passes a bound answers 'unavailable: mapping-failed'."
2241
+ "description": "A JSONata expression (https://jsonata.org): standard JSONata, with no functions added. It reads one document. On the way out that holds 'params', the call's parameters as the caller sent them; 'connection', the non-secret fields of the connection the request names; 'connections', on an endpoint's requests and map, each connection its 'requires' names that the call has, by its id, holding the same fields; 'now', the time of the call in ISO 8601, which $now() and $millis() also answer; 'steps', each earlier step's answer by its name; and, on the call an action makes, 'item', the item it runs on: {kind, id, title}, and 'element', the 'ref' of the list element it was run from, if it was. On the way back it also holds 'response', the answer being read: {status, headers, body}, with header names in lowercase. Every expression of a declarative plug-in also reads 'settings', the install's settings by key, and 'aliases', each unique setting's earlier values by key, as a list, except a token's 'exchange_body', which reads only its connection. Each evaluation is bounded: at most 1000 milliseconds, 500 levels of nesting and an answer of 1048576 bytes as JSON. An expression that fails or passes a bound answers 'unavailable: mapping-failed'."
1736
2242
  },
1737
2243
  "host": {
1738
2244
  "type": "string",
@@ -1821,8 +2327,15 @@
1821
2327
  "$ref": "#/$defs/graphqlRequest"
1822
2328
  },
1823
2329
  "connection": {
1824
- "$ref": "#/$defs/identifier",
1825
- "description": "Which connection's credential the request carries: a static connection's is the community's, an interactive one's the acting member's. A call made as a member carries a static connection's credential only when the endpoint's 'requires' names that connection, and is refused otherwise. Required on an endpoint's requests. Absent on 'after_connect' and 'health', which carry the credential of the connection they belong to."
2330
+ "anyOf": [
2331
+ {
2332
+ "$ref": "#/$defs/identifier"
2333
+ },
2334
+ {
2335
+ "$ref": "#/$defs/actorConnections"
2336
+ }
2337
+ ],
2338
+ "description": "Which connection's credential the request carries: one connection for every call, or, on a write endpoint's requests, one per actor ('actorConnections'), so a call made as the member carries the member's own credential and one made as the installation the community's. A static connection's credential is the community's, an interactive one's the acting member's, which a call made as the installation does not have. A call made as a member carries a static connection's credential only when the endpoint's 'requires' names that connection, and is refused otherwise. Required on an endpoint's requests. Absent on 'after_connect' and 'health', which carry the credential of the connection they belong to."
1826
2339
  },
1827
2340
  "paging": {
1828
2341
  "$ref": "#/$defs/paging"
@@ -1850,6 +2363,22 @@
1850
2363
  },
1851
2364
  "additionalProperties": false
1852
2365
  },
2366
+ "actorConnections": {
2367
+ "type": "object",
2368
+ "description": "One connection per actor, on a write endpoint's requests: a call carries the credential of the connection named for the actor it is made as. Names exactly the actors the endpoint's 'actors' declare. The member's connection need not be named in 'requires': a call made as the member requires the connection its requests name for the member.",
2369
+ "minProperties": 1,
2370
+ "properties": {
2371
+ "member": {
2372
+ "$ref": "#/$defs/identifier",
2373
+ "description": "An interactive connection, which a call made as the member carries: the member's own account."
2374
+ },
2375
+ "installation": {
2376
+ "$ref": "#/$defs/identifier",
2377
+ "description": "A static connection, which a call made as the installation carries: the community's own."
2378
+ }
2379
+ },
2380
+ "additionalProperties": false
2381
+ },
1853
2382
  "requestStep": {
1854
2383
  "type": "object",
1855
2384
  "required": [
@@ -2151,11 +2680,9 @@
2151
2680
  },
2152
2681
  "webhookEvent": {
2153
2682
  "type": "object",
2154
- "description": "One way a delivery becomes an event. Its expressions read 'headers' (names in lowercase), 'payload' (the parsed body), 'connection' (the routed connection's non-secret fields) and 'now'.",
2683
+ "description": "What one kind of delivery does: emit an event, change the plug-in's values, or both. It gives 'emit', 'write' or both, and 'map' exactly when it gives 'emit'. Its expressions read 'headers' (names in lowercase), 'payload' (the parsed body), 'connection' (the routed connection's non-secret fields), 'now', 'settings' and 'aliases'.",
2155
2684
  "required": [
2156
- "when",
2157
- "emit",
2158
- "map"
2685
+ "when"
2159
2686
  ],
2160
2687
  "properties": {
2161
2688
  "when": {
@@ -2169,6 +2696,10 @@
2169
2696
  "map": {
2170
2697
  "$ref": "#/$defs/expression",
2171
2698
  "description": "The event's payload: an object of that endpoint's declared returns."
2699
+ },
2700
+ "write": {
2701
+ "$ref": "#/$defs/expression",
2702
+ "description": "The changes Initiative makes to the plug-in's values, as the install: a list of at most 100 targeted changes ('targetedChange'). Answering nothing changes nothing."
2172
2703
  }
2173
2704
  },
2174
2705
  "additionalProperties": false