initiative-plugin-sdk 7.1.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.
- package/README.md +385 -51
- package/dist/build.d.ts +1 -1
- package/dist/build.js +32 -22
- package/dist/build.js.map +1 -1
- package/dist/client.d.ts +17 -6
- package/dist/client.js +12 -8
- package/dist/client.js.map +1 -1
- package/dist/contract.d.ts +452 -80
- package/dist/contract.js +22 -6
- package/dist/contract.js.map +1 -1
- package/dist/define.d.ts +78 -31
- package/dist/define.js +27 -9
- package/dist/define.js.map +1 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js.map +1 -1
- package/dist/plugin-api.generated.d.ts +97 -5
- package/dist/plugin-api.generated.js +15 -2
- package/dist/plugin-api.generated.js.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.js +21 -5
- package/dist/server.js.map +1 -1
- package/dist/statements.d.ts +111 -0
- package/dist/statements.js +205 -0
- package/dist/statements.js.map +1 -0
- package/dist/testing.d.ts +132 -21
- package/dist/testing.js +273 -51
- package/dist/testing.js.map +1 -1
- package/dist/validate.d.ts +13 -4
- package/dist/validate.js +240 -48
- package/dist/validate.js.map +1 -1
- package/manifest.contract.json +514 -23
- package/package.json +1 -1
- package/schemas/plugin-api.json +462 -33
- package/schemas/plugin-manifest.json +553 -22
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entitlement statements: what billing says a community holds of the plug-in,
|
|
3
|
+
* signed by billing and checked here.
|
|
4
|
+
*
|
|
5
|
+
* Billing signs each statement with its own Ed25519 key, a compact JWS with
|
|
6
|
+
* header `{alg: "EdDSA", typ: "initiative-entitlement+jwt", kid}` and claims
|
|
7
|
+
* `{iss: "initiative-billing", listing_uid, community_ref, version, values,
|
|
8
|
+
* iat}`. Initiative stores it and hands it on verbatim, on the `entitlement`
|
|
9
|
+
* hook and on `client.entitlement()`; it never signs one. The public half of
|
|
10
|
+
* billing's key reaches the plug-in from the deployment, as the runtime fact
|
|
11
|
+
* `entitlement_issuer_key` in {@link ENTITLEMENT_ISSUER_KEY_ENV}, so Initiative
|
|
12
|
+
* can withhold a statement but never mint one.
|
|
13
|
+
*
|
|
14
|
+
* A statement is checked in this order, and refused at the first failure:
|
|
15
|
+
*
|
|
16
|
+
* 1. the issuer key is configured and is Ed25519 (otherwise every statement is
|
|
17
|
+
* refused, and why is logged once);
|
|
18
|
+
* 2. the header's `alg` is `EdDSA` and its `typ` is
|
|
19
|
+
* `initiative-entitlement+jwt`, exactly: the header never picks the algorithm;
|
|
20
|
+
* 3. the Ed25519 signature over the JWS signing input;
|
|
21
|
+
* 4. `iss` is `initiative-billing`, `listing_uid` is the plug-in's own uid,
|
|
22
|
+
* `version` is a whole number of one or more and `values` an object.
|
|
23
|
+
*
|
|
24
|
+
* Only then are `version` and `values` handed on, taken from the verified
|
|
25
|
+
* claims and nothing else.
|
|
26
|
+
*
|
|
27
|
+
* One gap stays, by design: `community_ref` is billing's pairwise reference,
|
|
28
|
+
* which the plug-in cannot relate to the community it is told the statement is
|
|
29
|
+
* for. Initiative vouches for that mapping, so a compromised Initiative could
|
|
30
|
+
* at worst copy a real, signed statement onto another community.
|
|
31
|
+
*/
|
|
32
|
+
/** The environment variable the deployment fills with billing's statement key (SPKI PEM). */
|
|
33
|
+
export declare const ENTITLEMENT_ISSUER_KEY_ENV = "INITIATIVE_ENTITLEMENT_ISSUER_KEY_PEM";
|
|
34
|
+
/** The runtime fact that names billing's statement key. */
|
|
35
|
+
export declare const ENTITLEMENT_ISSUER_KEY_FACT = "entitlement_issuer_key";
|
|
36
|
+
/** Who signs every statement. */
|
|
37
|
+
export declare const ENTITLEMENT_ISSUER = "initiative-billing";
|
|
38
|
+
/** A statement's JOSE `typ`. */
|
|
39
|
+
export declare const ENTITLEMENT_STATEMENT_TYPE = "initiative-entitlement+jwt";
|
|
40
|
+
/** Why a statement was refused. Each is counted as `refused-<reason>`. */
|
|
41
|
+
export type EntitlementRefusal =
|
|
42
|
+
/** No issuer key is configured: the deployment runs no billing. */
|
|
43
|
+
"no-key"
|
|
44
|
+
/** The configured issuer key is not an Ed25519 public key. */
|
|
45
|
+
| "bad-key"
|
|
46
|
+
/** Not a compact JWS with a JSON header. */
|
|
47
|
+
| "malformed" | "alg" | "typ" | "signature" | "iss"
|
|
48
|
+
/** For another plug-in's listing. */
|
|
49
|
+
| "listing"
|
|
50
|
+
/** `version` or `values` missing or of the wrong type. */
|
|
51
|
+
| "claims";
|
|
52
|
+
/** A refused statement: `reason` is the counted outcome, `message` says it in words. */
|
|
53
|
+
export declare class EntitlementStatementError extends Error {
|
|
54
|
+
readonly reason: EntitlementRefusal;
|
|
55
|
+
constructor(reason: EntitlementRefusal);
|
|
56
|
+
}
|
|
57
|
+
/** A statement that verified: what the plug-in is handed. */
|
|
58
|
+
export interface VerifiedEntitlement {
|
|
59
|
+
version: number;
|
|
60
|
+
values: Record<string, unknown>;
|
|
61
|
+
}
|
|
62
|
+
export interface EntitlementStatementsOptions {
|
|
63
|
+
/** The plug-in's own listing uid; a statement for any other is refused. */
|
|
64
|
+
listingUid: string;
|
|
65
|
+
/**
|
|
66
|
+
* Billing's statement key, SPKI PEM (a PEM with literal `\n` or base64 of the
|
|
67
|
+
* PEM also do). Default: {@link ENTITLEMENT_ISSUER_KEY_ENV}. Absent, every
|
|
68
|
+
* statement is refused.
|
|
69
|
+
*/
|
|
70
|
+
issuerKeyPem?: string | null;
|
|
71
|
+
/** Where the one warning about a missing or unusable key goes. Default: `console.warn`. */
|
|
72
|
+
warn?: (message: string) => void;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Checks billing's entitlement statements for one plug-in. The server makes one
|
|
76
|
+
* for its hook and its client; give one to an `Initiative` made by hand to read
|
|
77
|
+
* `client.entitlement()`.
|
|
78
|
+
*/
|
|
79
|
+
export declare class EntitlementStatements {
|
|
80
|
+
private readonly key;
|
|
81
|
+
private readonly listingUid;
|
|
82
|
+
private readonly warn;
|
|
83
|
+
private warned;
|
|
84
|
+
constructor(options: EntitlementStatementsOptions);
|
|
85
|
+
/**
|
|
86
|
+
* The statement's `version` and `values`, once it verifies, for the community
|
|
87
|
+
* Initiative names (`installation`, used only to count new unlimited grants).
|
|
88
|
+
* Throws {@link EntitlementStatementError} when it is refused.
|
|
89
|
+
*/
|
|
90
|
+
verify(statement: unknown, installation: string): VerifiedEntitlement;
|
|
91
|
+
private check;
|
|
92
|
+
}
|
|
93
|
+
/** What the process has counted of entitlement statements since it started. */
|
|
94
|
+
export interface EntitlementMetrics {
|
|
95
|
+
/** Statements by outcome: `applied`, or `refused-<reason>`. */
|
|
96
|
+
statements: Record<string, number>;
|
|
97
|
+
/** Grants of an unlimited allowance to a community that did not have one. */
|
|
98
|
+
unlimitedGrants: number;
|
|
99
|
+
}
|
|
100
|
+
/** A snapshot of the entitlement statement counters, for the plug-in's metrics endpoint. */
|
|
101
|
+
export declare function entitlementMetrics(): EntitlementMetrics;
|
|
102
|
+
/**
|
|
103
|
+
* The same counters in the Prometheus text format, to append to what the
|
|
104
|
+
* plug-in's metrics endpoint serves:
|
|
105
|
+
*
|
|
106
|
+
* - `initiative_plugin_entitlement_statements_total{outcome="…"}`
|
|
107
|
+
* - `initiative_plugin_entitlement_unlimited_grants_total`
|
|
108
|
+
*
|
|
109
|
+
* Alert on any `refused-*` and on any rise in unlimited grants.
|
|
110
|
+
*/
|
|
111
|
+
export declare function entitlementMetricsText(): string;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entitlement statements: what billing says a community holds of the plug-in,
|
|
3
|
+
* signed by billing and checked here.
|
|
4
|
+
*
|
|
5
|
+
* Billing signs each statement with its own Ed25519 key, a compact JWS with
|
|
6
|
+
* header `{alg: "EdDSA", typ: "initiative-entitlement+jwt", kid}` and claims
|
|
7
|
+
* `{iss: "initiative-billing", listing_uid, community_ref, version, values,
|
|
8
|
+
* iat}`. Initiative stores it and hands it on verbatim, on the `entitlement`
|
|
9
|
+
* hook and on `client.entitlement()`; it never signs one. The public half of
|
|
10
|
+
* billing's key reaches the plug-in from the deployment, as the runtime fact
|
|
11
|
+
* `entitlement_issuer_key` in {@link ENTITLEMENT_ISSUER_KEY_ENV}, so Initiative
|
|
12
|
+
* can withhold a statement but never mint one.
|
|
13
|
+
*
|
|
14
|
+
* A statement is checked in this order, and refused at the first failure:
|
|
15
|
+
*
|
|
16
|
+
* 1. the issuer key is configured and is Ed25519 (otherwise every statement is
|
|
17
|
+
* refused, and why is logged once);
|
|
18
|
+
* 2. the header's `alg` is `EdDSA` and its `typ` is
|
|
19
|
+
* `initiative-entitlement+jwt`, exactly: the header never picks the algorithm;
|
|
20
|
+
* 3. the Ed25519 signature over the JWS signing input;
|
|
21
|
+
* 4. `iss` is `initiative-billing`, `listing_uid` is the plug-in's own uid,
|
|
22
|
+
* `version` is a whole number of one or more and `values` an object.
|
|
23
|
+
*
|
|
24
|
+
* Only then are `version` and `values` handed on, taken from the verified
|
|
25
|
+
* claims and nothing else.
|
|
26
|
+
*
|
|
27
|
+
* One gap stays, by design: `community_ref` is billing's pairwise reference,
|
|
28
|
+
* which the plug-in cannot relate to the community it is told the statement is
|
|
29
|
+
* for. Initiative vouches for that mapping, so a compromised Initiative could
|
|
30
|
+
* at worst copy a real, signed statement onto another community.
|
|
31
|
+
*/
|
|
32
|
+
import { createPublicKey, verify } from "node:crypto";
|
|
33
|
+
/** The environment variable the deployment fills with billing's statement key (SPKI PEM). */
|
|
34
|
+
export const ENTITLEMENT_ISSUER_KEY_ENV = "INITIATIVE_ENTITLEMENT_ISSUER_KEY_PEM";
|
|
35
|
+
/** The runtime fact that names billing's statement key. */
|
|
36
|
+
export const ENTITLEMENT_ISSUER_KEY_FACT = "entitlement_issuer_key";
|
|
37
|
+
/** Who signs every statement. */
|
|
38
|
+
export const ENTITLEMENT_ISSUER = "initiative-billing";
|
|
39
|
+
/** A statement's JOSE `typ`. */
|
|
40
|
+
export const ENTITLEMENT_STATEMENT_TYPE = "initiative-entitlement+jwt";
|
|
41
|
+
const REASONS = {
|
|
42
|
+
"no-key": `no entitlement issuer key is configured (${ENTITLEMENT_ISSUER_KEY_ENV})`,
|
|
43
|
+
"bad-key": `the entitlement issuer key (${ENTITLEMENT_ISSUER_KEY_ENV}) is not an Ed25519 public key`,
|
|
44
|
+
malformed: "the statement is not a compact JWS",
|
|
45
|
+
alg: "the statement is not signed EdDSA",
|
|
46
|
+
typ: `the statement's typ is not ${ENTITLEMENT_STATEMENT_TYPE}`,
|
|
47
|
+
signature: "the statement's signature does not verify under the issuer key",
|
|
48
|
+
iss: `the statement is not issued by ${ENTITLEMENT_ISSUER}`,
|
|
49
|
+
listing: "the statement is for another plug-in",
|
|
50
|
+
claims: "the statement's version or values are not well formed",
|
|
51
|
+
};
|
|
52
|
+
/** A refused statement: `reason` is the counted outcome, `message` says it in words. */
|
|
53
|
+
export class EntitlementStatementError extends Error {
|
|
54
|
+
reason;
|
|
55
|
+
constructor(reason) {
|
|
56
|
+
super(REASONS[reason]);
|
|
57
|
+
this.reason = reason;
|
|
58
|
+
this.name = "EntitlementStatementError";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Checks billing's entitlement statements for one plug-in. The server makes one
|
|
63
|
+
* for its hook and its client; give one to an `Initiative` made by hand to read
|
|
64
|
+
* `client.entitlement()`.
|
|
65
|
+
*/
|
|
66
|
+
export class EntitlementStatements {
|
|
67
|
+
key;
|
|
68
|
+
listingUid;
|
|
69
|
+
warn;
|
|
70
|
+
warned = false;
|
|
71
|
+
constructor(options) {
|
|
72
|
+
if (!options.listingUid)
|
|
73
|
+
throw new TypeError("listingUid is the plug-in's own listing uid");
|
|
74
|
+
this.listingUid = options.listingUid;
|
|
75
|
+
this.warn = options.warn ?? ((message) => console.warn(message));
|
|
76
|
+
const pem = options.issuerKeyPem === undefined ? process.env[ENTITLEMENT_ISSUER_KEY_ENV] : options.issuerKeyPem;
|
|
77
|
+
this.key = issuerKey(pem);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The statement's `version` and `values`, once it verifies, for the community
|
|
81
|
+
* Initiative names (`installation`, used only to count new unlimited grants).
|
|
82
|
+
* Throws {@link EntitlementStatementError} when it is refused.
|
|
83
|
+
*/
|
|
84
|
+
verify(statement, installation) {
|
|
85
|
+
let verified;
|
|
86
|
+
try {
|
|
87
|
+
verified = this.check(statement);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (error instanceof EntitlementStatementError)
|
|
91
|
+
count(`refused-${error.reason}`);
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
count("applied");
|
|
95
|
+
noteAllowance(this.listingUid, installation, verified.values);
|
|
96
|
+
return verified;
|
|
97
|
+
}
|
|
98
|
+
check(statement) {
|
|
99
|
+
if (typeof this.key === "string") {
|
|
100
|
+
if (!this.warned) {
|
|
101
|
+
this.warned = true;
|
|
102
|
+
this.warn(`entitlement statements are all refused: ${REASONS[this.key]}`);
|
|
103
|
+
}
|
|
104
|
+
throw new EntitlementStatementError(this.key);
|
|
105
|
+
}
|
|
106
|
+
const parts = typeof statement === "string" ? statement.split(".") : [];
|
|
107
|
+
// The signature may be empty here, as an unsigned (`alg: none`) token's is: the alg check refuses it by name.
|
|
108
|
+
if (parts.length !== 3 || !/^[A-Za-z0-9_-]+$/.test(parts[0]) || !/^[A-Za-z0-9_-]+$/.test(parts[1]) || !/^[A-Za-z0-9_-]*$/.test(parts[2])) {
|
|
109
|
+
throw new EntitlementStatementError("malformed");
|
|
110
|
+
}
|
|
111
|
+
const header = jsonOf(parts[0]);
|
|
112
|
+
if (!isRecord(header))
|
|
113
|
+
throw new EntitlementStatementError("malformed");
|
|
114
|
+
if (header.alg !== "EdDSA")
|
|
115
|
+
throw new EntitlementStatementError("alg");
|
|
116
|
+
if (header.typ !== ENTITLEMENT_STATEMENT_TYPE)
|
|
117
|
+
throw new EntitlementStatementError("typ");
|
|
118
|
+
const signature = Buffer.from(parts[2], "base64url");
|
|
119
|
+
if (!verify(null, Buffer.from(`${parts[0]}.${parts[1]}`), this.key, signature)) {
|
|
120
|
+
throw new EntitlementStatementError("signature");
|
|
121
|
+
}
|
|
122
|
+
const claims = jsonOf(parts[1]);
|
|
123
|
+
if (!isRecord(claims))
|
|
124
|
+
throw new EntitlementStatementError("claims");
|
|
125
|
+
if (claims.iss !== ENTITLEMENT_ISSUER)
|
|
126
|
+
throw new EntitlementStatementError("iss");
|
|
127
|
+
if (claims.listing_uid !== this.listingUid)
|
|
128
|
+
throw new EntitlementStatementError("listing");
|
|
129
|
+
const { version, values } = claims;
|
|
130
|
+
if (typeof version !== "number" || !Number.isSafeInteger(version) || version < 1 || !isRecord(values)) {
|
|
131
|
+
throw new EntitlementStatementError("claims");
|
|
132
|
+
}
|
|
133
|
+
return { version, values };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function issuerKey(pem) {
|
|
137
|
+
if (!pem)
|
|
138
|
+
return "no-key";
|
|
139
|
+
try {
|
|
140
|
+
const text = pem.includes("-----BEGIN") ? pem.replaceAll("\\n", "\n") : Buffer.from(pem, "base64").toString("utf-8");
|
|
141
|
+
const key = createPublicKey(text);
|
|
142
|
+
return key.asymmetricKeyType === "ed25519" ? key : "bad-key";
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return "bad-key";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function jsonOf(part) {
|
|
149
|
+
try {
|
|
150
|
+
return JSON.parse(Buffer.from(part, "base64url").toString("utf-8"));
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function isRecord(value) {
|
|
157
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
158
|
+
}
|
|
159
|
+
// Metrics, for alerts only: nothing here refuses a statement.
|
|
160
|
+
const outcomes = new Map();
|
|
161
|
+
let unlimitedGrants = 0;
|
|
162
|
+
/** Whether each community's last verified statement granted an unlimited allowance, by listing and community. */
|
|
163
|
+
const unlimited = new Map();
|
|
164
|
+
function count(outcome) {
|
|
165
|
+
outcomes.set(outcome, (outcomes.get(outcome) ?? 0) + 1);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Count a grant of an unlimited allowance (`monthly_allowance` null) for a
|
|
169
|
+
* community whose last statement this process saw did not grant one. The
|
|
170
|
+
* memory is the process's own, so the first statement after a start counts
|
|
171
|
+
* when it grants one.
|
|
172
|
+
*/
|
|
173
|
+
function noteAllowance(listingUid, installation, values) {
|
|
174
|
+
const key = `${listingUid}\u0000${installation}`;
|
|
175
|
+
const isUnlimited = "monthly_allowance" in values && values.monthly_allowance === null;
|
|
176
|
+
if (isUnlimited && unlimited.get(key) !== true)
|
|
177
|
+
unlimitedGrants += 1;
|
|
178
|
+
unlimited.set(key, isUnlimited);
|
|
179
|
+
}
|
|
180
|
+
/** A snapshot of the entitlement statement counters, for the plug-in's metrics endpoint. */
|
|
181
|
+
export function entitlementMetrics() {
|
|
182
|
+
return { statements: Object.fromEntries([...outcomes].sort(([a], [b]) => a.localeCompare(b))), unlimitedGrants };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The same counters in the Prometheus text format, to append to what the
|
|
186
|
+
* plug-in's metrics endpoint serves:
|
|
187
|
+
*
|
|
188
|
+
* - `initiative_plugin_entitlement_statements_total{outcome="…"}`
|
|
189
|
+
* - `initiative_plugin_entitlement_unlimited_grants_total`
|
|
190
|
+
*
|
|
191
|
+
* Alert on any `refused-*` and on any rise in unlimited grants.
|
|
192
|
+
*/
|
|
193
|
+
export function entitlementMetricsText() {
|
|
194
|
+
const { statements, unlimitedGrants: grants } = entitlementMetrics();
|
|
195
|
+
const lines = [
|
|
196
|
+
"# HELP initiative_plugin_entitlement_statements_total Entitlement statements, by outcome.",
|
|
197
|
+
"# TYPE initiative_plugin_entitlement_statements_total counter",
|
|
198
|
+
...Object.entries(statements).map(([outcome, value]) => `initiative_plugin_entitlement_statements_total{outcome="${outcome}"} ${value}`),
|
|
199
|
+
"# HELP initiative_plugin_entitlement_unlimited_grants_total Grants of an unlimited allowance to a community that did not have one.",
|
|
200
|
+
"# TYPE initiative_plugin_entitlement_unlimited_grants_total counter",
|
|
201
|
+
`initiative_plugin_entitlement_unlimited_grants_total ${grants}`,
|
|
202
|
+
];
|
|
203
|
+
return `${lines.join("\n")}\n`;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=statements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statements.js","sourceRoot":"","sources":["../src/statements.ts"],"names":[],"mappings":"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"}
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `initiative-plugin-sdk/testing`: a declarative plug-in's requests and
|
|
3
|
-
* run against recorded vendor answers, with no server and no network.
|
|
2
|
+
* `initiative-plugin-sdk/testing`: a declarative plug-in's requests, mappings and
|
|
3
|
+
* writes, run against recorded vendor answers, with no server and no network.
|
|
4
4
|
*
|
|
5
5
|
* Each runner renders the requests the way Initiative makes them (method,
|
|
6
6
|
* address with its query, headers, body), answers them from `responses` in
|
|
@@ -16,15 +16,28 @@
|
|
|
16
16
|
* to retry;
|
|
17
17
|
* - `{ refused }`: an `after_connect` that refused the connection.
|
|
18
18
|
*
|
|
19
|
+
* Every runner but {@link runExchangeBody}, whose expression reads only the
|
|
20
|
+
* connection's fields, takes the install's `settings`, each setting's value by
|
|
21
|
+
* key, and `aliases`, each unique setting's earlier values by key, which every
|
|
22
|
+
* other expression reads; both default to none.
|
|
23
|
+
*
|
|
24
|
+
* An action's run answers the `change` its `write` makes to the plug-in's values
|
|
25
|
+
* on the item, and a schedule's or a delivery's the targeted `writes`, each held
|
|
26
|
+
* to the contract's `change` or `targetedChange`, to one operation on each key,
|
|
27
|
+
* and to the plug-in's fields: a list field's whole value is a list of
|
|
28
|
+
* elements, and only a list's elements are upserted or removed. A `write`
|
|
29
|
+
* answering nothing changes nothing.
|
|
30
|
+
*
|
|
19
31
|
* A rendered request carries no credential: Initiative adds the one its
|
|
20
32
|
* `connection` names, as the plug-in's `auth` says. A predicate (`when`, `more`,
|
|
21
33
|
* `refuse_when`) holds when JSONata's `$boolean` of its answer is true.
|
|
22
34
|
*
|
|
23
35
|
* A run fails, rather than answering, on a mistake in the test or the plug-in: a
|
|
24
36
|
* request with no recorded response left for it, a recorded response nothing
|
|
25
|
-
* asked for,
|
|
37
|
+
* asked for, a mapped answer that does not fit the declared returns, or a
|
|
38
|
+
* `write` answering what is not a change.
|
|
26
39
|
*/
|
|
27
|
-
import type { ConnectionState } from "./contract.js";
|
|
40
|
+
import type { ActorKind, Change, ConnectionState, ItemKind, TargetedChange } from "./contract.js";
|
|
28
41
|
import type { AnyPlugin } from "./define.js";
|
|
29
42
|
export { evaluate, ExpressionError, parseExpression } from "./expression.js";
|
|
30
43
|
/** One answer the vendor gave, as a test records it. Status 200 and no headers when left out. */
|
|
@@ -39,7 +52,7 @@ export interface RenderedRequest {
|
|
|
39
52
|
url: string;
|
|
40
53
|
headers: Record<string, string>;
|
|
41
54
|
body?: unknown;
|
|
42
|
-
/** The connection whose credential Initiative would add. */
|
|
55
|
+
/** The connection whose credential Initiative would add, for the call's actor when the request names one per actor. */
|
|
43
56
|
connection?: string;
|
|
44
57
|
}
|
|
45
58
|
/** What a run made and what it answered. */
|
|
@@ -55,59 +68,157 @@ export type Run<T> = {
|
|
|
55
68
|
} | {
|
|
56
69
|
refused: string;
|
|
57
70
|
});
|
|
71
|
+
/** A declarative action's run: what it sent, and what its endpoint and its `write` answered. */
|
|
72
|
+
export type ActionRun = {
|
|
73
|
+
requests: RenderedRequest[];
|
|
74
|
+
} & ({
|
|
75
|
+
/** The endpoint's mapped answer, when the action names one. */
|
|
76
|
+
result?: Record<string, unknown>;
|
|
77
|
+
/** The change its `write` answered, when it has one that answered something. */
|
|
78
|
+
change?: Change;
|
|
79
|
+
} | {
|
|
80
|
+
unavailable: string;
|
|
81
|
+
detail?: string;
|
|
82
|
+
} | {
|
|
83
|
+
transient: true;
|
|
84
|
+
});
|
|
85
|
+
/** A declarative schedule's run: what it sent, the endpoint's mapped answer, and the changes its `write` answered. */
|
|
86
|
+
export type ScheduleRun = {
|
|
87
|
+
requests: RenderedRequest[];
|
|
88
|
+
} & ({
|
|
89
|
+
result: Record<string, unknown>;
|
|
90
|
+
writes: TargetedChange[];
|
|
91
|
+
} | {
|
|
92
|
+
unavailable: string;
|
|
93
|
+
detail?: string;
|
|
94
|
+
} | {
|
|
95
|
+
transient: true;
|
|
96
|
+
});
|
|
58
97
|
export interface Recorded {
|
|
59
98
|
/** The vendor's answers, in the order the requests are made. */
|
|
60
99
|
responses: RecordedResponse[];
|
|
61
100
|
/** The time of the call: `now`, and what `$now()` and `$millis()` answer. Default: the present. */
|
|
62
101
|
now?: Date | string | number;
|
|
63
102
|
}
|
|
103
|
+
/** The install's settings, as every expression reads them. */
|
|
104
|
+
export interface InstallSettings {
|
|
105
|
+
/** Each setting's value, by key. */
|
|
106
|
+
settings?: Record<string, string>;
|
|
107
|
+
/** Each unique setting's earlier values, by key. */
|
|
108
|
+
aliases?: Record<string, string[]>;
|
|
109
|
+
}
|
|
64
110
|
/**
|
|
65
111
|
* One declarative endpoint: its request or steps rendered from `params`, the
|
|
66
|
-
* `connections`' non-secret fields (keyed by connection id)
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* that
|
|
71
|
-
*
|
|
112
|
+
* `connections`' non-secret fields (keyed by connection id), the install's
|
|
113
|
+
* `settings` and `aliases`, and `now`, answered from `responses`, and its map
|
|
114
|
+
* held to the declared returns. The call is made as `actor`, by default the
|
|
115
|
+
* endpoint's first: a request naming a connection per actor carries the one it
|
|
116
|
+
* names for that actor, and the run throws a TypeError when it names none. An
|
|
117
|
+
* expression reads `connection`, the fields of the connection its request
|
|
118
|
+
* carries, and `connections`, the fields of each connection the endpoint's
|
|
119
|
+
* `requires` names that `connections` holds, by id. Who may make the call is
|
|
120
|
+
* Initiative's to decide.
|
|
72
121
|
*/
|
|
73
|
-
export declare function runEndpoint(plugin: AnyPlugin, name: string, call: Recorded & {
|
|
122
|
+
export declare function runEndpoint(plugin: AnyPlugin, name: string, call: Recorded & InstallSettings & {
|
|
74
123
|
params?: Record<string, unknown>;
|
|
75
124
|
connections?: Record<string, Record<string, unknown>>;
|
|
125
|
+
/** Who the call is made as. Default: the endpoint's first actor. */
|
|
126
|
+
actor?: ActorKind;
|
|
76
127
|
}): Promise<Run<Record<string, unknown>>>;
|
|
128
|
+
/**
|
|
129
|
+
* A declarative action, run on one item with the `params` the reader answered,
|
|
130
|
+
* from one of a list field's elements when `element` names its `ref`. Its
|
|
131
|
+
* endpoint, when it names one, is sent those params, made as `actor` (by
|
|
132
|
+
* default the endpoint's first), and rendered and answered as
|
|
133
|
+
* {@link runEndpoint} does it, its expressions also reading `item` and
|
|
134
|
+
* `element`; an answer of unavailable or transient is the run's, and nothing is
|
|
135
|
+
* written. Its `write`, when it has one, then answers the change Initiative
|
|
136
|
+
* makes to the plug-in's values on the item, reading `item`, `held` (those
|
|
137
|
+
* values as they stand, by key), `result` (the endpoint's mapped answer, absent
|
|
138
|
+
* with no endpoint), `params`, `element`, `settings`, `aliases` and `now`. Like
|
|
139
|
+
* the endpoint's, an expression that fails answers `mapping-failed`.
|
|
140
|
+
*/
|
|
141
|
+
export declare function runAction(plugin: AnyPlugin, name: string, call: Omit<Recorded, "responses"> & InstallSettings & {
|
|
142
|
+
/** The vendor's answers to the endpoint's calls; none when the action names no endpoint. */
|
|
143
|
+
responses?: RecordedResponse[];
|
|
144
|
+
item: {
|
|
145
|
+
kind: ItemKind;
|
|
146
|
+
id: number;
|
|
147
|
+
title: string;
|
|
148
|
+
};
|
|
149
|
+
held?: Record<string, unknown>;
|
|
150
|
+
/** What the reader answered, for the params the action asks for. */
|
|
151
|
+
params?: Record<string, unknown>;
|
|
152
|
+
/** The `ref` of the list element it was run from. */
|
|
153
|
+
element?: string;
|
|
154
|
+
connections?: Record<string, Record<string, unknown>>;
|
|
155
|
+
/** Who its endpoint's call is made as. Default: the endpoint's first actor. */
|
|
156
|
+
actor?: ActorKind;
|
|
157
|
+
}): Promise<ActionRun>;
|
|
158
|
+
/**
|
|
159
|
+
* A declarative schedule's run in one community, made as the installation. Its
|
|
160
|
+
* `params` answer the endpoint's params from `since`, `now`, `connection` (the
|
|
161
|
+
* fields of the connection the endpoint's first request names), `settings` and
|
|
162
|
+
* `aliases`; the endpoint is rendered and answered with them as
|
|
163
|
+
* {@link runEndpoint} does it; and its `write` answers, from the endpoint's
|
|
164
|
+
* mapped answer as `result` beside `since`, `now`, `settings` and `aliases`,
|
|
165
|
+
* the changes Initiative makes as the install. `since` is when the schedule
|
|
166
|
+
* last succeeded there, as ISO 8601: null, the default, the first time.
|
|
167
|
+
*/
|
|
168
|
+
export declare function runSchedule(plugin: AnyPlugin, name: string, call: Recorded & InstallSettings & {
|
|
169
|
+
since?: string | null;
|
|
170
|
+
connections?: Record<string, Record<string, unknown>>;
|
|
171
|
+
}): Promise<ScheduleRun>;
|
|
77
172
|
/**
|
|
78
173
|
* A declarative `after_connect`: its request or steps made with the flow's
|
|
79
174
|
* `params`, mapped, and refused when it says. The map and `refuse_when` read
|
|
80
175
|
* the last answer as `response` and, with steps, each one's as `steps.<name>`.
|
|
81
176
|
*/
|
|
82
|
-
export declare function runAfterConnect(plugin: AnyPlugin, connection: string, call: Recorded & {
|
|
177
|
+
export declare function runAfterConnect(plugin: AnyPlugin, connection: string, call: Recorded & InstallSettings & {
|
|
83
178
|
params?: Record<string, string>;
|
|
84
179
|
}): Promise<Run<{
|
|
85
180
|
values?: Record<string, unknown>;
|
|
86
181
|
account_label?: string;
|
|
87
182
|
}>>;
|
|
88
183
|
/** A connection's health check: its request made with the connection's non-secret `fields`, and the state it reads. */
|
|
89
|
-
export declare function runHealth(plugin: AnyPlugin, connection: string, call: Recorded & {
|
|
184
|
+
export declare function runHealth(plugin: AnyPlugin, connection: string, call: Recorded & InstallSettings & {
|
|
90
185
|
fields?: Record<string, unknown>;
|
|
91
186
|
}): Promise<Run<ConnectionState>>;
|
|
92
|
-
/**
|
|
187
|
+
/**
|
|
188
|
+
* The body Initiative posts with a connection's signed JWT, from its token's
|
|
189
|
+
* `exchange_body`: the object with each `{vendor.<key>}` in its strings filled
|
|
190
|
+
* from `vendor` and each `{<key>}` from the connection's non-secret `fields`,
|
|
191
|
+
* or the expression's answer, which reads those fields as `connection` and
|
|
192
|
+
* nothing else. It throws a TypeError where Initiative fails the token request,
|
|
193
|
+
* on a name with no value or an answer that is not an object, and for a
|
|
194
|
+
* connection with no `exchange_body`; an expression that fails throws
|
|
195
|
+
* {@link ExpressionError}.
|
|
196
|
+
*/
|
|
197
|
+
export declare function runExchangeBody(plugin: AnyPlugin, connection: string, call?: {
|
|
198
|
+
fields?: Record<string, unknown>;
|
|
199
|
+
vendor?: Record<string, string>;
|
|
200
|
+
}): Promise<Record<string, unknown>>;
|
|
201
|
+
/** What one delivery emits, the changes it makes, and the connection state it sets. */
|
|
93
202
|
export interface WebhookRun {
|
|
94
203
|
event?: {
|
|
95
204
|
emit: string;
|
|
96
205
|
payload: Record<string, unknown>;
|
|
97
206
|
};
|
|
207
|
+
/** The targeted changes the matching `events` row's `write` answered, when it has one. */
|
|
208
|
+
writes?: TargetedChange[];
|
|
98
209
|
status?: {
|
|
99
210
|
connection: string;
|
|
100
211
|
state: ConnectionState;
|
|
101
212
|
};
|
|
102
213
|
}
|
|
103
214
|
/**
|
|
104
|
-
* One webhook delivery, as a declarative plug-in maps it: the
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* sets. A delivery nothing matches answers neither.
|
|
108
|
-
* throws {@link ExpressionError}.
|
|
215
|
+
* One webhook delivery, as a declarative plug-in maps it: the first matching
|
|
216
|
+
* `events` row's event (by the emit endpoint's key, its payload held to that
|
|
217
|
+
* endpoint's returns) and the changes its `write` answers, and the state the
|
|
218
|
+
* first matching `status` row sets. A delivery nothing matches answers neither.
|
|
219
|
+
* An expression that fails throws {@link ExpressionError}.
|
|
109
220
|
*/
|
|
110
|
-
export declare function runWebhook(plugin: AnyPlugin, delivery: {
|
|
221
|
+
export declare function runWebhook(plugin: AnyPlugin, delivery: InstallSettings & {
|
|
111
222
|
headers?: Record<string, string>;
|
|
112
223
|
payload: unknown;
|
|
113
224
|
/** The routed connection's non-secret fields. */
|